changes
This commit is contained in:
parent
cd2b39726b
commit
8186847726
19
.env.example
19
.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
|
||||
# Server Configuration
|
||||
PORT=3006
|
||||
|
||||
DB_CONNECTION_LIMIT=20
|
||||
DB_ACQUIRE_TIMEOUT=60000
|
||||
DB_TIMEOUT=60000
|
||||
DB_IDLE_TIMEOUT=300000
|
||||
@ -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
208
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}`);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user