diff --git a/server/src/app.module.ts b/server/src/app.module.ts index d088af6188..cd19972206 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -13,7 +13,6 @@ import { entities } from 'src/entities'; import { ImmichWorker } from 'src/enum'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository } from 'src/interfaces/job.interface'; -import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { AuthGuard } from 'src/middleware/auth.guard'; import { ErrorInterceptor } from 'src/middleware/error.interceptor'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @@ -22,7 +21,7 @@ import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { providers, repositories } from 'src/repositories'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { teardownTelemetry } from 'src/repositories/telemetry.repository'; +import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; import { services } from 'src/services'; import { CliService } from 'src/services/cli.service'; import { DatabaseService } from 'src/services/database.service'; @@ -67,7 +66,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { logger: LoggingRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, - @Inject(ITelemetryRepository) private telemetryRepository: ITelemetryRepository, + private telemetryRepository: TelemetryRepository, ) { logger.setAppName(this.worker); } diff --git a/server/src/controllers/notification.controller.ts b/server/src/controllers/notification.controller.ts index 27034fd63a..39946a9fc9 100644 --- a/server/src/controllers/notification.controller.ts +++ b/server/src/controllers/notification.controller.ts @@ -3,8 +3,8 @@ import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { TemplateDto, TemplateResponseDto, TestEmailResponseDto } from 'src/dtos/notification.dto'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; -import { EmailTemplate } from 'src/interfaces/notification.interface'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; @ApiTags('Notifications') diff --git a/server/src/emails/album-invite.email.tsx b/server/src/emails/album-invite.email.tsx index 0b3819b332..4bd7abc305 100644 --- a/server/src/emails/album-invite.email.tsx +++ b/server/src/emails/album-invite.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumInviteEmailProps } from 'src/interfaces/notification.interface'; +import { AlbumInviteEmailProps } from 'src/repositories/notification.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumInviteEmail = ({ diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index 9dcd858e93..2311e896e1 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumUpdateEmailProps } from 'src/interfaces/notification.interface'; +import { AlbumUpdateEmailProps } from 'src/repositories/notification.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumUpdateEmail = ({ diff --git a/server/src/emails/test.email.tsx b/server/src/emails/test.email.tsx index 8ba19436c6..ac9bdbe0ea 100644 --- a/server/src/emails/test.email.tsx +++ b/server/src/emails/test.email.tsx @@ -1,7 +1,7 @@ import { Link, Row, Text } from '@react-email/components'; import * as React from 'react'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { TestEmailProps } from 'src/interfaces/notification.interface'; +import { TestEmailProps } from 'src/repositories/notification.repository'; export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => ( diff --git a/server/src/emails/welcome.email.tsx b/server/src/emails/welcome.email.tsx index ced0b77698..11a6602711 100644 --- a/server/src/emails/welcome.email.tsx +++ b/server/src/emails/welcome.email.tsx @@ -2,7 +2,7 @@ import { Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { WelcomeEmailProps } from 'src/interfaces/notification.interface'; +import { WelcomeEmailProps } from 'src/repositories/notification.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const WelcomeEmail = ({ baseUrl, displayName, username, password, customTemplate }: WelcomeEmailProps) => { diff --git a/server/src/interfaces/album-user.interface.ts b/server/src/interfaces/album-user.interface.ts deleted file mode 100644 index 835e835589..0000000000 --- a/server/src/interfaces/album-user.interface.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Insertable, Selectable, Updateable } from 'kysely'; -import { AlbumsSharedUsersUsers } from 'src/db'; - -export const IAlbumUserRepository = 'IAlbumUserRepository'; - -export type AlbumPermissionId = { - albumsId: string; - usersId: string; -}; - -export interface IAlbumUserRepository { - create(albumUser: Insertable): Promise>; - update( - id: AlbumPermissionId, - albumPermission: Updateable, - ): Promise>; - delete(id: AlbumPermissionId): Promise; -} diff --git a/server/src/interfaces/cron.interface.ts b/server/src/interfaces/cron.interface.ts deleted file mode 100644 index ceb554864a..0000000000 --- a/server/src/interfaces/cron.interface.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const ICronRepository = 'ICronRepository'; - -type CronBase = { - name: string; - start?: boolean; -}; - -export type CronCreate = CronBase & { - expression: string; - onTick: () => void; -}; - -export type CronUpdate = CronBase & { - expression?: string; -}; - -export interface ICronRepository { - create(cron: CronCreate): void; - update(cron: CronUpdate): void; -} diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index 7976f81302..1f2b92074a 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -1,5 +1,5 @@ import { ClassConstructor } from 'class-transformer'; -import { EmailImageAttachment } from 'src/interfaces/notification.interface'; +import { EmailImageAttachment } from 'src/repositories/notification.repository'; export enum QueueName { THUMBNAIL_GENERATION = 'thumbnailGeneration', diff --git a/server/src/interfaces/map.interface.ts b/server/src/interfaces/map.interface.ts deleted file mode 100644 index 0a04840a96..0000000000 --- a/server/src/interfaces/map.interface.ts +++ /dev/null @@ -1,31 +0,0 @@ -export const IMapRepository = 'IMapRepository'; - -export interface MapMarkerSearchOptions { - isArchived?: boolean; - isFavorite?: boolean; - fileCreatedBefore?: Date; - fileCreatedAfter?: Date; -} - -export interface GeoPoint { - latitude: number; - longitude: number; -} - -export interface ReverseGeocodeResult { - country: string | null; - state: string | null; - city: string | null; -} - -export interface MapMarker extends ReverseGeocodeResult { - id: string; - lat: number; - lon: number; -} - -export interface IMapRepository { - init(): Promise; - reverseGeocode(point: GeoPoint): Promise; - getMapMarkers(ownerIds: string[], albumIds: string[], options?: MapMarkerSearchOptions): Promise; -} diff --git a/server/src/interfaces/metadata.interface.ts b/server/src/interfaces/metadata.interface.ts deleted file mode 100644 index 574420e27a..0000000000 --- a/server/src/interfaces/metadata.interface.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { BinaryField, Tags } from 'exiftool-vendored'; - -export const IMetadataRepository = 'IMetadataRepository'; - -export interface ExifDuration { - Value: number; - Scale?: number; -} - -type StringOrNumber = string | number; - -type TagsWithWrongTypes = - | 'FocalLength' - | 'Duration' - | 'Description' - | 'ImageDescription' - | 'RegionInfo' - | 'TagsList' - | 'Keywords' - | 'HierarchicalSubject' - | 'ISO'; -export interface ImmichTags extends Omit { - ContentIdentifier?: string; - MotionPhoto?: number; - MotionPhotoVersion?: number; - MotionPhotoPresentationTimestampUs?: number; - MediaGroupUUID?: string; - ImagePixelDepth?: string; - FocalLength?: number; - Duration?: number | string | ExifDuration; - EmbeddedVideoType?: string; - EmbeddedVideoFile?: BinaryField; - MotionPhotoVideo?: BinaryField; - TagsList?: StringOrNumber[]; - HierarchicalSubject?: StringOrNumber[]; - Keywords?: StringOrNumber | StringOrNumber[]; - ISO?: number | number[]; - - // Type is wrong, can also be number. - Description?: StringOrNumber; - ImageDescription?: StringOrNumber; - - // Extended properties for image regions, such as faces - RegionInfo?: { - AppliedToDimensions: { - W: number; - H: number; - Unit: string; - }; - RegionList: { - Area: { - // (X,Y) // center of the rectangle - X: number; - Y: number; - W: number; - H: number; - Unit: string; - }; - Rotation?: number; - Type?: string; - Name?: string; - }[]; - }; -} - -export interface IMetadataRepository { - teardown(): Promise; - readTags(path: string): Promise; - writeTags(path: string, tags: Partial): Promise; - extractBinaryTag(tagName: string, path: string): Promise; -} diff --git a/server/src/interfaces/notification.interface.ts b/server/src/interfaces/notification.interface.ts deleted file mode 100644 index b20b3c50ae..0000000000 --- a/server/src/interfaces/notification.interface.ts +++ /dev/null @@ -1,101 +0,0 @@ -export const INotificationRepository = 'INotificationRepository'; - -export type EmailImageAttachment = { - filename: string; - path: string; - cid: string; -}; - -export type SendEmailOptions = { - from: string; - to: string; - replyTo?: string; - subject: string; - html: string; - text: string; - imageAttachments?: EmailImageAttachment[]; - smtp: SmtpOptions; -}; - -export type SmtpOptions = { - host: string; - port?: number; - username?: string; - password?: string; - ignoreCert?: boolean; -}; - -export enum EmailTemplate { - TEST_EMAIL = 'test', - - // AUTH - WELCOME = 'welcome', - RESET_PASSWORD = 'reset-password', - - // ALBUM - ALBUM_INVITE = 'album-invite', - ALBUM_UPDATE = 'album-update', -} - -interface BaseEmailProps { - baseUrl: string; - customTemplate?: string; -} - -export interface TestEmailProps extends BaseEmailProps { - displayName: string; -} - -export interface WelcomeEmailProps extends BaseEmailProps { - displayName: string; - username: string; - password?: string; -} - -export interface AlbumInviteEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - senderName: string; - recipientName: string; - cid?: string; -} - -export interface AlbumUpdateEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - recipientName: string; - cid?: string; -} - -export type EmailRenderRequest = - | { - template: EmailTemplate.TEST_EMAIL; - data: TestEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.WELCOME; - data: WelcomeEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_INVITE; - data: AlbumInviteEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_UPDATE; - data: AlbumUpdateEmailProps; - customTemplate: string; - }; - -export type SendEmailResponse = { - messageId: string; - response: any; -}; - -export interface INotificationRepository { - renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }>; - sendEmail(options: SendEmailOptions): Promise; - verifySmtp(options: SmtpOptions): Promise; -} diff --git a/server/src/interfaces/oauth.interface.ts b/server/src/interfaces/oauth.interface.ts deleted file mode 100644 index 5e629726a0..0000000000 --- a/server/src/interfaces/oauth.interface.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { UserinfoResponse } from 'openid-client'; - -export const IOAuthRepository = 'IOAuthRepository'; - -export type OAuthConfig = { - clientId: string; - clientSecret: string; - issuerUrl: string; - mobileOverrideEnabled: boolean; - mobileRedirectUri: string; - profileSigningAlgorithm: string; - scope: string; - signingAlgorithm: string; -}; -export type OAuthProfile = UserinfoResponse; - -export interface IOAuthRepository { - init(): void; - authorize(config: OAuthConfig, redirectUrl: string): Promise; - getLogoutEndpoint(config: OAuthConfig): Promise; - getProfile(config: OAuthConfig, url: string, redirectUrl: string): Promise; -} diff --git a/server/src/interfaces/server-info.interface.ts b/server/src/interfaces/server-info.interface.ts deleted file mode 100644 index 6dc857ddea..0000000000 --- a/server/src/interfaces/server-info.interface.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface GitHubRelease { - id: number; - url: string; - tag_name: string; - name: string; - created_at: string; - published_at: string; - body: string; -} - -export interface ServerBuildVersions { - nodejs: string; - ffmpeg: string; - libvips: string; - exiftool: string; - imagemagick: string; -} - -export const IServerInfoRepository = 'IServerInfoRepository'; - -export interface IServerInfoRepository { - getGitHubRelease(): Promise; - getBuildVersions(): Promise; -} diff --git a/server/src/interfaces/telemetry.interface.ts b/server/src/interfaces/telemetry.interface.ts deleted file mode 100644 index 688e52c21e..0000000000 --- a/server/src/interfaces/telemetry.interface.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MetricOptions } from '@opentelemetry/api'; -import { ClassConstructor } from 'class-transformer'; - -export const ITelemetryRepository = 'ITelemetryRepository'; - -export interface MetricGroupOptions { - enabled: boolean; -} - -export interface IMetricGroupRepository { - addToCounter(name: string, value: number, options?: MetricOptions): void; - addToGauge(name: string, value: number, options?: MetricOptions): void; - addToHistogram(name: string, value: number, options?: MetricOptions): void; - configure(options: MetricGroupOptions): this; -} - -export interface ITelemetryRepository { - setup(options: { repositories: ClassConstructor[] }): void; - api: IMetricGroupRepository; - host: IMetricGroupRepository; - jobs: IMetricGroupRepository; - repo: IMetricGroupRepository; -} diff --git a/server/src/interfaces/trash.interface.ts b/server/src/interfaces/trash.interface.ts deleted file mode 100644 index 38e7c523ce..0000000000 --- a/server/src/interfaces/trash.interface.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const ITrashRepository = 'ITrashRepository'; - -export interface ITrashRepository { - empty(userId: string): Promise; - restore(userId: string): Promise; - restoreAll(assetIds: string[]): Promise; - getDeletedIds(): AsyncIterableIterator<{ id: string }>; -} diff --git a/server/src/interfaces/version-history.interface.ts b/server/src/interfaces/version-history.interface.ts deleted file mode 100644 index c38552c24f..0000000000 --- a/server/src/interfaces/version-history.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { VersionHistoryEntity } from 'src/entities/version-history.entity'; - -export const IVersionHistoryRepository = 'IVersionHistoryRepository'; - -export interface IVersionHistoryRepository { - create(version: Omit): Promise; - getAll(): Promise; - getLatest(): Promise; -} diff --git a/server/src/repositories/album-user.repository.ts b/server/src/repositories/album-user.repository.ts index 5895721b63..f363f2e91a 100644 --- a/server/src/repositories/album-user.repository.ts +++ b/server/src/repositories/album-user.repository.ts @@ -4,10 +4,14 @@ import { InjectKysely } from 'nestjs-kysely'; import { AlbumsSharedUsersUsers, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AlbumUserRole } from 'src/enum'; -import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface'; + +export type AlbumPermissionId = { + albumsId: string; + usersId: string; +}; @Injectable() -export class AlbumUserRepository implements IAlbumUserRepository { +export class AlbumUserRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] }) @@ -16,10 +20,7 @@ export class AlbumUserRepository implements IAlbumUserRepository { } @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] }) - update( - { usersId, albumsId }: AlbumPermissionId, - dto: Updateable, - ): Promise> { + update({ usersId, albumsId }: AlbumPermissionId, dto: Updateable) { return this.db .updateTable('albums_shared_users_users') .set(dto) diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 00fa130bb4..9c506197d6 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -43,8 +43,8 @@ import { WithProperty, WithoutProperty, } from 'src/interfaces/asset.interface'; -import { MapMarker, MapMarkerSearchOptions } from 'src/interfaces/map.interface'; import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/interfaces/search.interface'; +import { MapMarker, MapMarkerSearchOptions } from 'src/repositories/map.repository'; import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database'; import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination'; diff --git a/server/src/repositories/cron.repository.ts b/server/src/repositories/cron.repository.ts index 12533f67af..e6e8fe7568 100644 --- a/server/src/repositories/cron.repository.ts +++ b/server/src/repositories/cron.repository.ts @@ -1,11 +1,24 @@ import { Injectable } from '@nestjs/common'; import { SchedulerRegistry } from '@nestjs/schedule'; import { CronJob, CronTime } from 'cron'; -import { CronCreate, CronUpdate, ICronRepository } from 'src/interfaces/cron.interface'; import { LoggingRepository } from 'src/repositories/logging.repository'; +type CronBase = { + name: string; + start?: boolean; +}; + +export type CronCreate = CronBase & { + expression: string; + onTick: () => void; +}; + +export type CronUpdate = CronBase & { + expression?: string; +}; + @Injectable() -export class CronRepository implements ICronRepository { +export class CronRepository { constructor( private schedulerRegistry: SchedulerRegistry, private logger: LoggingRepository, diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 7f4619d3de..b54c69e117 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -1,33 +1,23 @@ -import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ICronRepository } from 'src/interfaces/cron.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; -import { IMapRepository } from 'src/interfaces/map.interface'; -import { IMetadataRepository } from 'src/interfaces/metadata.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; -import { INotificationRepository } from 'src/interfaces/notification.interface'; -import { IOAuthRepository } from 'src/interfaces/oauth.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IProcessRepository } from 'src/interfaces/process.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; -import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { ISessionRepository } from 'src/interfaces/session.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; -import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; -import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; @@ -71,44 +61,44 @@ import { ViewRepository } from 'src/repositories/view-repository'; export const repositories = [ AccessRepository, ActivityRepository, + AlbumUserRepository, AuditRepository, ApiKeyRepository, ConfigRepository, + CronRepository, LoggingRepository, + MapRepository, MediaRepository, MemoryRepository, + MetadataRepository, + NotificationRepository, + OAuthRepository, + ServerInfoRepository, + TelemetryRepository, + TrashRepository, ViewRepository, + VersionHistoryRepository, ]; export const providers = [ { provide: IAlbumRepository, useClass: AlbumRepository }, - { provide: IAlbumUserRepository, useClass: AlbumUserRepository }, { provide: IAssetRepository, useClass: AssetRepository }, - { provide: ICronRepository, useClass: CronRepository }, { provide: ICryptoRepository, useClass: CryptoRepository }, { provide: IDatabaseRepository, useClass: DatabaseRepository }, { provide: IEventRepository, useClass: EventRepository }, { provide: IJobRepository, useClass: JobRepository }, { provide: ILibraryRepository, useClass: LibraryRepository }, { provide: IMachineLearningRepository, useClass: MachineLearningRepository }, - { provide: IMapRepository, useClass: MapRepository }, - { provide: IMetadataRepository, useClass: MetadataRepository }, { provide: IMoveRepository, useClass: MoveRepository }, - { provide: INotificationRepository, useClass: NotificationRepository }, - { provide: IOAuthRepository, useClass: OAuthRepository }, { provide: IPartnerRepository, useClass: PartnerRepository }, { provide: IPersonRepository, useClass: PersonRepository }, { provide: IProcessRepository, useClass: ProcessRepository }, { provide: ISearchRepository, useClass: SearchRepository }, - { provide: IServerInfoRepository, useClass: ServerInfoRepository }, { provide: ISessionRepository, useClass: SessionRepository }, { provide: ISharedLinkRepository, useClass: SharedLinkRepository }, { provide: IStackRepository, useClass: StackRepository }, { provide: IStorageRepository, useClass: StorageRepository }, { provide: ISystemMetadataRepository, useClass: SystemMetadataRepository }, { provide: ITagRepository, useClass: TagRepository }, - { provide: ITelemetryRepository, useClass: TelemetryRepository }, - { provide: ITrashRepository, useClass: TrashRepository }, { provide: IUserRepository, useClass: UserRepository }, - { provide: IVersionHistoryRepository, useClass: VersionHistoryRepository }, ]; diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index 3c5c3f5671..af24b0c94e 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -11,24 +11,41 @@ import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db'; import { AssetEntity, withExif } from 'src/entities/asset.entity'; import { NaturalEarthCountriesTempEntity } from 'src/entities/natural-earth-countries.entity'; import { LogLevel, SystemMetadataKey } from 'src/enum'; -import { - GeoPoint, - IMapRepository, - MapMarker, - MapMarkerSearchOptions, - ReverseGeocodeResult, -} from 'src/interfaces/map.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +export interface MapMarkerSearchOptions { + isArchived?: boolean; + isFavorite?: boolean; + fileCreatedBefore?: Date; + fileCreatedAfter?: Date; +} + +export interface GeoPoint { + latitude: number; + longitude: number; +} + +export interface ReverseGeocodeResult { + country: string | null; + state: string | null; + city: string | null; +} + +export interface MapMarker extends ReverseGeocodeResult { + id: string; + lat: number; + lon: number; +} + interface MapDB extends DB { geodata_places_tmp: GeodataPlaces; naturalearth_countries_tmp: NaturalearthCountries; } @Injectable() -export class MapRepository implements IMapRepository { +export class MapRepository { constructor( private configRepository: ConfigRepository, @Inject(ISystemMetadataRepository) private metadataRepository: ISystemMetadataRepository, diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index 90a3a9e765..3f297d709b 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -1,11 +1,72 @@ import { Injectable } from '@nestjs/common'; -import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; +import { BinaryField, DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; import geotz from 'geo-tz'; -import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; import { LoggingRepository } from 'src/repositories/logging.repository'; +interface ExifDuration { + Value: number; + Scale?: number; +} + +type StringOrNumber = string | number; + +type TagsWithWrongTypes = + | 'FocalLength' + | 'Duration' + | 'Description' + | 'ImageDescription' + | 'RegionInfo' + | 'TagsList' + | 'Keywords' + | 'HierarchicalSubject' + | 'ISO'; + +export interface ImmichTags extends Omit { + ContentIdentifier?: string; + MotionPhoto?: number; + MotionPhotoVersion?: number; + MotionPhotoPresentationTimestampUs?: number; + MediaGroupUUID?: string; + ImagePixelDepth?: string; + FocalLength?: number; + Duration?: number | string | ExifDuration; + EmbeddedVideoType?: string; + EmbeddedVideoFile?: BinaryField; + MotionPhotoVideo?: BinaryField; + TagsList?: StringOrNumber[]; + HierarchicalSubject?: StringOrNumber[]; + Keywords?: StringOrNumber | StringOrNumber[]; + ISO?: number | number[]; + + // Type is wrong, can also be number. + Description?: StringOrNumber; + ImageDescription?: StringOrNumber; + + // Extended properties for image regions, such as faces + RegionInfo?: { + AppliedToDimensions: { + W: number; + H: number; + Unit: string; + }; + RegionList: { + Area: { + // (X,Y) // center of the rectangle + X: number; + Y: number; + W: number; + H: number; + Unit: string; + }; + Rotation?: number; + Type?: string; + Name?: string; + }[]; + }; +} + @Injectable() -export class MetadataRepository implements IMetadataRepository { +export class MetadataRepository { private exiftool = new ExifTool({ defaultVideosToUTC: true, backfillTimezones: true, diff --git a/server/src/repositories/notification.repository.spec.ts b/server/src/repositories/notification.repository.spec.ts index f952e9ebed..0d8e826c66 100644 --- a/server/src/repositories/notification.repository.spec.ts +++ b/server/src/repositories/notification.repository.spec.ts @@ -1,6 +1,5 @@ -import { EmailRenderRequest, EmailTemplate } from 'src/interfaces/notification.interface'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { NotificationRepository } from 'src/repositories/notification.repository'; +import { EmailRenderRequest, EmailTemplate, NotificationRepository } from 'src/repositories/notification.repository'; import { ILoggingRepository } from 'src/types'; import { newLoggingRepositoryMock } from 'test/repositories/logger.repository.mock'; import { Mocked } from 'vitest'; diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index ecdaed3866..fdb74cfdb2 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -6,18 +6,104 @@ import { AlbumInviteEmail } from 'src/emails/album-invite.email'; import { AlbumUpdateEmail } from 'src/emails/album-update.email'; import { TestEmail } from 'src/emails/test.email'; import { WelcomeEmail } from 'src/emails/welcome.email'; -import { - EmailRenderRequest, - EmailTemplate, - INotificationRepository, - SendEmailOptions, - SendEmailResponse, - SmtpOptions, -} from 'src/interfaces/notification.interface'; import { LoggingRepository } from 'src/repositories/logging.repository'; +export type EmailImageAttachment = { + filename: string; + path: string; + cid: string; +}; + +export type SendEmailOptions = { + from: string; + to: string; + replyTo?: string; + subject: string; + html: string; + text: string; + imageAttachments?: EmailImageAttachment[]; + smtp: SmtpOptions; +}; + +export type SmtpOptions = { + host: string; + port?: number; + username?: string; + password?: string; + ignoreCert?: boolean; +}; + +export enum EmailTemplate { + TEST_EMAIL = 'test', + + // AUTH + WELCOME = 'welcome', + RESET_PASSWORD = 'reset-password', + + // ALBUM + ALBUM_INVITE = 'album-invite', + ALBUM_UPDATE = 'album-update', +} + +interface BaseEmailProps { + baseUrl: string; + customTemplate?: string; +} + +export interface TestEmailProps extends BaseEmailProps { + displayName: string; +} + +export interface WelcomeEmailProps extends BaseEmailProps { + displayName: string; + username: string; + password?: string; +} + +export interface AlbumInviteEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + senderName: string; + recipientName: string; + cid?: string; +} + +export interface AlbumUpdateEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + recipientName: string; + cid?: string; +} + +export type EmailRenderRequest = + | { + template: EmailTemplate.TEST_EMAIL; + data: TestEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.WELCOME; + data: WelcomeEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_INVITE; + data: AlbumInviteEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_UPDATE; + data: AlbumUpdateEmailProps; + customTemplate: string; + }; + +export type SendEmailResponse = { + messageId: string; + response: any; +}; + @Injectable() -export class NotificationRepository implements INotificationRepository { +export class NotificationRepository { constructor(private logger: LoggingRepository) { this.logger.setContext(NotificationRepository.name); } diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index dfd36edc2a..85263cd647 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -1,10 +1,21 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { custom, generators, Issuer } from 'openid-client'; -import { IOAuthRepository, OAuthConfig, OAuthProfile } from 'src/interfaces/oauth.interface'; +import { custom, generators, Issuer, UserinfoResponse } from 'openid-client'; import { LoggingRepository } from 'src/repositories/logging.repository'; +export type OAuthConfig = { + clientId: string; + clientSecret: string; + issuerUrl: string; + mobileOverrideEnabled: boolean; + mobileRedirectUri: string; + profileSigningAlgorithm: string; + scope: string; + signingAlgorithm: string; +}; +export type OAuthProfile = UserinfoResponse; + @Injectable() -export class OAuthRepository implements IOAuthRepository { +export class OAuthRepository { constructor(private logger: LoggingRepository) { this.logger.setContext(OAuthRepository.name); } diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index c0c0f88723..deb24123d0 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -4,10 +4,27 @@ import { exec as execCallback } from 'node:child_process'; import { readFile } from 'node:fs/promises'; import { promisify } from 'node:util'; import sharp from 'sharp'; -import { GitHubRelease, IServerInfoRepository, ServerBuildVersions } from 'src/interfaces/server-info.interface'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +export interface GitHubRelease { + id: number; + url: string; + tag_name: string; + name: string; + created_at: string; + published_at: string; + body: string; +} + +export interface ServerBuildVersions { + nodejs: string; + ffmpeg: string; + libvips: string; + exiftool: string; + imagemagick: string; +} + const exec = promisify(execCallback); const maybeFirstLine = async (command: string): Promise => { try { @@ -34,7 +51,7 @@ const getLockfileVersion = (name: string, lockfile?: BuildLockfile) => { }; @Injectable() -export class ServerInfoRepository implements IServerInfoRepository { +export class ServerInfoRepository { constructor( private configRepository: ConfigRepository, private logger: LoggingRepository, diff --git a/server/src/repositories/telemetry.repository.ts b/server/src/repositories/telemetry.repository.ts index 7a82ba07e7..7f93d4deba 100644 --- a/server/src/repositories/telemetry.repository.ts +++ b/server/src/repositories/telemetry.repository.ts @@ -15,11 +15,12 @@ import { MetricService } from 'nestjs-otel'; import { copyMetadataFromFunctionToFunction } from 'nestjs-otel/lib/opentelemetry.utils'; import { serverVersion } from 'src/constants'; import { ImmichTelemetry, MetadataKey } from 'src/enum'; -import { IMetricGroupRepository, ITelemetryRepository, MetricGroupOptions } from 'src/interfaces/telemetry.interface'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -class MetricGroupRepository implements IMetricGroupRepository { +type MetricGroupOptions = { enabled: boolean }; + +export class MetricGroupRepository { private enabled = false; constructor(private metricService: MetricService) {} @@ -86,7 +87,7 @@ export const teardownTelemetry = async () => { }; @Injectable() -export class TelemetryRepository implements ITelemetryRepository { +export class TelemetryRepository { api: MetricGroupRepository; host: MetricGroupRepository; jobs: MetricGroupRepository; diff --git a/server/src/repositories/trash.repository.ts b/server/src/repositories/trash.repository.ts index 06e75a8d2e..69507b1d58 100644 --- a/server/src/repositories/trash.repository.ts +++ b/server/src/repositories/trash.repository.ts @@ -3,9 +3,8 @@ import { InjectKysely } from 'nestjs-kysely'; import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetStatus } from 'src/enum'; -import { ITrashRepository } from 'src/interfaces/trash.interface'; -export class TrashRepository implements ITrashRepository { +export class TrashRepository { constructor(@InjectKysely() private db: Kysely) {} getDeletedIds(): AsyncIterableIterator<{ id: string }> { diff --git a/server/src/repositories/version-history.repository.ts b/server/src/repositories/version-history.repository.ts index e6ec8edcf4..063ee0da84 100644 --- a/server/src/repositories/version-history.repository.ts +++ b/server/src/repositories/version-history.repository.ts @@ -3,25 +3,23 @@ import { Insertable, Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DB, VersionHistory } from 'src/db'; import { GenerateSql } from 'src/decorators'; -import { VersionHistoryEntity } from 'src/entities/version-history.entity'; -import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; @Injectable() -export class VersionHistoryRepository implements IVersionHistoryRepository { +export class VersionHistoryRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql() - getAll(): Promise { + getAll() { return this.db.selectFrom('version_history').selectAll().orderBy('createdAt', 'desc').execute(); } @GenerateSql() - getLatest(): Promise { + getLatest() { return this.db.selectFrom('version_history').selectAll().orderBy('createdAt', 'desc').executeTakeFirst(); } @GenerateSql({ params: [{ version: 'v1.123.0' }] }) - create(version: Insertable): Promise { + create(version: Insertable) { return this.db.insertInto('version_history').values(version).returningAll().executeTakeFirstOrThrow(); } } diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 99c794adc9..fe732843b6 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -2,11 +2,11 @@ import { BadRequestException } from '@nestjs/common'; import _ from 'lodash'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { AlbumUserRole } from 'src/enum'; -import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { AlbumService } from 'src/services/album.service'; +import { IAlbumUserRepository } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index ffa280677a..780d802922 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -5,13 +5,12 @@ import { UserEntity } from 'src/entities/user.entity'; import { AuthType, Permission } from 'src/enum'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; -import { IOAuthRepository } from 'src/interfaces/oauth.interface'; import { ISessionRepository } from 'src/interfaces/session.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { AuthService } from 'src/services/auth.service'; -import { IApiKeyRepository } from 'src/types'; +import { IApiKeyRepository, IOAuthRepository } from 'src/types'; import { keyStub } from 'test/fixtures/api-key.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { sessionStub } from 'test/fixtures/session.stub'; diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 4c0cdbab91..f46eb93111 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -19,7 +19,7 @@ import { import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto'; import { UserEntity } from 'src/entities/user.entity'; import { AuthType, ImmichCookie, ImmichHeader, ImmichQuery, Permission } from 'src/enum'; -import { OAuthProfile } from 'src/interfaces/oauth.interface'; +import { OAuthProfile } from 'src/repositories/oauth.repository'; import { BaseService } from 'src/services/base.service'; import { AuthApiKey } from 'src/types'; import { isGranted } from 'src/utils/access'; diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index 29adf9d8e1..33d77a59aa 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -2,14 +2,13 @@ import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; import { ImmichWorker, StorageFolder } from 'src/enum'; -import { ICronRepository } from 'src/interfaces/cron.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { JobStatus } from 'src/interfaces/job.interface'; import { IProcessRepository } from 'src/interfaces/process.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { BackupService } from 'src/services/backup.service'; -import { IConfigRepository } from 'src/types'; +import { IConfigRepository, ICronRepository } from 'src/types'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { mockSpawn, newTestService } from 'test/utils'; import { describe, Mocked } from 'vitest'; diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 398b1508f5..865a16a9da 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -6,44 +6,44 @@ import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { Users } from 'src/db'; import { UserEntity } from 'src/entities/user.entity'; -import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ICronRepository } from 'src/interfaces/cron.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; -import { IMapRepository } from 'src/interfaces/map.interface'; -import { IMetadataRepository } from 'src/interfaces/metadata.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; -import { INotificationRepository } from 'src/interfaces/notification.interface'; -import { IOAuthRepository } from 'src/interfaces/oauth.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IProcessRepository } from 'src/interfaces/process.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; -import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { ISessionRepository } from 'src/interfaces/session.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; -import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; -import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; +import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { ApiKeyRepository } from 'src/repositories/api-key.repository'; import { AuditRepository } from 'src/repositories/audit.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { CronRepository } from 'src/repositories/cron.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { MapRepository } from 'src/repositories/map.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { MetadataRepository } from 'src/repositories/metadata.repository'; +import { NotificationRepository } from 'src/repositories/notification.repository'; +import { OAuthRepository } from 'src/repositories/oauth.repository'; +import { ServerInfoRepository } from 'src/repositories/server-info.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; +import { TrashRepository } from 'src/repositories/trash.repository'; +import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access'; import { getConfig, updateConfig } from 'src/utils/config'; @@ -57,10 +57,10 @@ export class BaseService { protected activityRepository: ActivityRepository, protected auditRepository: AuditRepository, @Inject(IAlbumRepository) protected albumRepository: IAlbumRepository, - @Inject(IAlbumUserRepository) protected albumUserRepository: IAlbumUserRepository, + protected albumUserRepository: AlbumUserRepository, @Inject(IAssetRepository) protected assetRepository: IAssetRepository, protected configRepository: ConfigRepository, - @Inject(ICronRepository) protected cronRepository: ICronRepository, + protected cronRepository: CronRepository, @Inject(ICryptoRepository) protected cryptoRepository: ICryptoRepository, @Inject(IDatabaseRepository) protected databaseRepository: IDatabaseRepository, @Inject(IEventRepository) protected eventRepository: IEventRepository, @@ -68,28 +68,28 @@ export class BaseService { protected keyRepository: ApiKeyRepository, @Inject(ILibraryRepository) protected libraryRepository: ILibraryRepository, @Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository, - @Inject(IMapRepository) protected mapRepository: IMapRepository, + protected mapRepository: MapRepository, protected mediaRepository: MediaRepository, protected memoryRepository: MemoryRepository, - @Inject(IMetadataRepository) protected metadataRepository: IMetadataRepository, + protected metadataRepository: MetadataRepository, @Inject(IMoveRepository) protected moveRepository: IMoveRepository, - @Inject(INotificationRepository) protected notificationRepository: INotificationRepository, - @Inject(IOAuthRepository) protected oauthRepository: IOAuthRepository, + protected notificationRepository: NotificationRepository, + protected oauthRepository: OAuthRepository, @Inject(IPartnerRepository) protected partnerRepository: IPartnerRepository, @Inject(IPersonRepository) protected personRepository: IPersonRepository, @Inject(IProcessRepository) protected processRepository: IProcessRepository, @Inject(ISearchRepository) protected searchRepository: ISearchRepository, - @Inject(IServerInfoRepository) protected serverInfoRepository: IServerInfoRepository, + protected serverInfoRepository: ServerInfoRepository, @Inject(ISessionRepository) protected sessionRepository: ISessionRepository, @Inject(ISharedLinkRepository) protected sharedLinkRepository: ISharedLinkRepository, @Inject(IStackRepository) protected stackRepository: IStackRepository, @Inject(IStorageRepository) protected storageRepository: IStorageRepository, @Inject(ISystemMetadataRepository) protected systemMetadataRepository: ISystemMetadataRepository, @Inject(ITagRepository) protected tagRepository: ITagRepository, - @Inject(ITelemetryRepository) protected telemetryRepository: ITelemetryRepository, - @Inject(ITrashRepository) protected trashRepository: ITrashRepository, + protected telemetryRepository: TelemetryRepository, + protected trashRepository: TrashRepository, @Inject(IUserRepository) protected userRepository: IUserRepository, - @Inject(IVersionHistoryRepository) protected versionRepository: IVersionHistoryRepository, + protected versionRepository: VersionHistoryRepository, protected viewRepository: ViewRepository, ) { this.logger.setContext(this.constructor.name); diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 195ed10156..5d11f895a1 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -3,10 +3,10 @@ import { defaults, SystemConfig } from 'src/config'; import { ImmichWorker } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IJobRepository, JobCommand, JobItem, JobName, JobStatus, QueueName } from 'src/interfaces/job.interface'; -import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { JobService } from 'src/services/job.service'; import { IConfigRepository, ILoggingRepository } from 'src/types'; import { assetStub } from 'test/fixtures/asset.stub'; +import { ITelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { newTestService } from 'test/utils'; import { Mocked } from 'vitest'; @@ -16,7 +16,7 @@ describe(JobService.name, () => { let configMock: Mocked; let jobMock: Mocked; let loggerMock: Mocked; - let telemetryMock: Mocked; + let telemetryMock: ITelemetryRepositoryMock; beforeEach(() => { ({ sut, assetMock, configMock, jobMock, loggerMock, telemetryMock } = newTestService(JobService, {})); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index e2d805b865..5f81d92ec2 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -5,7 +5,6 @@ import { mapLibrary } from 'src/dtos/library.dto'; import { UserEntity } from 'src/entities/user.entity'; import { AssetType, ImmichWorker } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ICronRepository } from 'src/interfaces/cron.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IJobRepository, @@ -18,7 +17,7 @@ import { import { ILibraryRepository } from 'src/interfaces/library.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { LibraryService } from 'src/services/library.service'; -import { IConfigRepository } from 'src/types'; +import { IConfigRepository, ICronRepository } from 'src/types'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { libraryStub } from 'test/fixtures/library.stub'; diff --git a/server/src/services/map.service.spec.ts b/server/src/services/map.service.spec.ts index fde2ba7e0f..30505f7f5b 100644 --- a/server/src/services/map.service.spec.ts +++ b/server/src/services/map.service.spec.ts @@ -1,7 +1,7 @@ import { IAlbumRepository } from 'src/interfaces/album.interface'; -import { IMapRepository } from 'src/interfaces/map.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { MapService } from 'src/services/map.service'; +import { IMapRepository } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 4688e8b119..8cc6e014d2 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -10,15 +10,14 @@ import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interfac import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; -import { IMapRepository } from 'src/interfaces/map.interface'; -import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; +import { ImmichTags } from 'src/repositories/metadata.repository'; import { MetadataService } from 'src/services/metadata.service'; -import { IConfigRepository, IMediaRepository } from 'src/types'; +import { IConfigRepository, IMapRepository, IMediaRepository, IMetadataRepository } from 'src/types'; import { assetStub } from 'test/fixtures/asset.stub'; import { fileStub } from 'test/fixtures/file.stub'; import { probeStub } from 'test/fixtures/media.stub'; diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 406f80038c..d5b7e6e4e4 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -18,8 +18,8 @@ import { WithoutProperty } from 'src/interfaces/asset.interface'; import { DatabaseLock } from 'src/interfaces/database.interface'; import { ArgOf } from 'src/interfaces/event.interface'; import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface'; -import { ReverseGeocodeResult } from 'src/interfaces/map.interface'; -import { ImmichTags } from 'src/interfaces/metadata.interface'; +import { ReverseGeocodeResult } from 'src/repositories/map.repository'; +import { ImmichTags } from 'src/repositories/metadata.repository'; import { BaseService } from 'src/services/base.service'; import { isFaceImportEnabled } from 'src/utils/misc'; import { usePagination } from 'src/utils/pagination'; diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 76da12bbd6..671cae0774 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -8,10 +8,11 @@ import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, INotifyAlbumUpdateJob, JobName, JobStatus } from 'src/interfaces/job.interface'; -import { EmailTemplate, INotificationRepository } from 'src/interfaces/notification.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; +import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; +import { INotificationRepository } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 37b265c6ae..85f72443d4 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -12,7 +12,7 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; -import { EmailImageAttachment, EmailTemplate } from 'src/interfaces/notification.interface'; +import { EmailImageAttachment, EmailTemplate } from 'src/repositories/notification.repository'; import { BaseService } from 'src/services/base.service'; import { getAssetFiles } from 'src/utils/asset.util'; import { getFilenameExtension } from 'src/utils/file'; diff --git a/server/src/services/trash.service.spec.ts b/server/src/services/trash.service.spec.ts index 4d877c9dfa..8b93e899e7 100644 --- a/server/src/services/trash.service.spec.ts +++ b/server/src/services/trash.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; -import { ITrashRepository } from 'src/interfaces/trash.interface'; import { TrashService } from 'src/services/trash.service'; +import { ITrashRepository } from 'src/types'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newTestService } from 'test/utils'; diff --git a/server/src/services/version.service.spec.ts b/server/src/services/version.service.spec.ts index 022e1de613..406d3c1439 100644 --- a/server/src/services/version.service.spec.ts +++ b/server/src/services/version.service.spec.ts @@ -4,11 +4,9 @@ import { serverVersion } from 'src/constants'; import { ImmichEnvironment, SystemMetadataKey } from 'src/enum'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; -import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; -import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; import { VersionService } from 'src/services/version.service'; -import { IConfigRepository, ILoggingRepository } from 'src/types'; +import { IConfigRepository, ILoggingRepository, IServerInfoRepository, IVersionHistoryRepository } from 'src/types'; import { mockEnvData } from 'test/repositories/config.repository.mock'; import { newTestService } from 'test/utils'; import { Mocked } from 'vitest'; diff --git a/server/src/types.ts b/server/src/types.ts index 69d9d8e647..9928669136 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -2,12 +2,22 @@ import { UserEntity } from 'src/entities/user.entity'; import { ExifOrientation, ImageFormat, Permission, TranscodeTarget, VideoCodec } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; +import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { ApiKeyRepository } from 'src/repositories/api-key.repository'; import { AuditRepository } from 'src/repositories/audit.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; +import { CronRepository } from 'src/repositories/cron.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { MapRepository } from 'src/repositories/map.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { MetadataRepository } from 'src/repositories/metadata.repository'; +import { NotificationRepository } from 'src/repositories/notification.repository'; +import { OAuthRepository } from 'src/repositories/oauth.repository'; +import { ServerInfoRepository } from 'src/repositories/server-info.repository'; +import { MetricGroupRepository, TelemetryRepository } from 'src/repositories/telemetry.repository'; +import { TrashRepository } from 'src/repositories/trash.repository'; +import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; export type DeepPartial = T extends object ? { [K in keyof T]?: DeepPartial } : T; @@ -23,9 +33,11 @@ export type RepositoryInterface = Pick; export type IActivityRepository = RepositoryInterface; export type IAccessRepository = { [K in keyof AccessRepository]: RepositoryInterface }; +export type IAlbumUserRepository = RepositoryInterface; export type IApiKeyRepository = RepositoryInterface; export type IAuditRepository = RepositoryInterface; export type IConfigRepository = RepositoryInterface; +export type ICronRepository = RepositoryInterface; export type ILoggingRepository = Pick< LoggingRepository, | 'verbose' @@ -39,9 +51,18 @@ export type ILoggingRepository = Pick< | 'setContext' | 'setAppName' >; +export type IMapRepository = RepositoryInterface; export type IMediaRepository = RepositoryInterface; export type IMemoryRepository = RepositoryInterface; +export type IMetadataRepository = RepositoryInterface; +export type IMetricGroupRepository = RepositoryInterface; +export type INotificationRepository = RepositoryInterface; +export type IOAuthRepository = RepositoryInterface; +export type IServerInfoRepository = RepositoryInterface; +export type ITelemetryRepository = RepositoryInterface; +export type ITrashRepository = RepositoryInterface; export type IViewRepository = RepositoryInterface; +export type IVersionHistoryRepository = RepositoryInterface; export type ActivityItem = | Awaited> diff --git a/server/test/fixtures/metadata.stub.ts b/server/test/fixtures/metadata.stub.ts index 05535303e4..e60d8d0eac 100644 --- a/server/test/fixtures/metadata.stub.ts +++ b/server/test/fixtures/metadata.stub.ts @@ -1,4 +1,4 @@ -import { ImmichTags } from 'src/interfaces/metadata.interface'; +import { ImmichTags } from 'src/repositories/metadata.repository'; import { personStub } from 'test/fixtures/person.stub'; export const metadataStub = { diff --git a/server/test/repositories/album-user.repository.mock.ts b/server/test/repositories/album-user.repository.mock.ts index 70c0487256..aa9436e33d 100644 --- a/server/test/repositories/album-user.repository.mock.ts +++ b/server/test/repositories/album-user.repository.mock.ts @@ -1,4 +1,4 @@ -import { IAlbumUserRepository } from 'src/interfaces/album-user.interface'; +import { IAlbumUserRepository } from 'src/types'; import { Mocked } from 'vitest'; export const newAlbumUserRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/cron.repository.mock.ts b/server/test/repositories/cron.repository.mock.ts index 2b0784e8ac..cc856909c8 100644 --- a/server/test/repositories/cron.repository.mock.ts +++ b/server/test/repositories/cron.repository.mock.ts @@ -1,4 +1,4 @@ -import { ICronRepository } from 'src/interfaces/cron.interface'; +import { ICronRepository } from 'src/types'; import { Mocked, vitest } from 'vitest'; export const newCronRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/map.repository.mock.ts b/server/test/repositories/map.repository.mock.ts index 703e8696f1..4b56b9443a 100644 --- a/server/test/repositories/map.repository.mock.ts +++ b/server/test/repositories/map.repository.mock.ts @@ -1,4 +1,4 @@ -import { IMapRepository } from 'src/interfaces/map.interface'; +import { IMapRepository } from 'src/types'; import { Mocked } from 'vitest'; export const newMapRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/metadata.repository.mock.ts b/server/test/repositories/metadata.repository.mock.ts index 60c5644b36..e9bb68b95b 100644 --- a/server/test/repositories/metadata.repository.mock.ts +++ b/server/test/repositories/metadata.repository.mock.ts @@ -1,4 +1,4 @@ -import { IMetadataRepository } from 'src/interfaces/metadata.interface'; +import { IMetadataRepository } from 'src/types'; import { Mocked, vitest } from 'vitest'; export const newMetadataRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/notification.repository.mock.ts b/server/test/repositories/notification.repository.mock.ts index 16862dc3d7..2065a0bf3e 100644 --- a/server/test/repositories/notification.repository.mock.ts +++ b/server/test/repositories/notification.repository.mock.ts @@ -1,4 +1,4 @@ -import { INotificationRepository } from 'src/interfaces/notification.interface'; +import { INotificationRepository } from 'src/types'; import { Mocked } from 'vitest'; export const newNotificationRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/oauth.repository.mock.ts b/server/test/repositories/oauth.repository.mock.ts index f87b3781e9..8980bfb14f 100644 --- a/server/test/repositories/oauth.repository.mock.ts +++ b/server/test/repositories/oauth.repository.mock.ts @@ -1,4 +1,4 @@ -import { IOAuthRepository } from 'src/interfaces/oauth.interface'; +import { IOAuthRepository } from 'src/types'; import { Mocked } from 'vitest'; export const newOAuthRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/server-info.repository.mock.ts b/server/test/repositories/server-info.repository.mock.ts index f55933d3c6..5e9ecd1387 100644 --- a/server/test/repositories/server-info.repository.mock.ts +++ b/server/test/repositories/server-info.repository.mock.ts @@ -1,4 +1,4 @@ -import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; +import { IServerInfoRepository } from 'src/types'; import { Mocked, vitest } from 'vitest'; export const newServerInfoRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/telemetry.repository.mock.ts b/server/test/repositories/telemetry.repository.mock.ts index 2d537e888a..afadcea0cf 100644 --- a/server/test/repositories/telemetry.repository.mock.ts +++ b/server/test/repositories/telemetry.repository.mock.ts @@ -1,4 +1,4 @@ -import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; +import { ITelemetryRepository, RepositoryInterface } from 'src/types'; import { Mocked, vitest } from 'vitest'; const newMetricGroupMock = () => { @@ -10,7 +10,11 @@ const newMetricGroupMock = () => { }; }; -export const newTelemetryRepositoryMock = (): Mocked => { +export type ITelemetryRepositoryMock = { + [K in keyof ITelemetryRepository]: Mocked>; +}; + +export const newTelemetryRepositoryMock = (): ITelemetryRepositoryMock => { return { setup: vitest.fn(), api: newMetricGroupMock(), diff --git a/server/test/repositories/trash.repository.mock.ts b/server/test/repositories/trash.repository.mock.ts index 472b315b01..f983afdce8 100644 --- a/server/test/repositories/trash.repository.mock.ts +++ b/server/test/repositories/trash.repository.mock.ts @@ -1,4 +1,4 @@ -import { ITrashRepository } from 'src/interfaces/trash.interface'; +import { ITrashRepository } from 'src/types'; import { Mocked, vitest } from 'vitest'; export const newTrashRepositoryMock = (): Mocked => { diff --git a/server/test/repositories/version-history.repository.mock.ts b/server/test/repositories/version-history.repository.mock.ts index 7c35e316d3..9ff7708796 100644 --- a/server/test/repositories/version-history.repository.mock.ts +++ b/server/test/repositories/version-history.repository.mock.ts @@ -1,4 +1,4 @@ -import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; +import { IVersionHistoryRepository } from 'src/types'; import { Mocked, vitest } from 'vitest'; export const newVersionHistoryRepositoryMock = (): Mocked => { diff --git a/server/test/utils.ts b/server/test/utils.ts index 0ab1739e14..94377ca18c 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -2,24 +2,42 @@ import { ChildProcessWithoutNullStreams } from 'node:child_process'; import { Writable } from 'node:stream'; import { PNG } from 'pngjs'; import { ImmichWorker } from 'src/enum'; -import { IMetadataRepository } from 'src/interfaces/metadata.interface'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; +import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { ApiKeyRepository } from 'src/repositories/api-key.repository'; import { AuditRepository } from 'src/repositories/audit.repository'; +import { CronRepository } from 'src/repositories/cron.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { MapRepository } from 'src/repositories/map.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { MetadataRepository } from 'src/repositories/metadata.repository'; +import { NotificationRepository } from 'src/repositories/notification.repository'; +import { OAuthRepository } from 'src/repositories/oauth.repository'; +import { ServerInfoRepository } from 'src/repositories/server-info.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; +import { TrashRepository } from 'src/repositories/trash.repository'; +import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; import { BaseService } from 'src/services/base.service'; import { IAccessRepository, IActivityRepository, + IAlbumUserRepository, IApiKeyRepository, IAuditRepository, + ICronRepository, ILoggingRepository, + IMapRepository, IMediaRepository, IMemoryRepository, + IMetadataRepository, + INotificationRepository, + IOAuthRepository, + IServerInfoRepository, + ITrashRepository, + IVersionHistoryRepository, IViewRepository, } from 'src/types'; import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; @@ -66,7 +84,7 @@ import { Mocked, vitest } from 'vitest'; type Overrides = { worker?: ImmichWorker; - metadataRepository?: IMetadataRepository; + metadataRepository?: MetadataRepository; }; type BaseServiceArgs = ConstructorParameters; type Constructor> = { @@ -125,10 +143,10 @@ export const newTestService = ( activityMock as IActivityRepository as ActivityRepository, auditMock as IAuditRepository as AuditRepository, albumMock, - albumUserMock, + albumUserMock as IAlbumUserRepository as AlbumUserRepository, assetMock, configMock, - cronMock, + cronMock as ICronRepository as CronRepository, cryptoMock, databaseMock, eventMock, @@ -136,28 +154,28 @@ export const newTestService = ( keyMock as IApiKeyRepository as ApiKeyRepository, libraryMock, machineLearningMock, - mapMock, + mapMock as IMapRepository as MapRepository, mediaMock as IMediaRepository as MediaRepository, memoryMock as IMemoryRepository as MemoryRepository, - metadataMock, + metadataMock as IMetadataRepository as MetadataRepository, moveMock, - notificationMock, - oauthMock, + notificationMock as INotificationRepository as NotificationRepository, + oauthMock as IOAuthRepository as OAuthRepository, partnerMock, personMock, processMock, searchMock, - serverInfoMock, + serverInfoMock as IServerInfoRepository as ServerInfoRepository, sessionMock, sharedLinkMock, stackMock, storageMock, systemMock, tagMock, - telemetryMock, - trashMock, + telemetryMock as unknown as TelemetryRepository, + trashMock as ITrashRepository as TrashRepository, userMock, - versionHistoryMock, + versionHistoryMock as IVersionHistoryRepository as VersionHistoryRepository, viewMock as IViewRepository as ViewRepository, );