- Fastify API on :3000 (tasks, groups, auth, connectors) - PostgreSQL schema: users, tasks, task_groups, goals, connectors - 8 default task groups - JWT auth (register/login/me) - API Bridge framework with webhooks - CLAUDE.md + polling scripts - Collab worker integration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
89 lines
3.8 KiB
JavaScript
89 lines
3.8 KiB
JavaScript
// Task Team — Tasks CRUD — 2026-03-29
|
|
async function taskRoutes(app) {
|
|
// List tasks
|
|
app.get('/tasks', async (req, reply) => {
|
|
const { status, group_id, limit = 50, offset = 0 } = req.query;
|
|
let query = 'SELECT t.*, tg.name as group_name, tg.color as group_color, tg.icon as group_icon FROM tasks t LEFT JOIN task_groups tg ON t.group_id = tg.id WHERE 1=1';
|
|
const params = [];
|
|
if (status) { params.push(status); query += ` AND t.status = $${params.length}`; }
|
|
if (group_id) { params.push(group_id); query += ` AND t.group_id = $${params.length}`; }
|
|
query += ' ORDER BY t.scheduled_at ASC NULLS LAST, t.priority DESC, t.created_at DESC';
|
|
params.push(limit); query += ` LIMIT $${params.length}`;
|
|
params.push(offset); query += ` OFFSET $${params.length}`;
|
|
const { rows } = await app.db.query(query, params);
|
|
return { data: rows, total: rows.length };
|
|
});
|
|
|
|
// Get single task
|
|
app.get('/tasks/:id', async (req) => {
|
|
const { rows } = await app.db.query(
|
|
'SELECT t.*, tg.name as group_name, tg.color as group_color FROM tasks t LEFT JOIN task_groups tg ON t.group_id = tg.id WHERE t.id = $1',
|
|
[req.params.id]
|
|
);
|
|
if (!rows.length) throw { statusCode: 404, message: 'Task not found' };
|
|
return { data: rows[0] };
|
|
});
|
|
|
|
// Create task
|
|
app.post('/tasks', async (req) => {
|
|
const { title, description, status, group_id, priority, scheduled_at, due_at, assigned_to } = req.body;
|
|
const { rows } = await app.db.query(
|
|
`INSERT INTO tasks (title, description, status, group_id, priority, scheduled_at, due_at, assigned_to)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,
|
|
[title, description || '', status || 'pending', group_id, priority || 'medium', scheduled_at, due_at, assigned_to || []]
|
|
);
|
|
return { data: rows[0] };
|
|
});
|
|
|
|
// Update task
|
|
app.put('/tasks/:id', async (req) => {
|
|
const fields = req.body;
|
|
const sets = [];
|
|
const params = [];
|
|
let i = 1;
|
|
for (const [key, value] of Object.entries(fields)) {
|
|
if (['title','description','status','group_id','priority','scheduled_at','due_at','assigned_to','completed_at'].includes(key)) {
|
|
sets.push(`${key} = $${i}`);
|
|
params.push(value);
|
|
i++;
|
|
}
|
|
}
|
|
if (fields.status === 'completed' && !fields.completed_at) {
|
|
sets.push(`completed_at = NOW()`);
|
|
}
|
|
sets.push(`updated_at = NOW()`);
|
|
params.push(req.params.id);
|
|
const { rows } = await app.db.query(
|
|
`UPDATE tasks SET ${sets.join(', ')} WHERE id = $${i} RETURNING *`, params
|
|
);
|
|
if (!rows.length) throw { statusCode: 404, message: 'Task not found' };
|
|
return { data: rows[0] };
|
|
});
|
|
|
|
// Delete task
|
|
app.delete('/tasks/:id', async (req) => {
|
|
const { rowCount } = await app.db.query('DELETE FROM tasks WHERE id = $1', [req.params.id]);
|
|
if (!rowCount) throw { statusCode: 404, message: 'Task not found' };
|
|
return { status: 'deleted' };
|
|
});
|
|
|
|
// Task comments
|
|
app.get('/tasks/:id/comments', async (req) => {
|
|
const { rows } = await app.db.query(
|
|
'SELECT * FROM task_comments WHERE task_id = $1 ORDER BY created_at ASC', [req.params.id]
|
|
);
|
|
return { data: rows };
|
|
});
|
|
|
|
app.post('/tasks/:id/comments', async (req) => {
|
|
const { content, is_ai } = req.body;
|
|
const { rows } = await app.db.query(
|
|
'INSERT INTO task_comments (task_id, content, is_ai) VALUES ($1, $2, $3) RETURNING *',
|
|
[req.params.id, content, is_ai || false]
|
|
);
|
|
return { data: rows[0] };
|
|
});
|
|
}
|
|
|
|
module.exports = taskRoutes;
|