mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
refactor(server): db env (#13167)
This commit is contained in:
parent
e2bf6808ca
commit
3ac00b0ffa
11 changed files with 84 additions and 31 deletions
|
@ -1,16 +1,17 @@
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
||||||
|
|
||||||
const url = process.env.DB_URL;
|
const { database } = new ConfigRepository().getEnv();
|
||||||
|
const { url, host, port, username, password, name } = database;
|
||||||
const urlOrParts = url
|
const urlOrParts = url
|
||||||
? { url }
|
? { url }
|
||||||
: {
|
: {
|
||||||
host: process.env.DB_HOSTNAME || 'database',
|
host,
|
||||||
port: Number.parseInt(process.env.DB_PORT || '5432'),
|
port,
|
||||||
username: process.env.DB_USERNAME || 'postgres',
|
username,
|
||||||
password: process.env.DB_PASSWORD || 'postgres',
|
password,
|
||||||
database: process.env.DB_DATABASE_NAME || 'immich',
|
database: name,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/
|
/* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/
|
||||||
|
@ -32,6 +33,3 @@ export const databaseConfig: PostgresConnectionOptions = {
|
||||||
* this export is ONLY to be used for TypeORM commands in package.json#scripts
|
* this export is ONLY to be used for TypeORM commands in package.json#scripts
|
||||||
*/
|
*/
|
||||||
export const dataSource = new DataSource({ ...databaseConfig, host: 'localhost' });
|
export const dataSource = new DataSource({ ...databaseConfig, host: 'localhost' });
|
||||||
|
|
||||||
export const getVectorExtension = () =>
|
|
||||||
process.env.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS;
|
|
||||||
|
|
|
@ -26,6 +26,12 @@ export interface EnvData {
|
||||||
};
|
};
|
||||||
|
|
||||||
database: {
|
database: {
|
||||||
|
url?: string;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
skipMigrations: boolean;
|
skipMigrations: boolean;
|
||||||
vectorExtension: VectorExtension;
|
vectorExtension: VectorExtension;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import { getVectorExtension } from 'src/database.config';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { getCLIPModelInfo } from 'src/utils/misc';
|
import { getCLIPModelInfo } from 'src/utils/misc';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension;
|
||||||
|
|
||||||
export class UsePgVectors1700713871511 implements MigrationInterface {
|
export class UsePgVectors1700713871511 implements MigrationInterface {
|
||||||
name = 'UsePgVectors1700713871511';
|
name = 'UsePgVectors1700713871511';
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
||||||
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${getVectorExtension()}`);
|
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${vectorExtension}`);
|
||||||
const faceDimQuery = await queryRunner.query(`
|
const faceDimQuery = await queryRunner.query(`
|
||||||
SELECT CARDINALITY(embedding::real[]) as dimsize
|
SELECT CARDINALITY(embedding::real[]) as dimsize
|
||||||
FROM asset_faces
|
FROM asset_faces
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { getVectorExtension } from 'src/database.config';
|
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||||
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension;
|
||||||
|
|
||||||
export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface {
|
export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface {
|
||||||
name = 'AddCLIPEmbeddingIndex1700713994428';
|
name = 'AddCLIPEmbeddingIndex1700713994428';
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
if (getVectorExtension() === DatabaseExtension.VECTORS) {
|
if (vectorExtension === DatabaseExtension.VECTORS) {
|
||||||
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
||||||
}
|
}
|
||||||
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { getVectorExtension } from 'src/database.config';
|
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||||
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension;
|
||||||
|
|
||||||
export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface {
|
export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface {
|
||||||
name = 'AddFaceEmbeddingIndex1700714033632';
|
name = 'AddFaceEmbeddingIndex1700714033632';
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
if (getVectorExtension() === DatabaseExtension.VECTORS) {
|
if (vectorExtension === DatabaseExtension.VECTORS) {
|
||||||
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
||||||
}
|
}
|
||||||
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { getVectorExtension } from 'src/database.config';
|
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||||
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension;
|
||||||
|
|
||||||
export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
|
export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
if (getVectorExtension() === DatabaseExtension.VECTORS) {
|
if (vectorExtension === DatabaseExtension.VECTORS) {
|
||||||
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
||||||
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
||||||
}
|
}
|
||||||
|
@ -13,9 +15,10 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
|
||||||
const columns = await queryRunner.query(
|
const columns = await queryRunner.query(
|
||||||
`SELECT column_name as name
|
`SELECT column_name as name
|
||||||
FROM information_schema.columns
|
FROM information_schema.columns
|
||||||
WHERE table_name = '${tableName}'`);
|
WHERE table_name = '${tableName}'`,
|
||||||
|
);
|
||||||
return columns.some((column: { name: string }) => column.name === 'embedding');
|
return columns.some((column: { name: string }) => column.name === 'embedding');
|
||||||
}
|
};
|
||||||
|
|
||||||
const hasAssetEmbeddings = await hasEmbeddings('smart_search');
|
const hasAssetEmbeddings = await hasEmbeddings('smart_search');
|
||||||
if (!hasAssetEmbeddings) {
|
if (!hasAssetEmbeddings) {
|
||||||
|
@ -31,7 +34,7 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
|
||||||
await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);
|
await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);
|
||||||
await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);
|
await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`);
|
||||||
|
|
||||||
const hasFaceEmbeddings = await hasEmbeddings('asset_faces')
|
const hasFaceEmbeddings = await hasEmbeddings('asset_faces');
|
||||||
if (hasFaceEmbeddings) {
|
if (hasFaceEmbeddings) {
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
INSERT INTO face_search("faceId", embedding)
|
INSERT INTO face_search("faceId", embedding)
|
||||||
|
@ -56,7 +59,7 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
if (getVectorExtension() === DatabaseExtension.VECTORS) {
|
if (vectorExtension === DatabaseExtension.VECTORS) {
|
||||||
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
await queryRunner.query(`SET search_path TO "$user", public, vectors`);
|
||||||
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
await queryRunner.query(`SET vectors.pgvector_compatibility=on`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { getVectorExtension } from 'src/database.config';
|
|
||||||
import { ImmichEnvironment, ImmichWorker, LogLevel } from 'src/enum';
|
import { ImmichEnvironment, ImmichWorker, LogLevel } from 'src/enum';
|
||||||
import { EnvData, IConfigRepository } from 'src/interfaces/config.interface';
|
import { EnvData, IConfigRepository } from 'src/interfaces/config.interface';
|
||||||
|
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||||
import { setDifference } from 'src/utils/set';
|
import { setDifference } from 'src/utils/set';
|
||||||
|
|
||||||
// TODO replace src/config validation with class-validator, here
|
// TODO replace src/config validation with class-validator, here
|
||||||
|
@ -65,8 +65,16 @@ export class ConfigRepository implements IConfigRepository {
|
||||||
},
|
},
|
||||||
|
|
||||||
database: {
|
database: {
|
||||||
|
url: process.env.DB_URL,
|
||||||
|
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',
|
||||||
|
name: process.env.DB_DATABASE_NAME || 'immich',
|
||||||
|
|
||||||
skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true',
|
skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true',
|
||||||
vectorExtension: getVectorExtension(),
|
vectorExtension:
|
||||||
|
process.env.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS,
|
||||||
},
|
},
|
||||||
|
|
||||||
licensePublicKey: isProd ? productionKeys : stagingKeys,
|
licensePublicKey: isProd ? productionKeys : stagingKeys,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||||
import AsyncLock from 'async-lock';
|
import AsyncLock from 'async-lock';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
||||||
import { getVectorExtension } from 'src/database.config';
|
import { IConfigRepository } from 'src/interfaces/config.interface';
|
||||||
import {
|
import {
|
||||||
DatabaseExtension,
|
DatabaseExtension,
|
||||||
DatabaseLock,
|
DatabaseLock,
|
||||||
|
@ -22,12 +22,15 @@ import { DataSource, EntityManager, QueryRunner } from 'typeorm';
|
||||||
@Instrumentation()
|
@Instrumentation()
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DatabaseRepository implements IDatabaseRepository {
|
export class DatabaseRepository implements IDatabaseRepository {
|
||||||
|
private vectorExtension: VectorExtension;
|
||||||
readonly asyncLock = new AsyncLock();
|
readonly asyncLock = new AsyncLock();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectDataSource() private dataSource: DataSource,
|
@InjectDataSource() private dataSource: DataSource,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
|
@Inject(IConfigRepository) configRepository: IConfigRepository,
|
||||||
) {
|
) {
|
||||||
|
this.vectorExtension = configRepository.getEnv().database.vectorExtension;
|
||||||
this.logger.setContext(DatabaseRepository.name);
|
this.logger.setContext(DatabaseRepository.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +122,7 @@ export class DatabaseRepository implements IDatabaseRepository {
|
||||||
try {
|
try {
|
||||||
await this.dataSource.query(`REINDEX INDEX ${index}`);
|
await this.dataSource.query(`REINDEX INDEX ${index}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getVectorExtension() !== DatabaseExtension.VECTORS) {
|
if (this.vectorExtension !== DatabaseExtension.VECTORS) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
this.logger.warn(`Could not reindex index ${index}. Attempting to auto-fix.`);
|
this.logger.warn(`Could not reindex index ${index}. Attempting to auto-fix.`);
|
||||||
|
@ -141,7 +144,7 @@ export class DatabaseRepository implements IDatabaseRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldReindex(name: VectorIndex): Promise<boolean> {
|
async shouldReindex(name: VectorIndex): Promise<boolean> {
|
||||||
if (getVectorExtension() !== DatabaseExtension.VECTORS) {
|
if (this.vectorExtension !== DatabaseExtension.VECTORS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { getVectorExtension } from 'src/database.config';
|
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
|
@ -10,7 +9,8 @@ import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
||||||
import { SmartInfoEntity } from 'src/entities/smart-info.entity';
|
import { SmartInfoEntity } from 'src/entities/smart-info.entity';
|
||||||
import { SmartSearchEntity } from 'src/entities/smart-search.entity';
|
import { SmartSearchEntity } from 'src/entities/smart-search.entity';
|
||||||
import { AssetType, PaginationMode } from 'src/enum';
|
import { AssetType, PaginationMode } from 'src/enum';
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { IConfigRepository } from 'src/interfaces/config.interface';
|
||||||
|
import { DatabaseExtension, VectorExtension } from 'src/interfaces/database.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import {
|
import {
|
||||||
AssetDuplicateResult,
|
AssetDuplicateResult,
|
||||||
|
@ -31,6 +31,7 @@ import { Repository, SelectQueryBuilder } from 'typeorm';
|
||||||
@Instrumentation()
|
@Instrumentation()
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchRepository implements ISearchRepository {
|
export class SearchRepository implements ISearchRepository {
|
||||||
|
private vectorExtension: VectorExtension;
|
||||||
private faceColumns: string[];
|
private faceColumns: string[];
|
||||||
private assetsByCityQuery: string;
|
private assetsByCityQuery: string;
|
||||||
|
|
||||||
|
@ -42,7 +43,9 @@ export class SearchRepository implements ISearchRepository {
|
||||||
@InjectRepository(SmartSearchEntity) private smartSearchRepository: Repository<SmartSearchEntity>,
|
@InjectRepository(SmartSearchEntity) private smartSearchRepository: Repository<SmartSearchEntity>,
|
||||||
@InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository<GeodataPlacesEntity>,
|
@InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository<GeodataPlacesEntity>,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
|
@Inject(IConfigRepository) configRepository: IConfigRepository,
|
||||||
) {
|
) {
|
||||||
|
this.vectorExtension = configRepository.getEnv().database.vectorExtension;
|
||||||
this.logger.setContext(SearchRepository.name);
|
this.logger.setContext(SearchRepository.name);
|
||||||
this.faceColumns = this.assetFaceRepository.manager.connection
|
this.faceColumns = this.assetFaceRepository.manager.connection
|
||||||
.getMetadata(AssetFaceEntity)
|
.getMetadata(AssetFaceEntity)
|
||||||
|
@ -440,7 +443,7 @@ export class SearchRepository implements ISearchRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRuntimeConfig(numResults?: number): string {
|
private getRuntimeConfig(numResults?: number): string {
|
||||||
if (getVectorExtension() === DatabaseExtension.VECTOR) {
|
if (this.vectorExtension === DatabaseExtension.VECTOR) {
|
||||||
return 'SET LOCAL hnsw.ef_search = 1000;'; // mitigate post-filter recall
|
return 'SET LOCAL hnsw.ef_search = 1000;'; // mitigate post-filter recall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,17 @@ describe(DatabaseService.name, () => {
|
||||||
])('should work with $extensionName', ({ extension, extensionName }) => {
|
])('should work with $extensionName', ({ extension, extensionName }) => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
configMock.getEnv.mockReturnValue(
|
configMock.getEnv.mockReturnValue(
|
||||||
mockEnvData({ database: { skipMigrations: false, vectorExtension: extension } }),
|
mockEnvData({
|
||||||
|
database: {
|
||||||
|
host: 'database',
|
||||||
|
port: 5432,
|
||||||
|
username: 'postgres',
|
||||||
|
password: 'postgres',
|
||||||
|
name: 'immich',
|
||||||
|
skipMigrations: false,
|
||||||
|
vectorExtension: extension,
|
||||||
|
},
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -245,6 +255,11 @@ describe(DatabaseService.name, () => {
|
||||||
configMock.getEnv.mockReturnValue(
|
configMock.getEnv.mockReturnValue(
|
||||||
mockEnvData({
|
mockEnvData({
|
||||||
database: {
|
database: {
|
||||||
|
host: 'database',
|
||||||
|
port: 5432,
|
||||||
|
username: 'postgres',
|
||||||
|
password: 'postgres',
|
||||||
|
name: 'immich',
|
||||||
skipMigrations: true,
|
skipMigrations: true,
|
||||||
vectorExtension: DatabaseExtension.VECTORS,
|
vectorExtension: DatabaseExtension.VECTORS,
|
||||||
},
|
},
|
||||||
|
@ -260,6 +275,11 @@ describe(DatabaseService.name, () => {
|
||||||
configMock.getEnv.mockReturnValue(
|
configMock.getEnv.mockReturnValue(
|
||||||
mockEnvData({
|
mockEnvData({
|
||||||
database: {
|
database: {
|
||||||
|
host: 'database',
|
||||||
|
port: 5432,
|
||||||
|
username: 'postgres',
|
||||||
|
password: 'postgres',
|
||||||
|
name: 'immich',
|
||||||
skipMigrations: true,
|
skipMigrations: true,
|
||||||
vectorExtension: DatabaseExtension.VECTOR,
|
vectorExtension: DatabaseExtension.VECTOR,
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,6 +10,12 @@ const envData: EnvData = {
|
||||||
buildMetadata: {},
|
buildMetadata: {},
|
||||||
|
|
||||||
database: {
|
database: {
|
||||||
|
host: 'database',
|
||||||
|
port: 5432,
|
||||||
|
username: 'postgres',
|
||||||
|
password: 'postgres',
|
||||||
|
name: 'immich',
|
||||||
|
|
||||||
skipMigrations: false,
|
skipMigrations: false,
|
||||||
vectorExtension: DatabaseExtension.VECTORS,
|
vectorExtension: DatabaseExtension.VECTORS,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue