add missing routes for admin

This commit is contained in:
chris 2025-08-08 10:42:38 -04:00
parent fbd18b8678
commit d2e50dccd1

349
server.js
View File

@ -1,5 +1,3 @@
// --- Time Tracker Backend Server (Updated) ---
require('dotenv').config(); require('dotenv').config();
const express = require('express'); const express = require('express');
const sqlite3 = require('sqlite3'); const sqlite3 = require('sqlite3');
@ -104,7 +102,7 @@ function setupRoutes() {
else res.status(403).json({ message: "Access denied." }); else res.status(403).json({ message: "Access denied." });
}; };
// --- Auth --- // --- Auth Routes ---
app.post('/api/login', async (req, res) => { app.post('/api/login', async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
@ -115,18 +113,17 @@ function setupRoutes() {
const tokenPayload = { id: user.id, username: user.username, role: user.role }; const tokenPayload = { id: user.id, username: user.username, role: user.role };
const token = jwt.sign(tokenPayload, JWT_SECRET, { expiresIn: '8h' }); const token = jwt.sign(tokenPayload, JWT_SECRET, { expiresIn: '8h' });
res.json({ token, user: tokenPayload }); res.json({ token, user: tokenPayload });
} catch { } catch (err) {
res.status(500).json({ message: "Server error during login." }); res.status(500).json({ message: "Server error during login." });
} }
}); });
// --- Punch --- // --- Employee-Facing Routes ---
app.post('/api/punch', authenticateToken, async (req, res) => { app.post('/api/punch', authenticateToken, async (req, res) => {
try { try {
const { id, username } = req.user; const { id, username } = req.user;
const openPunch = await db.get(`SELECT * FROM time_entries WHERE user_id = ? AND status = 'in' ORDER BY punch_in_time DESC LIMIT 1`, [id]); const openPunch = await db.get(`SELECT * FROM time_entries WHERE user_id = ? AND status = 'in' ORDER BY punch_in_time DESC LIMIT 1`, [id]);
const now = new Date().toISOString(); const now = new Date().toISOString();
if (openPunch) { if (openPunch) {
await db.run(`UPDATE time_entries SET punch_out_time = ?, status = 'out' WHERE id = ?`, [now, openPunch.id]); await db.run(`UPDATE time_entries SET punch_out_time = ?, status = 'out' WHERE id = ?`, [now, openPunch.id]);
res.json({ message: "Punched out." }); res.json({ message: "Punched out." });
@ -134,7 +131,7 @@ function setupRoutes() {
await db.run(`INSERT INTO time_entries (user_id, username, punch_in_time, status) VALUES (?, ?, ?, 'in')`, [id, username, now]); await db.run(`INSERT INTO time_entries (user_id, username, punch_in_time, status) VALUES (?, ?, ?, 'in')`, [id, username, now]);
res.json({ message: "Punched in." }); res.json({ message: "Punched in." });
} }
} catch { } catch (err) {
res.status(500).json({ message: "Server error during punch." }); res.status(500).json({ message: "Server error during punch." });
} }
}); });
@ -143,12 +140,11 @@ function setupRoutes() {
try { try {
const rows = await db.all(`SELECT * FROM time_entries WHERE user_id = ? ORDER BY punch_in_time DESC`, [req.user.id]); const rows = await db.all(`SELECT * FROM time_entries WHERE user_id = ? ORDER BY punch_in_time DESC`, [req.user.id]);
res.json(rows); res.json(rows);
} catch { } catch (err) {
res.status(500).json({ message: "Server error fetching status." }); res.status(500).json({ message: "Server error fetching status." });
} }
}); });
// --- User ---
app.post('/api/user/change-password', authenticateToken, async (req, res) => { app.post('/api/user/change-password', authenticateToken, async (req, res) => {
try { try {
const { currentPassword, newPassword } = req.body; const { currentPassword, newPassword } = req.body;
@ -159,7 +155,7 @@ function setupRoutes() {
const hashed = await bcrypt.hash(newPassword, 10); const hashed = await bcrypt.hash(newPassword, 10);
await db.run('UPDATE users SET password = ? WHERE id = ?', [hashed, req.user.id]); await db.run('UPDATE users SET password = ? WHERE id = ?', [hashed, req.user.id]);
res.json({ message: "Password updated successfully." }); res.json({ message: "Password updated successfully." });
} catch { } catch (err) {
res.status(500).json({ message: "Error changing password." }); res.status(500).json({ message: "Error changing password." });
} }
}); });
@ -170,214 +166,121 @@ function setupRoutes() {
const { id, username } = req.user; const { id, username } = req.user;
await db.run(`INSERT INTO time_off_requests (user_id, username, start_date, end_date, reason) VALUES (?, ?, ?, ?, ?)`, [id, username, startDate, endDate, reason]); await db.run(`INSERT INTO time_off_requests (user_id, username, start_date, end_date, reason) VALUES (?, ?, ?, ?, ?)`, [id, username, startDate, endDate, reason]);
res.status(201).json({ message: "Time off request submitted." }); res.status(201).json({ message: "Time off request submitted." });
} catch { } catch (err) {
res.status(500).json({ message: "Failed to submit request." }); res.status(500).json({ message: "Failed to submit request." });
} }
}); });
app.get('/api/user/time-off-requests', authenticateToken, async (req, res) => { app.get('/api/user/time-off-requests', authenticateToken, async (req, res) => {
try { try {
// This query now only gets requests that are pending OR have an end date in the future. const rows = await db.all(`SELECT * FROM time_off_requests WHERE user_id = ? AND (status = 'pending' OR end_date >= date('now')) ORDER BY start_date ASC`, [req.user.id]);
const rows = await db.all(
`SELECT * FROM time_off_requests
WHERE user_id = ? AND (status = 'pending' OR end_date >= date('now'))
ORDER BY start_date ASC`,
[req.user.id]
);
res.json(rows); res.json(rows);
} catch (err) { } catch (err) {
console.error("Error fetching time off requests:", err);
res.status(500).json({ message: "Failed to fetch requests." }); res.status(500).json({ message: "Failed to fetch requests." });
} }
}); });
app.get('/api/user/time-off-requests/history', authenticateToken, async (req, res) => { app.get('/api/user/time-off-requests/history', authenticateToken, async (req, res) => {
try { try {
// This query gets ALL requests for the user, sorted by newest first. const rows = await db.all("SELECT * FROM time_off_requests WHERE user_id = ? ORDER BY start_date DESC", [req.user.id]);
const rows = await db.all(
"SELECT * FROM time_off_requests WHERE user_id = ? ORDER BY start_date DESC",
[req.user.id]
);
res.json(rows); res.json(rows);
} catch (err) { } catch (err) {
console.error("Error fetching time off history:", err);
res.status(500).json({ message: "Failed to fetch request history." }); res.status(500).json({ message: "Failed to fetch request history." });
} }
}); });
app.get('/api/user/notes', authenticateToken, async (req, res) => {
// --- Admin Tools ---
app.post('/api/admin/force-clock-out', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { userId } = req.body; const notes = await db.all("SELECT admin_username, note_text, created_at FROM notes WHERE employee_user_id = ? ORDER BY created_at DESC", [req.user.id]);
const punch = await db.get(`SELECT * FROM time_entries WHERE user_id = ? AND status = 'in' ORDER BY punch_in_time DESC LIMIT 1`, [userId]); res.json(notes);
if (!punch) return res.status(404).json({ message: "No active punch-in found." });
await db.run(`UPDATE time_entries SET punch_out_time = ?, status = 'out' WHERE id = ?`, [new Date().toISOString(), punch.id]);
res.json({ message: "User has been clocked out." });
} catch {
res.status(500).json({ message: "Server error forcing clock out." });
}
});
app.post('/api/cleanup/malformed-entries', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const result = await db.run(`
UPDATE time_entries
SET status = 'out'
WHERE status = 'in' AND punch_out_time IS NOT NULL
`);
res.json({ message: `Corrected ${result.changes} malformed entries.` });
} catch (err) { } catch (err) {
console.error("Error during cleanup:", err); res.status(500).json({ message: 'Failed to fetch notes.' });
res.status(500).json({ message: "Server error during cleanup." });
} }
}); });
app.get('/api/admin/logs', authenticateToken, requireRole('admin'), async (req, res) => {
try { // --- Admin User Management ---
const rows = await db.all("SELECT * FROM time_entries ORDER BY punch_in_time DESC");
res.json(rows);
} catch {
res.status(500).json({ message: "Server error fetching logs." });
}
});
// Gets all users for the management table
app.get('/api/admin/users', authenticateToken, requireRole('admin'), async (req, res) => { app.get('/api/admin/users', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const users = await db.all("SELECT id, username, role FROM users"); const users = await db.all("SELECT id, username, role FROM users");
// Add a flag to identify the primary admin to protect them from deletion/demotion const usersWithPrimaryFlag = users.map(u => ({...u, isPrimary: u.username === ADMIN_USERNAME}));
const usersWithPrimaryFlag = users.map(u => ({
...u,
isPrimary: u.username === ADMIN_USERNAME
}));
res.json(usersWithPrimaryFlag); res.json(usersWithPrimaryFlag);
} catch { } catch (err) {
res.status(500).json({ message: "Failed to fetch users." }); res.status(500).json({ message: "Failed to fetch users." });
} }
}); });
app.post('/api/admin/add-punch', authenticateToken, requireRole('admin'), async (req, res) => { app.post('/api/admin/create-user', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { userId, username, punchInTime, punchOutTime } = req.body; const { username, password, role } = req.body;
if (!username || !password || !role) return res.status(400).json({ message: "Username, password, and role are required." });
if (!userId || !punchInTime) { const existingUser = await db.get("SELECT id FROM users WHERE username = ?", [username]);
return res.status(400).json({ message: "User and Punch In time are required." }); if (existingUser) return res.status(409).json({ message: "Username already exists." });
}
// SCENARIO 1: A "punch-in only" was submitted
if (!punchOutTime) {
// First, ensure this user doesn't already have an active punch
const existingPunch = await db.get("SELECT id FROM time_entries WHERE user_id = ? AND status = 'in'", [userId]);
if (existingPunch) {
return res.status(409).json({ message: `${username} is already punched in. Please edit their existing entry.` });
}
// If clear, insert the new active punch
await db.run(
'INSERT INTO time_entries (user_id, username, punch_in_time, status) VALUES (?, ?, ?, ?)',
[userId, username, punchInTime, 'in']
);
return res.status(201).json({ message: `Active punch successfully started for ${username}.` });
}
// SCENARIO 2: A complete entry (in and out) was submitted
if (new Date(punchOutTime) < new Date(punchInTime)) {
return res.status(400).json({ message: "Punch out time cannot be before punch in time." });
}
await db.run(
'INSERT INTO time_entries (user_id, username, punch_in_time, punch_out_time, status) VALUES (?, ?, ?, ?, ?)',
[userId, username, punchInTime, punchOutTime, 'out']
);
res.status(201).json({ message: `Completed entry added successfully for ${username}.` });
const hashedPassword = await bcrypt.hash(password, 10);
await db.run('INSERT INTO users (username, password, role) VALUES (?, ?, ?)', [username, hashedPassword, role]);
res.status(201).json({ message: 'User created successfully.' });
} catch (err) { } catch (err) {
console.error("Error adding manual punch:", err); res.status(500).json({ message: 'Failed to create user.' });
res.status(500).json({ message: 'Failed to add manual punch.' }); }
});
app.post('/api/admin/update-role', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const { username, newRole } = req.body;
if (username === ADMIN_USERNAME) return res.status(403).json({ message: "Cannot change the role of the primary admin." });
await db.run('UPDATE users SET role = ? WHERE username = ?', [newRole, username]);
res.json({ message: 'User role updated.' });
} catch (err) {
res.status(500).json({ message: 'Failed to update role.' });
} }
}); });
app.post('/api/admin/reset-password', authenticateToken, requireRole('admin'), async (req, res) => { app.post('/api/admin/reset-password', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { username, newPassword } = req.body; const { username, newPassword } = req.body;
if (!username || !newPassword) return res.status(400).json({ message: "Username and new password are required." });
if (!username || !newPassword) {
return res.status(400).json({ message: "Username and new password are required." });
}
const hashedPassword = await bcrypt.hash(newPassword, 10); const hashedPassword = await bcrypt.hash(newPassword, 10);
const result = await db.run('UPDATE users SET password = ? WHERE username = ?', [hashedPassword, username]); const result = await db.run('UPDATE users SET password = ? WHERE username = ?', [hashedPassword, username]);
if (result.changes === 0) return res.status(404).json({ message: "User not found." });
if (result.changes === 0) { res.json({ message: `Password for ${username} has been reset.` });
return res.status(404).json({ message: "User not found." });
}
res.json({ message: `Password for ${username} has been reset successfully.` });
} catch (err) { } catch (err) {
console.error("Error resetting password:", err);
res.status(500).json({ message: 'Failed to reset password.' }); res.status(500).json({ message: 'Failed to reset password.' });
} }
}); });
app.delete('/api/admin/delete-user/:username', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const { username } = req.params;
if (username === req.user.username) return res.status(403).json({ message: "Cannot delete yourself." });
if (username === ADMIN_USERNAME) return res.status(403).json({ message: "Cannot delete the primary admin." });
// Gets all time entries for the detailed log view const result = await db.run('DELETE FROM users WHERE username = ?', [username]);
if (result.changes === 0) return res.status(404).json({ message: "User not found." });
res.json({ message: 'User and all associated data deleted.' });
} catch (err) {
res.status(500).json({ message: 'Failed to delete user.' });
}
});
// --- Admin Time & Log Management ---
app.get('/api/admin/logs', authenticateToken, requireRole('admin'), async (req, res) => { app.get('/api/admin/logs', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const rows = await db.all("SELECT * FROM time_entries ORDER BY punch_in_time DESC"); const rows = await db.all("SELECT * FROM time_entries ORDER BY punch_in_time DESC");
res.json(rows); res.json(rows);
} catch { } catch (err) {
res.status(500).json({ message: "Server error fetching logs." }); res.status(500).json({ message: "Server error fetching logs." });
} }
}); });
// Gets only PENDING time off requests for the main dashboard view
app.get('/api/admin/time-off-requests/pending', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM time_off_requests WHERE status = 'pending' ORDER BY start_date ASC");
res.json(rows);
} catch {
res.status(500).json({ message: "Failed to fetch pending requests." });
}
});
// Gets APPROVED/DENIED requests for the history view
app.get('/api/admin/time-off-requests/history', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM time_off_requests WHERE status != 'pending' ORDER BY start_date DESC");
res.json(rows);
} catch {
res.status(500).json({ message: "Failed to fetch request history." });
}
});
// Gets archived time entries
app.get('/api/admin/archives', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM archived_time_entries ORDER BY archived_at DESC");
res.json(rows);
} catch {
res.status(500).json({ message: "Failed to fetch archives." });
}
});
// THIS IS THE NEW/FIXED ROUTE
app.put('/api/admin/logs/:id', authenticateToken, requireRole('admin'), async (req, res) => { app.put('/api/admin/logs/:id', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { id } = req.params; const { id } = req.params;
const { punch_in_time, punch_out_time } = req.body; const { punch_in_time, punch_out_time } = req.body;
// **THE FIX IS HERE**
// Determine the correct status based on the punch_out_time.
const status = punch_out_time ? 'out' : 'in'; const status = punch_out_time ? 'out' : 'in';
await db.run(`UPDATE time_entries SET punch_in_time = ?, punch_out_time = ?, status = ? WHERE id = ?`, [punch_in_time, punch_out_time, status, id]);
await db.run(
`UPDATE time_entries
SET punch_in_time = ?, punch_out_time = ?, status = ?
WHERE id = ?`,
[punch_in_time, punch_out_time, status, id]
);
res.json({ message: 'Entry updated successfully.' }); res.json({ message: 'Entry updated successfully.' });
} catch (err) { } catch (err) {
console.error("Error updating log:", err);
res.status(500).json({ message: 'Failed to update entry.' }); res.status(500).json({ message: 'Failed to update entry.' });
} }
}); });
@ -386,79 +289,129 @@ app.delete('/api/admin/logs/:id', authenticateToken, requireRole('admin'), async
try { try {
const { id } = req.params; const { id } = req.params;
const result = await db.run('DELETE FROM time_entries WHERE id = ?', [id]); const result = await db.run('DELETE FROM time_entries WHERE id = ?', [id]);
if (result.changes === 0) return res.status(404).json({ message: "Entry not found." });
if (result.changes === 0) {
return res.status(404).json({ message: "Entry not found or already deleted." });
}
res.json({ message: 'Time entry deleted successfully.' }); res.json({ message: 'Time entry deleted successfully.' });
} catch (err) { } catch (err) {
console.error("Error deleting log:", err);
res.status(500).json({ message: 'Failed to delete time entry.' }); res.status(500).json({ message: 'Failed to delete time entry.' });
} }
}); });
// Admin creates a note for an employee app.post('/api/admin/force-clock-out', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const { userId } = req.body;
const punch = await db.get(`SELECT * FROM time_entries WHERE user_id = ? AND status = 'in' ORDER BY punch_in_time DESC LIMIT 1`, [userId]);
if (!punch) return res.status(404).json({ message: "No active punch-in found." });
await db.run(`UPDATE time_entries SET punch_out_time = ?, status = 'out' WHERE id = ?`, [new Date().toISOString(), punch.id]);
res.json({ message: "User has been clocked out." });
} catch (err) {
res.status(500).json({ message: "Server error forcing clock out." });
}
});
app.post('/api/admin/add-punch', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const { userId, username, punchInTime, punchOutTime } = req.body;
if (!userId || !punchInTime) return res.status(400).json({ message: "User and Punch In time are required." });
if (!punchOutTime) {
const existingPunch = await db.get("SELECT id FROM time_entries WHERE user_id = ? AND status = 'in'", [userId]);
if (existingPunch) return res.status(409).json({ message: `${username} is already punched in.` });
await db.run('INSERT INTO time_entries (user_id, username, punch_in_time, status) VALUES (?, ?, ?, ?)', [userId, username, punchInTime, 'in']);
return res.status(201).json({ message: `Active punch started for ${username}.` });
}
if (new Date(punchOutTime) < new Date(punchInTime)) return res.status(400).json({ message: "Punch out time cannot be before punch in time." });
await db.run('INSERT INTO time_entries (user_id, username, punch_in_time, punch_out_time, status) VALUES (?, ?, ?, ?, ?)', [userId, username, punchInTime, punchOutTime, 'out']);
res.status(201).json({ message: `Completed entry added for ${username}.` });
} catch (err) {
res.status(500).json({ message: 'Failed to add manual punch.' });
}
});
app.get('/api/admin/archives', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM archived_time_entries ORDER BY archived_at DESC");
res.json(rows);
} catch (err) {
res.status(500).json({ message: "Failed to fetch archives." });
}
});
app.post('/api/admin/archive', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const entriesToArchive = await db.all("SELECT * FROM time_entries WHERE status = 'out'");
if (entriesToArchive.length === 0) return res.json({ message: "No completed entries to archive." });
const archivedAt = new Date().toISOString();
await db.exec('BEGIN TRANSACTION');
for (const entry of entriesToArchive) {
await db.run('INSERT INTO archived_time_entries VALUES (?, ?, ?, ?, ?, ?, ?)',
[entry.id, entry.user_id, entry.username, entry.punch_in_time, entry.punch_out_time, entry.status, archivedAt]
);
await db.run('DELETE FROM time_entries WHERE id = ?', [entry.id]);
}
await db.exec('COMMIT');
res.json({ message: `${entriesToArchive.length} entries archived successfully.` });
} catch (err) {
await db.exec('ROLLBACK');
res.status(500).json({ message: 'Failed to archive entries.' });
}
});
// --- Admin Time Off & Note Management ---
app.get('/api/admin/time-off-requests/pending', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM time_off_requests WHERE status = 'pending' ORDER BY start_date ASC");
res.json(rows);
} catch (err) {
res.status(500).json({ message: "Failed to fetch pending requests." });
}
});
app.get('/api/admin/time-off-requests/history', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const rows = await db.all("SELECT * FROM time_off_requests WHERE status != 'pending' ORDER BY start_date DESC");
res.json(rows);
} catch (err) {
res.status(500).json({ message: "Failed to fetch request history." });
}
});
app.post('/api/admin/update-time-off-status', authenticateToken, requireRole('admin'), async (req, res) => {
try {
const { requestId, status } = req.body;
await db.run('UPDATE time_off_requests SET status = ? WHERE id = ?', [status, requestId]);
res.json({ message: `Request status updated to ${status}.` });
} catch (err) {
res.status(500).json({ message: 'Failed to update request status.' });
}
});
app.post('/api/admin/notes', authenticateToken, requireRole('admin'), async (req, res) => { app.post('/api/admin/notes', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { userId, noteText } = req.body; const { userId, noteText } = req.body;
const adminUsername = req.user.username; // Get admin's username from their token const adminUsername = req.user.username;
if (!userId || !noteText) return res.status(400).json({ message: "Employee and note text are required." });
if (!userId || !noteText) { await db.run('INSERT INTO notes (admin_username, employee_user_id, note_text) VALUES (?, ?, ?)', [adminUsername, userId, noteText]);
return res.status(400).json({ message: "Employee and note text are required." });
}
await db.run(
'INSERT INTO notes (admin_username, employee_user_id, note_text) VALUES (?, ?, ?)',
[adminUsername, userId, noteText]
);
res.status(201).json({ message: "Note successfully posted." }); res.status(201).json({ message: "Note successfully posted." });
} catch (err) { } catch (err) {
console.error("Error posting note:", err);
res.status(500).json({ message: 'Failed to post note.' }); res.status(500).json({ message: 'Failed to post note.' });
} }
}); });
// Employee fetches their notes
app.get('/api/user/notes', authenticateToken, async (req, res) => {
try {
const notes = await db.all(
"SELECT admin_username, note_text, created_at FROM notes WHERE employee_user_id = ? ORDER BY created_at DESC",
[req.user.id]
);
res.json(notes);
} catch (err) {
console.error("Error fetching notes:", err);
res.status(500).json({ message: 'Failed to fetch notes.' });
}
});
// --- Note Management Routes ---
// Admin gets all notes for a specific employee
app.get('/api/admin/notes/:userId', authenticateToken, requireRole('admin'), async (req, res) => { app.get('/api/admin/notes/:userId', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { userId } = req.params; const { userId } = req.params;
const notes = await db.all( const notes = await db.all("SELECT id, admin_username, note_text, created_at FROM notes WHERE employee_user_id = ? ORDER BY created_at DESC", [userId]);
"SELECT id, admin_username, note_text, created_at FROM notes WHERE employee_user_id = ? ORDER BY created_at DESC",
[userId]
);
res.json(notes); res.json(notes);
} catch (err) { } catch (err) {
res.status(500).json({ message: 'Failed to fetch notes.' }); res.status(500).json({ message: 'Failed to fetch notes.' });
} }
}); });
// Admin deletes a specific note
app.delete('/api/admin/notes/:noteId', authenticateToken, requireRole('admin'), async (req, res) => { app.delete('/api/admin/notes/:noteId', authenticateToken, requireRole('admin'), async (req, res) => {
try { try {
const { noteId } = req.params; const { noteId } = req.params;
const result = await db.run('DELETE FROM notes WHERE id = ?', [noteId]); const result = await db.run('DELETE FROM notes WHERE id = ?', [noteId]);
if (result.changes === 0) return res.status(404).json({ message: "Note not found." });
if (result.changes === 0) {
return res.status(404).json({ message: "Note not found." });
}
res.json({ message: 'Note deleted successfully.' }); res.json({ message: 'Note deleted successfully.' });
} catch (err) { } catch (err) {
res.status(500).json({ message: 'Failed to delete note.' }); res.status(500).json({ message: 'Failed to delete note.' });