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

144 lines
4.3 KiB
TypeScript
Raw Normal View History

2024-04-27 18:53:30 +01:00
import {
Global,
Logger,
Module,
OnApplicationBootstrap,
OnModuleDestroy,
} from '@nestjs/common';
import { InjectKysely, KyselyModule } from 'nestjs-kysely';
2024-03-29 01:46:11 +00:00
import { EnvironmentService } from '../integrations/environment/environment.service';
2024-04-27 18:53:30 +01:00
import { CamelCasePlugin, LogEvent, PostgresDialect, sql } from 'kysely';
2024-04-01 01:23:52 +01:00
import { Pool, types } from 'pg';
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';
2024-03-29 01:46:11 +00:00
2024-04-01 01:23:52 +01:00
// https://github.com/brianc/node-postgres/issues/811
types.setTypeParser(types.builtins.INT8, (val) => Number(val));
2024-03-29 01:46:11 +00:00
@Global()
@Module({
imports: [
KyselyModule.forRootAsync({
imports: [],
inject: [EnvironmentService],
useFactory: (environmentService: EnvironmentService) => ({
dialect: new PostgresDialect({
pool: new Pool({
connectionString: environmentService.getDatabaseURL(),
2024-07-05 18:59:16 +01:00
}).on('error', (err) => {
console.error('Database error:', err.message);
2024-03-29 16:25:42 +00:00
}),
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;
2024-03-29 01:46:11 +00:00
if (event.level === 'query') {
// console.log(event.query.sql);
2024-04-16 21:55:24 +01:00
//if (event.query.parameters.length > 0) {
//console.log('parameters: ' + event.query.parameters);
//}
// console.log('time: ' + event.queryDurationMillis);
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,
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,
2024-03-29 01:46:11 +00:00
],
})
2024-04-27 18:53:30 +01:00
export class DatabaseModule implements OnModuleDestroy, OnApplicationBootstrap {
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
}
async onModuleDestroy(): Promise<void> {
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);
}
}
}
}
}