Meal Tracker - full feature set with auth, favorites, admin panel
This commit is contained in:
97
server/routes/admin.js
Normal file
97
server/routes/admin.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import { getDb } from '../models/db.js';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
function isAdmin(req, res, next) {
|
||||
const userId = req.headers['x-user-id'];
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
const user = db.prepare('SELECT username FROM users WHERE id = ?').get(userId);
|
||||
|
||||
if (!user || user.username !== 'admin') {
|
||||
return res.status(403).json({ error: 'Admin access required' });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export default function setupRoutes(app) {
|
||||
// Get all users
|
||||
app.get('/api/admin/users', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
const users = db.prepare('SELECT id, username, created_at FROM users ORDER BY created_at DESC').all();
|
||||
res.json(users);
|
||||
});
|
||||
|
||||
// Get all entries (admin view)
|
||||
app.get('/api/admin/entries', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
const entries = db.prepare(`
|
||||
SELECT e.*, u.username
|
||||
FROM entries e
|
||||
JOIN users u ON e.user_id = u.id
|
||||
ORDER BY e.created_at DESC
|
||||
LIMIT 100
|
||||
`).all();
|
||||
res.json(entries);
|
||||
});
|
||||
|
||||
// Get stats
|
||||
app.get('/api/admin/stats', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
|
||||
const totalUsers = db.prepare('SELECT COUNT(*) as count FROM users').get().count;
|
||||
const totalEntries = db.prepare('SELECT COUNT(*) as count FROM entries').get().count;
|
||||
const todayEntries = db.prepare("SELECT COUNT(*) as count FROM entries WHERE date(created_at) = date('now')").get().count;
|
||||
const topFoods = db.prepare(`SELECT name, COUNT(*) as count FROM entries GROUP BY name ORDER BY count DESC LIMIT 5`).all();
|
||||
const todayCalories = db.prepare("SELECT COALESCE(SUM(calories), 0) as total FROM entries WHERE date(created_at) = date('now')").get().total;
|
||||
|
||||
res.json({ totalUsers, totalEntries, todayEntries, todayCalories, topFoods });
|
||||
});
|
||||
|
||||
// Get activity log
|
||||
app.get('/api/admin/activity', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
const limit = parseInt(req.query.limit) || 50;
|
||||
const activity = db.prepare(`
|
||||
SELECT al.*, u.username
|
||||
FROM activity_log al
|
||||
LEFT JOIN users u ON al.user_id = u.id
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ?
|
||||
`).all(limit);
|
||||
res.json(activity);
|
||||
});
|
||||
|
||||
// Delete user
|
||||
app.delete('/api/admin/users/:id', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
const { id } = req.params;
|
||||
const userId = req.headers['x-user-id'];
|
||||
|
||||
if (userId == id) {
|
||||
return res.status(400).json({ error: 'Cannot delete yourself' });
|
||||
}
|
||||
|
||||
db.prepare('DELETE FROM entries WHERE user_id = ?').run(id);
|
||||
db.prepare('DELETE FROM activity_log WHERE user_id = ?').run(id);
|
||||
db.prepare('DELETE FROM users WHERE id = ?').run(id);
|
||||
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// Reset password
|
||||
app.post('/api/admin/users/:id/reset-password', isAdmin, (req, res) => {
|
||||
const db = getDb();
|
||||
const { id } = req.params;
|
||||
const { password } = req.body;
|
||||
|
||||
const hash = bcrypt.hashSync(password, 10);
|
||||
db.prepare('UPDATE users SET password = ? WHERE id = ?').run(hash, id);
|
||||
db.prepare('INSERT INTO activity_log (user_id, action, details) VALUES (?, ?, ?)').run(id, 'password_reset', 'Password reset by admin');
|
||||
|
||||
res.json({ success: true });
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user