Browse Source

Feat: Improve Repository pattern (#1380)

* improve repository pattern

* fix errors
pull/1648/head
Bernd Storath 9 months ago
committed by Bernd Storath
parent
commit
9338f29290
  1. 2
      src/server/api/account/create.post.ts
  2. 4
      src/server/api/account/setup.post.ts
  3. 2
      src/server/api/cnf/[oneTimeLink].ts
  4. 2
      src/server/api/features.get.ts
  5. 2
      src/server/api/lang.get.ts
  6. 4
      src/server/api/session.post.ts
  7. 2
      src/server/api/wireguard/client/[clientId]/generateOneTimeLink.post.ts
  8. 4
      src/server/middleware/session.ts
  9. 2
      src/server/middleware/setup.ts
  10. 40
      src/server/utils/WireGuard.ts
  11. 2
      src/server/utils/config.ts
  12. 2
      src/server/utils/session.ts
  13. 142
      src/services/database/lowdb.ts
  14. 14
      src/services/database/migrations/1.ts
  15. 25
      src/services/database/repositories/client.ts
  16. 38
      src/services/database/repositories/database.ts
  17. 4
      src/services/database/repositories/system.ts
  18. 12
      src/services/database/repositories/user.ts

2
src/server/api/account/create.post.ts

@ -3,6 +3,6 @@ export default defineEventHandler(async (event) => {
event, event,
validateZod(passwordType) validateZod(passwordType)
); );
await Database.createUser(username, password); await Database.user.create(username, password);
return { success: true }; return { success: true };
}); });

4
src/server/api/account/setup.post.ts

@ -3,13 +3,13 @@ export default defineEventHandler(async (event) => {
event, event,
validateZod(passwordType) validateZod(passwordType)
); );
const users = await Database.getUsers(); const users = await Database.user.findAll();
if (users.length !== 0) { if (users.length !== 0) {
throw createError({ throw createError({
statusCode: 400, statusCode: 400,
statusMessage: 'Invalid state', statusMessage: 'Invalid state',
}); });
} }
await Database.createUser(username, password); await Database.user.create(username, password);
return { success: true }; return { success: true };
}); });

2
src/server/api/cnf/[oneTimeLink].ts

