diff --git a/backend/.env.example b/backend/.env.example index 065679f..e771b4e 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -17,8 +17,11 @@ GOOGLE_CLIENT_ID=your_google_client_id_here # MongoDB (used for scan history / analytics dashboard) MONGODB_URI=mongodb+srv://:@/?retryWrites=true&w=majority +# Default Admin +ADMIN_EMAIL=admin@example.com +ADMIN_PASSWORD=admin123 # IMAP scheduled scanning (issue #186) — used to encrypt stored inbox credentials at rest. # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" IMAP_ENCRYPTION_KEY= # Where the sqlite store for IMAP connections/scan history lives (defaults to backend/imap_connections.db) -IMAP_DB_PATH= \ No newline at end of file +IMAP_DB_PATH= diff --git a/backend/package-lock.json b/backend/package-lock.json index 68b4f52..dcdaff1 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -22,7 +22,8 @@ "jsonwebtoken": "^9.0.3", "mongoose": "^9.6.3", "multer": "^2.2.0", - "nodemon": "^3.1.14" + "nodemon": "^3.1.14", + "uuid": "^14.0.1" } }, "node_modules/@mongodb-js/saslprep": { @@ -3224,6 +3225,19 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/validator": { "version": "13.15.35", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", diff --git a/backend/package.json b/backend/package.json index 8fd4028..3485f34 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,6 +24,7 @@ "jsonwebtoken": "^9.0.3", "mongoose": "^9.6.3", "multer": "^2.2.0", - "nodemon": "^3.1.14" + "nodemon": "^3.1.14", + "uuid": "^14.0.1" } } diff --git a/backend/seeders/adminSeeder.js b/backend/seeders/adminSeeder.js new file mode 100644 index 0000000..88cabd7 --- /dev/null +++ b/backend/seeders/adminSeeder.js @@ -0,0 +1,26 @@ +const User = require('../models/User'); + +const seedAdminUser = async () => { + try{ + const adminExists = await User.findOne({ role: 'admin' }); + if(!adminExists){ + const email = process.env.ADMIN_EMAIL || 'admin@example.com'; + const password = process.env.ADMIN_PASSWORD || 'admin123'; + + await User.create({ + email, + password, + role: 'admin', + name: 'Admin' + }); + + conso;e.log('Admin user created successfully'); + cnsole.log(`Email: ${email}`); + console.log(`Password: ${password}`); + } + }catch(error){ + console.error('Error seeding admin user:', error); + } +}; + +module.exports = seedAdminUser; \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 38b25e7..fe84efa 100644 --- a/backend/server.js +++ b/backend/server.js @@ -5,8 +5,10 @@ const validateEnv = require('./utils/validateEnv'); validateEnv(); // Validate environment variables dns.setServers(["8.8.8.8", "1.1.1.1"]); // ensure SRV records resolve on all networks const express = require("express"); +const seedAdminUser = require("./seeders/adminSeeder"); const { getHealthStatus } = require('./utils/healthCheck'); const cors = require("cors"); +const { v4: uuidv4 } = require('uuid'); const axios = require("axios"); const mongoose = require("mongoose"); @@ -21,7 +23,10 @@ const app = express(); // Connect to MongoDB Atlas mongoose .connect(process.env.MONGODB_URI) - .then(() => console.log("✅ MongoDB connected")) + .then(() => { + console.log("✅ MongoDB connected"); + seedAdminUser(); // ✅ Inside .then() + }) .catch((err) => console.error("❌ MongoDB connection error:", err)); app.use(cors()); @@ -35,14 +40,46 @@ app.get('/health', (req, res) => { }); }); +// ===== REQUEST ID MIDDLEWARE ===== +app.use((req, res, next) => { + // Generate a unique request ID + const requestId = uuidv4().substring(0, 8); // Shorten the UUID for easier logging + req.requestId = requestId; + + //Add to response headers + res.setHeader('X-Request-ID', requestId); + + // Log the request with the request ID + console.log(`[${requestId}] ${req.method} ${req.originalUrl}`); + + //Track time + const startTime = Date.now(); + + //Log when response is finished + res.on('finish', () => { + const duration = Date.now() - start; + console.log(`[${requestId}] ⬅️ ${req.method} ${req.originalUrl} completed in ${duration}ms (${res.statusCode})`); + }); + + next(); +}); + // Auth routes , History routes const authRoutes = require("./routes/authRoutes"); const historyRoutes = require("./routes/historyRoutes"); const analyticsRoutes = require("./routes/analyticsRoutes"); const chatRoutes = require("./routes/chatRoutes"); + +// Versioned routes (v1) +app.use("/api/v1/auth", authRoutes); +app.use("/api/v1/history", historyRoutes); +app.use("/api/v1/analytics", analyticsRoutes); +app.use("/api/v1/chat", chatRoutes); + +// Keep old routes for backward compatibility app.use("/api/auth", authRoutes); app.use("/api/history", historyRoutes); -app.use("/analytics", analyticsRoutes); +app.use("/api/analytics", analyticsRoutes); app.use("/api/chat", chatRoutes); const { protect } = require("./middleware/authMiddleware"); @@ -134,7 +171,7 @@ app.post("/predict", protect, async (req, res) => { res.json(response.data); } catch (error) { - console.error(error.message); + console.error(`[${req.requestId}]`,error.message); res.status(500).json({ error: "Something went wrong" }); } }); diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..e649661 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,146 @@ +\# Spam Detection System - API Documentation + + + +\## Base URL + +http://localhost:3000/api/v1 + + + +\## Authentication + +All protected endpoints require a Bearer token: + +Authorization: Bearer + + + +\## Auth Routes + + + +| Method | Endpoint | Description | Auth | + +|--------|----------|-------------|------| + +| POST | `/auth/register` | Register new user | ❌ | + +| POST | `/auth/login` | Login user | ❌ | + +| GET | `/auth/profile` | Get user profile | ✅ | + + + +\--- + + + +\## Prediction Routes + + + +| Method | Endpoint | Description | Auth | + +|--------|----------|-------------|------| + +| POST | `/predict` | Predict spam/ham | ✅ | + +| POST | `/bulk-predict` | Bulk spam detection | ✅ | + +| POST | `/feedback` | Submit feedback | ✅ | + +| POST | `/analyze-email-header` | Analyze email headers | ✅ | + +| POST | `/bulk-predict/export` | Export predictions as CSV | ✅ | + + + +\--- + + + +\## Analytics Routes + + + +| Method | Endpoint | Description | Auth | + +|--------|----------|-------------|------| + +| GET | `/analytics/page-visits` | Page visit metrics | ✅ | + +| GET | `/analytics/visits-by-role` | Visits by role | ✅ | + + + +\--- + + + +\## Email Integration Routes + + + +| Method | Endpoint | Description | Auth | + +|--------|----------|-------------|------| + +| GET | `/gmail/auth-url` | Get Gmail auth URL | ✅ | + +| GET | `/gmail/emails` | Get Gmail emails | ✅ | + +| GET | `/outlook/auth-url` | Get Outlook auth URL | ✅ | + +| GET | `/outlook/emails` | Get Outlook emails | ✅ | + +| POST | `/scan-emails` | Scan connected emails | ✅ | + + + +\--- + + + +\## Public Routes + + + +| Method | Endpoint | Description | Auth | + +|--------|----------|-------------|------| + +| GET | `/health` | Health check | ❌ | + +| GET | `/api/wordcloud` | Word cloud data | ❌ | + +| GET | `/importance` | Feature importance | ❌ | + + + +\--- + + + +\## Response Format + + + +\### Success Response + +```json + +{ + + "success": true, + + "data": { ... } + +} + +``` + + + + +