mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-10-29 17:40:28 +00:00
refactor(server)*: tsconfigs (#2689)
* refactor(server): tsconfigs * chore: dummy commit * fix: start.sh * chore: restore original entry scripts
This commit is contained in:
3
server/src/domain/storage/index.ts
Normal file
3
server/src/domain/storage/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './storage.core';
|
||||
export * from './storage.repository';
|
||||
export * from './storage.service';
|
||||
27
server/src/domain/storage/storage.core.ts
Normal file
27
server/src/domain/storage/storage.core.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { join } from 'node:path';
|
||||
import { APP_MEDIA_LOCATION } from '../domain.constant';
|
||||
|
||||
export enum StorageFolder {
|
||||
ENCODED_VIDEO = 'encoded-video',
|
||||
LIBRARY = 'library',
|
||||
UPLOAD = 'upload',
|
||||
PROFILE = 'profile',
|
||||
THUMBNAILS = 'thumbs',
|
||||
}
|
||||
|
||||
export class StorageCore {
|
||||
getFolderLocation(
|
||||
folder: StorageFolder.ENCODED_VIDEO | StorageFolder.UPLOAD | StorageFolder.PROFILE | StorageFolder.THUMBNAILS,
|
||||
userId: string,
|
||||
) {
|
||||
return join(this.getBaseFolder(folder), userId);
|
||||
}
|
||||
|
||||
getLibraryFolder(user: { storageLabel: string | null; id: string }) {
|
||||
return join(this.getBaseFolder(StorageFolder.LIBRARY), user.storageLabel || user.id);
|
||||
}
|
||||
|
||||
getBaseFolder(folder: StorageFolder) {
|
||||
return join(APP_MEDIA_LOCATION, folder);
|
||||
}
|
||||
}
|
||||
26
server/src/domain/storage/storage.repository.ts
Normal file
26
server/src/domain/storage/storage.repository.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ReadStream } from 'fs';
|
||||
|
||||
export interface ImmichReadStream {
|
||||
stream: ReadStream;
|
||||
type: string;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export interface DiskUsage {
|
||||
available: number;
|
||||
free: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export const IStorageRepository = 'IStorageRepository';
|
||||
|
||||
export interface IStorageRepository {
|
||||
createReadStream(filepath: string, mimeType: string): Promise<ImmichReadStream>;
|
||||
unlink(filepath: string): Promise<void>;
|
||||
unlinkDir(folder: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>;
|
||||
removeEmptyDirs(folder: string): Promise<void>;
|
||||
moveFile(source: string, target: string): Promise<void>;
|
||||
checkFileExists(filepath: string, mode?: number): Promise<boolean>;
|
||||
mkdirSync(filepath: string): void;
|
||||
checkDiskUsage(folder: string): Promise<DiskUsage>;
|
||||
}
|
||||
46
server/src/domain/storage/storage.service.spec.ts
Normal file
46
server/src/domain/storage/storage.service.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { newStorageRepositoryMock } from '@test';
|
||||
import { IStorageRepository } from '.';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
describe(StorageService.name, () => {
|
||||
let sut: StorageService;
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
storageMock = newStorageRepositoryMock();
|
||||
sut = new StorageService(storageMock);
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
describe('init', () => {
|
||||
it('should create the library folder on initialization', () => {
|
||||
sut.init();
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/library');
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleDeleteFiles', () => {
|
||||
it('should handle null values', async () => {
|
||||
await sut.handleDeleteFiles({ files: [undefined, null] });
|
||||
|
||||
expect(storageMock.unlink).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle an error removing a file', async () => {
|
||||
storageMock.unlink.mockRejectedValue(new Error('something-went-wrong'));
|
||||
|
||||
await sut.handleDeleteFiles({ files: ['path/to/something'] });
|
||||
|
||||
expect(storageMock.unlink).toHaveBeenCalledWith('path/to/something');
|
||||
});
|
||||
|
||||
it('should remove the file', async () => {
|
||||
await sut.handleDeleteFiles({ files: ['path/to/something'] });
|
||||
|
||||
expect(storageMock.unlink).toHaveBeenCalledWith('path/to/something');
|
||||
});
|
||||
});
|
||||
});
|
||||
36
server/src/domain/storage/storage.service.ts
Normal file
36
server/src/domain/storage/storage.service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { IDeleteFilesJob } from '../job';
|
||||
import { StorageCore, StorageFolder } from './storage.core';
|
||||
import { IStorageRepository } from './storage.repository';
|
||||
|
||||
@Injectable()
|
||||
export class StorageService {
|
||||
private logger = new Logger(StorageService.name);
|
||||
private storageCore = new StorageCore();
|
||||
|
||||
constructor(@Inject(IStorageRepository) private storageRepository: IStorageRepository) {}
|
||||
|
||||
init() {
|
||||
const libraryBase = this.storageCore.getBaseFolder(StorageFolder.LIBRARY);
|
||||
this.storageRepository.mkdirSync(libraryBase);
|
||||
}
|
||||
|
||||
async handleDeleteFiles(job: IDeleteFilesJob) {
|
||||
const { files } = job;
|
||||
|
||||
// TODO: one job per file
|
||||
for (const file of files) {
|
||||
if (!file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.storageRepository.unlink(file);
|
||||
} catch (error: any) {
|
||||
this.logger.warn('Unable to remove file from disk', error);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user