import type { Group, User } from '@/interfaces'; import { SQL } from 'bun'; // create tables if they don't exist const createTableScript = ` -- GROUP TRABLE -- CREATE TABLE IF NOT EXISTS groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT UNIQUE NOT NULL, name TEXT NOT NULL, mail TEXT NOT NULL, image BLOB, phase TEXT DEFAULT 'gathering', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- USER TABLE -- CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, mail TEXT NOT NULL, image BLOB, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- MEMBERSHIP TABLE -- CREATE TABLE IF NOT EXISTS membership ( id INTEGER PRIMARY KEY AUTOINCREMENT, group_id INTEGER, user_id INTEGER, UNIQUE(user_id, group_id), FOREIGN KEY(group_id) REFERENCES groups(id), FOREIGN KEY(user_id) REFERENCES users(id) ); -- BAN TABLE -- CREATE TABLE IF NOT EXISTS bans ( id INTEGER PRIMARY KEY AUTOINCREMENT, group_id INTEGER, user_id INTEGER, user_id2 INTEGER, FOREIGN KEY(group_id) REFERENCES groups(id), FOREIGN KEY(user_id) REFERENCES users(id), FOREIGN KEY(user_id2) REFERENCES users(id) UNIQUE(group_id, user_id, user_id2) ); -- WHISHLIST TABLE -- CREATE TABLE IF NOT EXISTS wishlist ( id INTEGER PRIMARY KEY AUTOINCREMENT, group_id INTEGER, user_id INTEGER, item TEXT NOT NULL, FOREIGN KEY(group_id) REFERENCES groups(id), FOREIGN KEY(user_id) REFERENCES users(id) ); `; class DB { private instance: SQL; private executeScript(script: string, name: string) { try { void this.instance`${script}`; } catch (err) { console.error(`error executing script ${name}: ${err as Error}`); } } /** * Prepare the database by creating necessary tables. */ private prepareDB () { this.executeScript(createTableScript, 'createTableScript'); } /* USERS */ /** * Create a new user * @param mail: string * @returns created user */ public async createUser(mail: string): Promise { const user: User[] = await this.instance` INSERT INTO users (mail) VALUES (${mail}) RETURNING * `; return user[0] ?? null; } /** * Get user by mail * @param mail: string * @returns user object or null */ public async getUserByMail(mail: string): Promise { const user: User[] = await this.instance` SELECT * FROM users WHERE mail = ${mail} `; return user[0] ?? null; } /* GROUPS */ /** * Create a new group * @param name: string * @param mail: string * @returns object with id of the created group */ public async createGroup( {name, mail}: {name: string, mail: string}): Promise { const code = Math.random().toString(36).substring(2, 8).toUpperCase(); const group: Group[] = await this.instance` INSERT INTO groups (code, name, mail) VALUES (${code}, ${name}, ${mail}) RETURNING * `; console.log('Created group:', group); return group[0] ?? null; }; /** * Get group by ID * @param id: string * @returns group object or null */ public async getGroupByCode(code: string): Promise { const group: Group[] = await this.instance` SELECT * FROM groups WHERE code = ${code} `; return group[0] ?? null; } /* GROUP MEMBER */ /** * Add user to group * @param userId: number * @param groupId: number * @returns object with id of the created group member entry */ public async UserEntersGroup(userId: number, groupId: number): Promise<{ id: number | bigint; }> { const membership: { id: number | bigint} = await this.instance` INSERT OR IGNORE INTO membership (user_id, group_id) VALUES (${userId}, ${groupId}) RETURNING * `; return membership; } public async getGroupMembers(groupId: number): Promise { const users: User[] = await this.instance` SELECT * FROM users u JOIN membership m ON u.id = m.user_id WHERE m.group_id = ${groupId} `; return users; } constructor(url: string = './data.sqlite') { this.instance = new SQL(url, {adapter: 'sqlite'}); this.prepareDB(); console.log('Database initialized at', url); } } export default DB;