refactor(server): database config (#13730)

This commit is contained in:
Jason Rasmussen 2024-10-24 17:12:25 -04:00 committed by GitHub
parent 151ba9f1d9
commit fb995816a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 87 additions and 84 deletions

View File

@ -24,10 +24,10 @@
"typeorm": "typeorm", "typeorm": "typeorm",
"lifecycle": "node ./dist/utils/lifecycle.js", "lifecycle": "node ./dist/utils/lifecycle.js",
"typeorm:migrations:create": "typeorm migration:create", "typeorm:migrations:create": "typeorm migration:create",
"typeorm:migrations:generate": "typeorm migration:generate -d ./dist/database.config.js", "typeorm:migrations:generate": "typeorm migration:generate -d ./dist/bin/database.js",
"typeorm:migrations:run": "typeorm migration:run -d ./dist/database.config.js", "typeorm:migrations:run": "typeorm migration:run -d ./dist/bin/database.js",
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/database.config.js", "typeorm:migrations:revert": "typeorm migration:revert -d ./dist/bin/database.js",
"typeorm:schema:drop": "typeorm query -d ./dist/database.config.js 'DROP schema public cascade; CREATE schema public;'", "typeorm:schema:drop": "typeorm query -d ./dist/bin/database.js 'DROP schema public cascade; CREATE schema public;'",
"typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run", "typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run",
"sync:open-api": "node ./dist/bin/sync-open-api.js", "sync:open-api": "node ./dist/bin/sync-open-api.js",
"sync:sql": "node ./dist/bin/sync-sql.js", "sync:sql": "node ./dist/bin/sync-sql.js",

View File

@ -9,7 +9,6 @@ import { OpenTelemetryModule } from 'nestjs-otel';
import { commands } from 'src/commands'; import { commands } from 'src/commands';
import { clsConfig, immichAppConfig } from 'src/config'; import { clsConfig, immichAppConfig } from 'src/config';
import { controllers } from 'src/controllers'; import { controllers } from 'src/controllers';
import { databaseConfig } from 'src/database.config';
import { entities } from 'src/entities'; import { entities } from 'src/entities';
import { ImmichWorker } from 'src/enum'; import { ImmichWorker } from 'src/enum';
import { IEventRepository } from 'src/interfaces/event.interface'; import { IEventRepository } from 'src/interfaces/event.interface';
@ -38,7 +37,7 @@ const middleware = [
]; ];
const configRepository = new ConfigRepository(); const configRepository = new ConfigRepository();
const { bull, otel } = configRepository.getEnv(); const { bull, database, otel } = configRepository.getEnv();
const imports = [ const imports = [
BullModule.forRoot(bull.config), BullModule.forRoot(bull.config),
@ -50,7 +49,7 @@ const imports = [
inject: [ModuleRef], inject: [ModuleRef],
useFactory: (moduleRef: ModuleRef) => { useFactory: (moduleRef: ModuleRef) => {
return { return {
...databaseConfig, ...database.config,
poolErrorHandler: (error) => { poolErrorHandler: (error) => {
moduleRef.get(DatabaseService, { strict: false }).handleConnectionError(error); moduleRef.get(DatabaseService, { strict: false }).handleConnectionError(error);
}, },

View File

@ -0,0 +1,11 @@
import { ConfigRepository } from 'src/repositories/config.repository';
import { DataSource } from 'typeorm';
const { database } = new ConfigRepository().getEnv();
/**
* @deprecated - DO NOT USE THIS
*
* this export is ONLY to be used for TypeORM commands in package.json#scripts
*/
export const dataSource = new DataSource({ ...database.config, host: 'localhost' });

View File

@ -8,7 +8,6 @@ import { OpenTelemetryModule } from 'nestjs-otel';
import { mkdir, rm, writeFile } from 'node:fs/promises'; import { mkdir, rm, writeFile } from 'node:fs/promises';
import { join } from 'node:path'; import { join } from 'node:path';
import { format } from 'sql-formatter'; import { format } from 'sql-formatter';
import { databaseConfig } from 'src/database.config';
import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators'; import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators';
import { entities } from 'src/entities'; import { entities } from 'src/entities';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
@ -74,12 +73,12 @@ class SqlGenerator {
await rm(this.options.targetDir, { force: true, recursive: true }); await rm(this.options.targetDir, { force: true, recursive: true });
await mkdir(this.options.targetDir); await mkdir(this.options.targetDir);
const { otel } = new ConfigRepository().getEnv(); const { database, otel } = new ConfigRepository().getEnv();
const moduleFixture = await Test.createTestingModule({ const moduleFixture = await Test.createTestingModule({
imports: [ imports: [
TypeOrmModule.forRoot({ TypeOrmModule.forRoot({
...databaseConfig, ...database.config,
host: 'localhost', host: 'localhost',
entities, entities,
logging: ['query'], logging: ['query'],

View File

@ -1,35 +0,0 @@
import { ConfigRepository } from 'src/repositories/config.repository';
import { DataSource } from 'typeorm';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
const { database } = new ConfigRepository().getEnv();
const { url, host, port, username, password, name } = database;
const urlOrParts = url
? { url }
: {
host,
port,
username,
password,
database: name,
};
/* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/
export const databaseConfig: PostgresConnectionOptions = {
type: 'postgres',
entities: [__dirname + '/entities/*.entity.{js,ts}'],
migrations: [__dirname + '/migrations/*.{js,ts}'],
subscribers: [__dirname + '/subscribers/*.{js,ts}'],
migrationsRun: false,
synchronize: false,
connectTimeoutMS: 10_000, // 10 seconds
parseInt8: true,
...urlOrParts,
};
/**
* @deprecated - DO NOT USE THIS
*
* this export is ONLY to be used for TypeORM commands in package.json#scripts
*/
export const dataSource = new DataSource({ ...databaseConfig, host: 'localhost' });

View File

@ -4,6 +4,7 @@ import { RedisOptions } from 'ioredis';
import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces'; import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces';
import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum'; import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
import { VectorExtension } from 'src/interfaces/database.interface'; import { VectorExtension } from 'src/interfaces/database.interface';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
export const IConfigRepository = 'IConfigRepository'; export const IConfigRepository = 'IConfigRepository';
@ -36,12 +37,7 @@ export interface EnvData {
}; };
database: { database: {
url?: string; config: PostgresConnectionOptions;
host: string;
port: number;
username: string;
password: string;
name: string;
skipMigrations: boolean; skipMigrations: boolean;
vectorExtension: VectorExtension; vectorExtension: VectorExtension;
}; };

View File

@ -66,12 +66,14 @@ describe('getEnv', () => {
it('should use defaults', () => { it('should use defaults', () => {
const { database } = getEnv(); const { database } = getEnv();
expect(database).toEqual({ expect(database).toEqual({
url: undefined, config: expect.objectContaining({
host: 'database', type: 'postgres',
port: 5432, host: 'database',
name: 'immich', port: 5432,
username: 'postgres', database: 'immich',
password: 'postgres', username: 'postgres',
password: 'postgres',
}),
skipMigrations: false, skipMigrations: false,
vectorExtension: 'vectors', vectorExtension: 'vectors',
}); });

View File

@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { join } from 'node:path'; import { join, resolve } from 'node:path';
import { citiesFile, excludePaths } from 'src/constants'; import { citiesFile, excludePaths } from 'src/constants';
import { Telemetry } from 'src/decorators'; import { Telemetry } from 'src/decorators';
import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum'; import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
@ -46,10 +46,14 @@ const getEnv = (): EnvData => {
const isProd = environment === ImmichEnvironment.PRODUCTION; const isProd = environment === ImmichEnvironment.PRODUCTION;
const buildFolder = process.env.IMMICH_BUILD_DATA || '/build'; const buildFolder = process.env.IMMICH_BUILD_DATA || '/build';
const folders = { const folders = {
// eslint-disable-next-line unicorn/prefer-module
dist: resolve(`${__dirname}/..`),
geodata: join(buildFolder, 'geodata'), geodata: join(buildFolder, 'geodata'),
web: join(buildFolder, 'www'), web: join(buildFolder, 'www'),
}; };
const databaseUrl = process.env.DB_URL;
let redisConfig = { let redisConfig = {
host: process.env.REDIS_HOSTNAME || 'redis', host: process.env.REDIS_HOSTNAME || 'redis',
port: Number.parseInt(process.env.REDIS_PORT || '') || 6379, port: Number.parseInt(process.env.REDIS_PORT || '') || 6379,
@ -118,12 +122,25 @@ const getEnv = (): EnvData => {
}, },
database: { database: {
url: process.env.DB_URL, config: {
host: process.env.DB_HOSTNAME || 'database', type: 'postgres',
port: Number(process.env.DB_PORT) || 5432, entities: [`${folders.dist}/entities` + '/*.entity.{js,ts}'],
username: process.env.DB_USERNAME || 'postgres', migrations: [`${folders.dist}/migrations` + '/*.{js,ts}'],
password: process.env.DB_PASSWORD || 'postgres', subscribers: [`${folders.dist}/subscribers` + '/*.{js,ts}'],
name: process.env.DB_DATABASE_NAME || 'immich', migrationsRun: false,
synchronize: false,
connectTimeoutMS: 10_000, // 10 seconds
parseInt8: true,
...(databaseUrl
? { url: databaseUrl }
: {
host: process.env.DB_HOSTNAME || 'database',
port: Number(process.env.DB_PORT) || 5432,
username: process.env.DB_USERNAME || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
database: process.env.DB_DATABASE_NAME || 'immich',
}),
},
skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true', skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true',
vectorExtension: vectorExtension:

View File

@ -60,11 +60,14 @@ describe(DatabaseService.name, () => {
configMock.getEnv.mockReturnValue( configMock.getEnv.mockReturnValue(
mockEnvData({ mockEnvData({
database: { database: {
host: 'database', config: {
port: 5432, type: 'postgres',
username: 'postgres', host: 'database',
password: 'postgres', port: 5432,
name: 'immich', username: 'postgres',
password: 'postgres',
database: 'immich',
},
skipMigrations: false, skipMigrations: false,
vectorExtension: extension, vectorExtension: extension,
}, },
@ -286,11 +289,14 @@ describe(DatabaseService.name, () => {
configMock.getEnv.mockReturnValue( configMock.getEnv.mockReturnValue(
mockEnvData({ mockEnvData({
database: { database: {
host: 'database', config: {
port: 5432, type: 'postgres',
username: 'postgres', host: 'database',
password: 'postgres', port: 5432,
name: 'immich', username: 'postgres',
password: 'postgres',
database: 'immich',
},
skipMigrations: true, skipMigrations: true,
vectorExtension: DatabaseExtension.VECTORS, vectorExtension: DatabaseExtension.VECTORS,
}, },
@ -306,11 +312,14 @@ describe(DatabaseService.name, () => {
configMock.getEnv.mockReturnValue( configMock.getEnv.mockReturnValue(
mockEnvData({ mockEnvData({
database: { database: {
host: 'database', config: {
port: 5432, type: 'postgres',
username: 'postgres', host: 'database',
password: 'postgres', port: 5432,
name: 'immich', username: 'postgres',
password: 'postgres',
database: 'immich',
},
skipMigrations: true, skipMigrations: true,
vectorExtension: DatabaseExtension.VECTOR, vectorExtension: DatabaseExtension.VECTOR,
}, },

View File

@ -16,11 +16,16 @@ const envData: EnvData = {
}, },
database: { database: {
host: 'database', config: {
port: 5432, type: 'postgres',
username: 'postgres', host: 'database',
password: 'postgres', port: 5432,
name: 'immich', username: 'postgres',
password: 'postgres',
name: 'immich',
synchronize: false,
migrationsRun: true,
},
skipMigrations: false, skipMigrations: false,
vectorExtension: DatabaseExtension.VECTORS, vectorExtension: DatabaseExtension.VECTORS,