Files
rentierroulette/src/server/db.ts
2026-01-07 10:25:33 +01:00

167 lines
4.7 KiB
TypeScript

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<User | null> {
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<User | null> {
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<Group | null> {
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<Group | null> {
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<User[]> {
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;