Files
Nutrition-Management/server/index.js
T
2026-06-09 10:36:35 +08:00

217 lines
6.5 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const mysql = require('mysql2/promise');
const app = express();
app.use(cors());
app.use(express.json());
const dbConfig = {
host: process.env.DB_HOST || 'db',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_NAME || 'nutrition',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
};
let pool;
// Wait for DB to be ready
const connectDB = async () => {
let retries = 5;
while (retries) {
try {
pool = mysql.createPool(dbConfig);
await pool.query('SELECT 1');
console.log('Connected to MariaDB');
// Automatically initialize tables if they don't exist
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(50) PRIMARY KEY,
height FLOAT NOT NULL,
weight FLOAT NOT NULL,
age INT NOT NULL,
gender VARCHAR(10) NOT NULL,
activityLevel VARCHAR(20) NOT NULL,
goal VARCHAR(20) NOT NULL,
bmi FLOAT NOT NULL,
tdee INT NOT NULL,
targetCalories INT NOT NULL,
targetProtein INT NOT NULL,
targetFat INT NOT NULL,
targetCarbs INT NOT NULL,
targetWater INT NOT NULL
)
`);
await pool.query(`
CREATE TABLE IF NOT EXISTS daily_logs (
date DATE NOT NULL,
user_id VARCHAR(50) NOT NULL,
waterIntake INT DEFAULT 0,
weight FLOAT DEFAULT NULL,
PRIMARY KEY (date, user_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
`);
await pool.query(`
CREATE TABLE IF NOT EXISTS meals (
id VARCHAR(50) PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
date DATE NOT NULL,
name VARCHAR(100) NOT NULL,
time VARCHAR(10) NOT NULL,
calories INT NOT NULL,
protein INT DEFAULT 0,
fat INT DEFAULT 0,
carbs INT DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
`);
console.log('Database tables verified/created');
break;
} catch (err) {
console.error('Failed to connect to MariaDB, retrying...', err);
retries -= 1;
await new Promise(res => setTimeout(res, 5000));
}
}
};
connectDB();
// Get Profile
app.get('/api/profile/:userId', async (req, res) => {
try {
const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [req.params.userId]);
if (rows.length > 0) {
res.json(rows[0]);
} else {
res.status(404).json({ error: 'Profile not found' });
}
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Save Profile
app.post('/api/profile', async (req, res) => {
const p = req.body;
const userId = p.id || 'default_user';
try {
await pool.query(`
INSERT INTO users (id, height, weight, age, gender, activityLevel, goal, bmi, tdee, targetCalories, targetProtein, targetFat, targetCarbs, targetWater)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
height=?, weight=?, age=?, gender=?, activityLevel=?, goal=?, bmi=?, tdee=?, targetCalories=?, targetProtein=?, targetFat=?, targetCarbs=?, targetWater=?
`, [
userId, p.height, p.weight, p.age, p.gender, p.activityLevel, p.goal, p.bmi, p.tdee, p.targetCalories, p.targetProtein, p.targetFat, p.targetCarbs, p.targetWater,
p.height, p.weight, p.age, p.gender, p.activityLevel, p.goal, p.bmi, p.tdee, p.targetCalories, p.targetProtein, p.targetFat, p.targetCarbs, p.targetWater
]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Get Logs (returns a mapped object { "YYYY-MM-DD": DailyLog })
app.get('/api/logs/:userId', async (req, res) => {
try {
const [logRows] = await pool.query('SELECT * FROM daily_logs WHERE user_id = ?', [req.params.userId]);
const [mealRows] = await pool.query('SELECT * FROM meals WHERE user_id = ?', [req.params.userId]);
const logs = {};
logRows.forEach(row => {
// row.date is Date object, format to YYYY-MM-DD
const dateStr = new Date(row.date).toISOString().split('T')[0];
logs[dateStr] = {
date: dateStr,
waterIntake: row.waterIntake,
weight: row.weight,
meals: []
};
});
mealRows.forEach(row => {
const dateStr = new Date(row.date).toISOString().split('T')[0];
if (!logs[dateStr]) {
logs[dateStr] = { date: dateStr, waterIntake: 0, meals: [] };
}
logs[dateStr].meals.push({
id: row.id,
name: row.name,
time: row.time,
calories: row.calories,
protein: row.protein,
fat: row.fat,
carbs: row.carbs
});
});
res.json(logs);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Add Meal
app.post('/api/logs/meal', async (req, res) => {
const { userId, date, meal } = req.body;
try {
await pool.query(`
INSERT INTO meals (id, user_id, date, name, time, calories, protein, fat, carbs)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [meal.id, userId, date, meal.name, meal.time, meal.calories, meal.protein, meal.fat, meal.carbs]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Add Water
app.post('/api/logs/water', async (req, res) => {
const { userId, date, amount } = req.body;
try {
await pool.query(`
INSERT INTO daily_logs (date, user_id, waterIntake)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE waterIntake = waterIntake + ?
`, [date, userId, amount, amount]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Log Weight
app.post('/api/logs/weight', async (req, res) => {
const { userId, date, weight } = req.body;
try {
await pool.query(`
INSERT INTO daily_logs (date, user_id, weight)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE weight = ?
`, [date, userId, weight, weight]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Clear Data
app.delete('/api/data/:userId', async (req, res) => {
try {
await pool.query('DELETE FROM users WHERE id = ?', [req.params.userId]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}`);
});