Initial commit: Project Tracker - Node.js + Express + SQLite backend, dark-themed frontend\n\n- Express REST API (GET/POST/PUT/DELETE /api/projects)\n- SQLite database with better-sqlite3\n- Dark-themed single-page UI with filter bar and drill-down panel\n- nginx reverse proxy config\n- Deployment script
This commit is contained in:
103
backend/server.js
Normal file
103
backend/server.js
Normal file
@@ -0,0 +1,103 @@
|
||||
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 = 3000;
|
||||
const DB_PATH = '/opt/project-tracker/data/projects.db';
|
||||
const DATA_DIR = '/opt/project-tracker/data';
|
||||
|
||||
// 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}`);
|
||||
});
|
||||
Reference in New Issue
Block a user