Files
docmost_custom/apps/server/src/database/database.module.ts
T

164 lines
5.0 KiB
TypeScript
Raw Normal View History

2024-04-27 18:53:30 +01:00
import {
Global,
Logger,
Module,
OnApplicationBootstrap,
2025-03-06 13:38:37 +00:00
BeforeApplicationShutdown,
2024-04-27 18:53:30 +01:00
} from '@nestjs/common';
import { InjectKysely, KyselyModule } from 'nestjs-kysely';
2024-03-29 01:46:11 +00:00
import { EnvironmentService } from '../integrations/environment/environment.service';
import { CamelCasePlugin, LogEvent, sql } from 'kysely';
2024-03-29 01:46:11 +00:00
import { GroupRepo } from '@docmost/db/repos/group/group.repo';
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
import { GroupUserRepo } from '@docmost/db/repos/group/group-user.repo';
import { SpaceRepo } from '@docmost/db/repos/space/space.repo';
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
import { PageRepo } from './repos/page/page.repo';
import { CommentRepo } from './repos/comment/comment.repo';
import { PageHistoryRepo } from './repos/page/page-history.repo';
import { AttachmentRepo } from './repos/attachment/attachment.repo';
2024-04-27 18:53:30 +01:00
import { KyselyDB } from '@docmost/db/types/kysely.types';
import * as process from 'node:process';
2024-06-07 17:29:34 +01:00
import { MigrationService } from '@docmost/db/services/migration.service';
2024-09-19 15:51:51 +01:00
import { UserTokenRepo } from './repos/user-token/user-token.repo';
import { BacklinkRepo } from '@docmost/db/repos/backlink/backlink.repo';
2025-04-22 20:37:32 +01:00
import { ShareRepo } from '@docmost/db/repos/share/share.repo';
2026-02-14 20:00:38 -08:00
import { NotificationRepo } from '@docmost/db/repos/notification/notification.repo';
import { WatcherRepo } from '@docmost/db/repos/watcher/watcher.repo';
2025-10-07 17:34:32 +01:00
import { PageListener } from '@docmost/db/listeners/page.listener';
import { PostgresJSDialect } from 'kysely-postgres-js';
import * as postgres from 'postgres';
import { normalizePostgresUrl } from '../common/helpers';
2024-04-01 01:23:52 +01:00
2024-03-29 01:46:11 +00:00
@Global()
@Module({
imports: [
KyselyModule.forRootAsync({
imports: [],
inject: [EnvironmentService],
useFactory: (environmentService: EnvironmentService) => ({
dialect: new PostgresJSDialect({
postgres: postgres(
normalizePostgresUrl(environmentService.getDatabaseURL()),
{
max: environmentService.getDatabaseMaxPool(),
onnotice: () => {},
types: {
bigint: {
to: 20,
from: [20, 1700],
serialize: (value: number) => value.toString(),
parse: (value: string) => Number.parseInt(value),
},
},
},
),
2024-03-29 01:46:11 +00:00
}),
2024-03-29 16:25:42 +00:00
plugins: [new CamelCasePlugin()],
2024-03-29 01:46:11 +00:00
log: (event: LogEvent) => {
2024-06-07 17:29:34 +01:00
if (environmentService.getNodeEnv() !== 'development') return;
2025-03-07 00:06:25 +00:00
const logger = new Logger(DatabaseModule.name);
if (process.env.DEBUG_DB?.toLowerCase() === 'true') {
logger.debug(event.query.sql);
logger.debug('query time: ' + event.queryDurationMillis + ' ms');
2024-03-29 01:46:11 +00:00
}
},
}),
}),
],
providers: [
2024-06-07 17:29:34 +01:00
MigrationService,
2024-03-29 01:46:11 +00:00
WorkspaceRepo,
UserRepo,
GroupRepo,
GroupUserRepo,
SpaceRepo,
SpaceMemberRepo,
PageRepo,
PageHistoryRepo,
CommentRepo,
AttachmentRepo,
2024-09-19 15:51:51 +01:00
UserTokenRepo,
BacklinkRepo,
2025-10-07 17:34:32 +01:00
ShareRepo,
2026-02-14 20:00:38 -08:00
NotificationRepo,
WatcherRepo,
2025-10-07 17:34:32 +01:00
PageListener,
2024-03-29 01:46:11 +00:00
],
exports: [
WorkspaceRepo,
UserRepo,
GroupRepo,
GroupUserRepo,
SpaceRepo,
SpaceMemberRepo,
PageRepo,
PageHistoryRepo,
CommentRepo,
AttachmentRepo,
2024-09-19 15:51:51 +01:00
UserTokenRepo,
BacklinkRepo,
2025-10-07 17:34:32 +01:00
ShareRepo,
2026-02-14 20:00:38 -08:00
NotificationRepo,
WatcherRepo,
2024-03-29 01:46:11 +00:00
],
})
2025-03-06 13:38:37 +00:00
export class DatabaseModule
implements OnApplicationBootstrap, BeforeApplicationShutdown
{
2024-04-27 18:53:30 +01:00
private readonly logger = new Logger(DatabaseModule.name);
2024-06-07 17:29:34 +01:00
constructor(
@InjectKysely() private readonly db: KyselyDB,
private readonly migrationService: MigrationService,
private readonly environmentService: EnvironmentService,
) {}
2024-04-27 18:53:30 +01:00
async onApplicationBootstrap() {
await this.establishConnection();
2024-06-07 17:29:34 +01:00
if (this.environmentService.getNodeEnv() === 'production') {
await this.migrationService.migrateToLatest();
}
2024-04-27 18:53:30 +01:00
}
2025-03-06 13:38:37 +00:00
async beforeApplicationShutdown(): Promise<void> {
2024-04-27 18:53:30 +01:00
if (this.db) {
await this.db.destroy();
}
}
async establishConnection() {
2024-07-05 19:00:55 +01:00
const retryAttempts = 15;
2024-04-27 18:53:30 +01:00
const retryDelay = 3000;
this.logger.log('Establishing database connection');
for (let i = 0; i < retryAttempts; i++) {
try {
await sql`SELECT 1=1`.execute(this.db);
this.logger.log('Database connection successful');
break;
} catch (err) {
if (err['errors']) {
this.logger.error(err['errors'][0]);
} else {
this.logger.error(err);
}
if (i < retryAttempts - 1) {
this.logger.log(
2024-06-07 17:29:34 +01:00
`Retrying [${i + 1}/${retryAttempts}] in ${retryDelay / 1000} seconds`,
2024-04-27 18:53:30 +01:00
);
await new Promise((resolve) => setTimeout(resolve, retryDelay));
} else {
this.logger.error(
`Failed to connect to database after ${retryAttempts} attempts. Exiting...`,
);
process.exit(1);
}
}
}
}
}