Files
meal-tracker/server/index.js
2026-03-31 07:42:06 -04:00

77 lines
4.6 KiB
JavaScript

import express from 'express';
import cors from 'cors';
import path from 'path';
import Database from 'better-sqlite3';
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
app.use(express.static('./public'));
const db = new Database('./data/meal-tracker.db');
const users = { 1: { id: 1, username: 'admin', password: 'admin123', isAdmin: true }, 2: { id: 2, username: 'rob', password: 'meal123', isAdmin: false } };
function getUserId(req) { return req.headers['x-user-id'] || req.body.user_id || 1; }
function getLocalDate() {
const now = new Date();
const offset = now.getTimezoneOffset() * 60000;
return new Date(now.getTime() - offset).toISOString().split('T')[0];
}
app.get('/api/entries', (req, res) => {
const u = getUserId(req);
const date = req.query.date || getLocalDate();
// Use LIKE to match date at start (handles UTC vs local)
const dateLike = date + '%';
const entries = db.prepare('SELECT * FROM entries WHERE user_id = ? AND created_at LIKE ? ORDER BY created_at DESC').all(u, dateLike);
res.json(entries);
});
app.get('/api/entries/:id', (req, res) => {
const entry = db.prepare('SELECT * FROM entries WHERE id = ? AND user_id = ?').get(req.params.id, getUserId(req));
if (!entry) return res.status(404).json({error: 'Entry not found'});
res.json(entry);
});
app.get('/api/summary/daily', (req, res) => {
const u = getUserId(req);
const date = req.query.date || getLocalDate();
const dateLike = date + '%';
const s = db.prepare('SELECT COALESCE(SUM(calories),0) as total_calories, COALESCE(SUM(protein),0) as total_protein, COALESCE(SUM(carbs),0) as total_carbs, COALESCE(SUM(fat),0) as total_fat FROM entries WHERE user_id = ? AND created_at LIKE ?').get(u, dateLike);
res.json(s);
});
app.get('/api/favorites', (req, res) => res.json(db.prepare('SELECT * FROM entries WHERE favorite = 1 AND user_id = ?').all(getUserId(req))));
app.get('/api/goals', (req, res) => res.json({calories: 2000, protein: 150, carbs: 250, fat: 65}));
app.get('/api/streak', (req, res) => res.json({currentStreak: 0, longestStreak: 0}));
app.post('/api/entries', (req, res) => {
const userId = getUserId(req);
const { name, description, notes, calories, protein, carbs, fat, meal_time, image_url } = req.body;
if (!name) return res.status(400).json({error: 'Name required'});
const result = db.prepare('INSERT INTO entries (name, description, notes, calories, protein, carbs, fat, meal_time, image_url, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').run(
name, description||null, notes||null, Number(calories)||0, Number(protein)||0, Number(carbs)||0, Number(fat)||0, meal_time||null, image_url||null, userId
);
res.json({ id: result.lastInsertRowid, name, calories, protein, carbs, fat });
});
app.post('/api/entries/:id/favorite', (req, res) => { db.prepare('UPDATE entries SET favorite = CASE WHEN favorite = 1 THEN 0 ELSE 1 END WHERE id = ? AND user_id = ?').run(req.params.id, getUserId(req)); res.json({success: true}); });
app.delete('/api/entries/:id', (req, res) => { db.prepare('DELETE FROM entries WHERE id = ? AND user_id = ?').run(req.params.id, getUserId(req)); res.json({success: true}); });
app.post('/api/auth/login', (req, res) => { const {username, password} = req.body; const u = Object.values(users).find(x => x.username === username && x.password === password); if(u) res.json({id: u.id, username: u.username, isAdmin: u.isAdmin}); else res.status(401).json({error: 'Invalid credentials'}); });
app.post('/api/auth/register', (req, res) => res.json({id: 3, username: req.body.username, isAdmin: false}));
app.get('/api/auth/me', (req, res) => { const u = users[req.headers['x-user-id']]; if(u) res.json({id: u.id, username: u.username, isAdmin: u.isAdmin}); else res.status(401).json({error: 'Not authenticated'}); });
app.get('/api/admin/users', (req, res) => { if(getUserId(req)!=1) return res.status(403).json({error:'Admin only'}); res.json(Object.values(users).map(u=>({id:u.id, username:u.username, isAdmin:u.isAdmin}))); });
app.delete('/api/admin/users/:id', (req, res) => res.json({success:true}));
app.post('/api/admin/users/:id/reset-password', (req, res) => { if(users[req.params.id]) { users[req.params.id].password = req.body.password; res.json({success:true}); } else res.status(404).json({error:'Not found'}); });
app.post('/api/admin/users', (req, res) => res.json({id:3, username:req.body.username, isAdmin:false}));
app.get('*', (req, res) => res.sendFile(path.join(process.cwd(), 'public', 'index.html')));
app.listen(PORT, '0.0.0.0', () => console.log('Meal Tracker running on port ' + PORT));