import { Injectable } from '@angular/core'; import { Message, User, Room, Reaction, BanEntry } from '../models/index'; /** CQRS API exposed by the Electron preload script via `contextBridge`. */ interface ElectronAPI { command(command: { type: string; payload: unknown }): Promise; query(query: { type: string; payload: unknown }): Promise; } /** * Database service for the Electron (desktop) runtime. * * The SQLite database is managed by TypeORM in the Electron **main process** * (`electron/main.ts`). This service is a thin CQRS IPC client that dispatches * structured command/query objects through the unified `cqrs:command` and * `cqrs:query` channels exposed by the preload script. * * No initialisation IPC call is needed - the database is initialised and * migrations are run in main.ts before the renderer window is created. */ @Injectable({ providedIn: 'root' }) export class ElectronDatabaseService { /** Shorthand accessor for the preload-exposed CQRS API. */ private get api(): ElectronAPI { // eslint-disable-next-line return (window as any).electronAPI as ElectronAPI; } /** * No-op: the database is initialised in the main process before the * renderer window opens and requires no explicit bootstrap call here. */ async initialize(): Promise { /* no-op */ } /** Persist a single chat message. */ saveMessage(message: Message): Promise { return this.api.command({ type: 'save-message', payload: { message } }); } /** * Retrieve messages for a room, sorted oldest-first. * * @param roomId - Target room. * @param limit - Maximum number of messages to return. * @param offset - Number of messages to skip (for pagination). */ getMessages(roomId: string, limit = 100, offset = 0): Promise { return this.api.query({ type: 'get-messages', payload: { roomId, limit, offset } }); } /** Permanently delete a message by ID. */ deleteMessage(messageId: string): Promise { return this.api.command({ type: 'delete-message', payload: { messageId } }); } /** Apply partial updates to an existing message. */ updateMessage(messageId: string, updates: Partial): Promise { return this.api.command({ type: 'update-message', payload: { messageId, updates } }); } /** Retrieve a single message by ID, or `null` if not found. */ getMessageById(messageId: string): Promise { return this.api.query({ type: 'get-message-by-id', payload: { messageId } }); } /** Remove every message belonging to a room. */ clearRoomMessages(roomId: string): Promise { return this.api.command({ type: 'clear-room-messages', payload: { roomId } }); } /** Persist a reaction (deduplication is handled main-process side). */ saveReaction(reaction: Reaction): Promise { return this.api.command({ type: 'save-reaction', payload: { reaction } }); } /** Remove a specific reaction (user + emoji + message). */ removeReaction(messageId: string, userId: string, emoji: string): Promise { return this.api.command({ type: 'remove-reaction', payload: { messageId, userId, emoji } }); } /** Return all reactions for a given message. */ getReactionsForMessage(messageId: string): Promise { return this.api.query({ type: 'get-reactions-for-message', payload: { messageId } }); } /** Persist a user record. */ saveUser(user: User): Promise { return this.api.command({ type: 'save-user', payload: { user } }); } /** Retrieve a user by ID, or `null` if not found. */ getUser(userId: string): Promise { return this.api.query({ type: 'get-user', payload: { userId } }); } /** Retrieve the last-authenticated ("current") user, or `null`. */ getCurrentUser(): Promise { return this.api.query({ type: 'get-current-user', payload: {} }); } /** Store which user ID is considered "current" (logged-in). */ setCurrentUserId(userId: string): Promise { return this.api.command({ type: 'set-current-user-id', payload: { userId } }); } /** Retrieve users associated with a room. */ getUsersByRoom(roomId: string): Promise { return this.api.query({ type: 'get-users-by-room', payload: { roomId } }); } /** Apply partial updates to an existing user. */ updateUser(userId: string, updates: Partial): Promise { return this.api.command({ type: 'update-user', payload: { userId, updates } }); } /** Persist a room record. */ saveRoom(room: Room): Promise { return this.api.command({ type: 'save-room', payload: { room } }); } /** Retrieve a room by ID, or `null` if not found. */ getRoom(roomId: string): Promise { return this.api.query({ type: 'get-room', payload: { roomId } }); } /** Return every persisted room. */ getAllRooms(): Promise { return this.api.query({ type: 'get-all-rooms', payload: {} }); } /** Delete a room by ID (also removes its messages). */ deleteRoom(roomId: string): Promise { return this.api.command({ type: 'delete-room', payload: { roomId } }); } /** Apply partial updates to an existing room. */ updateRoom(roomId: string, updates: Partial): Promise { return this.api.command({ type: 'update-room', payload: { roomId, updates } }); } /** Persist a ban entry. */ saveBan(ban: BanEntry): Promise { return this.api.command({ type: 'save-ban', payload: { ban } }); } /** Remove a ban by the banned user's `oderId`. */ removeBan(oderId: string): Promise { return this.api.command({ type: 'remove-ban', payload: { oderId } }); } /** Return active bans for a room. */ getBansForRoom(roomId: string): Promise { return this.api.query({ type: 'get-bans-for-room', payload: { roomId } }); } /** Check whether a user is currently banned from a room. */ isUserBanned(userId: string, roomId: string): Promise { return this.api.query({ type: 'is-user-banned', payload: { userId, roomId } }); } /** Persist attachment metadata. */ // eslint-disable-next-line saveAttachment(attachment: any): Promise { return this.api.command({ type: 'save-attachment', payload: { attachment } }); } /** Return all attachment records for a message. */ // eslint-disable-next-line getAttachmentsForMessage(messageId: string): Promise { // eslint-disable-next-line return this.api.query({ type: 'get-attachments-for-message', payload: { messageId } }); } /** Return every persisted attachment record. */ // eslint-disable-next-line getAllAttachments(): Promise { // eslint-disable-next-line return this.api.query({ type: 'get-all-attachments', payload: {} }); } /** Delete all attachment records for a message. */ deleteAttachmentsForMessage(messageId: string): Promise { return this.api.command({ type: 'delete-attachments-for-message', payload: { messageId } }); } /** Wipe every table, removing all persisted data. */ clearAllData(): Promise { return this.api.command({ type: 'clear-all-data', payload: {} }); } }