This commit is contained in:
Vidhya 2026-07-16 11:07:58 +05:30
parent cd2b39726b
commit 8186847726
3 changed files with 207 additions and 33 deletions

View File

@ -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
# Server Configuration
PORT=3006
DB_CONNECTION_LIMIT=20
DB_ACQUIRE_TIMEOUT=60000
DB_TIMEOUT=60000
DB_IDLE_TIMEOUT=300000

View File

@ -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;

208
server.js
View File

@ -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}`);