@ -1,5 +1,5 @@
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const system = await Database.getSystem(); const system = await Database.system.get();
if (!system.oneTimeLinks.enabled) { if (!system.oneTimeLinks.enabled) {
throw createError({ throw createError({
statusCode: 404, statusCode: 404,

2
src/server/api/features.get.ts

@ -1,5 +1,5 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {
const system = await Database.getSystem(); const system = await Database.system.get();
return { return {
trafficStats: system.trafficStats, trafficStats: system.trafficStats,
sortClients: system.sortClients, sortClients: system.sortClients,

2
src/server/api/lang.get.ts

@ -1,5 +1,5 @@
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
setHeader(event, 'Content-Type', 'application/json'); setHeader(event, 'Content-Type', 'application/json');
const system = await Database.getSystem(); const system = await Database.system.get();
return system.lang; return system.lang;
}); });

4
src/server/api/session.post.ts

@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => {
validateZod(credentialsType) validateZod(credentialsType)
); );
const users = await Database.getUsers(); const users = await Database.user.findAll();
const user = users.find((user) => user.username == username); const user = users.find((user) => user.username == username);
if (!user) if (!user)
throw createError({ throw createError({
@ -23,7 +23,7 @@ export default defineEventHandler(async (event) => {
}); });
} }
const system = await Database.getSystem(); const system = await Database.system.get();
const conf: SessionConfig = system.sessionConfig; const conf: SessionConfig = system.sessionConfig;

2
src/server/api/wireguard/client/[clientId]/generateOneTimeLink.post.ts

@ -1,5 +1,5 @@
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const system = await Database.getSystem(); const system = await Database.system.get();
if (!system.oneTimeLinks.enabled) { if (!system.oneTimeLinks.enabled) {
throw createError({ throw createError({
status: 404, status: 404,

4
src/server/middleware/session.ts

@ -10,7 +10,7 @@ export default defineEventHandler(async (event) => {
) { ) {
return; return;
} }
const system = await Database.getSystem(); const system = await Database.system.get();
if (!system) if (!system)
throw createError({ throw createError({
statusCode: 500, statusCode: 500,
@ -24,7 +24,7 @@ export default defineEventHandler(async (event) => {
const authorization = getHeader(event, 'Authorization'); const authorization = getHeader(event, 'Authorization');
if (url.pathname.startsWith('/api/') && authorization) { if (url.pathname.startsWith('/api/') && authorization) {
const users = await Database.getUsers(); const users = await Database.user.findAll();
const user = users.find((user) => user.id == session.data.userId); const user = users.find((user) => user.id == session.data.userId);
if (!user) if (!user)
throw createError({ throw createError({

2
src/server/middleware/setup.ts

@ -10,7 +10,7 @@ export default defineEventHandler(async (event) => {
return; return;
} }
const users = await Database.getUsers(); const users = await Database.user.findAll();
if (users.length === 0) { if (users.length === 0) {
if (url.pathname.startsWith('/api/')) { if (url.pathname.startsWith('/api/')) {
throw createError({ throw createError({

40
src/server/utils/WireGuard.ts

@ -16,8 +16,8 @@ class WireGuard {
} }
async #saveWireguardConfig() { async #saveWireguardConfig() {
const system = await Database.getSystem(); const system = await Database.system.get();
const clients = await Database.getClients(); const clients = await Database.client.findAll();
const result = []; const result = [];
result.push(wg.generateServerInterface(system)); result.push(wg.generateServerInterface(system));
@ -42,7 +42,7 @@ class WireGuard {
} }
async getClients() { async getClients() {
const dbClients = await Database.getClients(); const dbClients = await Database.client.findAll();
const clients = Object.entries(dbClients).map(([clientId, client]) => ({ const clients = Object.entries(dbClients).map(([clientId, client]) => ({
id: clientId, id: clientId,
name: client.name, name: client.name,
@ -91,7 +91,7 @@ class WireGuard {
} }
async getClient({ clientId }: { clientId: string }) { async getClient({ clientId }: { clientId: string }) {
const client = await Database.getClient(clientId); const client = await Database.client.findById(clientId);
if (!client) { if (!client) {
throw createError({ throw createError({
statusCode: 404, statusCode: 404,
@ -103,7 +103,7 @@ class WireGuard {
} }
async getClientConfiguration({ clientId }: { clientId: string }) { async getClientConfiguration({ clientId }: { clientId: string }) {
const system = await Database.getSystem(); const system = await Database.system.get();
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
return wg.generateClientConfig(system, client); return wg.generateClientConfig(system, client);
@ -124,8 +124,8 @@ class WireGuard {
name: string; name: string;
expireDate: string | null; expireDate: string | null;
}) { }) {
const system = await Database.getSystem(); const system = await Database.system.get();
const clients = await Database.getClients(); const clients = await Database.client.findAll();
const privateKey = await wg.generatePrivateKey(); const privateKey = await wg.generatePrivateKey();
const publicKey = await wg.getPublicKey(privateKey); const publicKey = await wg.getPublicKey(privateKey);
@ -162,7 +162,7 @@ class WireGuard {
client.expiresAt = date.toISOString(); client.expiresAt = date.toISOString();
} }
await Database.createClient(client); await Database.client.create(client);
await this.saveConfig(); await this.saveConfig();
@ -170,12 +170,12 @@ class WireGuard {
} }
async deleteClient({ clientId }: { clientId: string }) { async deleteClient({ clientId }: { clientId: string }) {
await Database.deleteClient(clientId); await Database.client.delete(clientId);
await this.saveConfig(); await this.saveConfig();
} }
async enableClient({ clientId }: { clientId: string }) { async enableClient({ clientId }: { clientId: string }) {
await Database.toggleClient(clientId, true); await Database.client.toggle(clientId, true);
await this.saveConfig(); await this.saveConfig();
} }
@ -184,7 +184,7 @@ class WireGuard {
const key = `${clientId}-${Math.floor(Math.random() * 1000)}`; const key = `${clientId}-${Math.floor(Math.random() * 1000)}`;
const oneTimeLink = Math.abs(CRC32.str(key)).toString(16); const oneTimeLink = Math.abs(CRC32.str(key)).toString(16);
const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString(); const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString();
await Database.createOneTimeLink(clientId, { await Database.client.createOneTimeLink(clientId, {
oneTimeLink, oneTimeLink,
expiresAt, expiresAt,
}); });
@ -192,12 +192,12 @@ class WireGuard {
} }
async eraseOneTimeLink({ clientId }: { clientId: string }) { async eraseOneTimeLink({ clientId }: { clientId: string }) {
await Database.deleteOneTimeLink(clientId); await Database.client.deleteOneTimeLink(clientId);
await this.saveConfig(); await this.saveConfig();
} }
async disableClient({ clientId }: { clientId: string }) { async disableClient({ clientId }: { clientId: string }) {
await Database.toggleClient(clientId, false); await Database.client.toggle(clientId, false);
await this.saveConfig(); await this.saveConfig();
} }
@ -209,7 +209,7 @@ class WireGuard {
clientId: string; clientId: string;
name: string; name: string;
}) { }) {
await Database.updateClientName(clientId, name); await Database.client.updateName(clientId, name);
await this.saveConfig(); await this.saveConfig();
} }
@ -228,7 +228,7 @@ class WireGuard {
}); });
} }
await Database.updateClientAddress4(clientId, address4); await Database.client.updateAddress4(clientId, address4);
await this.saveConfig(); await this.saveConfig();
} }
@ -250,7 +250,7 @@ class WireGuard {
updatedDate = date.toISOString(); updatedDate = date.toISOString();
} }
await Database.updateClientExpirationDate(clientId, updatedDate); await Database.client.updateExpirationDate(clientId, updatedDate);
await this.saveConfig(); await this.saveConfig();
} }
@ -323,8 +323,8 @@ class WireGuard {
} }
async cronJob() { async cronJob() {
const clients = await Database.getClients(); const clients = await Database.client.findAll();
const system = await Database.getSystem(); const system = await Database.system.get();
// Expires Feature // Expires Feature
if (system.clientExpiration.enabled) { if (system.clientExpiration.enabled) {
for (const client of Object.values(clients)) { for (const client of Object.values(clients)) {
@ -334,7 +334,7 @@ class WireGuard {
new Date() > new Date(client.expiresAt) new Date() > new Date(client.expiresAt)
) { ) {
DEBUG(`Client ${client.id} expired.`); DEBUG(`Client ${client.id} expired.`);
await Database.toggleClient(client.id, false); await Database.client.toggle(client.id, false);
} }
} }
} }
@ -346,7 +346,7 @@ class WireGuard {
new Date() > new Date(client.oneTimeLink.expiresAt) new Date() > new Date(client.oneTimeLink.expiresAt)
) { ) {
DEBUG(`Client ${client.id} One Time Link expired.`); DEBUG(`Client ${client.id} One Time Link expired.`);
await Database.deleteOneTimeLink(client.id); await Database.client.deleteOneTimeLink(client.id);
} }
} }
} }

2
src/server/utils/config.ts

@ -34,7 +34,7 @@ export async function migrateConfig(input: unknown) {
if (!res.success) { if (!res.success) {
throw new Error('Invalid Config'); throw new Error('Invalid Config');
} }
const system = await Database.getSystem(); const system = await Database.system.get();
const oldConfig = res.data; const oldConfig = res.data;
const oldCidr = parseCidr(oldConfig.server.address + '/24'); const oldCidr = parseCidr(oldConfig.server.address + '/24');
const db = { const db = {

2
src/server/utils/session.ts

@ -5,7 +5,7 @@ export type WGSession = {
}; };
export async function useWGSession(event: H3Event) { export async function useWGSession(event: H3Event) {
const system = await Database.getSystem(); const system = await Database.system.get();
if (!system) throw new Error('Invalid'); if (!system) throw new Error('Invalid');
return useSession<Partial<WGSession>>(event, system.sessionConfig); return useSession<Partial<WGSession>>(event, system.sessionConfig);
} }

142
src/services/database/lowdb.ts

@ -9,52 +9,26 @@ import {
import { JSONFilePreset } from 'lowdb/node'; import { JSONFilePreset } from 'lowdb/node';
import type { Low } from 'lowdb'; import type { Low } from 'lowdb';
import type { User } from './repositories/user'; import { UserRepository, type User } from './repositories/user';
import type { Database } from './repositories/database'; import type { Database } from './repositories/database';
import { migrationRunner } from './migrations'; import { migrationRunner } from './migrations';
import type { Client, NewClient, OneTimeLink } from './repositories/client'; import {
ClientRepository,
type Client,
type NewClient,
type OneTimeLink,
} from './repositories/client';
import { SystemRepository } from './repositories/system';
const DEBUG = debug('LowDB'); const DEBUG = debug('LowDB');
export default class LowDB extends DatabaseProvider { export class LowDBSystem extends SystemRepository {
#db!: Low<Database>; #db: Low<Database>;
#connected = false; constructor(db: Low<Database>) {
super();
private async __init() { this.#db = db;
const dbFilePath = '/etc/wireguard/db.json';
this.#db = await JSONFilePreset(dbFilePath, DEFAULT_DATABASE);
} }
async get() {
/**
* @throws
*/
async connect() {
if (this.#connected) {
return;
}
try {
await this.__init();
DEBUG('Running Migrations');
await migrationRunner(this.#db);
DEBUG('Migrations ran successfully');
} catch (e) {
DEBUG(e);
throw new Error('Failed to initialize Database');
}
this.#connected = true;
DEBUG('Connected successfully');
}
get connected() {
return this.#connected;
}
async disconnect() {
this.#connected = false;
DEBUG('Disconnected successfully');
}
async getSystem() {
DEBUG('Get System'); DEBUG('Get System');
const system = this.#db.data.system; const system = this.#db.data.system;
// system is only null if migration failed // system is only null if migration failed
@ -63,18 +37,25 @@ export default class LowDB extends DatabaseProvider {
} }
return system; return system;
} }
}
export class LowDBUser extends UserRepository {
#db: Low<Database>;
constructor(db: Low<Database>) {
super();
this.#db = db;
}
// TODO: return copy to avoid mutation (everywhere) // TODO: return copy to avoid mutation (everywhere)
async getUsers() { async findAll() {
return this.#db.data.users; return this.#db.data.users;
} }
async getUser(id: string) { async findById(id: string) {
DEBUG('Get User'); DEBUG('Get User');
return this.#db.data.users.find((user) => user.id === id); return this.#db.data.users.find((user) => user.id === id);
} }
async createUser(username: string, password: string) { async create(username: string, password: string) {
DEBUG('Create User'); DEBUG('Create User');
const isUserExist = this.#db.data.users.find( const isUserExist = this.#db.data.users.find(
@ -106,9 +87,9 @@ export default class LowDB extends DatabaseProvider {
await this.#db.update((data) => data.users.push(newUser)); await this.#db.update((data) => data.users.push(newUser));
} }
async updateUser(user: User) { async update(user: User) {
// TODO: avoid mutation, prefer .update, updatedAt // TODO: avoid mutation, prefer .update, updatedAt
let oldUser = await this.getUser(user.id); let oldUser = await this.findById(user.id);
if (oldUser) { if (oldUser) {
DEBUG('Update User'); DEBUG('Update User');
oldUser = user; oldUser = user;
@ -116,25 +97,32 @@ export default class LowDB extends DatabaseProvider {
} }
} }
async deleteUser(id: string) { async delete(id: string) {
DEBUG('Delete User'); DEBUG('Delete User');
const idx = this.#db.data.users.findIndex((user) => user.id === id); const idx = this.#db.data.users.findIndex((user) => user.id === id);
if (idx !== -1) { if (idx !== -1) {
await this.#db.update((data) => data.users.splice(idx, 1)); await this.#db.update((data) => data.users.splice(idx, 1));
} }
} }
}
async getClients() { export class LowDBClient extends ClientRepository {
#db: Low<Database>;
constructor(db: Low<Database>) {
super();
this.#db = db;
}
async findAll() {
DEBUG('GET Clients'); DEBUG('GET Clients');
return this.#db.data.clients; return this.#db.data.clients;
} }
async getClient(id: string) { async findById(id: string) {
DEBUG('Get Client'); DEBUG('Get Client');
return this.#db.data.clients[id]; return this.#db.data.clients[id];
} }
async createClient(client: NewClient) { async create(client: NewClient) {
DEBUG('Create Client'); DEBUG('Create Client');
const now = new Date().toISOString(); const now = new Date().toISOString();
const newClient: Client = { ...client, createdAt: now, updatedAt: now }; const newClient: Client = { ...client, createdAt: now, updatedAt: now };
@ -143,7 +131,7 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
async deleteClient(id: string) { async delete(id: string) {
DEBUG('Delete Client'); DEBUG('Delete Client');
await this.#db.update((data) => { await this.#db.update((data) => {
// TODO: find something better than delete // TODO: find something better than delete
@ -152,7 +140,7 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
async toggleClient(id: string, enable: boolean) { async toggle(id: string, enable: boolean) {
DEBUG('Toggle Client'); DEBUG('Toggle Client');
await this.#db.update((data) => { await this.#db.update((data) => {
if (data.clients[id]) { if (data.clients[id]) {
@ -161,7 +149,7 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
async updateClientName(id: string, name: string) { async updateName(id: string, name: string) {
DEBUG('Update Client Name'); DEBUG('Update Client Name');
await this.#db.update((data) => { await this.#db.update((data) => {
if (data.clients[id]) { if (data.clients[id]) {
@ -170,7 +158,7 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
async updateClientAddress4(id: string, address4: string) { async updateAddress4(id: string, address4: string) {
DEBUG('Update Client Address4'); DEBUG('Update Client Address4');
await this.#db.update((data) => { await this.#db.update((data) => {
if (data.clients[id]) { if (data.clients[id]) {
@ -179,7 +167,7 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
async updateClientExpirationDate(id: string, expirationDate: string | null) { async updateExpirationDate(id: string, expirationDate: string | null) {
DEBUG('Update Client Expiration Date'); DEBUG('Update Client Expiration Date');
await this.#db.update((data) => { await this.#db.update((data) => {
if (data.clients[id]) { if (data.clients[id]) {
@ -211,3 +199,49 @@ export default class LowDB extends DatabaseProvider {
}); });
} }
} }
export default class LowDB extends DatabaseProvider {
#db!: Low<Database>;
#connected = false;
system!: LowDBSystem;
user!: LowDBUser;
client!: LowDBClient;
/**
* @throws
*/
async connect() {
if (this.#connected) {
return;
}
try {
DEBUG('Connecting');
this.#db = await JSONFilePreset(
'/etc/wireguard/db.json',
DEFAULT_DATABASE
);
DEBUG('Running Migrations');
await migrationRunner(this.#db);
DEBUG('Migrations ran successfully');
} catch (e) {
DEBUG(e);
throw new Error('Failed to initialize Database');
}
this.system = new LowDBSystem(this.#db);
this.user = new LowDBUser(this.#db);
this.client = new LowDBClient(this.#db);
this.#connected = true;
DEBUG('Connected successfully');
}
get connected() {
return this.#connected;
}
async disconnect() {
this.#connected = false;
DEBUG('Disconnected successfully');
}
}

14
src/services/database/migrations/1.ts

@ -72,29 +72,27 @@ export async function run1(db: Low<Database>) {
}; };
// TODO: properly check if ipv6 support // TODO: properly check if ipv6 support
database.system.iptables.PostUp = ` database.system.iptables.PostUp =
iptables -t nat -A POSTROUTING -s ${database.system.userConfig.address4Range} -o ${database.system.wgDevice} -j MASQUERADE; `iptables -t nat -A POSTROUTING -s ${database.system.userConfig.address4Range} -o ${database.system.wgDevice} -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; iptables -A INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT;
ip6tables -t nat -A POSTROUTING -s ${database.system.userConfig.address6Range} -o ${database.system.wgDevice} -j MASQUERADE; ip6tables -t nat -A POSTROUTING -s ${database.system.userConfig.address6Range} -o ${database.system.wgDevice} -j MASQUERADE;
ip6tables -A INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT;
ip6tables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT;`
`
.split('\n') .split('\n')
.join(' '); .join(' ');
database.system.iptables.PostDown = ` database.system.iptables.PostDown =
iptables -t nat -D POSTROUTING -s ${database.system.userConfig.address4Range} -o ${database.system.wgDevice} -j MASQUERADE; `iptables -t nat -D POSTROUTING -s ${database.system.userConfig.address4Range} -o ${database.system.wgDevice} -j MASQUERADE;
iptables -D INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; iptables -D INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT;
iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT;
ip6tables -t nat -D POSTROUTING -s ${database.system.userConfig.address6Range} -o ${database.system.wgDevice} -j MASQUERADE; ip6tables -t nat -D POSTROUTING -s ${database.system.userConfig.address6Range} -o ${database.system.wgDevice} -j MASQUERADE;
ip6tables -D INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; ip6tables -D INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT;
ip6tables -D FORWARD -o wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT;`
`
.split('\n') .split('\n')
.join(' '); .join(' ');

25
src/services/database/repositories/client.ts

@ -31,18 +31,21 @@ export type NewClient = Omit<Client, 'createdAt' | 'updatedAt'>;
* Interface for client-related database operations. * Interface for client-related database operations.
* This interface provides methods for managing client data. * This interface provides methods for managing client data.
*/ */
export interface ClientRepository { export abstract class ClientRepository {
getClients(): Promise<Record<string, Client>>; abstract findAll(): Promise<Record<string, Client>>;
getClient(id: string): Promise<Client | undefined>; abstract findById(id: string): Promise<Client | undefined>;
createClient(client: NewClient): Promise<void>; abstract create(client: NewClient): Promise<void>;
deleteClient(id: string): Promise<void>; abstract delete(id: string): Promise<void>;
toggleClient(id: string, enable: boolean): Promise<void>; abstract toggle(id: string, enable: boolean): Promise<void>;
updateClientName(id: string, name: string): Promise<void>; abstract updateName(id: string, name: string): Promise<void>;
updateClientAddress4(id: string, address4: string): Promise<void>; abstract updateAddress4(id: string, address4: string): Promise<void>;
updateClientExpirationDate( abstract updateExpirationDate(
id: string, id: string,
expirationDate: string | null expirationDate: string | null
): Promise<void>; ): Promise<void>;
deleteOneTimeLink(id: string): Promise<void>; abstract deleteOneTimeLink(id: string): Promise<void>;
createOneTimeLink(id: string, oneTimeLink: OneTimeLink): Promise<void>; abstract createOneTimeLink(
id: string,
oneTimeLink: OneTimeLink
): Promise<void>;
} }

38
src/services/database/repositories/database.ts

@ -1,9 +1,4 @@
import type { import type { ClientRepository, Client } from './client';
ClientRepository,
Client,
NewClient,
OneTimeLink,
} from './client';
import type { System, SystemRepository } from './system'; import type { System, SystemRepository } from './system';
import type { User, UserRepository } from './user'; import type { User, UserRepository } from './user';
@ -29,9 +24,7 @@ export const DEFAULT_DATABASE: Database = {
* **Note :** Always throw `DatabaseError` to ensure proper API error handling. * **Note :** Always throw `DatabaseError` to ensure proper API error handling.
* *
*/ */
export abstract class DatabaseProvider export abstract class DatabaseProvider {
implements SystemRepository, UserRepository, ClientRepository
{
/** /**
* Connects to the database. * Connects to the database.
*/ */
@ -42,30 +35,9 @@ export abstract class DatabaseProvider
*/ */
abstract disconnect(): Promise<void>; abstract disconnect(): Promise<void>;
abstract getSystem(): Promise<System>; abstract system: SystemRepository;
abstract user: UserRepository;
abstract getUsers(): Promise<User[]>; abstract client: ClientRepository;
abstract getUser(id: string): Promise<User | undefined>;
abstract createUser(username: string, password: string): Promise<void>;
abstract updateUser(user: User): Promise<void>;
abstract deleteUser(id: string): Promise<void>;
abstract getClients(): Promise<Record<string, Client>>;
abstract getClient(id: string): Promise<Client | undefined>;
abstract createClient(client: NewClient): Promise<void>;
abstract deleteClient(id: string): Promise<void>;
abstract toggleClient(id: string, enable: boolean): Promise<void>;
abstract updateClientName(id: string, name: string): Promise<void>;
abstract updateClientAddress4(id: string, address4: string): Promise<void>;
abstract updateClientExpirationDate(
id: string,
expirationDate: string | null
): Promise<void>;
abstract deleteOneTimeLink(id: string): Promise<void>;
abstract createOneTimeLink(
id: string,
oneTimeLink: OneTimeLink
): Promise<void>;
} }
/** /**

4
src/services/database/repositories/system.ts

@ -80,9 +80,9 @@ export type System = {
* This interface provides methods for retrieving system configuration data * This interface provides methods for retrieving system configuration data
* and specific system properties, such as the language setting, from the database. * and specific system properties, such as the language setting, from the database.
*/ */
export interface SystemRepository { export abstract class SystemRepository {
/** /**
* Retrieves the system configuration data from the database. * Retrieves the system configuration data from the database.
*/ */
getSystem(): Promise<System>; abstract get(): Promise<System>;
} }

12
src/services/database/repositories/user.ts

@ -28,26 +28,26 @@ export type User = {
* Interface for user-related database operations. * Interface for user-related database operations.
* This interface provides methods for managing user data. * This interface provides methods for managing user data.
*/ */
export interface UserRepository { export abstract class UserRepository {
/** /**
* Retrieves all users from the database. * Retrieves all users from the database.
*/ */
getUsers(): Promise<User[]>; abstract findAll(): Promise<User[]>;
/** /**
* Retrieves a user by their ID or User object from the database. * Retrieves a user by their ID or User object from the database.
*/ */
getUser(id: string): Promise<User | undefined>; abstract findById(id: string): Promise<User | undefined>;
createUser(username: string, password: string): Promise<void>; abstract create(username: string, password: string): Promise<void>;
/** /**
* Updates a user in the database. * Updates a user in the database.
*/ */
updateUser(user: User): Promise<void>; abstract update(user: User): Promise<void>;
/** /**
* Deletes a user from the database. * Deletes a user from the database.
*/ */
deleteUser(id: string): Promise<void>; abstract delete(id: string): Promise<void>;
} }

Loading…
Cancel
Save