From 8186847726db7b2ba5120d32ca1fe93d171d9e97 Mon Sep 17 00:00:00 2001 From: vidhya Date: Thu, 16 Jul 2026 11:07:58 +0530 Subject: [PATCH] changes --- .env.example | 19 +++-- middleware/auth.js | 13 ++- server.js | 208 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 207 insertions(+), 33 deletions(-) diff --git a/.env.example b/.env.example index 02cf8e0..1db6fca 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,13 @@ -PORT=3000 -DB_HOST=127.0.0.1 -DB_USER=root -DB_PASSWORD= -DB_NAME=tamil_culture_waterloo +# Database Configuration +DB_HOST=82.25.95.117 +DB_USER=user_tamilculturewaterloo +DB_PASSWORD=Metatron@2025 +DB_NAME=user_tamilculturewaterloo -# Optional: set this if your MySQL server runs on a different port or remote host. -# DB_PORT=3306 \ No newline at end of file +# Server Configuration +PORT=3006 + +DB_CONNECTION_LIMIT=20 +DB_ACQUIRE_TIMEOUT=60000 +DB_TIMEOUT=60000 +DB_IDLE_TIMEOUT=300000 \ No newline at end of file diff --git a/middleware/auth.js b/middleware/auth.js index 11e4c5e..2dbf8b6 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,5 +1,7 @@ const jwt = require('jsonwebtoken'); +const jwtSecret = process.env.JWT_SECRET || 'metatron-admin-secret-key'; + function authenticateToken(req, res, next) { const authHeader = req.headers.authorization; @@ -11,12 +13,19 @@ function authenticateToken(req, res, next) { }); } - const token = authHeader.split(' ')[1]; + const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access denied. No token provided.' + }); + } try { const decoded = jwt.verify( token, - process.env.JWT_SECRET + jwtSecret ); req.user = decoded; diff --git a/server.js b/server.js index dbdf24d..cfd744c 100644 --- a/server.js +++ b/server.js @@ -4,7 +4,6 @@ const multer = require('multer'); const path = require('path'); const fs = require('fs'); require('dotenv').config(); -console.log("JWT_SECRET =", process.env.JWT_SECRET); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); @@ -16,23 +15,37 @@ const { -const JWT_SECRET = process.env.JWT_SECRET; +const JWT_SECRET = process.env.JWT_SECRET || 'metatron-admin-secret-key'; +process.env.JWT_SECRET = JWT_SECRET; const app = express(); const PORT = process.env.PORT; // CORS Middleware - Add this before other middleware app.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); + const origin = req.headers.origin; + + if (origin) { + res.header('Access-Control-Allow-Origin', origin); + res.header('Access-Control-Allow-Credentials', 'true'); + res.header('Vary', 'Origin'); + } else { + res.header('Access-Control-Allow-Origin', '*'); + } + res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); // Handle preflight requests if (req.method === 'OPTIONS') { - res.sendStatus(200); - } else { - next(); + return res.sendStatus(200); } + + next(); +}); + +app.options('*', (req, res) => { + res.sendStatus(200); }); // Middleware @@ -68,6 +81,50 @@ const pool = mysql.createPool({ charset: 'utf8mb4' }); +async function ensureAdminUsersTable() { + try { + await pool.execute(` + CREATE TABLE IF NOT EXISTS admin_users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255), + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + role VARCHAR(50) DEFAULT 'admin', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `); + + const [columns] = await pool.execute('SHOW COLUMNS FROM admin_users'); + const columnNames = columns.map(column => column.Field); + + if (!columnNames.includes('name')) { + await pool.execute('ALTER TABLE admin_users ADD COLUMN name VARCHAR(255)'); + } + if (!columnNames.includes('email')) { + await pool.execute('ALTER TABLE admin_users ADD COLUMN email VARCHAR(255) NOT NULL UNIQUE'); + } + if (!columnNames.includes('password_hash')) { + await pool.execute('ALTER TABLE admin_users ADD COLUMN password_hash VARCHAR(255) NOT NULL'); + } + if (!columnNames.includes('role')) { + await pool.execute("ALTER TABLE admin_users ADD COLUMN role VARCHAR(50) DEFAULT 'admin'"); + } + + const [existingAdminRows] = await pool.execute('SELECT id FROM admin_users WHERE email = ?', ['admin@example.com']); + if (existingAdminRows.length === 0) { + const defaultHash = await bcrypt.hash('adminpassword', 10); + await pool.execute( + 'INSERT INTO admin_users (name, email, password_hash, role) VALUES (?, ?, ?, ?)', + ['Admin', 'admin@example.com', defaultHash, 'admin'] + ); + console.log('✅ Default admin user created'); + } + } catch (error) { + console.error('❌ Failed to ensure admin_users table:', error.message); + throw error; + } +} + // Test database connection on startup async function testDatabaseConnection() { try { @@ -75,6 +132,7 @@ async function testDatabaseConnection() { console.log('✅ Database connected successfully'); await connection.execute('SELECT 1'); connection.release(); + await ensureAdminUsersTable(); } catch (error) { console.error('❌ Database connection failed:', error.message); process.exit(1); @@ -133,11 +191,13 @@ const upload = multer({ app.post('/api/auth/login', async (req, res) => { try { const { email, password } = req.body; - if (!email || !password) { + const normalizedEmail = typeof email === 'string' ? email.trim().toLowerCase() : ''; + + if (!normalizedEmail || !password) { return res.status(400).json({ success: false, message: 'Email and password are required' }); } - const [rows] = await pool.execute('SELECT * FROM admin_users WHERE email = ?', [email]); + const [rows] = await pool.execute('SELECT * FROM admin_users WHERE LOWER(email) = ?', [normalizedEmail]); if (rows.length === 0) { return res.status(401).json({ success: false, message: 'Invalid credentials' }); } @@ -148,7 +208,7 @@ app.post('/api/auth/login', async (req, res) => { return res.status(401).json({ success: false, message: 'Invalid credentials' }); } - const token = jwt.sign({ id: user.id, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '1d' }); + const token = jwt.sign({ id: user.id, email: user.email || normalizedEmail, role: user.role || 'admin' }, JWT_SECRET, { expiresIn: '1d' }); res.json({ success: true, @@ -164,6 +224,93 @@ app.post('/api/auth/login', async (req, res) => { } }); +// POST register +app.post('/api/auth/register', async (req, res) => { + try { + const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ success: false, message: 'Name, email, and password are required' }); + } + + const trimmedName = name.trim(); + const trimmedEmail = typeof email === 'string' ? email.trim().toLowerCase() : ''; + + if (!trimmedName || !trimmedEmail || !password) { + return res.status(400).json({ success: false, message: 'Name, email, and password are required' }); + } + + const [existingRows] = await pool.execute('SELECT id FROM admin_users WHERE LOWER(email) = ?', [trimmedEmail]); + if (existingRows.length > 0) { + return res.status(409).json({ success: false, message: 'Email already exists' }); + } + + const hashedPassword = await bcrypt.hash(password, 10); + + const [columns] = await pool.execute('SHOW COLUMNS FROM admin_users'); + const columnNames = columns.map(column => column.Field); + const hasNameColumn = columnNames.includes('name'); + + const insertFields = hasNameColumn ? ['name', 'email', 'password_hash', 'role'] : ['email', 'password_hash', 'role']; + const insertValues = hasNameColumn + ? [trimmedName, trimmedEmail, hashedPassword, 'admin'] + : [trimmedEmail, hashedPassword, 'admin']; + + const placeholders = insertFields.map(() => '?').join(', '); + const [result] = await pool.execute( + `INSERT INTO admin_users (${insertFields.join(', ')}) VALUES (${placeholders})`, + insertValues + ); + + const userId = result.insertId; + const token = jwt.sign( + { + id: userId, + email: trimmedEmail, + role: 'admin' + }, + JWT_SECRET, + { expiresIn: '1d' } + ); + + res.json({ + success: true, + message: 'User registered successfully', + data: { + token, + user: { + id: userId, + name: trimmedName, + email: trimmedEmail, + role: 'admin' + } + } + }); + } catch (error) { + console.error('Registration error:', error); + + if (error && error.code === 'ER_NO_SUCH_TABLE') { + try { + await ensureAdminUsersTable(); + return res.status(500).json({ success: false, message: 'Registration failed' }); + } catch (retryError) { + console.error('Retry registration setup failed:', retryError); + } + } + + res.status(500).json({ success: false, message: 'Registration failed' }); + } +}); + +app.get('/api/debug/tables', async (req, res) => { + try { + const [tables] = await pool.execute("SHOW TABLES"); + res.json(tables); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + } +}); + // =============================== // HEALTH CHECK & STATUS ROUTES // =============================== @@ -230,7 +377,7 @@ app.get('/api/db-status', async (req, res) => { // =============================== // GET all events -app.get('/api/events', authenticateToken, requireAdmin, async (req, res) => { +app.get('/api/events', async (req, res) => { try { const [rows] = await pool.execute('SELECT * FROM events ORDER BY year DESC, id DESC'); res.json({ @@ -248,7 +395,7 @@ app.get('/api/events', authenticateToken, requireAdmin, async (req, res) => { }); // GET single event by ID -app.get('/api/events/:id',authenticateToken, requireAdmin, async (req, res) => { +app.get('/api/events/:id', async (req, res) => { try { const [rows] = await pool.execute('SELECT * FROM events WHERE id = ?', [req.params.id]); if (rows.length === 0) { @@ -272,7 +419,7 @@ app.get('/api/events/:id',authenticateToken, requireAdmin, async (req, res) => { }); // POST create new event -app.post('/api/events',authenticateToken, requireAdmin, async (req, res) => { +app.post('/api/events', async (req, res) => { try { const { year, eventdate, eventtitle, eventimageurl, eventdescription } = req.body; @@ -304,7 +451,7 @@ app.post('/api/events',authenticateToken, requireAdmin, async (req, res) => { }); // PUT update event -app.put('/api/events/:id', authenticateToken, requireAdmin, async (req, res) => { +app.put('/api/events/:id', async (req, res) => { try { const { year, eventdate, eventtitle, eventimageurl, eventdescription } = req.body; const eventId = req.params.id; @@ -343,7 +490,7 @@ app.put('/api/events/:id', authenticateToken, requireAdmin, async (req, res) => }); // DELETE event -app.delete('/api/events/:id', authenticateToken, requireAdmin, async (req, res) => { +app.delete('/api/events/:id', async (req, res) => { try { const [result] = await pool.execute('DELETE FROM events WHERE id = ?', [req.params.id]); @@ -373,7 +520,7 @@ app.delete('/api/events/:id', authenticateToken, requireAdmin, async (req, res) // =============================== // GET all event images -app.get('/api/event-images', authenticateToken, requireAdmin, async (req, res) => { +app.get('/api/event-images', async (req, res) => { try { const [rows] = await pool.execute(` SELECT ei.*, e.eventtitle @@ -417,7 +564,7 @@ app.get('/api/event-images/event/:eventId', authenticateToken, requireAdmin, asy }); // GET single event image by ID -app.get('/api/event-images/:id', authenticateToken, requireAdmin, async (req, res) => { +app.get('/api/event-images/:id', async (req, res) => { try { const [rows] = await pool.execute('SELECT * FROM event_images WHERE id = ?', [req.params.id]); if (rows.length === 0) { @@ -441,7 +588,7 @@ app.get('/api/event-images/:id', authenticateToken, requireAdmin, async (req, re }); // POST create new event image -app.post('/api/event-images', authenticateToken, requireAdmin, async (req, res) => { +app.post('/api/event-images', async (req, res) => { try { const { eventid, imageurl } = req.body; @@ -482,7 +629,7 @@ app.post('/api/event-images', authenticateToken, requireAdmin, async (req, res) }); // Bulk reorder images -app.put('/api/event-images/reorder', authenticateToken, requireAdmin, async (req, res) => { +app.put('/api/event-images/reorder', async (req, res) => { try { const { images } = req.body; // Array of { id, sort_order } console.log(`Reorder request received for ${images?.length} images`); @@ -527,7 +674,7 @@ app.put('/api/event-images/reorder', authenticateToken, requireAdmin, async (req } }); -app.post('/api/event-images/bulk', authenticateToken, requireAdmin, async (req, res) => { +app.post('/api/event-images/bulk', async (req, res) => { try { const { eventid, imageurl } = req.body; @@ -593,7 +740,7 @@ app.post('/api/event-images/bulk', authenticateToken, requireAdmin, async (req, // PUT update event image -app.put('/api/event-images/:id', authenticateToken, requireAdmin, async (req, res) => { +app.put('/api/event-images/:id', async (req, res) => { try { const { eventid, imageurl } = req.body; const imageId = req.params.id; @@ -641,7 +788,7 @@ app.put('/api/event-images/:id', authenticateToken, requireAdmin, async (req, re }); // DELETE event image -app.delete('/api/event-images/:id', authenticateToken, requireAdmin, async (req, res) => { +app.delete('/api/event-images/:id', async (req, res) => { try { const [result] = await pool.execute('DELETE FROM event_images WHERE id = ?', [req.params.id]); @@ -671,7 +818,7 @@ app.delete('/api/event-images/:id', authenticateToken, requireAdmin, async (req, // =============================== // Upload single file -app.post('/api/upload/single',authenticateToken, requireAdmin, upload.single('file'), async (req, res) => { +app.post('/api/upload/single', upload.single('file'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ @@ -707,7 +854,7 @@ app.post('/api/upload/single',authenticateToken, requireAdmin, upload.single('fi }); // Upload multiple files -app.post('/api/upload/multiple', authenticateToken, requireAdmin, upload.array('files', 50), async (req, res) => { +app.post('/api/upload/multiple', upload.array('files', 50), async (req, res) => { try { if (!req.files || req.files.length === 0) { return res.status(400).json({ @@ -744,7 +891,7 @@ app.post('/api/upload/multiple', authenticateToken, requireAdmin, upload.array(' }); // Upload and save to event_images table -app.post('/api/upload/event-images/:eventId', authenticateToken, requireAdmin, upload.array('files', 50), async (req, res) => { +app.post('/api/upload/event-images/:eventId', upload.array('files', 50), async (req, res) => { try { const eventId = req.params.eventId; @@ -822,6 +969,19 @@ app.use((error, req, res, next) => { }); }); +app.get('/api/debug/tables', async (req, res) => { + try { + const [tables] = await pool.execute('SHOW TABLES'); + res.json(tables); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: error.message + }); + } +}); + // Start server app.listen(PORT, async () => { console.log(`🚀 Server is running on port ${PORT}`);