const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const path = require('path'); const bodyParser = require('body-parser'); const fs = require('fs'); const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, '../public'))); // Ensure data directory exists const dataDir = path.join(__dirname, '../data'); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); } // Initialize SQLite database const dbPath = process.env.DATABASE_PATH || path.join(dataDir, 'tasks.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('Error opening database:', err); } else { console.log('Connected to SQLite database at:', dbPath); initDatabase(); } }); function initDatabase() { db.run(` CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT, size TEXT NOT NULL, reward TEXT, completed INTEGER DEFAULT 0, position INTEGER, subtasks TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); } // API Routes // Get all tasks app.get('/api/tasks', (req, res) => { db.all('SELECT * FROM tasks ORDER BY position ASC, created_at ASC', [], (err, rows) => { if (err) { console.error('Error fetching tasks:', err); return res.status(500).json({ error: 'Failed to fetch tasks' }); } const tasks = rows.map(row => ({ id: row.id, title: row.title, description: row.description, size: row.size, reward: row.reward, completed: row.completed === 1, subtasks: row.subtasks ? JSON.parse(row.subtasks) : [] })); res.json(tasks); }); }); // Create a new task app.post('/api/tasks', (req, res) => { const { title, description, size, reward } = req.body; const id = Date.now().toString(); db.run( `INSERT INTO tasks (id, title, description, size, reward, position) VALUES (?, ?, ?, ?, ?, (SELECT COALESCE(MAX(position), 0) + 1 FROM tasks))`, [id, title, description || '', size, reward || ''], function(err) { if (err) { console.error('Error creating task:', err); return res.status(500).json({ error: 'Failed to create task' }); } res.json({ id, title, description: description || '', size, reward: reward || '', completed: false, subtasks: [] }); } ); }); // Update a task app.put('/api/tasks/:id', (req, res) => { const { id } = req.params; const { title, description, size, reward, completed, subtasks } = req.body; db.run( `UPDATE tasks SET title = ?, description = ?, size = ?, reward = ?, completed = ?, subtasks = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, [ title, description || '', size, reward || '', completed ? 1 : 0, JSON.stringify(subtasks || []), id ], function(err) { if (err) { console.error('Error updating task:', err); return res.status(500).json({ error: 'Failed to update task' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Task not found' }); } res.json({ success: true }); } ); }); // Delete a task app.delete('/api/tasks/:id', (req, res) => { const { id } = req.params; db.run('DELETE FROM tasks WHERE id = ?', [id], function(err) { if (err) { console.error('Error deleting task:', err); return res.status(500).json({ error: 'Failed to delete task' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Task not found' }); } res.json({ success: true }); }); }); // Reorder tasks app.post('/api/tasks/reorder', (req, res) => { const taskIds = req.body; if (!Array.isArray(taskIds)) { return res.status(400).json({ error: 'Invalid task order data' }); } const promises = taskIds.map((id, index) => { return new Promise((resolve, reject) => { db.run('UPDATE tasks SET position = ? WHERE id = ?', [index, id], (err) => { if (err) reject(err); else resolve(); }); }); }); Promise.all(promises) .then(() => res.json({ success: true })) .catch(err => { console.error('Error reordering tasks:', err); res.status(500).json({ error: 'Failed to reorder tasks' }); }); }); // Serve the main HTML file app.get('/', (req, res) => { res.sendFile(path.join(__dirname, '../public/index.html')); }); // Start server app.listen(PORT, () => { console.log(`🚀 Learning Progress Tracker running on http://localhost:${PORT}`); }); // Graceful shutdown process.on('SIGINT', () => { db.close((err) => { if (err) { console.error('Error closing database:', err); } else { console.log('Database connection closed'); } process.exit(0); }); });