196 lines
5.5 KiB
JavaScript
196 lines
5.5 KiB
JavaScript
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);
|
|
});
|
|
}); |