feat: add initial database schema and task service

main
borja (aider) 3 months ago
parent 7646e85c30
commit 644883109f

@ -0,0 +1,39 @@
import { Database } from 'bun:sqlite';
export const db = new Database('tasks.db');
export function initializeDatabase() {
db.exec(`
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
due_date TIMESTAMP NULL,
completed BOOLEAN DEFAULT FALSE,
completed_at TIMESTAMP NULL,
group_id TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS task_assignments (
task_id INTEGER NOT NULL,
user_id TEXT NOT NULL,
assigned_by TEXT NOT NULL,
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (task_id, user_id),
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS users (
phone_number TEXT PRIMARY KEY,
wa_id TEXT NOT NULL, // WhatsApp's @s.whatsapp.net ID
name TEXT,
last_seen TIMESTAMP
);
CREATE TABLE IF NOT EXISTS groups (
id TEXT PRIMARY KEY,
community_id TEXT NOT NULL,
name TEXT
);
`);
}

@ -0,0 +1,16 @@
export interface Task {
id: number;
description: string;
created_at: Date;
due_date: Date | null;
completed: boolean;
completed_at: Date | null;
group_id: string; // WhatsApp group ID where task was created
}
export interface TaskAssignment {
task_id: number;
user_id: string; // Normalized phone number
assigned_by: string; // Who assigned this
assigned_at: Date;
}

@ -0,0 +1,37 @@
import { db } from '../db';
import type { Task, TaskAssignment } from './model';
export class TaskService {
static createTask(task: Omit<Task, 'id'>, assignments: Omit<TaskAssignment, 'task_id'>[]) {
return db.transaction(() => {
const insertTask = db.prepare(`
INSERT INTO tasks (description, due_date, group_id)
VALUES (?, ?, ?)
RETURNING id
`);
const taskResult = insertTask.get(
task.description,
task.due_date?.toISOString(),
task.group_id
) as { id: number };
const insertAssignment = db.prepare(`
INSERT INTO task_assignments (task_id, user_id, assigned_by)
VALUES (?, ?, ?)
`);
assignments.forEach(assignment => {
insertAssignment.run(
taskResult.id,
assignment.user_id,
assignment.assigned_by
);
});
return taskResult.id;
});
}
// We'll add more methods here as we progress
}

@ -0,0 +1,25 @@
import { db, initializeDatabase } from '../../src/db';
import { describe, test, expect, beforeEach } from 'bun:test';
describe('Database', () => {
beforeEach(() => {
// Reset database between tests
db.exec('DROP TABLE IF EXISTS tasks');
db.exec('DROP TABLE IF EXISTS task_assignments');
db.exec('DROP TABLE IF EXISTS users');
db.exec('DROP TABLE IF EXISTS groups');
initializeDatabase();
});
test('should create all required tables', () => {
const tables = db
.query("SELECT name FROM sqlite_master WHERE type='table'")
.all()
.map((t: any) => t.name);
expect(tables).toContain('tasks');
expect(tables).toContain('task_assignments');
expect(tables).toContain('users');
expect(tables).toContain('groups');
});
});
Loading…
Cancel
Save