From 270c3297fd999307c1914b10db384efc42d285e5 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Sat, 29 Nov 2025 16:05:18 +0100 Subject: [PATCH] added blocked database --- src/@shared/src/database/index.ts | 4 +- src/@shared/src/database/init.sql | 14 ++++++ src/@shared/src/database/mixin/blocked.ts | 55 +++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/@shared/src/database/mixin/blocked.ts diff --git a/src/@shared/src/database/index.ts b/src/@shared/src/database/index.ts index 597efac..ea111db 100644 --- a/src/@shared/src/database/index.ts +++ b/src/@shared/src/database/index.ts @@ -4,11 +4,13 @@ import { FastifyInstance, FastifyPluginAsync } from 'fastify'; import { isNullish } from '@shared/utils'; import { Database as DbImpl } from './mixin/_base'; import { IUserDb, UserImpl } from './mixin/user'; +import { IBlockedDb, BlockedImpl } from './mixin/blocked'; Object.assign(DbImpl.prototype, UserImpl); +Object.assign(DbImpl.prototype, BlockedImpl); -export interface Database extends DbImpl, IUserDb { } +export interface Database extends DbImpl, IUserDb, IBlockedDb { } // When using .decorate you have to specify added properties for Typescript declare module 'fastify' { diff --git a/src/@shared/src/database/init.sql b/src/@shared/src/database/init.sql index b53675b..b4e1415 100644 --- a/src/@shared/src/database/init.sql +++ b/src/@shared/src/database/init.sql @@ -7,3 +7,17 @@ CREATE TABLE IF NOT EXISTS user ( guest INTEGER NOT NULL DEFAULT 0, oauth2 TEXT DEFAULT NULL ); + + +CREATE TABLE IF NOT EXISTS blocked ( + id INTEGER PRIMARY KEY NOT NULL, + user TEXT NOT NULL, + blocked TEXT NOT NULL, + + FOREIGN KEY(user) REFERENCES user(id); + FOREIGN KEY(blocked) REFERENCES user(id); +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_blocked_user_pair + ON blocked(user, blocked); + diff --git a/src/@shared/src/database/mixin/blocked.ts b/src/@shared/src/database/mixin/blocked.ts new file mode 100644 index 0000000..2f3c9f4 --- /dev/null +++ b/src/@shared/src/database/mixin/blocked.ts @@ -0,0 +1,55 @@ +import { isNullish } from '@shared/utils'; +import type { Database } from './_base'; +import { UserId } from './user'; + +// never use this directly + +// describe every function in the object +export interface IBlockedDb extends Database { + getBlockedUserFor(id: UserId): BlockedData[], + addBlockedUserFor(id: UserId, blocked: UserId): void, + removeBlockedUserFor(id: UserId, blocked: UserId): void, + unblockAllUserFor(id: UserId): void, +}; + +export const BlockedImpl: Omit = { + getBlockedUserFor(this: IBlockedDb, id: UserId): BlockedData[] { + const query = this.prepare('SELECT * FROM blocked WHERE user = @id'); + const data = query.all({ id }) as Partial[]; + return data.map(blockedFromRow).filter(b => !isNullish(b)); + }, + + unblockAllUserFor(this: IBlockedDb, id: UserId): void { + this.prepare('DELETE FROM blocked WHERE user = @id').run({ id }); + }, + addBlockedUserFor(this: IBlockedDb, id: UserId, blocked: UserId): void { + this.prepare('INSERT OR IGNORE INTO blocked (user, blocked) VALUES (@id, @blocked)').run({ id, blocked }); + }, + removeBlockedUserFor(this: IBlockedDb, id: UserId, blocked: UserId): void { + this.prepare('DELETE FROM blocked WHERE user = @id AND blocked = @blocked').run({ id, blocked }); + }, +}; + +export type BlockedId = number & { readonly __brand: unique symbol }; + +export type BlockedData = { + readonly id: BlockedId; + readonly user: UserId; + readonly blocked: UserId; +}; + +/** + * Get a blocked from a row + * + * @param row The data from sqlite + * + * @returns The blocked if it exists, undefined otherwise + */ +export function blockedFromRow(row?: Partial): BlockedData | undefined { + if (isNullish(row)) return undefined; + if (isNullish(row.id)) return undefined; + if (isNullish(row.user)) return undefined; + if (isNullish(row.blocked)) return undefined; + + return row as BlockedData; +}