const express = require('express'); const sqlite3 = require('better-sqlite3'); const cors = require('cors'); const fs = require('fs'); const path = require('path'); const app = express(); const PORT = process.env.PORT || 3000; const DATA_DIR = process.env.DATA_DIR || '/app/data'; const DB_PATH = path.join(DATA_DIR, 'projects.db'); // Ensure data directory exists if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } // Initialize database const db = new sqlite3(DB_PATH); // Create table db.exec(` CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, priority TEXT NOT NULL, url TEXT DEFAULT '', notes TEXT DEFAULT '', status TEXT DEFAULT 'Backlog', owner TEXT DEFAULT 'Ada', tags TEXT DEFAULT '', created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ) `); // Middleware app.use(cors()); app.use(express.json()); // GET all projects app.get('/api/projects', (req, res) => { const { status } = req.query; let stmt = status ? db.prepare('SELECT * FROM projects WHERE status = ? ORDER BY CASE priority WHEN \'High\' THEN 1 WHEN \'Med-High\' THEN 2 WHEN \'Medium\' THEN 3 WHEN \'Low\' THEN 4 ELSE 5 END, created_at DESC') : db.prepare('SELECT * FROM projects ORDER BY CASE priority WHEN \'High\' THEN 1 WHEN \'Med-High\' THEN 2 WHEN \'Medium\' THEN 3 WHEN \'Low\' THEN 4 ELSE 5 END, created_at DESC'); const rows = status ? stmt.all(status) : stmt.all(); res.json(rows); }); // GET single project app.get('/api/projects/:id', (req, res) => { const stmt = db.prepare('SELECT * FROM projects WHERE id = ?'); const row = stmt.get(req.params.id); if (row) res.json(row); else res.status(404).json({ error: 'Not found' }); }); // POST create project app.post('/api/projects', (req, res) => { const { name, priority, url, notes, status, owner, tags } = req.body; if (!name || !priority) { return res.status(400).json({ error: 'name and priority are required' }); } const stmt = db.prepare(` INSERT INTO projects (name, priority, url, notes, status, owner, tags) VALUES (?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run(name, priority, url || '', notes || '', status || 'Backlog', owner || 'Ada', tags || ''); res.json({ id: result.lastInsertRowid, message: 'Created' }); }); // PUT update project app.put('/api/projects/:id', (req, res) => { const { name, priority, url, notes, status, owner, tags } = req.body; const fields = []; const values = []; if (name !== undefined) { fields.push('name = ?'); values.push(name); } if (priority !== undefined) { fields.push('priority = ?'); values.push(priority); } if (url !== undefined) { fields.push('url = ?'); values.push(url); } if (notes !== undefined) { fields.push('notes = ?'); values.push(notes); } if (status !== undefined) { fields.push('status = ?'); values.push(status); } if (owner !== undefined) { fields.push('owner = ?'); values.push(owner); } if (tags !== undefined) { fields.push('tags = ?'); values.push(tags); } fields.push('updated_at = datetime(\'now\')'); values.push(req.params.id); if (fields.length === 1) { return res.status(400).json({ error: 'No fields to update' }); } const stmt = db.prepare(`UPDATE projects SET ${fields.join(', ')} WHERE id = ?`); stmt.run(...values); res.json({ message: 'Updated' }); }); // DELETE project app.delete('/api/projects/:id', (req, res) => { const stmt = db.prepare('DELETE FROM projects WHERE id = ?'); stmt.run(req.params.id); res.json({ message: 'Deleted' }); }); app.listen(PORT, '0.0.0.0', () => { console.log(`Project Tracker API running on port ${PORT}`); });