mirror of
https://github.com/immich-app/immich.git
synced 2025-12-30 01:11:52 +03:00
feat: storage template file move hardening (#5917)
* fix: pgvecto.rs extension breaks typeorm schema:drop command * fix: parse postgres bigints to javascript number types when selecting data * feat: verify file size is the same as original asset after copying file for storage template job * feat: allow disabling of storage template job, defaults to disabled for new instances * fix: don't allow setting concurrency for storage template migration, can cause race conditions above 1 * feat: add checksum verification when file is copied for storage template job * fix: extract metadata for assets that aren't visible on timeline
This commit is contained in:
@@ -20,6 +20,7 @@ export const databaseConfig: PostgresConnectionOptions = {
|
||||
subscribers: [__dirname + '/subscribers/*.{js,ts}'],
|
||||
migrationsRun: false,
|
||||
connectTimeoutMS: 10000, // 10 seconds
|
||||
parseInt8: true,
|
||||
...urlOrParts,
|
||||
};
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ export enum SystemConfigKey {
|
||||
|
||||
PASSWORD_LOGIN_ENABLED = 'passwordLogin.enabled',
|
||||
|
||||
STORAGE_TEMPLATE_ENABLED = 'storageTemplate.enabled',
|
||||
STORAGE_TEMPLATE_HASH_VERIFICATION_ENABLED = 'storageTemplate.hashVerificationEnabled',
|
||||
STORAGE_TEMPLATE = 'storageTemplate.template',
|
||||
|
||||
THUMBNAIL_WEBP_SIZE = 'thumbnail.webpSize',
|
||||
@@ -171,7 +173,7 @@ export interface SystemConfig {
|
||||
accel: TranscodeHWAccel;
|
||||
tonemap: ToneMapping;
|
||||
};
|
||||
job: Record<QueueName, { concurrency: number }>;
|
||||
job: Record<Exclude<QueueName, QueueName.STORAGE_TEMPLATE_MIGRATION>, { concurrency: number }>;
|
||||
logging: {
|
||||
enabled: boolean;
|
||||
level: LogLevel;
|
||||
@@ -216,6 +218,8 @@ export interface SystemConfig {
|
||||
enabled: boolean;
|
||||
};
|
||||
storageTemplate: {
|
||||
enabled: boolean;
|
||||
hashVerificationEnabled: boolean;
|
||||
template: string;
|
||||
};
|
||||
thumbnail: {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
export class DefaultStorageTemplateOnForExistingInstallations1703288449127 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`)
|
||||
if(adminCount[0].count > 0) {
|
||||
await queryRunner.query(`INSERT INTO system_config (key, value) VALUES ('storageTemplate.enabled', 'true')`)
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DELETE FROM system_config WHERE key = 'storageTemplate.enabled'`)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,14 +9,10 @@ import {
|
||||
import { ImmichLogger } from '@app/infra/logger';
|
||||
import archiver from 'archiver';
|
||||
import { constants, createReadStream, existsSync, mkdirSync } from 'fs';
|
||||
import fs, { readdir, writeFile } from 'fs/promises';
|
||||
import fs, { copyFile, readdir, rename, writeFile } from 'fs/promises';
|
||||
import { glob } from 'glob';
|
||||
import mv from 'mv';
|
||||
import { promisify } from 'node:util';
|
||||
import path from 'path';
|
||||
|
||||
const moveFile = promisify<string, string, mv.Options>(mv);
|
||||
|
||||
export class FilesystemProvider implements IStorageRepository {
|
||||
private logger = new ImmichLogger(FilesystemProvider.name);
|
||||
|
||||
@@ -54,15 +50,9 @@ export class FilesystemProvider implements IStorageRepository {
|
||||
|
||||
writeFile = writeFile;
|
||||
|
||||
async moveFile(source: string, destination: string): Promise<void> {
|
||||
this.logger.verbose(`Moving ${source} to ${destination}`);
|
||||
rename = rename;
|
||||
|
||||
if (await this.checkFileExists(destination)) {
|
||||
throw new Error(`Destination file already exists: ${destination}`);
|
||||
}
|
||||
|
||||
await moveFile(source, destination, { mkdirp: true, clobber: true });
|
||||
}
|
||||
copyFile = copyFile;
|
||||
|
||||
async checkFileExists(filepath: string, mode = constants.F_OK): Promise<boolean> {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user