diff --git a/server/package.json b/server/package.json index 72207526a5..9e3d32862d 100644 --- a/server/package.json +++ b/server/package.json @@ -29,7 +29,6 @@ "migrations:run": "node ./dist/bin/migrations.js run", "schema:drop": "node ./dist/bin/migrations.js query 'DROP schema public cascade; CREATE schema public;'", "schema:reset": "npm run schema:drop && npm run migrations:run", - "kysely:codegen": "npx kysely-codegen --include-pattern=\"(public|vectors).*\" --dialect postgres --url postgres://postgres:postgres@localhost/immich --log-level debug --out-file=./src/db.d.ts", "sync:open-api": "node ./dist/bin/sync-open-api.js", "sync:sql": "node ./dist/bin/sync-sql.js", "email:dev": "email dev -p 3050 --dir src/emails", diff --git a/server/src/database.ts b/server/src/database.ts index 4042617e98..b28bb24451 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -1,5 +1,4 @@ import { Selectable } from 'kysely'; -import { Albums, Exif as DatabaseExif } from 'src/db'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AlbumUserRole, @@ -13,6 +12,8 @@ import { UserAvatarColor, UserStatus, } from 'src/enum'; +import { AlbumTable } from 'src/schema/tables/album.table'; +import { ExifTable } from 'src/schema/tables/exif.table'; import { UserMetadataItem } from 'src/types'; export type AuthUser = { @@ -193,7 +194,7 @@ export type SharedLink = { userId: string; }; -export type Album = Selectable & { +export type Album = Selectable & { owner: User; assets: MapAsset[]; }; @@ -239,7 +240,7 @@ export type Session = { pinExpiresAt: Date | null; }; -export type Exif = Omit, 'updatedAt' | 'updateId'>; +export type Exif = Omit, 'updatedAt' | 'updateId'>; export type Person = { createdAt: Date; diff --git a/server/src/db.d.ts b/server/src/db.d.ts deleted file mode 100644 index 68ddd245f3..0000000000 --- a/server/src/db.d.ts +++ /dev/null @@ -1,540 +0,0 @@ -/** - * This file was generated by kysely-codegen. - * Please do not edit it manually. - */ - -import type { ColumnType } from 'kysely'; -import { - AlbumUserRole, - AssetFileType, - AssetOrder, - AssetStatus, - AssetType, - AssetVisibility, - MemoryType, - NotificationLevel, - NotificationType, - Permission, - SharedLinkType, - SourceType, - SyncEntityType, -} from 'src/enum'; -import { MemoryAssetAuditTable } from 'src/schema/tables/memory-asset-audit.table'; -import { MemoryAssetTable } from 'src/schema/tables/memory-asset.table'; -import { MemoryAuditTable } from 'src/schema/tables/memory-audit.table'; -import { UserTable } from 'src/schema/tables/user.table'; -import { UserMetadataItem } from 'src/types'; - -export type ArrayType = ArrayTypeImpl extends (infer U)[] ? U[] : ArrayTypeImpl; - -export type ArrayTypeImpl = T extends ColumnType ? ColumnType : T[]; - -export type Generated = - T extends ColumnType ? ColumnType : ColumnType; - -export type Int8 = ColumnType; - -export type Json = JsonValue; - -export type JsonArray = JsonValue[]; - -export type JsonObject = { - [x: string]: JsonValue | undefined; -}; - -export type JsonPrimitive = boolean | number | string | null; - -export type JsonValue = JsonArray | JsonObject | JsonPrimitive; - -export type Timestamp = ColumnType; - -export interface Activity { - albumId: string; - assetId: string | null; - comment: string | null; - createdAt: Generated; - id: Generated; - isLiked: Generated; - updatedAt: Generated; - updateId: Generated; - userId: string; -} - -export interface Albums { - albumName: Generated; - /** - * Asset ID to be used as thumbnail - */ - albumThumbnailAssetId: string | null; - createdAt: Generated; - deletedAt: Timestamp | null; - description: Generated; - id: Generated; - isActivityEnabled: Generated; - order: Generated; - ownerId: string; - updatedAt: Generated; - updateId: Generated; -} - -export interface AlbumsAudit { - deletedAt: Generated; - id: Generated; - albumId: string; - userId: string; -} - -export interface AlbumUsersAudit { - deletedAt: Generated; - id: Generated; - albumId: string; - userId: string; -} - -export interface AlbumsAssetsAssets { - albumsId: string; - assetsId: string; - createdAt: Generated; - updatedAt: Generated; - updateId: Generated; -} - -export interface AlbumAssetsAudit { - deletedAt: Generated; - id: Generated; - albumId: string; - assetId: string; -} - -export interface AlbumsSharedUsersUsers { - albumsId: string; - role: Generated; - usersId: string; - createId: Generated; - createdAt: Generated; - updateId: Generated; - updatedAt: Generated; -} - -export interface ApiKeys { - createdAt: Generated; - id: Generated; - key: string; - name: string; - permissions: Permission[]; - updatedAt: Generated; - updateId: Generated; - userId: string; -} - -export interface AssetFaces { - assetId: string; - boundingBoxX1: Generated; - boundingBoxX2: Generated; - boundingBoxY1: Generated; - boundingBoxY2: Generated; - deletedAt: Timestamp | null; - id: Generated; - imageHeight: Generated; - imageWidth: Generated; - personId: string | null; - sourceType: Generated; -} - -export interface AssetFiles { - assetId: string; - createdAt: Generated; - id: Generated; - path: string; - type: AssetFileType; - updatedAt: Generated; - updateId: Generated; -} - -export interface AssetJobStatus { - assetId: string; - duplicatesDetectedAt: Timestamp | null; - facesRecognizedAt: Timestamp | null; - metadataExtractedAt: Timestamp | null; - previewAt: Timestamp | null; - thumbnailAt: Timestamp | null; -} - -export interface AssetsAudit { - deletedAt: Generated; - id: Generated; - assetId: string; - ownerId: string; -} - -export interface Assets { - checksum: Buffer; - createdAt: Generated; - deletedAt: Timestamp | null; - deviceAssetId: string; - deviceId: string; - duplicateId: string | null; - duration: string | null; - encodedVideoPath: Generated; - fileCreatedAt: Timestamp; - fileModifiedAt: Timestamp; - id: Generated; - isExternal: Generated; - isFavorite: Generated; - isOffline: Generated; - visibility: Generated; - libraryId: string | null; - livePhotoVideoId: string | null; - localDateTime: Timestamp; - originalFileName: string; - originalPath: string; - ownerId: string; - sidecarPath: string | null; - stackId: string | null; - status: Generated; - thumbhash: Buffer | null; - type: AssetType; - updatedAt: Generated; - updateId: Generated; -} - -export interface AssetStack { - id: Generated; - ownerId: string; - primaryAssetId: string; -} - -export interface Audit { - action: string; - createdAt: Generated; - entityId: string; - entityType: string; - id: Generated; - ownerId: string; -} - -export interface Exif { - assetId: string; - updateId: Generated; - updatedAt: Generated; - autoStackId: string | null; - bitsPerSample: number | null; - city: string | null; - colorspace: string | null; - country: string | null; - dateTimeOriginal: Timestamp | null; - description: Generated; - exifImageHeight: number | null; - exifImageWidth: number | null; - exposureTime: string | null; - fileSizeInByte: Int8 | null; - fNumber: number | null; - focalLength: number | null; - fps: number | null; - iso: number | null; - latitude: number | null; - lensModel: string | null; - livePhotoCID: string | null; - longitude: number | null; - make: string | null; - model: string | null; - modifyDate: Timestamp | null; - orientation: string | null; - profileDescription: string | null; - projectionType: string | null; - rating: number | null; - state: string | null; - timeZone: string | null; -} - -export interface FaceSearch { - embedding: string; - faceId: string; -} - -export interface GeodataPlaces { - admin1Code: string | null; - admin1Name: string | null; - admin2Code: string | null; - admin2Name: string | null; - alternateNames: string | null; - countryCode: string; - id: number; - latitude: number; - longitude: number; - modificationDate: Timestamp; - name: string; -} - -export interface Libraries { - createdAt: Generated; - deletedAt: Timestamp | null; - exclusionPatterns: string[]; - id: Generated; - importPaths: string[]; - name: string; - ownerId: string; - refreshedAt: Timestamp | null; - updatedAt: Generated; - updateId: Generated; -} - -export interface Memories { - createdAt: Generated; - data: object; - deletedAt: Timestamp | null; - hideAt: Timestamp | null; - id: Generated; - isSaved: Generated; - memoryAt: Timestamp; - ownerId: string; - seenAt: Timestamp | null; - showAt: Timestamp | null; - type: MemoryType; - updatedAt: Generated; - updateId: Generated; -} - -export interface Notifications { - id: Generated; - createdAt: Generated; - updatedAt: Generated; - deletedAt: Timestamp | null; - updateId: Generated; - userId: string; - level: Generated; - type: NotificationType; - title: string; - description: string | null; - data: any | null; - readAt: Timestamp | null; -} - -export interface Migrations { - id: Generated; - name: string; - timestamp: Int8; -} - -export interface MoveHistory { - entityId: string; - id: Generated; - newPath: string; - oldPath: string; - pathType: string; -} - -export interface NaturalearthCountries { - admin: string; - admin_a3: string; - coordinates: string; - id: Generated; - type: string; -} - -export interface PartnersAudit { - deletedAt: Generated; - id: Generated; - sharedById: string; - sharedWithId: string; -} - -export interface Partners { - createdAt: Generated; - createId: Generated; - inTimeline: Generated; - sharedById: string; - sharedWithId: string; - updatedAt: Generated; - updateId: Generated; -} - -export interface Person { - birthDate: Timestamp | null; - color: string | null; - createdAt: Generated; - faceAssetId: string | null; - id: Generated; - isFavorite: Generated; - isHidden: Generated; - name: Generated; - ownerId: string; - thumbnailPath: Generated; - updatedAt: Generated; - updateId: Generated; -} - -export interface Sessions { - createdAt: Generated; - deviceOS: Generated; - deviceType: Generated; - id: Generated; - parentId: string | null; - expiresAt: Date | null; - token: string; - updatedAt: Generated; - updateId: Generated; - userId: string; - pinExpiresAt: Timestamp | null; -} - -export interface SessionSyncCheckpoints { - ack: string; - createdAt: Generated; - sessionId: string; - type: SyncEntityType; - updatedAt: Generated; - updateId: Generated; -} - -export interface SharedLinkAsset { - assetsId: string; - sharedLinksId: string; -} - -export interface SharedLinks { - albumId: string | null; - allowDownload: Generated; - allowUpload: Generated; - createdAt: Generated; - description: string | null; - expiresAt: Timestamp | null; - id: Generated; - key: Buffer; - password: string | null; - showExif: Generated; - type: SharedLinkType; - userId: string; -} - -export interface SmartSearch { - assetId: string; - embedding: string; -} - -export interface SocketIoAttachments { - created_at: Generated; - id: Generated; - payload: Buffer | null; -} - -export interface SystemConfig { - key: string; - value: string | null; -} - -export interface SystemMetadata { - key: string; - value: Json; -} - -export interface TagAsset { - assetsId: string; - tagsId: string; -} - -export interface Tags { - color: string | null; - createdAt: Generated; - id: Generated; - parentId: string | null; - updatedAt: Generated; - updateId: Generated; - userId: string; - value: string; -} - -export interface TagsClosure { - id_ancestor: string; - id_descendant: string; -} - -export interface TypeormMetadata { - database: string | null; - name: string | null; - schema: string | null; - table: string | null; - type: string; - value: string | null; -} - -export interface UserMetadata extends UserMetadataItem { - userId: string; -} - -export interface UsersAudit { - id: Generated; - userId: string; - deletedAt: Generated; -} - -export interface VectorsPgVectorIndexStat { - idx_growing: ArrayType | null; - idx_indexing: boolean | null; - idx_options: string | null; - idx_sealed: ArrayType | null; - idx_size: Int8 | null; - idx_status: string | null; - idx_tuples: Int8 | null; - idx_write: Int8 | null; - indexname: string | null; - indexrelid: number | null; - tablename: string | null; - tablerelid: number | null; -} - -export interface VersionHistory { - createdAt: Generated; - id: Generated; - version: string; -} - -export interface DB { - activity: Activity; - albums: Albums; - albums_audit: AlbumsAudit; - albums_assets_assets: AlbumsAssetsAssets; - album_assets_audit: AlbumAssetsAudit; - albums_shared_users_users: AlbumsSharedUsersUsers; - album_users_audit: AlbumUsersAudit; - api_keys: ApiKeys; - asset_faces: AssetFaces; - asset_files: AssetFiles; - asset_job_status: AssetJobStatus; - asset_stack: AssetStack; - assets: Assets; - assets_audit: AssetsAudit; - audit: Audit; - exif: Exif; - face_search: FaceSearch; - geodata_places: GeodataPlaces; - libraries: Libraries; - memories: Memories; - memories_audit: MemoryAuditTable; - memories_assets_assets: MemoryAssetTable; - memory_assets_audit: MemoryAssetAuditTable; - migrations: Migrations; - notifications: Notifications; - move_history: MoveHistory; - naturalearth_countries: NaturalearthCountries; - partners_audit: PartnersAudit; - partners: Partners; - person: Person; - sessions: Sessions; - session_sync_checkpoints: SessionSyncCheckpoints; - shared_link__asset: SharedLinkAsset; - shared_links: SharedLinks; - smart_search: SmartSearch; - socket_io_attachments: SocketIoAttachments; - system_config: SystemConfig; - system_metadata: SystemMetadata; - tag_asset: TagAsset; - tags: Tags; - tags_closure: TagsClosure; - typeorm_metadata: TypeormMetadata; - user_metadata: UserMetadata; - users: UserTable; - users_audit: UsersAudit; - 'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat; - version_history: VersionHistory; -} diff --git a/server/src/dtos/person.dto.ts b/server/src/dtos/person.dto.ts index c59ab905bd..18da39b6d4 100644 --- a/server/src/dtos/person.dto.ts +++ b/server/src/dtos/person.dto.ts @@ -4,10 +4,10 @@ import { IsArray, IsInt, IsNotEmpty, IsNumber, IsString, Max, Min, ValidateNeste import { Selectable } from 'kysely'; import { DateTime } from 'luxon'; import { AssetFace, Person } from 'src/database'; -import { AssetFaces } from 'src/db'; import { PropertyLifecycle } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { SourceType } from 'src/enum'; +import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { asDateString } from 'src/utils/date'; import { IsDateStringFormat, @@ -232,7 +232,7 @@ export function mapPerson(person: Person): PersonResponseDto { }; } -export function mapFacesWithoutPerson(face: Selectable): AssetFaceWithoutPersonResponseDto { +export function mapFacesWithoutPerson(face: Selectable): AssetFaceWithoutPersonResponseDto { return { id: face.id, imageHeight: face.imageHeight, diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 17f69c0e52..8863d117bc 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { Kysely, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { AlbumUserRole, AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; import { asUuid } from 'src/utils/database'; class ActivityAccess { diff --git a/server/src/repositories/activity.repository.ts b/server/src/repositories/activity.repository.ts index ecde95cf78..0714d058cf 100644 --- a/server/src/repositories/activity.repository.ts +++ b/server/src/repositories/activity.repository.ts @@ -3,9 +3,10 @@ import { Insertable, Kysely, NotNull, sql } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { Activity, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; +import { ActivityTable } from 'src/schema/tables/activity.table'; import { asUuid } from 'src/utils/database'; export interface ActivitySearch { @@ -48,7 +49,7 @@ export class ActivityRepository { } @GenerateSql({ params: [{ albumId: DummyValue.UUID, userId: DummyValue.UUID }] }) - async create(activity: Insertable) { + async create(activity: Insertable) { return this.db .insertInto('activity') .values(activity) diff --git a/server/src/repositories/album-user.repository.ts b/server/src/repositories/album-user.repository.ts index ad7ba8d6cd..82e631a6dc 100644 --- a/server/src/repositories/album-user.repository.ts +++ b/server/src/repositories/album-user.repository.ts @@ -1,9 +1,10 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, Updateable } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { AlbumsSharedUsersUsers, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AlbumUserRole } from 'src/enum'; +import { DB } from 'src/schema'; +import { AlbumUserTable } from 'src/schema/tables/album-user.table'; export type AlbumPermissionId = { albumsId: string; @@ -15,7 +16,7 @@ export class AlbumUserRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] }) - create(albumUser: Insertable) { + create(albumUser: Insertable) { return this.db .insertInto('albums_shared_users_users') .values(albumUser) @@ -24,7 +25,7 @@ export class AlbumUserRepository { } @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] }) - update({ usersId, albumsId }: AlbumPermissionId, dto: Updateable) { + update({ usersId, albumsId }: AlbumPermissionId, dto: Updateable) { return this.db .updateTable('albums_shared_users_users') .set(dto) diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index 7131a72f61..a52fa9c990 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -3,9 +3,10 @@ import { ExpressionBuilder, Insertable, Kysely, NotNull, sql, Updateable } from import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { columns, Exif } from 'src/database'; -import { Albums, DB } from 'src/db'; import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { AlbumUserCreateDto } from 'src/dtos/album.dto'; +import { DB } from 'src/schema'; +import { AlbumTable } from 'src/schema/tables/album.table'; import { withDefaultVisibility } from 'src/utils/database'; export interface AlbumAssetCount { @@ -269,7 +270,7 @@ export class AlbumRepository { await this.addAssets(this.db, albumId, assetIds); } - create(album: Insertable, assetIds: string[], albumUsers: AlbumUserCreateDto[]) { + create(album: Insertable, assetIds: string[], albumUsers: AlbumUserCreateDto[]) { return this.db.transaction().execute(async (tx) => { const newAlbum = await tx.insertInto('albums').values(album).returning('albums.id').executeTakeFirst(); @@ -302,7 +303,7 @@ export class AlbumRepository { }); } - update(id: string, album: Updateable) { + update(id: string, album: Updateable) { return this.db .updateTable('albums') .set(album) diff --git a/server/src/repositories/api-key.repository.ts b/server/src/repositories/api-key.repository.ts index 0085db2337..5f71399a38 100644 --- a/server/src/repositories/api-key.repository.ts +++ b/server/src/repositories/api-key.repository.ts @@ -3,19 +3,20 @@ import { Insertable, Kysely, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { ApiKeys, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { ApiKeyTable } from 'src/schema/tables/api-key.table'; import { asUuid } from 'src/utils/database'; @Injectable() export class ApiKeyRepository { constructor(@InjectKysely() private db: Kysely) {} - create(dto: Insertable) { + create(dto: Insertable) { return this.db.insertInto('api_keys').values(dto).returning(columns.apiKey).executeTakeFirstOrThrow(); } - async update(userId: string, id: string, dto: Updateable) { + async update(userId: string, id: string, dto: Updateable) { return this.db .updateTable('api_keys') .set(dto) diff --git a/server/src/repositories/asset-job.repository.ts b/server/src/repositories/asset-job.repository.ts index af9d5affb1..4ae52556b4 100644 --- a/server/src/repositories/asset-job.repository.ts +++ b/server/src/repositories/asset-job.repository.ts @@ -3,9 +3,9 @@ import { Kysely } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { Asset, columns } from 'src/database'; -import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetFileType, AssetType, AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; import { StorageAsset } from 'src/types'; import { anyUuid, diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 2a3975f446..f3e798d2b4 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -3,9 +3,13 @@ import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; -import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; +import { AssetFileTable } from 'src/schema/tables/asset-files.table'; +import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; +import { AssetTable } from 'src/schema/tables/asset.table'; +import { ExifTable } from 'src/schema/tables/exif.table'; import { anyUuid, asUuid, @@ -110,7 +114,7 @@ interface GetByIdsRelations { export class AssetRepository { constructor(@InjectKysely() private db: Kysely) {} - async upsertExif(exif: Insertable): Promise { + async upsertExif(exif: Insertable): Promise { const value = { ...exif, assetId: asUuid(exif.assetId) }; await this.db .insertInto('exif') @@ -157,7 +161,7 @@ export class AssetRepository { @GenerateSql({ params: [[DummyValue.UUID], { model: DummyValue.STRING }] }) @Chunked() - async updateAllExif(ids: string[], options: Updateable): Promise { + async updateAllExif(ids: string[], options: Updateable): Promise { if (ids.length === 0) { return; } @@ -165,7 +169,7 @@ export class AssetRepository { await this.db.updateTable('exif').set(options).where('assetId', 'in', ids).execute(); } - async upsertJobStatus(...jobStatus: Insertable[]): Promise { + async upsertJobStatus(...jobStatus: Insertable[]): Promise { if (jobStatus.length === 0) { return; } @@ -191,11 +195,11 @@ export class AssetRepository { .execute(); } - create(asset: Insertable) { + create(asset: Insertable) { return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow(); } - createAll(assets: Insertable[]) { + createAll(assets: Insertable[]) { return this.db.insertInto('assets').values(assets).returningAll().execute(); } @@ -375,18 +379,18 @@ export class AssetRepository { @GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] }) @Chunked() - async updateAll(ids: string[], options: Updateable): Promise { + async updateAll(ids: string[], options: Updateable): Promise { if (ids.length === 0) { return; } await this.db.updateTable('assets').set(options).where('id', '=', anyUuid(ids)).execute(); } - async updateByLibraryId(libraryId: string, options: Updateable): Promise { + async updateByLibraryId(libraryId: string, options: Updateable): Promise { await this.db.updateTable('assets').set(options).where('libraryId', '=', asUuid(libraryId)).execute(); } - async update(asset: Updateable & { id: string }) { + async update(asset: Updateable & { id: string }) { const value = omitBy(asset, isUndefined); delete value.id; if (!isEmpty(value)) { @@ -742,7 +746,7 @@ export class AssetRepository { .execute(); } - async upsertFile(file: Pick, 'assetId' | 'path' | 'type'>): Promise { + async upsertFile(file: Pick, 'assetId' | 'path' | 'type'>): Promise { const value = { ...file, assetId: asUuid(file.assetId) }; await this.db .insertInto('asset_files') @@ -755,7 +759,7 @@ export class AssetRepository { .execute(); } - async upsertFiles(files: Pick, 'assetId' | 'path' | 'type'>[]): Promise { + async upsertFiles(files: Pick, 'assetId' | 'path' | 'type'>[]): Promise { if (files.length === 0) { return; } @@ -772,7 +776,7 @@ export class AssetRepository { .execute(); } - async deleteFiles(files: Pick, 'id'>[]): Promise { + async deleteFiles(files: Pick, 'id'>[]): Promise { if (files.length === 0) { return; } diff --git a/server/src/repositories/audit.repository.ts b/server/src/repositories/audit.repository.ts index 1193e26ebe..85ca4a79f8 100644 --- a/server/src/repositories/audit.repository.ts +++ b/server/src/repositories/audit.repository.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { DatabaseAction, EntityType } from 'src/enum'; +import { DB } from 'src/schema'; export interface AuditSearch { action?: DatabaseAction; diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 8d9141f182..b611ff6c7f 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -15,11 +15,11 @@ import { VECTORCHORD_VERSION_RANGE, VECTORS_VERSION_RANGE, } from 'src/constants'; -import { DB } from 'src/db'; import { GenerateSql } from 'src/decorators'; import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { DB } from 'src/schema'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; diff --git a/server/src/repositories/download.repository.ts b/server/src/repositories/download.repository.ts index 4c4bed07ff..aef00f1ed1 100644 --- a/server/src/repositories/download.repository.ts +++ b/server/src/repositories/download.repository.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; import { anyUuid } from 'src/utils/database'; const builder = (db: Kysely) => diff --git a/server/src/repositories/duplicate.repository.ts b/server/src/repositories/duplicate.repository.ts index 73f09aa710..d654e1844e 100644 --- a/server/src/repositories/duplicate.repository.ts +++ b/server/src/repositories/duplicate.repository.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Kysely, NotNull, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { Chunked, DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetType, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; +import { DB } from 'src/schema'; import { anyUuid, asUuid, withDefaultVisibility } from 'src/utils/database'; interface DuplicateSearch { diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index b6c5ebbe08..704f809689 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -1,10 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB, Libraries } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { AssetType, AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; +import { LibraryTable } from 'src/schema/tables/library.table'; export enum AssetSyncResult { DO_NOTHING, @@ -47,7 +48,7 @@ export class LibraryRepository { .execute(); } - create(library: Insertable) { + create(library: Insertable) { return this.db.insertInto('libraries').values(library).returningAll().executeTakeFirstOrThrow(); } @@ -59,7 +60,7 @@ export class LibraryRepository { await this.db.updateTable('libraries').set({ deletedAt: new Date() }).where('libraries.id', '=', id).execute(); } - update(id: string, library: Updateable) { + update(id: string, library: Updateable) { return this.db .updateTable('libraries') .set(library) diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index 3f559442aa..cdfe0dbe22 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -6,12 +6,14 @@ import { createReadStream, existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import readLine from 'node:readline'; import { citiesFile } from 'src/constants'; -import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetVisibility, SystemMetadataKey } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { DB } from 'src/schema'; +import { GeodataPlacesTable } from 'src/schema/tables/geodata-places.table'; +import { NaturalEarthCountriesTable } from 'src/schema/tables/natural-earth-countries.table'; export interface MapMarkerSearchOptions { isArchived?: boolean; @@ -38,8 +40,8 @@ export interface MapMarker extends ReverseGeocodeResult { } interface MapDB extends DB { - geodata_places_tmp: GeodataPlaces; - naturalearth_countries_tmp: NaturalearthCountries; + geodata_places_tmp: GeodataPlacesTable; + naturalearth_countries_tmp: NaturalEarthCountriesTable; } @Injectable() @@ -193,11 +195,11 @@ export class MapRepository { return; } - const entities: Insertable[] = []; + const entities: Insertable[] = []; for (const feature of geoJSONData.features) { for (const entry of feature.geometry.coordinates) { const coordinates: number[][][] = feature.geometry.type === 'MultiPolygon' ? entry[0] : entry; - const featureRecord: Insertable = { + const featureRecord: Insertable = { admin: feature.properties.ADMIN, admin_a3: feature.properties.ADM0_A3, type: feature.properties.TYPE, diff --git a/server/src/repositories/memory.repository.ts b/server/src/repositories/memory.repository.ts index 0f29b3746f..65975a232d 100644 --- a/server/src/repositories/memory.repository.ts +++ b/server/src/repositories/memory.repository.ts @@ -3,10 +3,11 @@ import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; -import { DB, Memories } from 'src/db'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { MemorySearchDto } from 'src/dtos/memory.dto'; import { AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; +import { MemoryTable } from 'src/schema/tables/memory.table'; import { IBulkAsset } from 'src/types'; @Injectable() @@ -80,7 +81,7 @@ export class MemoryRepository implements IBulkAsset { return this.getByIdBuilder(id).executeTakeFirst(); } - async create(memory: Insertable, assetIds: Set) { + async create(memory: Insertable, assetIds: Set) { const id = await this.db.transaction().execute(async (tx) => { const { id } = await tx.insertInto('memories').values(memory).returning('id').executeTakeFirstOrThrow(); @@ -96,7 +97,7 @@ export class MemoryRepository implements IBulkAsset { } @GenerateSql({ params: [DummyValue.UUID, { ownerId: DummyValue.UUID, isSaved: true }] }) - async update(id: string, memory: Updateable) { + async update(id: string, memory: Updateable) { await this.db.updateTable('memories').set(memory).where('id', '=', id).execute(); return this.getByIdBuilder(id).executeTakeFirstOrThrow(); } diff --git a/server/src/repositories/move.repository.ts b/server/src/repositories/move.repository.ts index a21167fffd..3538dd4627 100644 --- a/server/src/repositories/move.repository.ts +++ b/server/src/repositories/move.repository.ts @@ -1,15 +1,16 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB, MoveHistory } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetPathType, PathType } from 'src/enum'; +import { DB } from 'src/schema'; +import { MoveTable } from 'src/schema/tables/move.table'; @Injectable() export class MoveRepository { constructor(@InjectKysely() private db: Kysely) {} - create(entity: Insertable) { + create(entity: Insertable) { return this.db.insertInto('move_history').values(entity).returningAll().executeTakeFirstOrThrow(); } @@ -23,7 +24,7 @@ export class MoveRepository { .executeTakeFirst(); } - update(id: string, entity: Updateable) { + update(id: string, entity: Updateable) { return this.db .updateTable('move_history') .set(entity) diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index b35f532094..bbbd059e42 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -2,9 +2,10 @@ import { Insertable, Kysely, Updateable } from 'kysely'; import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB, Notifications } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { NotificationSearchDto } from 'src/dtos/notification.dto'; +import { DB } from 'src/schema'; +import { NotificationTable } from 'src/schema/tables/notification.table'; export class NotificationRepository { constructor(@InjectKysely() private db: Kysely) {} @@ -53,7 +54,7 @@ export class NotificationRepository { .execute(); } - create(notification: Insertable) { + create(notification: Insertable) { return this.db .insertInto('notifications') .values(notification) @@ -70,7 +71,7 @@ export class NotificationRepository { .executeTakeFirst(); } - update(id: string, notification: Updateable) { + update(id: string, notification: Updateable) { return this.db .updateTable('notifications') .set(notification) @@ -80,7 +81,7 @@ export class NotificationRepository { .executeTakeFirstOrThrow(); } - async updateAll(ids: string[], notification: Updateable) { + async updateAll(ids: string[], notification: Updateable) { await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute(); } diff --git a/server/src/repositories/partner.repository.ts b/server/src/repositories/partner.repository.ts index 31350541ca..af6d5d69c4 100644 --- a/server/src/repositories/partner.repository.ts +++ b/server/src/repositories/partner.repository.ts @@ -3,8 +3,9 @@ import { ExpressionBuilder, Insertable, Kysely, NotNull, Updateable } from 'kyse import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB, Partners } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { PartnerTable } from 'src/schema/tables/partner.table'; export interface PartnerIds { sharedById: string; @@ -47,7 +48,7 @@ export class PartnerRepository { .executeTakeFirst(); } - create(values: Insertable) { + create(values: Insertable) { return this.db .insertInto('partners') .values(values) @@ -59,7 +60,7 @@ export class PartnerRepository { } @GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }, { inTimeline: true }] }) - update({ sharedWithId, sharedById }: PartnerIds, values: Updateable) { + update({ sharedWithId, sharedById }: PartnerIds, values: Updateable) { return this.db .updateTable('partners') .set(values) diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 8fef625a70..949d39747e 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -2,9 +2,12 @@ import { Injectable } from '@nestjs/common'; import { ExpressionBuilder, Insertable, Kysely, Selectable, sql, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; -import { AssetFaces, DB, FaceSearch, Person } from 'src/db'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { AssetFileType, AssetVisibility, SourceType } from 'src/enum'; +import { DB } from 'src/schema'; +import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; +import { FaceSearchTable } from 'src/schema/tables/face-search.table'; +import { PersonTable } from 'src/schema/tables/person.table'; import { removeUndefinedKeys } from 'src/utils/database'; import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; @@ -57,7 +60,7 @@ export interface GetAllFacesOptions { export type UnassignFacesOptions = DeleteFacesOptions; -export type SelectFaceOptions = (keyof Selectable)[]; +export type SelectFaceOptions = (keyof Selectable)[]; const withPerson = (eb: ExpressionBuilder) => { return jsonObjectFrom( @@ -378,11 +381,11 @@ export class PersonRepository { .executeTakeFirstOrThrow(); } - create(person: Insertable) { + create(person: Insertable) { return this.db.insertInto('person').values(person).returningAll().executeTakeFirstOrThrow(); } - async createAll(people: Insertable[]): Promise { + async createAll(people: Insertable[]): Promise { if (people.length === 0) { return []; } @@ -393,9 +396,9 @@ export class PersonRepository { @GenerateSql({ params: [[], [], [{ faceId: DummyValue.UUID, embedding: DummyValue.VECTOR }]] }) async refreshFaces( - facesToAdd: (Insertable & { assetId: string })[], + facesToAdd: (Insertable & { assetId: string })[], faceIdsToRemove: string[], - embeddingsToAdd?: Insertable[], + embeddingsToAdd?: Insertable[], ): Promise { let query = this.db; if (facesToAdd.length > 0) { @@ -415,7 +418,7 @@ export class PersonRepository { await query.selectFrom(sql`(select 1)`.as('dummy')).execute(); } - async update(person: Updateable & { id: string }) { + async update(person: Updateable & { id: string }) { return this.db .updateTable('person') .set(person) @@ -424,7 +427,7 @@ export class PersonRepository { .executeTakeFirstOrThrow(); } - async updateAll(people: Insertable[]): Promise { + async updateAll(people: Insertable[]): Promise { if (people.length === 0) { return; } @@ -496,7 +499,7 @@ export class PersonRepository { return result?.latestDate; } - async createAssetFace(face: Insertable): Promise { + async createAssetFace(face: Insertable): Promise { await this.db.insertInto('asset_faces').values(face).execute(); } diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 2455b65b20..9026b795ca 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -2,11 +2,12 @@ import { Injectable } from '@nestjs/common'; import { Kysely, OrderByDirection, Selectable, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { randomUUID } from 'node:crypto'; -import { DB, Exif } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; +import { DB } from 'src/schema'; +import { ExifTable } from 'src/schema/tables/exif.table'; import { anyUuid, searchAssetBuilder } from 'src/utils/database'; import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -385,7 +386,7 @@ export class SearchRepository { .select((eb) => eb .fn('to_jsonb', [eb.table('exif')]) - .$castTo>() + .$castTo>() .as('exifInfo'), ) .orderBy('exif.city') diff --git a/server/src/repositories/session.repository.ts b/server/src/repositories/session.repository.ts index 6c3d10cb9a..b9fa5458e4 100644 --- a/server/src/repositories/session.repository.ts +++ b/server/src/repositories/session.repository.ts @@ -4,8 +4,9 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB, Sessions } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { SessionTable } from 'src/schema/tables/session.table'; import { asUuid } from 'src/utils/database'; export type SessionSearchOptions = { updatedBefore: Date }; @@ -72,11 +73,11 @@ export class SessionRepository { .execute(); } - create(dto: Insertable) { + create(dto: Insertable) { return this.db.insertInto('sessions').values(dto).returningAll().executeTakeFirstOrThrow(); } - update(id: string, dto: Updateable) { + update(id: string, dto: Updateable) { return this.db .updateTable('sessions') .set(dto) diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 67a97dc2d5..0875b535e2 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -4,10 +4,11 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres'; import _ from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Album, columns } from 'src/database'; -import { DB, SharedLinks } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { SharedLinkType } from 'src/enum'; +import { DB } from 'src/schema'; +import { SharedLinkTable } from 'src/schema/tables/shared-link.table'; export type SharedLinkSearchOptions = { userId: string; @@ -183,7 +184,7 @@ export class SharedLinkRepository { .executeTakeFirst(); } - async create(entity: Insertable & { assetIds?: string[] }) { + async create(entity: Insertable & { assetIds?: string[] }) { const { id } = await this.db .insertInto('shared_links') .values(_.omit(entity, 'assetIds')) @@ -200,7 +201,7 @@ export class SharedLinkRepository { return this.getSharedLinks(id); } - async update(entity: Updateable & { id: string; assetIds?: string[] }) { + async update(entity: Updateable & { id: string; assetIds?: string[] }) { const { id } = await this.db .updateTable('shared_links') .set(_.omit(entity, 'assets', 'album', 'assetIds')) diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index 78ff255579..b7ee46c671 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -3,8 +3,9 @@ import { ExpressionBuilder, Kysely, Updateable } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { AssetStack, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { StackTable } from 'src/schema/tables/stack.table'; import { asUuid, withDefaultVisibility } from 'src/utils/database'; export interface StackSearch { @@ -130,7 +131,7 @@ export class StackRepository { await this.db.deleteFrom('asset_stack').where('id', 'in', ids).execute(); } - update(id: string, entity: Updateable) { + update(id: string, entity: Updateable) { return this.db .updateTable('asset_stack') .set(entity) diff --git a/server/src/repositories/sync-checkpoint.repository.ts b/server/src/repositories/sync-checkpoint.repository.ts index aae0da0890..5133ba8904 100644 --- a/server/src/repositories/sync-checkpoint.repository.ts +++ b/server/src/repositories/sync-checkpoint.repository.ts @@ -1,9 +1,10 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB, SessionSyncCheckpoints } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; +import { DB } from 'src/schema'; +import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table'; @Injectable() export class SyncCheckpointRepository { @@ -18,7 +19,7 @@ export class SyncCheckpointRepository { .execute(); } - upsertAll(items: Insertable[]) { + upsertAll(items: Insertable[]) { return this.db .insertInto('session_sync_checkpoints') .values(items) diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 2b1157492d..73a1aca749 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { Kysely, SelectQueryBuilder, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; import { SyncAck } from 'src/types'; type AuditTables = diff --git a/server/src/repositories/system-metadata.repository.ts b/server/src/repositories/system-metadata.repository.ts index fcccde6a5c..755dc4cd30 100644 --- a/server/src/repositories/system-metadata.repository.ts +++ b/server/src/repositories/system-metadata.repository.ts @@ -2,11 +2,12 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { readFile } from 'node:fs/promises'; -import { DB, SystemMetadata as DbSystemMetadata } from 'src/db'; import { GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { SystemMetadataTable } from 'src/schema/tables/system-metadata.table'; import { SystemMetadata } from 'src/types'; -type Upsert = Insertable; +type Upsert = Insertable; @Injectable() export class SystemMetadataRepository { diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index a7cdc9554c..1c38242f53 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -2,9 +2,11 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB, TagAsset, Tags } from 'src/db'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { DB } from 'src/schema'; +import { TagAssetTable } from 'src/schema/tables/tag-asset.table'; +import { TagTable } from 'src/schema/tables/tag.table'; @Injectable() export class TagRepository { @@ -72,12 +74,12 @@ export class TagRepository { } @GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] }) - create(tag: Insertable) { + create(tag: Insertable) { return this.db.insertInto('tags').values(tag).returningAll().executeTakeFirstOrThrow(); } @GenerateSql({ params: [DummyValue.UUID, { color: DummyValue.STRING }] }) - update(id: string, dto: Updateable) { + update(id: string, dto: Updateable) { return this.db.updateTable('tags').set(dto).where('id', '=', id).returningAll().executeTakeFirstOrThrow(); } @@ -128,7 +130,7 @@ export class TagRepository { @GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagsIds: [DummyValue.UUID] }]] }) @Chunked() - upsertAssetIds(items: Insertable[]) { + upsertAssetIds(items: Insertable[]) { if (items.length === 0) { return Promise.resolve([]); } diff --git a/server/src/repositories/trash.repository.ts b/server/src/repositories/trash.repository.ts index 69507b1d58..3389678c50 100644 --- a/server/src/repositories/trash.repository.ts +++ b/server/src/repositories/trash.repository.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetStatus } from 'src/enum'; +import { DB } from 'src/schema'; export class TrashRepository { constructor(@InjectKysely() private db: Kysely) {} diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index ef55c93222..91c4b71139 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -4,14 +4,15 @@ import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; -import { DB, UserMetadata as DbUserMetadata } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetType, AssetVisibility, UserStatus } from 'src/enum'; +import { DB } from 'src/schema'; +import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserTable } from 'src/schema/tables/user.table'; import { UserMetadata, UserMetadataItem } from 'src/types'; import { asUuid } from 'src/utils/database'; -type Upsert = Insertable; +type Upsert = Insertable; export interface UserListFilter { id?: string; diff --git a/server/src/repositories/version-history.repository.ts b/server/src/repositories/version-history.repository.ts index b1d2696164..b69e0e5faa 100644 --- a/server/src/repositories/version-history.repository.ts +++ b/server/src/repositories/version-history.repository.ts @@ -1,8 +1,9 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB, VersionHistory } from 'src/db'; import { GenerateSql } from 'src/decorators'; +import { DB } from 'src/schema'; +import { VersionHistoryTable } from 'src/schema/tables/version-history.table'; @Injectable() export class VersionHistoryRepository { @@ -18,7 +19,7 @@ export class VersionHistoryRepository { return this.db.selectFrom('version_history').selectAll().orderBy('createdAt', 'desc').executeTakeFirst(); } - create(version: Insertable) { + create(version: Insertable) { return this.db.insertInto('version_history').values(version).returningAll().executeTakeFirstOrThrow(); } } diff --git a/server/src/repositories/view-repository.ts b/server/src/repositories/view-repository.ts index 3a498d5b3e..d0d4983e09 100644 --- a/server/src/repositories/view-repository.ts +++ b/server/src/repositories/view-repository.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetVisibility } from 'src/enum'; +import { DB } from 'src/schema'; import { asUuid, withExif } from 'src/utils/database'; export class ViewRepository { diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index 6dac1ff51e..a8508b2c4b 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -21,7 +21,7 @@ import { AlbumAuditTable } from 'src/schema/tables/album-audit.table'; import { AlbumUserAuditTable } from 'src/schema/tables/album-user-audit.table'; import { AlbumUserTable } from 'src/schema/tables/album-user.table'; import { AlbumTable } from 'src/schema/tables/album.table'; -import { APIKeyTable } from 'src/schema/tables/api-key.table'; +import { ApiKeyTable } from 'src/schema/tables/api-key.table'; import { AssetAuditTable } from 'src/schema/tables/asset-audit.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFileTable } from 'src/schema/tables/asset-files.table'; @@ -51,11 +51,12 @@ import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.ta import { SystemMetadataTable } from 'src/schema/tables/system-metadata.table'; import { TagAssetTable } from 'src/schema/tables/tag-asset.table'; import { TagClosureTable } from 'src/schema/tables/tag-closure.table'; +import { TagTable } from 'src/schema/tables/tag.table'; import { UserAuditTable } from 'src/schema/tables/user-audit.table'; import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserTable } from 'src/schema/tables/user.table'; import { VersionHistoryTable } from 'src/schema/tables/version-history.table'; -import { Database, Extensions } from 'src/sql-tools'; +import { Database, Extensions, Generated, Int8 } from 'src/sql-tools'; @Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql']) @Database({ name: 'immich' }) @@ -68,7 +69,7 @@ export class ImmichDatabase { AlbumUserAuditTable, AlbumUserTable, AlbumTable, - APIKeyTable, + ApiKeyTable, AssetAuditTable, AssetFaceTable, AssetJobStatusTable, @@ -96,6 +97,7 @@ export class ImmichDatabase { StackTable, SessionSyncCheckpointTable, SystemMetadataTable, + TagTable, TagAssetTable, TagClosureTable, UserAuditTable, @@ -122,3 +124,55 @@ export class ImmichDatabase { enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum]; } + +export interface Migrations { + id: Generated; + name: string; + timestamp: Int8; +} + +export interface DB { + activity: ActivityTable; + albums: AlbumTable; + albums_audit: AlbumAuditTable; + albums_assets_assets: AlbumAssetTable; + album_assets_audit: AlbumAssetAuditTable; + albums_shared_users_users: AlbumUserTable; + album_users_audit: AlbumUserAuditTable; + api_keys: ApiKeyTable; + asset_faces: AssetFaceTable; + asset_files: AssetFileTable; + asset_job_status: AssetJobStatusTable; + asset_stack: StackTable; + assets: AssetTable; + assets_audit: AssetAuditTable; + audit: AuditTable; + exif: ExifTable; + face_search: FaceSearchTable; + geodata_places: GeodataPlacesTable; + libraries: LibraryTable; + memories: MemoryTable; + memories_audit: MemoryAuditTable; + memories_assets_assets: MemoryAssetTable; + memory_assets_audit: MemoryAssetAuditTable; + migrations: Migrations; + notifications: NotificationTable; + move_history: MoveTable; + naturalearth_countries: NaturalEarthCountriesTable; + partners_audit: PartnerAuditTable; + partners: PartnerTable; + person: PersonTable; + sessions: SessionTable; + session_sync_checkpoints: SessionSyncCheckpointTable; + shared_link__asset: SharedLinkAssetTable; + shared_links: SharedLinkTable; + smart_search: SmartSearchTable; + system_metadata: SystemMetadataTable; + tag_asset: TagAssetTable; + tags: TagTable; + tags_closure: TagClosureTable; + user_metadata: UserMetadataTable; + users: UserTable; + users_audit: UserAuditTable; + version_history: VersionHistoryTable; +} diff --git a/server/src/schema/tables/activity.table.ts b/server/src/schema/tables/activity.table.ts index 802a86a303..47fca24bb1 100644 --- a/server/src/schema/tables/activity.table.ts +++ b/server/src/schema/tables/activity.table.ts @@ -7,9 +7,11 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, Index, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -27,13 +29,13 @@ import { }) export class ActivityTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @ForeignKeyColumn(() => AlbumTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) albumId!: string; @@ -48,8 +50,8 @@ export class ActivityTable { comment!: string | null; @Column({ type: 'boolean', default: false }) - isLiked!: boolean; + isLiked!: Generated; @UpdateIdColumn({ indexName: 'IDX_activity_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/album-asset-audit.table.ts b/server/src/schema/tables/album-asset-audit.table.ts index d2b71aa599..7c8da313c0 100644 --- a/server/src/schema/tables/album-asset-audit.table.ts +++ b/server/src/schema/tables/album-asset-audit.table.ts @@ -1,11 +1,11 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; import { AlbumTable } from 'src/schema/tables/album.table'; -import { Column, CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('album_assets_audit') export class AlbumAssetAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @ForeignKeyColumn(() => AlbumTable, { type: 'uuid', @@ -19,5 +19,5 @@ export class AlbumAssetAuditTable { assetId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_assets_audit_deleted_at' }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/album-asset.table.ts b/server/src/schema/tables/album-asset.table.ts index 567a6f9d4b..f0841edc44 100644 --- a/server/src/schema/tables/album-asset.table.ts +++ b/server/src/schema/tables/album-asset.table.ts @@ -2,7 +2,15 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { album_assets_delete_audit } from 'src/schema/functions'; import { AlbumTable } from 'src/schema/tables/album.table'; import { AssetTable } from 'src/schema/tables/asset.table'; -import { AfterDeleteTrigger, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; +import { + AfterDeleteTrigger, + CreateDateColumn, + ForeignKeyColumn, + Generated, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; @Table({ name: 'albums_assets_assets', primaryConstraintName: 'PK_c67bc36fa845fb7b18e0e398180' }) @UpdatedAtTrigger('album_assets_updated_at') @@ -21,11 +29,11 @@ export class AlbumAssetTable { assetsId!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @UpdateIdColumn({ indexName: 'IDX_album_assets_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/album-audit.table.ts b/server/src/schema/tables/album-audit.table.ts index 66b70654e9..3dd2682232 100644 --- a/server/src/schema/tables/album-audit.table.ts +++ b/server/src/schema/tables/album-audit.table.ts @@ -1,10 +1,10 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('albums_audit') export class AlbumAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @Column({ type: 'uuid', indexName: 'IDX_albums_audit_album_id' }) albumId!: string; @@ -13,5 +13,5 @@ export class AlbumAuditTable { userId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_albums_audit_deleted_at' }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/album-user-audit.table.ts b/server/src/schema/tables/album-user-audit.table.ts index 46ad6b682b..01ad991a52 100644 --- a/server/src/schema/tables/album-user-audit.table.ts +++ b/server/src/schema/tables/album-user-audit.table.ts @@ -1,10 +1,10 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('album_users_audit') export class AlbumUserAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @Column({ type: 'uuid', indexName: 'IDX_album_users_audit_album_id' }) albumId!: string; @@ -13,5 +13,5 @@ export class AlbumUserAuditTable { userId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_users_audit_deleted_at' }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/album-user.table.ts b/server/src/schema/tables/album-user.table.ts index 15300f7c6a..65e3db7185 100644 --- a/server/src/schema/tables/album-user.table.ts +++ b/server/src/schema/tables/album-user.table.ts @@ -9,8 +9,10 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, Index, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -50,17 +52,17 @@ export class AlbumUserTable { usersId!: string; @Column({ type: 'character varying', default: AlbumUserRole.EDITOR }) - role!: AlbumUserRole; + role!: Generated; @CreateIdColumn({ indexName: 'IDX_album_users_create_id' }) - createId?: string; + createId!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateIdColumn({ indexName: 'IDX_album_users_update_id' }) - updateId?: string; + updateId!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; } diff --git a/server/src/schema/tables/album.table.ts b/server/src/schema/tables/album.table.ts index 5d02cc9f25..509b7d2cec 100644 --- a/server/src/schema/tables/album.table.ts +++ b/server/src/schema/tables/album.table.ts @@ -9,8 +9,10 @@ import { CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -25,16 +27,16 @@ import { }) export class AlbumTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) ownerId!: string; @Column({ default: 'Untitled Album' }) - albumName!: string; + albumName!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @ForeignKeyColumn(() => AssetTable, { nullable: true, @@ -42,23 +44,23 @@ export class AlbumTable { onUpdate: 'CASCADE', comment: 'Asset ID to be used as thumbnail', }) - albumThumbnailAssetId!: string; + albumThumbnailAssetId!: string | null; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column({ type: 'text', default: '' }) - description!: string; + description!: Generated; @DeleteDateColumn() - deletedAt!: Date | null; + deletedAt!: Timestamp | null; @Column({ type: 'boolean', default: true }) - isActivityEnabled!: boolean; + isActivityEnabled!: Generated; @Column({ default: AssetOrder.DESC }) - order!: AssetOrder; + order!: Generated; @UpdateIdColumn({ indexName: 'IDX_albums_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/api-key.table.ts b/server/src/schema/tables/api-key.table.ts index 1d4cc83172..4bcd4beae8 100644 --- a/server/src/schema/tables/api-key.table.ts +++ b/server/src/schema/tables/api-key.table.ts @@ -5,14 +5,19 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @Table('api_keys') @UpdatedAtTrigger('api_keys_updated_at') -export class APIKeyTable { +export class ApiKeyTable { + @PrimaryGeneratedColumn() + id!: Generated; + @Column() name!: string; @@ -23,17 +28,14 @@ export class APIKeyTable { userId!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; - - @PrimaryGeneratedColumn() - id!: string; + updatedAt!: Generated; @Column({ array: true, type: 'character varying' }) permissions!: Permission[]; @UpdateIdColumn({ indexName: 'IDX_api_keys_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/asset-audit.table.ts b/server/src/schema/tables/asset-audit.table.ts index 030256480c..8ca6aa956d 100644 --- a/server/src/schema/tables/asset-audit.table.ts +++ b/server/src/schema/tables/asset-audit.table.ts @@ -1,10 +1,10 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('assets_audit') export class AssetAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @Column({ type: 'uuid', indexName: 'IDX_assets_audit_asset_id' }) assetId!: string; @@ -13,5 +13,5 @@ export class AssetAuditTable { ownerId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_assets_audit_deleted_at' }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/asset-face.table.ts b/server/src/schema/tables/asset-face.table.ts index 52f4364a93..329783d6df 100644 --- a/server/src/schema/tables/asset-face.table.ts +++ b/server/src/schema/tables/asset-face.table.ts @@ -2,12 +2,24 @@ import { SourceType } from 'src/enum'; import { asset_face_source_type } from 'src/schema/enums'; import { AssetTable } from 'src/schema/tables/asset.table'; import { PersonTable } from 'src/schema/tables/person.table'; -import { Column, DeleteDateColumn, ForeignKeyColumn, Index, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { + Column, + DeleteDateColumn, + ForeignKeyColumn, + Generated, + Index, + PrimaryGeneratedColumn, + Table, + Timestamp, +} from 'src/sql-tools'; @Table({ name: 'asset_faces' }) @Index({ name: 'IDX_asset_faces_assetId_personId', columns: ['assetId', 'personId'] }) @Index({ columns: ['personId', 'assetId'] }) export class AssetFaceTable { + @PrimaryGeneratedColumn() + id!: Generated; + @ForeignKeyColumn(() => AssetTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', @@ -26,29 +38,26 @@ export class AssetFaceTable { personId!: string | null; @Column({ default: 0, type: 'integer' }) - imageWidth!: number; + imageWidth!: Generated; @Column({ default: 0, type: 'integer' }) - imageHeight!: number; + imageHeight!: Generated; @Column({ default: 0, type: 'integer' }) - boundingBoxX1!: number; + boundingBoxX1!: Generated; @Column({ default: 0, type: 'integer' }) - boundingBoxY1!: number; + boundingBoxY1!: Generated; @Column({ default: 0, type: 'integer' }) - boundingBoxX2!: number; + boundingBoxX2!: Generated; @Column({ default: 0, type: 'integer' }) - boundingBoxY2!: number; - - @PrimaryGeneratedColumn() - id!: string; + boundingBoxY2!: Generated; @Column({ default: SourceType.MACHINE_LEARNING, enum: asset_face_source_type }) - sourceType!: SourceType; + sourceType!: Generated; @DeleteDateColumn() - deletedAt!: Date | null; + deletedAt!: Timestamp | null; } diff --git a/server/src/schema/tables/asset-files.table.ts b/server/src/schema/tables/asset-files.table.ts index 0859bd5cf0..d0721e5b91 100644 --- a/server/src/schema/tables/asset-files.table.ts +++ b/server/src/schema/tables/asset-files.table.ts @@ -5,8 +5,10 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, Unique, UpdateDateColumn, } from 'src/sql-tools'; @@ -16,20 +18,20 @@ import { @UpdatedAtTrigger('asset_files_updated_at') export class AssetFileTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @ForeignKeyColumn(() => AssetTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', indexName: 'IDX_asset_files_assetId', }) - assetId?: string; + assetId!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column() type!: AssetFileType; @@ -38,5 +40,5 @@ export class AssetFileTable { path!: string; @UpdateIdColumn({ indexName: 'IDX_asset_files_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/asset-job-status.table.ts b/server/src/schema/tables/asset-job-status.table.ts index 669ea0a20d..5ae10edbfa 100644 --- a/server/src/schema/tables/asset-job-status.table.ts +++ b/server/src/schema/tables/asset-job-status.table.ts @@ -1,5 +1,5 @@ import { AssetTable } from 'src/schema/tables/asset.table'; -import { Column, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { Column, ForeignKeyColumn, Table, Timestamp } from 'src/sql-tools'; @Table('asset_job_status') export class AssetJobStatusTable { @@ -7,17 +7,17 @@ export class AssetJobStatusTable { assetId!: string; @Column({ type: 'timestamp with time zone', nullable: true }) - facesRecognizedAt!: Date | null; + facesRecognizedAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - metadataExtractedAt!: Date | null; + metadataExtractedAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - duplicatesDetectedAt!: Date | null; + duplicatesDetectedAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - previewAt!: Date | null; + previewAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - thumbnailAt!: Date | null; + thumbnailAt!: Timestamp | null; } diff --git a/server/src/schema/tables/asset.table.ts b/server/src/schema/tables/asset.table.ts index d337984a46..0a8120e656 100644 --- a/server/src/schema/tables/asset.table.ts +++ b/server/src/schema/tables/asset.table.ts @@ -11,9 +11,11 @@ import { CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, + Generated, Index, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; @@ -60,7 +62,7 @@ import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; // For all assets, each originalpath must be unique per user and library export class AssetTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @Column() deviceAssetId!: string; @@ -78,13 +80,13 @@ export class AssetTable { originalPath!: string; @Column({ type: 'timestamp with time zone', indexName: 'idx_asset_file_created_at' }) - fileCreatedAt!: Date; + fileCreatedAt!: Timestamp; @Column({ type: 'timestamp with time zone' }) - fileModifiedAt!: Date; + fileModifiedAt!: Timestamp; @Column({ type: 'boolean', default: false }) - isFavorite!: boolean; + isFavorite!: Generated; @Column({ type: 'character varying', nullable: true }) duration!: string | null; @@ -99,10 +101,10 @@ export class AssetTable { livePhotoVideoId!: string | null; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @Column({ index: true }) originalFileName!: string; @@ -114,32 +116,32 @@ export class AssetTable { thumbhash!: Buffer | null; @Column({ type: 'boolean', default: false }) - isOffline!: boolean; + isOffline!: Generated; @ForeignKeyColumn(() => LibraryTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) - libraryId?: string | null; + libraryId!: string | null; @Column({ type: 'boolean', default: false }) - isExternal!: boolean; + isExternal!: Generated; @DeleteDateColumn() - deletedAt!: Date | null; + deletedAt!: Timestamp | null; @Column({ type: 'timestamp with time zone' }) - localDateTime!: Date; + localDateTime!: Timestamp; @ForeignKeyColumn(() => StackTable, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' }) - stackId?: string | null; + stackId!: string | null; @Column({ type: 'uuid', nullable: true, indexName: 'IDX_assets_duplicateId' }) duplicateId!: string | null; @Column({ enum: assets_status_enum, default: AssetStatus.ACTIVE }) - status!: AssetStatus; + status!: Generated; @UpdateIdColumn({ indexName: 'IDX_assets_update_id' }) - updateId?: string; + updateId!: Generated; @Column({ enum: asset_visibility_enum, default: AssetVisibility.TIMELINE }) - visibility!: AssetVisibility; + visibility!: Generated; } diff --git a/server/src/schema/tables/audit.table.ts b/server/src/schema/tables/audit.table.ts index 9674bbf308..049bba9a1b 100644 --- a/server/src/schema/tables/audit.table.ts +++ b/server/src/schema/tables/audit.table.ts @@ -1,11 +1,11 @@ import { DatabaseAction, EntityType } from 'src/enum'; -import { Column, CreateDateColumn, Index, PrimaryColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Index, PrimaryColumn, Table, Timestamp } from 'src/sql-tools'; @Table('audit') @Index({ name: 'IDX_ownerId_createdAt', columns: ['ownerId', 'createdAt'] }) export class AuditTable { @PrimaryColumn({ type: 'serial', synchronize: false }) - id!: number; + id!: Generated; @Column() entityType!: EntityType; @@ -20,5 +20,5 @@ export class AuditTable { ownerId!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; } diff --git a/server/src/schema/tables/exif.table.ts b/server/src/schema/tables/exif.table.ts index ca300945c3..668f215776 100644 --- a/server/src/schema/tables/exif.table.ts +++ b/server/src/schema/tables/exif.table.ts @@ -1,6 +1,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { AssetTable } from 'src/schema/tables/asset.table'; -import { Column, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; +import { Column, ForeignKeyColumn, Generated, Int8, Table, Timestamp, UpdateDateColumn } from 'src/sql-tools'; @Table('exif') @UpdatedAtTrigger('asset_exif_updated_at') @@ -21,16 +21,16 @@ export class ExifTable { exifImageHeight!: number | null; @Column({ type: 'bigint', nullable: true }) - fileSizeInByte!: number | null; + fileSizeInByte!: Int8 | null; @Column({ type: 'character varying', nullable: true }) orientation!: string | null; @Column({ type: 'timestamp with time zone', nullable: true }) - dateTimeOriginal!: Date | null; + dateTimeOriginal!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - modifyDate!: Date | null; + modifyDate!: Timestamp | null; @Column({ type: 'character varying', nullable: true }) lensModel!: string | null; @@ -60,10 +60,10 @@ export class ExifTable { country!: string | null; @Column({ type: 'text', default: '' }) - description!: string; // or caption + description!: Generated; // or caption @Column({ type: 'double precision', nullable: true }) - fps?: number | null; + fps!: number | null; @Column({ type: 'character varying', nullable: true }) exposureTime!: string | null; @@ -93,8 +93,8 @@ export class ExifTable { rating!: number | null; @UpdateDateColumn({ default: () => 'clock_timestamp()' }) - updatedAt?: Date; + updatedAt!: Generated; @UpdateIdColumn({ indexName: 'IDX_asset_exif_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/geodata-places.table.ts b/server/src/schema/tables/geodata-places.table.ts index 3e78b4cfcf..11c3ef5d25 100644 --- a/server/src/schema/tables/geodata-places.table.ts +++ b/server/src/schema/tables/geodata-places.table.ts @@ -1,4 +1,4 @@ -import { Column, Index, PrimaryColumn, Table } from 'src/sql-tools'; +import { Column, Index, PrimaryColumn, Table, Timestamp } from 'src/sql-tools'; @Table({ name: 'geodata_places', synchronize: false }) @Index({ @@ -47,20 +47,20 @@ export class GeodataPlacesTable { countryCode!: string; @Column({ type: 'character varying', length: 20, nullable: true }) - admin1Code!: string; + admin1Code!: string | null; @Column({ type: 'character varying', length: 80, nullable: true }) - admin2Code!: string; + admin2Code!: string | null; @Column({ type: 'date' }) - modificationDate!: Date; + modificationDate!: Timestamp; @Column({ type: 'character varying', nullable: true }) - admin1Name!: string; + admin1Name!: string | null; @Column({ type: 'character varying', nullable: true }) - admin2Name!: string; + admin2Name!: string | null; @Column({ type: 'character varying', nullable: true }) - alternateNames!: string; + alternateNames!: string | null; } diff --git a/server/src/schema/tables/library.table.ts b/server/src/schema/tables/library.table.ts index 8b21d5feb0..0bf7e8ea82 100644 --- a/server/src/schema/tables/library.table.ts +++ b/server/src/schema/tables/library.table.ts @@ -5,8 +5,10 @@ import { CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -14,7 +16,7 @@ import { @UpdatedAtTrigger('libraries_updated_at') export class LibraryTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @Column() name!: string; @@ -29,17 +31,17 @@ export class LibraryTable { exclusionPatterns!: string[]; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @DeleteDateColumn() - deletedAt?: Date; + deletedAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - refreshedAt!: Date | null; + refreshedAt!: Timestamp | null; @UpdateIdColumn({ indexName: 'IDX_libraries_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/memory-asset.table.ts b/server/src/schema/tables/memory-asset.table.ts index 9512ed61da..c8ef9297be 100644 --- a/server/src/schema/tables/memory-asset.table.ts +++ b/server/src/schema/tables/memory-asset.table.ts @@ -1,13 +1,16 @@ -import { ColumnType } from 'kysely'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { memory_assets_delete_audit } from 'src/schema/functions'; import { AssetTable } from 'src/schema/tables/asset.table'; import { MemoryTable } from 'src/schema/tables/memory.table'; -import { AfterDeleteTrigger, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; - -type Timestamp = ColumnType; -type Generated = - T extends ColumnType ? ColumnType : ColumnType; +import { + AfterDeleteTrigger, + CreateDateColumn, + ForeignKeyColumn, + Generated, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; @Table('memories_assets_assets') @UpdatedAtTrigger('memory_assets_updated_at') diff --git a/server/src/schema/tables/memory-audit.table.ts b/server/src/schema/tables/memory-audit.table.ts index efc6b66fd1..d84573a023 100644 --- a/server/src/schema/tables/memory-audit.table.ts +++ b/server/src/schema/tables/memory-audit.table.ts @@ -1,10 +1,5 @@ -import { ColumnType } from 'kysely'; import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; - -type Timestamp = ColumnType; -type Generated = - T extends ColumnType ? ColumnType : ColumnType; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('memories_audit') export class MemoryAuditTable { @@ -18,5 +13,5 @@ export class MemoryAuditTable { userId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_memories_audit_deleted_at' }) - deletedAt!: Timestamp; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/memory.table.ts b/server/src/schema/tables/memory.table.ts index d715820a9c..8345eacbed 100644 --- a/server/src/schema/tables/memory.table.ts +++ b/server/src/schema/tables/memory.table.ts @@ -8,8 +8,10 @@ import { CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -24,16 +26,16 @@ import { }) export class MemoryTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @DeleteDateColumn() - deletedAt?: Date; + deletedAt!: Timestamp | null; @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) ownerId!: string; @@ -46,22 +48,22 @@ export class MemoryTable { /** unless set to true, will be automatically deleted in the future */ @Column({ type: 'boolean', default: false }) - isSaved!: boolean; + isSaved!: Generated; /** memories are sorted in ascending order by this value */ @Column({ type: 'timestamp with time zone' }) - memoryAt!: Date; + memoryAt!: Timestamp; /** when the user last viewed the memory */ @Column({ type: 'timestamp with time zone', nullable: true }) - seenAt?: Date; + seenAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - showAt?: Date; + showAt!: Timestamp | null; @Column({ type: 'timestamp with time zone', nullable: true }) - hideAt?: Date; + hideAt!: Timestamp | null; @UpdateIdColumn({ indexName: 'IDX_memories_update_id' }) - updateId?: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/move.table.ts b/server/src/schema/tables/move.table.ts index cdc00efcaf..1afda2767a 100644 --- a/server/src/schema/tables/move.table.ts +++ b/server/src/schema/tables/move.table.ts @@ -1,5 +1,5 @@ import { PathType } from 'src/enum'; -import { Column, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools'; +import { Column, Generated, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools'; @Table('move_history') // path lock (per entity) @@ -8,7 +8,7 @@ import { Column, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools'; @Unique({ name: 'UQ_newPath', columns: ['newPath'] }) export class MoveTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @Column({ type: 'uuid' }) entityId!: string; diff --git a/server/src/schema/tables/natural-earth-countries.table.ts b/server/src/schema/tables/natural-earth-countries.table.ts index e5e6ead772..c59d15fc21 100644 --- a/server/src/schema/tables/natural-earth-countries.table.ts +++ b/server/src/schema/tables/natural-earth-countries.table.ts @@ -1,9 +1,9 @@ -import { Column, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { Column, Generated, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; @Table({ name: 'naturalearth_countries', primaryConstraintName: 'naturalearth_countries_pkey' }) export class NaturalEarthCountriesTable { @PrimaryGeneratedColumn({ strategy: 'identity' }) - id!: number; + id!: Generated; @Column({ type: 'character varying', length: 50 }) admin!: string; diff --git a/server/src/schema/tables/notification.table.ts b/server/src/schema/tables/notification.table.ts index bf9b8bdf3b..aa6cc60be9 100644 --- a/server/src/schema/tables/notification.table.ts +++ b/server/src/schema/tables/notification.table.ts @@ -6,8 +6,10 @@ import { CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -15,28 +17,28 @@ import { @UpdatedAtTrigger('notifications_updated_at') export class NotificationTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @DeleteDateColumn() - deletedAt?: Date; + deletedAt!: Timestamp | null; @UpdateIdColumn({ indexName: 'IDX_notifications_update_id' }) - updateId?: string; + updateId!: Generated; @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) userId!: string; @Column({ default: NotificationLevel.Info }) - level!: NotificationLevel; + level!: Generated; @Column({ default: NotificationLevel.Info }) - type!: NotificationType; + type!: Generated; @Column({ type: 'jsonb', nullable: true }) data!: any | null; @@ -45,8 +47,8 @@ export class NotificationTable { title!: string; @Column({ type: 'text', nullable: true }) - description!: string; + description!: string | null; @Column({ type: 'timestamp with time zone', nullable: true }) - readAt?: Date | null; + readAt!: Timestamp | null; } diff --git a/server/src/schema/tables/partner-audit.table.ts b/server/src/schema/tables/partner-audit.table.ts index da5243dc75..35595e47b4 100644 --- a/server/src/schema/tables/partner-audit.table.ts +++ b/server/src/schema/tables/partner-audit.table.ts @@ -1,10 +1,10 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('partners_audit') export class PartnerAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @Column({ type: 'uuid', indexName: 'IDX_partners_audit_shared_by_id' }) sharedById!: string; @@ -13,5 +13,5 @@ export class PartnerAuditTable { sharedWithId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_partners_audit_deleted_at' }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/partner.table.ts b/server/src/schema/tables/partner.table.ts index 8cec2ee58a..a973f02320 100644 --- a/server/src/schema/tables/partner.table.ts +++ b/server/src/schema/tables/partner.table.ts @@ -1,7 +1,16 @@ import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { partners_delete_audit } from 'src/schema/functions'; import { UserTable } from 'src/schema/tables/user.table'; -import { AfterDeleteTrigger, Column, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; +import { + AfterDeleteTrigger, + Column, + CreateDateColumn, + ForeignKeyColumn, + Generated, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; @Table('partners') @UpdatedAtTrigger('partners_updated_at') @@ -25,17 +34,17 @@ export class PartnerTable { sharedWithId!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @CreateIdColumn({ indexName: 'IDX_partners_create_id' }) - createId!: string; + createId!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column({ type: 'boolean', default: false }) - inTimeline!: boolean; + inTimeline!: Generated; @UpdateIdColumn({ indexName: 'IDX_partners_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/person.table.ts b/server/src/schema/tables/person.table.ts index 1320b91f18..5835b2528c 100644 --- a/server/src/schema/tables/person.table.ts +++ b/server/src/schema/tables/person.table.ts @@ -6,8 +6,10 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -16,38 +18,38 @@ import { @Check({ name: 'CHK_b0f82b0ed662bfc24fbb58bb45', expression: `"birthDate" <= CURRENT_DATE` }) export class PersonTable { @PrimaryGeneratedColumn('uuid') - id!: string; + id!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) ownerId!: string; @Column({ default: '' }) - name!: string; + name!: Generated; @Column({ default: '' }) - thumbnailPath!: string; + thumbnailPath!: Generated; @Column({ type: 'boolean', default: false }) - isHidden!: boolean; + isHidden!: Generated; @Column({ type: 'date', nullable: true }) - birthDate!: Date | string | null; + birthDate!: Timestamp | null; @ForeignKeyColumn(() => AssetFaceTable, { onDelete: 'SET NULL', nullable: true }) faceAssetId!: string | null; @Column({ type: 'boolean', default: false }) - isFavorite!: boolean; + isFavorite!: Generated; @Column({ type: 'character varying', nullable: true, default: null }) - color?: string | null; + color!: string | null; @UpdateIdColumn({ indexName: 'IDX_person_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/session.table.ts b/server/src/schema/tables/session.table.ts index 6bd5d84cb2..cc05500beb 100644 --- a/server/src/schema/tables/session.table.ts +++ b/server/src/schema/tables/session.table.ts @@ -4,8 +4,10 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; @@ -13,20 +15,20 @@ import { @UpdatedAtTrigger('sessions_updated_at') export class SessionTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; // TODO convert to byte[] @Column() token!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column({ type: 'timestamp with time zone', nullable: true }) - expiresAt!: Date | null; + expiresAt!: Timestamp | null; @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) userId!: string; @@ -35,14 +37,14 @@ export class SessionTable { parentId!: string | null; @Column({ default: '' }) - deviceType!: string; + deviceType!: Generated; @Column({ default: '' }) - deviceOS!: string; + deviceOS!: Generated; @UpdateIdColumn({ indexName: 'IDX_sessions_update_id' }) - updateId!: string; + updateId!: Generated; @Column({ type: 'timestamp with time zone', nullable: true }) - pinExpiresAt!: Date | null; + pinExpiresAt!: Timestamp | null; } diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 39693f3893..0670a7c5f5 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -1,13 +1,22 @@ import { SharedLinkType } from 'src/enum'; import { AlbumTable } from 'src/schema/tables/album.table'; import { UserTable } from 'src/schema/tables/user.table'; -import { Column, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools'; +import { + Column, + CreateDateColumn, + ForeignKeyColumn, + Generated, + PrimaryGeneratedColumn, + Table, + Timestamp, + Unique, +} from 'src/sql-tools'; @Table('shared_links') @Unique({ name: 'UQ_sharedlink_key', columns: ['key'] }) export class SharedLinkTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @Column({ type: 'character varying', nullable: true }) description!: string | null; @@ -22,10 +31,10 @@ export class SharedLinkTable { type!: SharedLinkType; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @Column({ type: 'timestamp with time zone', nullable: true }) - expiresAt!: Date | null; + expiresAt!: Timestamp | null; @Column({ type: 'boolean', default: false }) allowUpload!: boolean; @@ -36,13 +45,13 @@ export class SharedLinkTable { onUpdate: 'CASCADE', indexName: 'IDX_sharedlink_albumId', }) - albumId!: string; + albumId!: string | null; @Column({ type: 'boolean', default: true }) - allowDownload!: boolean; + allowDownload!: Generated; @Column({ type: 'boolean', default: true }) - showExif!: boolean; + showExif!: Generated; @Column({ type: 'character varying', nullable: true }) password!: string | null; diff --git a/server/src/schema/tables/stack.table.ts b/server/src/schema/tables/stack.table.ts index 222114d715..78700d82f9 100644 --- a/server/src/schema/tables/stack.table.ts +++ b/server/src/schema/tables/stack.table.ts @@ -1,11 +1,11 @@ import { AssetTable } from 'src/schema/tables/asset.table'; import { UserTable } from 'src/schema/tables/user.table'; -import { ForeignKeyColumn, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { ForeignKeyColumn, Generated, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; @Table('asset_stack') export class StackTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; //TODO: Add constraint to ensure primary asset exists in the assets array @ForeignKeyColumn(() => AssetTable, { nullable: false, unique: true }) diff --git a/server/src/schema/tables/sync-checkpoint.table.ts b/server/src/schema/tables/sync-checkpoint.table.ts index 21fd7983ac..a5ae9181e3 100644 --- a/server/src/schema/tables/sync-checkpoint.table.ts +++ b/server/src/schema/tables/sync-checkpoint.table.ts @@ -1,7 +1,16 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; import { SessionTable } from 'src/schema/tables/session.table'; -import { Column, CreateDateColumn, ForeignKeyColumn, PrimaryColumn, Table, UpdateDateColumn } from 'src/sql-tools'; +import { + Column, + CreateDateColumn, + ForeignKeyColumn, + Generated, + PrimaryColumn, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; @Table('session_sync_checkpoints') @UpdatedAtTrigger('session_sync_checkpoints_updated_at') @@ -13,14 +22,14 @@ export class SessionSyncCheckpointTable { type!: SyncEntityType; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column() ack!: string; @UpdateIdColumn({ indexName: 'IDX_session_sync_checkpoints_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/tag.table.ts b/server/src/schema/tables/tag.table.ts index a9f2a57f27..55abe0a852 100644 --- a/server/src/schema/tables/tag.table.ts +++ b/server/src/schema/tables/tag.table.ts @@ -4,8 +4,10 @@ import { Column, CreateDateColumn, ForeignKeyColumn, + Generated, PrimaryGeneratedColumn, Table, + Timestamp, Unique, UpdateDateColumn, } from 'src/sql-tools'; @@ -15,7 +17,7 @@ import { @Unique({ columns: ['userId', 'value'] }) export class TagTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', @@ -29,17 +31,17 @@ export class TagTable { value!: string; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @UpdateDateColumn() - updatedAt!: Date; + updatedAt!: Generated; @Column({ type: 'character varying', nullable: true, default: null }) color!: string | null; @ForeignKeyColumn(() => TagTable, { nullable: true, onDelete: 'CASCADE' }) - parentId?: string; + parentId!: string | null; @UpdateIdColumn({ indexName: 'IDX_tags_update_id' }) - updateId!: string; + updateId!: Generated; } diff --git a/server/src/schema/tables/user-audit.table.ts b/server/src/schema/tables/user-audit.table.ts index e0c9afcdc3..0c20323a54 100644 --- a/server/src/schema/tables/user-audit.table.ts +++ b/server/src/schema/tables/user-audit.table.ts @@ -1,14 +1,14 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('users_audit') export class UserAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: Generated; + @Column({ type: 'uuid' }) userId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_users_audit_deleted_at' }) - deletedAt!: Date; - - @PrimaryGeneratedUuidV7Column() - id!: string; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/user.table.ts b/server/src/schema/tables/user.table.ts index c806d6e3f7..c4b32f571a 100644 --- a/server/src/schema/tables/user.table.ts +++ b/server/src/schema/tables/user.table.ts @@ -7,16 +7,14 @@ import { Column, CreateDateColumn, DeleteDateColumn, + Generated, Index, PrimaryGeneratedColumn, Table, + Timestamp, UpdateDateColumn, } from 'src/sql-tools'; -type Timestamp = ColumnType; -type Generated = - T extends ColumnType ? ColumnType : ColumnType; - @Table('users') @UpdatedAtTrigger('users_updated_at') @AfterDeleteTrigger({ diff --git a/server/src/schema/tables/version-history.table.ts b/server/src/schema/tables/version-history.table.ts index 18805a2de3..143852c527 100644 --- a/server/src/schema/tables/version-history.table.ts +++ b/server/src/schema/tables/version-history.table.ts @@ -1,12 +1,12 @@ -import { Column, CreateDateColumn, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Generated, PrimaryGeneratedColumn, Table, Timestamp } from 'src/sql-tools'; @Table('version_history') export class VersionHistoryTable { @PrimaryGeneratedColumn() - id!: string; + id!: Generated; @CreateDateColumn() - createdAt!: Date; + createdAt!: Generated; @Column() version!: string; diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 2add5f484b..8c7f79ee5c 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -6,7 +6,6 @@ import path, { basename, isAbsolute, parse } from 'node:path'; import picomatch from 'picomatch'; import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { Assets } from 'src/db'; import { OnEvent, OnJob } from 'src/decorators'; import { CreateLibraryDto, @@ -21,6 +20,7 @@ import { import { AssetStatus, AssetType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { AssetSyncResult } from 'src/repositories/library.repository'; +import { AssetTable } from 'src/schema/tables/asset.table'; import { BaseService } from 'src/services/base.service'; import { JobOf } from 'src/types'; import { mimeTypes } from 'src/utils/mime-types'; @@ -237,7 +237,7 @@ export class LibraryService extends BaseService { return JobStatus.FAILED; } - const assetImports: Insertable[] = []; + const assetImports: Insertable[] = []; await Promise.all( job.paths.map((path) => this.processEntity(path, library.ownerId, job.libraryId) diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 7c6d659124..3212d150f6 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -9,7 +9,6 @@ import path from 'node:path'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { Asset, AssetFace } from 'src/database'; -import { AssetFaces, Exif, Person } from 'src/db'; import { OnEvent, OnJob } from 'src/decorators'; import { AssetType, @@ -25,6 +24,9 @@ import { import { ArgOf } from 'src/repositories/event.repository'; import { ReverseGeocodeResult } from 'src/repositories/map.repository'; import { ImmichTags } from 'src/repositories/metadata.repository'; +import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; +import { ExifTable } from 'src/schema/tables/exif.table'; +import { PersonTable } from 'src/schema/tables/person.table'; import { BaseService } from 'src/services/base.service'; import { JobItem, JobOf } from 'src/types'; import { isFaceImportEnabled } from 'src/utils/misc'; @@ -162,7 +164,7 @@ export class MetadataService extends BaseService { private async linkLivePhotos( asset: { id: string; type: AssetType; ownerId: string; libraryId: string | null }, - exifInfo: Insertable, + exifInfo: Insertable, ): Promise { if (!exifInfo.livePhotoCID) { return; @@ -240,7 +242,7 @@ export class MetadataService extends BaseService { } } - const exifData: Insertable = { + const exifData: Insertable = { assetId: asset.id, // dates @@ -710,10 +712,10 @@ export class MetadataService extends BaseService { return; } - const facesToAdd: (Insertable & { assetId: string })[] = []; + const facesToAdd: (Insertable & { assetId: string })[] = []; const existingNames = await this.personRepository.getDistinctNames(asset.ownerId, { withHidden: true }); const existingNameMap = new Map(existingNames.map(({ id, name }) => [name.toLowerCase(), id])); - const missing: (Insertable & { ownerId: string })[] = []; + const missing: (Insertable & { ownerId: string })[] = []; const missingWithFaceAsset: { id: string; ownerId: string; faceAssetId: string }[] = []; const adjustedRegionInfo = this.orientRegionInfo(tags.RegionInfo, tags.Orientation); diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index 61978ba8d5..af34e6eda9 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -2,7 +2,6 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/comm import { Insertable, Updateable } from 'kysely'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { Person } from 'src/database'; -import { AssetFaces, FaceSearch } from 'src/db'; import { Chunked, OnJob } from 'src/decorators'; import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -37,6 +36,8 @@ import { } from 'src/enum'; import { BoundingBox } from 'src/repositories/machine-learning.repository'; import { UpdateFacesData } from 'src/repositories/person.repository'; +import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; +import { FaceSearchTable } from 'src/schema/tables/face-search.table'; import { BaseService } from 'src/services/base.service'; import { JobItem, JobOf } from 'src/types'; import { ImmichFileResponse } from 'src/utils/file'; @@ -317,8 +318,8 @@ export class PersonService extends BaseService { ); this.logger.debug(`${faces.length} faces detected in ${previewFile.path}`); - const facesToAdd: (Insertable & { id: string })[] = []; - const embeddings: FaceSearch[] = []; + const facesToAdd: (Insertable & { id: string })[] = []; + const embeddings: FaceSearchTable[] = []; const mlFaceIds = new Set(); for (const face of asset.faces) { diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 62ec1f02a8..bd3ce0da47 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -3,7 +3,6 @@ import { Insertable } from 'kysely'; import { DateTime } from 'luxon'; import { Writable } from 'node:stream'; import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; -import { SessionSyncCheckpoints } from 'src/db'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -17,6 +16,7 @@ import { SyncStreamDto, } from 'src/dtos/sync.dto'; import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; +import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table'; import { BaseService } from 'src/services/base.service'; import { SyncAck } from 'src/types'; import { getMyPartnerIds } from 'src/utils/asset.util'; @@ -90,7 +90,7 @@ export class SyncService extends BaseService { return throwSessionRequired(); } - const checkpoints: Record> = {}; + const checkpoints: Record> = {}; for (const ack of dto.acks) { const { type } = fromAck(ack); // TODO proper ack validation via class validator diff --git a/server/src/services/tag.service.ts b/server/src/services/tag.service.ts index ecf4d6e9fb..5e8b2b4057 100644 --- a/server/src/services/tag.service.ts +++ b/server/src/services/tag.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { Insertable } from 'kysely'; -import { TagAsset } from 'src/db'; import { OnJob } from 'src/decorators'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -14,6 +13,7 @@ import { mapTag, } from 'src/dtos/tag.dto'; import { JobName, JobStatus, Permission, QueueName } from 'src/enum'; +import { TagAssetTable } from 'src/schema/tables/tag-asset.table'; import { BaseService } from 'src/services/base.service'; import { addAssets, removeAssets } from 'src/utils/asset.util'; import { upsertTags } from 'src/utils/tag'; @@ -81,7 +81,7 @@ export class TagService extends BaseService { this.checkAccess({ auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds }), ]); - const items: Insertable[] = []; + const items: Insertable[] = []; for (const tagsId of tagIds) { for (const assetsId of assetIds) { items.push({ tagsId, assetsId }); diff --git a/server/src/sql-tools/types.ts b/server/src/sql-tools/types.ts index 22bdf7d7a6..0641487e0e 100644 --- a/server/src/sql-tools/types.ts +++ b/server/src/sql-tools/types.ts @@ -1,4 +1,4 @@ -import { Kysely } from 'kysely'; +import { Kysely, ColumnType as KyselyColumnType } from 'kysely'; export type PostgresDB = { pg_am: { @@ -476,3 +476,10 @@ export enum Reason { MissingInSource = 'missing in source', MissingInTarget = 'missing in target', } + +export type Timestamp = KyselyColumnType; +export type Generated = + T extends KyselyColumnType + ? KyselyColumnType + : KyselyColumnType; +export type Int8 = KyselyColumnType; diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 54a21fe994..191e02eb63 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -16,9 +16,9 @@ import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { parse } from 'pg-connection-string'; import postgres, { Notice } from 'postgres'; import { columns, Exif, Person } from 'src/database'; -import { DB } from 'src/db'; import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; +import { DB } from 'src/schema'; import { DatabaseConnectionParams, VectorExtension } from 'src/types'; type Ssl = 'require' | 'allow' | 'prefer' | 'verify-full' | boolean | object; diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 454be00844..aa38a520ee 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -35,6 +35,7 @@ export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif primaryAssetId: assets[0].id, createdAt: new Date('2023-02-23T05:06:29.716Z'), updatedAt: new Date('2023-02-23T05:06:29.716Z'), + updateId: 'uuid-v7', }; }; diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index a2dd36527c..6d1a75502b 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -4,7 +4,6 @@ import { DateTime } from 'luxon'; import { createHash, randomBytes } from 'node:crypto'; import { Writable } from 'node:stream'; import { AssetFace } from 'src/database'; -import { Albums, AssetJobStatus, Assets, DB, Exif, FaceSearch, Memories, Person, Sessions } from 'src/db'; import { AuthDto, LoginResponseDto } from 'src/dtos/auth.dto'; import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; @@ -32,6 +31,15 @@ import { SyncRepository } from 'src/repositories/sync.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; +import { DB } from 'src/schema'; +import { AlbumTable } from 'src/schema/tables/album.table'; +import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; +import { AssetTable } from 'src/schema/tables/asset.table'; +import { ExifTable } from 'src/schema/tables/exif.table'; +import { FaceSearchTable } from 'src/schema/tables/face-search.table'; +import { MemoryTable } from 'src/schema/tables/memory.table'; +import { PersonTable } from 'src/schema/tables/person.table'; +import { SessionTable } from 'src/schema/tables/session.table'; import { UserTable } from 'src/schema/tables/user.table'; import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service'; import { SyncService } from 'src/services/sync.service'; @@ -125,13 +133,13 @@ export class MediumTestContext { return { partner, result }; } - async newAsset(dto: Partial> = {}) { + async newAsset(dto: Partial> = {}) { const asset = mediumFactory.assetInsert(dto); const result = await this.get(AssetRepository).create(asset); return { asset, result }; } - async newMemory(dto: Partial> = {}) { + async newMemory(dto: Partial> = {}) { const memory = mediumFactory.memoryInsert(dto); const result = await this.get(MemoryRepository).create(memory, new Set()); return { memory, result }; @@ -142,12 +150,12 @@ export class MediumTestContext { return { memoryAsset: dto, result }; } - async newExif(dto: Insertable) { + async newExif(dto: Insertable) { const result = await this.get(AssetRepository).upsertExif(dto); return { result }; } - async newAlbum(dto: Insertable) { + async newAlbum(dto: Insertable) { const album = mediumFactory.albumInsert(dto); const result = await this.get(AlbumRepository).create(album, [], []); return { album, result }; @@ -164,19 +172,19 @@ export class MediumTestContext { return { albumUser: { albumId, userId, role }, result }; } - async newJobStatus(dto: Partial> & { assetId: string }) { + async newJobStatus(dto: Partial> & { assetId: string }) { const jobStatus = mediumFactory.assetJobStatusInsert({ assetId: dto.assetId }); const result = await this.get(AssetRepository).upsertJobStatus(jobStatus); return { jobStatus, result }; } - async newPerson(dto: Partial> & { ownerId: string }) { + async newPerson(dto: Partial> & { ownerId: string }) { const person = mediumFactory.personInsert(dto); const result = await this.get(PersonRepository).create(person); return { person, result }; } - async newSession(dto: Partial> & { userId: string }) { + async newSession(dto: Partial> & { userId: string }) { const session = mediumFactory.sessionInsert(dto); const result = await this.get(SessionRepository).create(session); return { session, result }; @@ -338,10 +346,10 @@ const newMockRepository = (key: ClassConstructor) => { } }; -const assetInsert = (asset: Partial> = {}) => { +const assetInsert = (asset: Partial> = {}) => { const id = asset.id || newUuid(); const now = newDate(); - const defaults: Insertable = { + const defaults: Insertable = { deviceAssetId: '', deviceId: '', originalFileName: '', @@ -363,9 +371,9 @@ const assetInsert = (asset: Partial> = {}) => { }; }; -const albumInsert = (album: Partial> & { ownerId: string }) => { +const albumInsert = (album: Partial> & { ownerId: string }) => { const id = album.id || newUuid(); - const defaults: Omit, 'ownerId'> = { + const defaults: Omit, 'ownerId'> = { albumName: 'Album', }; @@ -376,7 +384,7 @@ const albumInsert = (album: Partial> & { ownerId: string }) = }; }; -const faceInsert = (face: Partial> & { faceId: string }) => { +const faceInsert = (face: Partial> & { faceId: string }) => { const defaults = { faceId: face.faceId, embedding: face.embedding || newEmbedding(), @@ -409,10 +417,10 @@ const assetFaceInsert = (assetFace: Partial & { assetId: string }) => }; const assetJobStatusInsert = ( - job: Partial> & { assetId: string }, -): Insertable => { + job: Partial> & { assetId: string }, +): Insertable => { const date = DateTime.now().minus({ days: 15 }).toISO(); - const defaults: Omit, 'assetId'> = { + const defaults: Omit, 'assetId'> = { duplicatesDetectedAt: date, facesRecognizedAt: date, metadataExtractedAt: date, @@ -426,7 +434,7 @@ const assetJobStatusInsert = ( }; }; -const personInsert = (person: Partial> & { ownerId: string }) => { +const personInsert = (person: Partial> & { ownerId: string }) => { const defaults = { birthDate: person.birthDate || null, color: person.color || null, @@ -449,8 +457,12 @@ const personInsert = (person: Partial> & { ownerId: string }) const sha256 = (value: string) => createHash('sha256').update(value).digest('base64'); -const sessionInsert = ({ id = newUuid(), userId, ...session }: Partial> & { userId: string }) => { - const defaults: Insertable = { +const sessionInsert = ({ + id = newUuid(), + userId, + ...session +}: Partial> & { userId: string }) => { + const defaults: Insertable = { id, userId, token: sha256(id), @@ -478,11 +490,11 @@ const userInsert = (user: Partial> = {}) => { return { ...defaults, ...user, id }; }; -const memoryInsert = (memory: Partial> = {}) => { +const memoryInsert = (memory: Partial> = {}) => { const id = memory.id || newUuid(); const date = newDate(); - const defaults: Insertable = { + const defaults: Insertable = { id, createdAt: date, updatedAt: date, diff --git a/server/test/medium/globalSetup.ts b/server/test/medium/globalSetup.ts index 811fb2cadb..4f56216b84 100644 --- a/server/test/medium/globalSetup.ts +++ b/server/test/medium/globalSetup.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { ConfigRepository } from 'src/repositories/config.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { DB } from 'src/schema'; import { getKyselyConfig } from 'src/utils/database'; import { GenericContainer, Wait } from 'testcontainers'; diff --git a/server/test/medium/specs/services/asset.service.spec.ts b/server/test/medium/specs/services/asset.service.spec.ts index db9a7de082..11343bd6e1 100644 --- a/server/test/medium/specs/services/asset.service.spec.ts +++ b/server/test/medium/specs/services/asset.service.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AssetRepository } from 'src/repositories/asset.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { DB } from 'src/schema'; import { AssetService } from 'src/services/asset.service'; import { newMediumService } from 'test/medium.factory'; import { factory } from 'test/small.factory'; diff --git a/server/test/medium/specs/services/auth.service.spec.ts b/server/test/medium/specs/services/auth.service.spec.ts index 53deaa1291..f1d50ce841 100644 --- a/server/test/medium/specs/services/auth.service.spec.ts +++ b/server/test/medium/specs/services/auth.service.spec.ts @@ -1,7 +1,6 @@ import { BadRequestException } from '@nestjs/common'; import { hash } from 'bcrypt'; import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AuthType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; @@ -13,6 +12,7 @@ import { SessionRepository } from 'src/repositories/session.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { AuthService } from 'src/services/auth.service'; import { mediumFactory, newMediumService } from 'test/medium.factory'; import { factory } from 'test/small.factory'; diff --git a/server/test/medium/specs/services/memory.service.spec.ts b/server/test/medium/specs/services/memory.service.spec.ts index 53ab970d47..bdd06b4a3f 100644 --- a/server/test/medium/specs/services/memory.service.spec.ts +++ b/server/test/medium/specs/services/memory.service.spec.ts @@ -1,6 +1,5 @@ import { Kysely } from 'kysely'; import { DateTime } from 'luxon'; -import { DB } from 'src/db'; import { AssetFileType, MemoryType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -10,6 +9,7 @@ import { MemoryRepository } from 'src/repositories/memory.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { MemoryService } from 'src/services/memory.service'; import { newMediumService } from 'test/medium.factory'; import { factory } from 'test/small.factory'; diff --git a/server/test/medium/specs/services/person.service.spec.ts b/server/test/medium/specs/services/person.service.spec.ts index 2a1080704c..f26834c5e2 100644 --- a/server/test/medium/specs/services/person.service.spec.ts +++ b/server/test/medium/specs/services/person.service.spec.ts @@ -1,10 +1,10 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AccessRepository } from 'src/repositories/access.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; +import { DB } from 'src/schema'; import { PersonService } from 'src/services/person.service'; import { newMediumService } from 'test/medium.factory'; import { factory } from 'test/small.factory'; diff --git a/server/test/medium/specs/services/user.service.spec.ts b/server/test/medium/specs/services/user.service.spec.ts index a16ea3c877..535ac30aec 100644 --- a/server/test/medium/specs/services/user.service.spec.ts +++ b/server/test/medium/specs/services/user.service.spec.ts @@ -1,6 +1,5 @@ import { Kysely } from 'kysely'; import { DateTime } from 'luxon'; -import { DB } from 'src/db'; import { ImmichEnvironment, JobName, JobStatus } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; @@ -8,6 +7,7 @@ import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { UserService } from 'src/services/user.service'; import { mediumFactory, newMediumService } from 'test/medium.factory'; import { factory } from 'test/small.factory'; diff --git a/server/test/medium/specs/services/version.service.spec.ts b/server/test/medium/specs/services/version.service.spec.ts index 6e9984cc50..9feda5b8c4 100644 --- a/server/test/medium/specs/services/version.service.spec.ts +++ b/server/test/medium/specs/services/version.service.spec.ts @@ -1,11 +1,11 @@ import { Kysely } from 'kysely'; import { serverVersion } from 'src/constants'; -import { DB } from 'src/db'; import { JobName } from 'src/enum'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; +import { DB } from 'src/schema'; import { VersionService } from 'src/services/version.service'; import { newMediumService } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts index 811dbb374b..2c6b98e949 100644 --- a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts @@ -1,6 +1,6 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index ae4833de34..6bfb6b5d27 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -1,6 +1,6 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts index c1af6676f8..a0c1d413a0 100644 --- a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumRepository } from 'src/repositories/album.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-album-user.spec.ts b/server/test/medium/specs/sync/sync-album-user.spec.ts index 215752ca6e..798b3d607d 100644 --- a/server/test/medium/specs/sync/sync-album-user.spec.ts +++ b/server/test/medium/specs/sync/sync-album-user.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-album.spec.ts b/server/test/medium/specs/sync/sync-album.spec.ts index 90aa8c4576..83e9f12651 100644 --- a/server/test/medium/specs/sync/sync-album.spec.ts +++ b/server/test/medium/specs/sync/sync-album.spec.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { AlbumRepository } from 'src/repositories/album.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-asset-exif.spec.ts index 8ddb05a2b7..425ea89054 100644 --- a/server/test/medium/specs/sync/sync-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-exif.spec.ts @@ -1,6 +1,6 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 5ffec3612e..8603296028 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-memory-asset.spec.ts b/server/test/medium/specs/sync/sync-memory-asset.spec.ts index a6cfadea6d..a3247637d7 100644 --- a/server/test/medium/specs/sync/sync-memory-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-memory-asset.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-memory.spec.ts b/server/test/medium/specs/sync/sync-memory.spec.ts index df41671fde..fa833ad094 100644 --- a/server/test/medium/specs/sync/sync-memory.spec.ts +++ b/server/test/medium/specs/sync/sync-memory.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts index 2fee9b09a9..c33eb59dbb 100644 --- a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts @@ -1,6 +1,6 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 4faa2f6966..d9655add57 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -1,9 +1,9 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; import { getKyselyDB, wait } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-partner.spec.ts b/server/test/medium/specs/sync/sync-partner.spec.ts index 11d843243d..d20970da8f 100644 --- a/server/test/medium/specs/sync/sync-partner.spec.ts +++ b/server/test/medium/specs/sync/sync-partner.spec.ts @@ -1,8 +1,8 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 10a06bd9f7..24137e3aea 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -1,7 +1,7 @@ import { Kysely } from 'kysely'; -import { DB } from 'src/db'; import { SyncEntityType, SyncRequestType } from 'src/enum'; import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; diff --git a/server/test/utils.ts b/server/test/utils.ts index 0b4c743802..af6f2826f9 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -7,7 +7,6 @@ import { ChildProcessWithoutNullStreams } from 'node:child_process'; import { Writable } from 'node:stream'; import { PNG } from 'pngjs'; import postgres from 'postgres'; -import { DB } from 'src/db'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { AuthGuard } from 'src/middleware/auth.guard'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @@ -56,6 +55,7 @@ import { TrashRepository } from 'src/repositories/trash.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; +import { DB } from 'src/schema'; import { AuthService } from 'src/services/auth.service'; import { BaseService } from 'src/services/base.service'; import { RepositoryInterface } from 'src/types';