mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-01 19:17:12 -04:00 
			
		
		
		
	refactor: database types (#19624)
This commit is contained in:
		
							parent
							
								
									09cbc5d3f4
								
							
						
					
					
						commit
						e60bc3c304
					
				| @ -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", | ||||
|  | ||||
| @ -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<Albums> & { | ||||
| export type Album = Selectable<AlbumTable> & { | ||||
|   owner: User; | ||||
|   assets: MapAsset[]; | ||||
| }; | ||||
| @ -239,7 +240,7 @@ export type Session = { | ||||
|   pinExpiresAt: Date | null; | ||||
| }; | ||||
| 
 | ||||
| export type Exif = Omit<Selectable<DatabaseExif>, 'updatedAt' | 'updateId'>; | ||||
| export type Exif = Omit<Selectable<ExifTable>, 'updatedAt' | 'updateId'>; | ||||
| 
 | ||||
| export type Person = { | ||||
|   createdAt: Date; | ||||
|  | ||||
							
								
								
									
										540
									
								
								server/src/db.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										540
									
								
								server/src/db.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -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<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>; | ||||
| 
 | ||||
| export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S[], I[], U[]> : T[]; | ||||
| 
 | ||||
| export type Generated<T> = | ||||
|   T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>; | ||||
| 
 | ||||
| export type Int8 = ColumnType<number>; | ||||
| 
 | ||||
| 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<Date, Date | string, Date | string>; | ||||
| 
 | ||||
| export interface Activity { | ||||
|   albumId: string; | ||||
|   assetId: string | null; | ||||
|   comment: string | null; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   isLiked: Generated<boolean>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
|   userId: string; | ||||
| } | ||||
| 
 | ||||
| export interface Albums { | ||||
|   albumName: Generated<string>; | ||||
|   /** | ||||
|    * Asset ID to be used as thumbnail | ||||
|    */ | ||||
|   albumThumbnailAssetId: string | null; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   deletedAt: Timestamp | null; | ||||
|   description: Generated<string>; | ||||
|   id: Generated<string>; | ||||
|   isActivityEnabled: Generated<boolean>; | ||||
|   order: Generated<AssetOrder>; | ||||
|   ownerId: string; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface AlbumsAudit { | ||||
|   deletedAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   albumId: string; | ||||
|   userId: string; | ||||
| } | ||||
| 
 | ||||
| export interface AlbumUsersAudit { | ||||
|   deletedAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   albumId: string; | ||||
|   userId: string; | ||||
| } | ||||
| 
 | ||||
| export interface AlbumsAssetsAssets { | ||||
|   albumsId: string; | ||||
|   assetsId: string; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface AlbumAssetsAudit { | ||||
|   deletedAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   albumId: string; | ||||
|   assetId: string; | ||||
| } | ||||
| 
 | ||||
| export interface AlbumsSharedUsersUsers { | ||||
|   albumsId: string; | ||||
|   role: Generated<AlbumUserRole>; | ||||
|   usersId: string; | ||||
|   createId: Generated<string>; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
| } | ||||
| 
 | ||||
| export interface ApiKeys { | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   key: string; | ||||
|   name: string; | ||||
|   permissions: Permission[]; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
|   userId: string; | ||||
| } | ||||
| 
 | ||||
| export interface AssetFaces { | ||||
|   assetId: string; | ||||
|   boundingBoxX1: Generated<number>; | ||||
|   boundingBoxX2: Generated<number>; | ||||
|   boundingBoxY1: Generated<number>; | ||||
|   boundingBoxY2: Generated<number>; | ||||
|   deletedAt: Timestamp | null; | ||||
|   id: Generated<string>; | ||||
|   imageHeight: Generated<number>; | ||||
|   imageWidth: Generated<number>; | ||||
|   personId: string | null; | ||||
|   sourceType: Generated<SourceType>; | ||||
| } | ||||
| 
 | ||||
| export interface AssetFiles { | ||||
|   assetId: string; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   path: string; | ||||
|   type: AssetFileType; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| 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<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   assetId: string; | ||||
|   ownerId: string; | ||||
| } | ||||
| 
 | ||||
| export interface Assets { | ||||
|   checksum: Buffer; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   deletedAt: Timestamp | null; | ||||
|   deviceAssetId: string; | ||||
|   deviceId: string; | ||||
|   duplicateId: string | null; | ||||
|   duration: string | null; | ||||
|   encodedVideoPath: Generated<string | null>; | ||||
|   fileCreatedAt: Timestamp; | ||||
|   fileModifiedAt: Timestamp; | ||||
|   id: Generated<string>; | ||||
|   isExternal: Generated<boolean>; | ||||
|   isFavorite: Generated<boolean>; | ||||
|   isOffline: Generated<boolean>; | ||||
|   visibility: Generated<AssetVisibility>; | ||||
|   libraryId: string | null; | ||||
|   livePhotoVideoId: string | null; | ||||
|   localDateTime: Timestamp; | ||||
|   originalFileName: string; | ||||
|   originalPath: string; | ||||
|   ownerId: string; | ||||
|   sidecarPath: string | null; | ||||
|   stackId: string | null; | ||||
|   status: Generated<AssetStatus>; | ||||
|   thumbhash: Buffer | null; | ||||
|   type: AssetType; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface AssetStack { | ||||
|   id: Generated<string>; | ||||
|   ownerId: string; | ||||
|   primaryAssetId: string; | ||||
| } | ||||
| 
 | ||||
| export interface Audit { | ||||
|   action: string; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   entityId: string; | ||||
|   entityType: string; | ||||
|   id: Generated<number>; | ||||
|   ownerId: string; | ||||
| } | ||||
| 
 | ||||
| export interface Exif { | ||||
|   assetId: string; | ||||
|   updateId: Generated<string>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   autoStackId: string | null; | ||||
|   bitsPerSample: number | null; | ||||
|   city: string | null; | ||||
|   colorspace: string | null; | ||||
|   country: string | null; | ||||
|   dateTimeOriginal: Timestamp | null; | ||||
|   description: Generated<string>; | ||||
|   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<Timestamp>; | ||||
|   deletedAt: Timestamp | null; | ||||
|   exclusionPatterns: string[]; | ||||
|   id: Generated<string>; | ||||
|   importPaths: string[]; | ||||
|   name: string; | ||||
|   ownerId: string; | ||||
|   refreshedAt: Timestamp | null; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface Memories { | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   data: object; | ||||
|   deletedAt: Timestamp | null; | ||||
|   hideAt: Timestamp | null; | ||||
|   id: Generated<string>; | ||||
|   isSaved: Generated<boolean>; | ||||
|   memoryAt: Timestamp; | ||||
|   ownerId: string; | ||||
|   seenAt: Timestamp | null; | ||||
|   showAt: Timestamp | null; | ||||
|   type: MemoryType; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface Notifications { | ||||
|   id: Generated<string>; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   deletedAt: Timestamp | null; | ||||
|   updateId: Generated<string>; | ||||
|   userId: string; | ||||
|   level: Generated<NotificationLevel>; | ||||
|   type: NotificationType; | ||||
|   title: string; | ||||
|   description: string | null; | ||||
|   data: any | null; | ||||
|   readAt: Timestamp | null; | ||||
| } | ||||
| 
 | ||||
| export interface Migrations { | ||||
|   id: Generated<number>; | ||||
|   name: string; | ||||
|   timestamp: Int8; | ||||
| } | ||||
| 
 | ||||
| export interface MoveHistory { | ||||
|   entityId: string; | ||||
|   id: Generated<string>; | ||||
|   newPath: string; | ||||
|   oldPath: string; | ||||
|   pathType: string; | ||||
| } | ||||
| 
 | ||||
| export interface NaturalearthCountries { | ||||
|   admin: string; | ||||
|   admin_a3: string; | ||||
|   coordinates: string; | ||||
|   id: Generated<number>; | ||||
|   type: string; | ||||
| } | ||||
| 
 | ||||
| export interface PartnersAudit { | ||||
|   deletedAt: Generated<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   sharedById: string; | ||||
|   sharedWithId: string; | ||||
| } | ||||
| 
 | ||||
| export interface Partners { | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   createId: Generated<string>; | ||||
|   inTimeline: Generated<boolean>; | ||||
|   sharedById: string; | ||||
|   sharedWithId: string; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface Person { | ||||
|   birthDate: Timestamp | null; | ||||
|   color: string | null; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   faceAssetId: string | null; | ||||
|   id: Generated<string>; | ||||
|   isFavorite: Generated<boolean>; | ||||
|   isHidden: Generated<boolean>; | ||||
|   name: Generated<string>; | ||||
|   ownerId: string; | ||||
|   thumbnailPath: Generated<string>; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface Sessions { | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   deviceOS: Generated<string>; | ||||
|   deviceType: Generated<string>; | ||||
|   id: Generated<string>; | ||||
|   parentId: string | null; | ||||
|   expiresAt: Date | null; | ||||
|   token: string; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
|   userId: string; | ||||
|   pinExpiresAt: Timestamp | null; | ||||
| } | ||||
| 
 | ||||
| export interface SessionSyncCheckpoints { | ||||
|   ack: string; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   sessionId: string; | ||||
|   type: SyncEntityType; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
| } | ||||
| 
 | ||||
| export interface SharedLinkAsset { | ||||
|   assetsId: string; | ||||
|   sharedLinksId: string; | ||||
| } | ||||
| 
 | ||||
| export interface SharedLinks { | ||||
|   albumId: string | null; | ||||
|   allowDownload: Generated<boolean>; | ||||
|   allowUpload: Generated<boolean>; | ||||
|   createdAt: Generated<Timestamp>; | ||||
|   description: string | null; | ||||
|   expiresAt: Timestamp | null; | ||||
|   id: Generated<string>; | ||||
|   key: Buffer; | ||||
|   password: string | null; | ||||
|   showExif: Generated<boolean>; | ||||
|   type: SharedLinkType; | ||||
|   userId: string; | ||||
| } | ||||
| 
 | ||||
| export interface SmartSearch { | ||||
|   assetId: string; | ||||
|   embedding: string; | ||||
| } | ||||
| 
 | ||||
| export interface SocketIoAttachments { | ||||
|   created_at: Generated<Timestamp | null>; | ||||
|   id: Generated<Int8>; | ||||
|   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<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   parentId: string | null; | ||||
|   updatedAt: Generated<Timestamp>; | ||||
|   updateId: Generated<string>; | ||||
|   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<string>; | ||||
|   userId: string; | ||||
|   deletedAt: Generated<Timestamp>; | ||||
| } | ||||
| 
 | ||||
| export interface VectorsPgVectorIndexStat { | ||||
|   idx_growing: ArrayType<Int8> | null; | ||||
|   idx_indexing: boolean | null; | ||||
|   idx_options: string | null; | ||||
|   idx_sealed: ArrayType<Int8> | 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<Timestamp>; | ||||
|   id: Generated<string>; | ||||
|   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; | ||||
| } | ||||
| @ -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<AssetFaces>): AssetFaceWithoutPersonResponseDto { | ||||
| export function mapFacesWithoutPerson(face: Selectable<AssetFaceTable>): AssetFaceWithoutPersonResponseDto { | ||||
|   return { | ||||
|     id: face.id, | ||||
|     imageHeight: face.imageHeight, | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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<Activity>) { | ||||
|   async create(activity: Insertable<ActivityTable>) { | ||||
|     return this.db | ||||
|       .insertInto('activity') | ||||
|       .values(activity) | ||||
|  | ||||
| @ -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<DB>) {} | ||||
| 
 | ||||
|   @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] }) | ||||
|   create(albumUser: Insertable<AlbumsSharedUsersUsers>) { | ||||
|   create(albumUser: Insertable<AlbumUserTable>) { | ||||
|     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<AlbumsSharedUsersUsers>) { | ||||
|   update({ usersId, albumsId }: AlbumPermissionId, dto: Updateable<AlbumUserTable>) { | ||||
|     return this.db | ||||
|       .updateTable('albums_shared_users_users') | ||||
|       .set(dto) | ||||
|  | ||||
| @ -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<Albums>, assetIds: string[], albumUsers: AlbumUserCreateDto[]) { | ||||
|   create(album: Insertable<AlbumTable>, 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<Albums>) { | ||||
|   update(id: string, album: Updateable<AlbumTable>) { | ||||
|     return this.db | ||||
|       .updateTable('albums') | ||||
|       .set(album) | ||||
|  | ||||
| @ -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<DB>) {} | ||||
| 
 | ||||
|   create(dto: Insertable<ApiKeys>) { | ||||
|   create(dto: Insertable<ApiKeyTable>) { | ||||
|     return this.db.insertInto('api_keys').values(dto).returning(columns.apiKey).executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   async update(userId: string, id: string, dto: Updateable<ApiKeys>) { | ||||
|   async update(userId: string, id: string, dto: Updateable<ApiKeyTable>) { | ||||
|     return this.db | ||||
|       .updateTable('api_keys') | ||||
|       .set(dto) | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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<DB>) {} | ||||
| 
 | ||||
|   async upsertExif(exif: Insertable<Exif>): Promise<void> { | ||||
|   async upsertExif(exif: Insertable<ExifTable>): Promise<void> { | ||||
|     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<Exif>): Promise<void> { | ||||
|   async updateAllExif(ids: string[], options: Updateable<ExifTable>): Promise<void> { | ||||
|     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<AssetJobStatus>[]): Promise<void> { | ||||
|   async upsertJobStatus(...jobStatus: Insertable<AssetJobStatusTable>[]): Promise<void> { | ||||
|     if (jobStatus.length === 0) { | ||||
|       return; | ||||
|     } | ||||
| @ -191,11 +195,11 @@ export class AssetRepository { | ||||
|       .execute(); | ||||
|   } | ||||
| 
 | ||||
|   create(asset: Insertable<Assets>) { | ||||
|   create(asset: Insertable<AssetTable>) { | ||||
|     return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   createAll(assets: Insertable<Assets>[]) { | ||||
|   createAll(assets: Insertable<AssetTable>[]) { | ||||
|     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<Assets>): Promise<void> { | ||||
|   async updateAll(ids: string[], options: Updateable<AssetTable>): Promise<void> { | ||||
|     if (ids.length === 0) { | ||||
|       return; | ||||
|     } | ||||
|     await this.db.updateTable('assets').set(options).where('id', '=', anyUuid(ids)).execute(); | ||||
|   } | ||||
| 
 | ||||
|   async updateByLibraryId(libraryId: string, options: Updateable<Assets>): Promise<void> { | ||||
|   async updateByLibraryId(libraryId: string, options: Updateable<AssetTable>): Promise<void> { | ||||
|     await this.db.updateTable('assets').set(options).where('libraryId', '=', asUuid(libraryId)).execute(); | ||||
|   } | ||||
| 
 | ||||
|   async update(asset: Updateable<Assets> & { id: string }) { | ||||
|   async update(asset: Updateable<AssetTable> & { 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<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>): Promise<void> { | ||||
|   async upsertFile(file: Pick<Insertable<AssetFileTable>, 'assetId' | 'path' | 'type'>): Promise<void> { | ||||
|     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<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>[]): Promise<void> { | ||||
|   async upsertFiles(files: Pick<Insertable<AssetFileTable>, 'assetId' | 'path' | 'type'>[]): Promise<void> { | ||||
|     if (files.length === 0) { | ||||
|       return; | ||||
|     } | ||||
| @ -772,7 +776,7 @@ export class AssetRepository { | ||||
|       .execute(); | ||||
|   } | ||||
| 
 | ||||
|   async deleteFiles(files: Pick<Selectable<AssetFiles>, 'id'>[]): Promise<void> { | ||||
|   async deleteFiles(files: Pick<Selectable<AssetFileTable>, 'id'>[]): Promise<void> { | ||||
|     if (files.length === 0) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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<DB>) => | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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<Libraries>) { | ||||
|   create(library: Insertable<LibraryTable>) { | ||||
|     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<Libraries>) { | ||||
|   update(id: string, library: Updateable<LibraryTable>) { | ||||
|     return this.db | ||||
|       .updateTable('libraries') | ||||
|       .set(library) | ||||
|  | ||||
| @ -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<NaturalearthCountries>[] = []; | ||||
|     const entities: Insertable<NaturalEarthCountriesTable>[] = []; | ||||
|     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<NaturalearthCountries> = { | ||||
|         const featureRecord: Insertable<NaturalEarthCountriesTable> = { | ||||
|           admin: feature.properties.ADMIN, | ||||
|           admin_a3: feature.properties.ADM0_A3, | ||||
|           type: feature.properties.TYPE, | ||||
|  | ||||
| @ -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<Memories>, assetIds: Set<string>) { | ||||
|   async create(memory: Insertable<MemoryTable>, assetIds: Set<string>) { | ||||
|     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<Memories>) { | ||||
|   async update(id: string, memory: Updateable<MemoryTable>) { | ||||
|     await this.db.updateTable('memories').set(memory).where('id', '=', id).execute(); | ||||
|     return this.getByIdBuilder(id).executeTakeFirstOrThrow(); | ||||
|   } | ||||
|  | ||||
| @ -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<DB>) {} | ||||
| 
 | ||||
|   create(entity: Insertable<MoveHistory>) { | ||||
|   create(entity: Insertable<MoveTable>) { | ||||
|     return this.db.insertInto('move_history').values(entity).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
| @ -23,7 +24,7 @@ export class MoveRepository { | ||||
|       .executeTakeFirst(); | ||||
|   } | ||||
| 
 | ||||
|   update(id: string, entity: Updateable<MoveHistory>) { | ||||
|   update(id: string, entity: Updateable<MoveTable>) { | ||||
|     return this.db | ||||
|       .updateTable('move_history') | ||||
|       .set(entity) | ||||
|  | ||||
| @ -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<DB>) {} | ||||
| @ -53,7 +54,7 @@ export class NotificationRepository { | ||||
|       .execute(); | ||||
|   } | ||||
| 
 | ||||
|   create(notification: Insertable<Notifications>) { | ||||
|   create(notification: Insertable<NotificationTable>) { | ||||
|     return this.db | ||||
|       .insertInto('notifications') | ||||
|       .values(notification) | ||||
| @ -70,7 +71,7 @@ export class NotificationRepository { | ||||
|       .executeTakeFirst(); | ||||
|   } | ||||
| 
 | ||||
|   update(id: string, notification: Updateable<Notifications>) { | ||||
|   update(id: string, notification: Updateable<NotificationTable>) { | ||||
|     return this.db | ||||
|       .updateTable('notifications') | ||||
|       .set(notification) | ||||
| @ -80,7 +81,7 @@ export class NotificationRepository { | ||||
|       .executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   async updateAll(ids: string[], notification: Updateable<Notifications>) { | ||||
|   async updateAll(ids: string[], notification: Updateable<NotificationTable>) { | ||||
|     await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute(); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -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<Partners>) { | ||||
|   create(values: Insertable<PartnerTable>) { | ||||
|     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<Partners>) { | ||||
|   update({ sharedWithId, sharedById }: PartnerIds, values: Updateable<PartnerTable>) { | ||||
|     return this.db | ||||
|       .updateTable('partners') | ||||
|       .set(values) | ||||
|  | ||||
| @ -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<AssetFaces>)[]; | ||||
| export type SelectFaceOptions = (keyof Selectable<AssetFaceTable>)[]; | ||||
| 
 | ||||
| const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => { | ||||
|   return jsonObjectFrom( | ||||
| @ -378,11 +381,11 @@ export class PersonRepository { | ||||
|       .executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   create(person: Insertable<Person>) { | ||||
|   create(person: Insertable<PersonTable>) { | ||||
|     return this.db.insertInto('person').values(person).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   async createAll(people: Insertable<Person>[]): Promise<string[]> { | ||||
|   async createAll(people: Insertable<PersonTable>[]): Promise<string[]> { | ||||
|     if (people.length === 0) { | ||||
|       return []; | ||||
|     } | ||||
| @ -393,9 +396,9 @@ export class PersonRepository { | ||||
| 
 | ||||
|   @GenerateSql({ params: [[], [], [{ faceId: DummyValue.UUID, embedding: DummyValue.VECTOR }]] }) | ||||
|   async refreshFaces( | ||||
|     facesToAdd: (Insertable<AssetFaces> & { assetId: string })[], | ||||
|     facesToAdd: (Insertable<AssetFaceTable> & { assetId: string })[], | ||||
|     faceIdsToRemove: string[], | ||||
|     embeddingsToAdd?: Insertable<FaceSearch>[], | ||||
|     embeddingsToAdd?: Insertable<FaceSearchTable>[], | ||||
|   ): Promise<void> { | ||||
|     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<Person> & { id: string }) { | ||||
|   async update(person: Updateable<PersonTable> & { id: string }) { | ||||
|     return this.db | ||||
|       .updateTable('person') | ||||
|       .set(person) | ||||
| @ -424,7 +427,7 @@ export class PersonRepository { | ||||
|       .executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   async updateAll(people: Insertable<Person>[]): Promise<void> { | ||||
|   async updateAll(people: Insertable<PersonTable>[]): Promise<void> { | ||||
|     if (people.length === 0) { | ||||
|       return; | ||||
|     } | ||||
| @ -496,7 +499,7 @@ export class PersonRepository { | ||||
|     return result?.latestDate; | ||||
|   } | ||||
| 
 | ||||
|   async createAssetFace(face: Insertable<AssetFaces>): Promise<void> { | ||||
|   async createAssetFace(face: Insertable<AssetFaceTable>): Promise<void> { | ||||
|     await this.db.insertInto('asset_faces').values(face).execute(); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -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<Selectable<Exif>>() | ||||
|           .$castTo<Selectable<ExifTable>>() | ||||
|           .as('exifInfo'), | ||||
|       ) | ||||
|       .orderBy('exif.city') | ||||
|  | ||||
| @ -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<Sessions>) { | ||||
|   create(dto: Insertable<SessionTable>) { | ||||
|     return this.db.insertInto('sessions').values(dto).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   update(id: string, dto: Updateable<Sessions>) { | ||||
|   update(id: string, dto: Updateable<SessionTable>) { | ||||
|     return this.db | ||||
|       .updateTable('sessions') | ||||
|       .set(dto) | ||||
|  | ||||
| @ -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<SharedLinks> & { assetIds?: string[] }) { | ||||
|   async create(entity: Insertable<SharedLinkTable> & { 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<SharedLinks> & { id: string; assetIds?: string[] }) { | ||||
|   async update(entity: Updateable<SharedLinkTable> & { id: string; assetIds?: string[] }) { | ||||
|     const { id } = await this.db | ||||
|       .updateTable('shared_links') | ||||
|       .set(_.omit(entity, 'assets', 'album', 'assetIds')) | ||||
|  | ||||
| @ -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<AssetStack>) { | ||||
|   update(id: string, entity: Updateable<StackTable>) { | ||||
|     return this.db | ||||
|       .updateTable('asset_stack') | ||||
|       .set(entity) | ||||
|  | ||||
| @ -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<SessionSyncCheckpoints>[]) { | ||||
|   upsertAll(items: Insertable<SessionSyncCheckpointTable>[]) { | ||||
|     return this.db | ||||
|       .insertInto('session_sync_checkpoints') | ||||
|       .values(items) | ||||
|  | ||||
| @ -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 = | ||||
|  | ||||
| @ -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<DbSystemMetadata>; | ||||
| type Upsert = Insertable<SystemMetadataTable>; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class SystemMetadataRepository { | ||||
|  | ||||
| @ -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<Tags>) { | ||||
|   create(tag: Insertable<TagTable>) { | ||||
|     return this.db.insertInto('tags').values(tag).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, { color: DummyValue.STRING }] }) | ||||
|   update(id: string, dto: Updateable<Tags>) { | ||||
|   update(id: string, dto: Updateable<TagTable>) { | ||||
|     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<TagAsset>[]) { | ||||
|   upsertAssetIds(items: Insertable<TagAssetTable>[]) { | ||||
|     if (items.length === 0) { | ||||
|       return Promise.resolve([]); | ||||
|     } | ||||
|  | ||||
| @ -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<DB>) {} | ||||
|  | ||||
| @ -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<DbUserMetadata>; | ||||
| type Upsert = Insertable<UserMetadataTable>; | ||||
| 
 | ||||
| export interface UserListFilter { | ||||
|   id?: string; | ||||
|  | ||||
| @ -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<VersionHistory>) { | ||||
|   create(version: Insertable<VersionHistoryTable>) { | ||||
|     return this.db.insertInto('version_history').values(version).returningAll().executeTakeFirstOrThrow(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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<number>; | ||||
|   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; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<boolean>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_activity_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_album_assets_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<AlbumUserRole>; | ||||
| 
 | ||||
|   @CreateIdColumn({ indexName: 'IDX_album_users_create_id' }) | ||||
|   createId?: string; | ||||
|   createId!: Generated<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_album_users_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) | ||||
|   ownerId!: string; | ||||
| 
 | ||||
|   @Column({ default: 'Untitled Album' }) | ||||
|   albumName!: string; | ||||
|   albumName!: Generated<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| 
 | ||||
|   @Column({ type: 'text', default: '' }) | ||||
|   description!: string; | ||||
|   description!: Generated<string>; | ||||
| 
 | ||||
|   @DeleteDateColumn() | ||||
|   deletedAt!: Date | null; | ||||
|   deletedAt!: Timestamp | null; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: true }) | ||||
|   isActivityEnabled!: boolean; | ||||
|   isActivityEnabled!: Generated<boolean>; | ||||
| 
 | ||||
|   @Column({ default: AssetOrder.DESC }) | ||||
|   order!: AssetOrder; | ||||
|   order!: Generated<AssetOrder>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_albums_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @Column() | ||||
|   name!: string; | ||||
| 
 | ||||
| @ -23,17 +28,14 @@ export class APIKeyTable { | ||||
|   userId!: string; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
| 
 | ||||
|   @PrimaryGeneratedColumn() | ||||
|   id!: string; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column({ array: true, type: 'character varying' }) | ||||
|   permissions!: Permission[]; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_api_keys_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<number>; | ||||
| 
 | ||||
|   @Column({ default: 0, type: 'integer' }) | ||||
|   imageHeight!: number; | ||||
|   imageHeight!: Generated<number>; | ||||
| 
 | ||||
|   @Column({ default: 0, type: 'integer' }) | ||||
|   boundingBoxX1!: number; | ||||
|   boundingBoxX1!: Generated<number>; | ||||
| 
 | ||||
|   @Column({ default: 0, type: 'integer' }) | ||||
|   boundingBoxY1!: number; | ||||
|   boundingBoxY1!: Generated<number>; | ||||
| 
 | ||||
|   @Column({ default: 0, type: 'integer' }) | ||||
|   boundingBoxX2!: number; | ||||
|   boundingBoxX2!: Generated<number>; | ||||
| 
 | ||||
|   @Column({ default: 0, type: 'integer' }) | ||||
|   boundingBoxY2!: number; | ||||
| 
 | ||||
|   @PrimaryGeneratedColumn() | ||||
|   id!: string; | ||||
|   boundingBoxY2!: Generated<number>; | ||||
| 
 | ||||
|   @Column({ default: SourceType.MACHINE_LEARNING, enum: asset_face_source_type }) | ||||
|   sourceType!: SourceType; | ||||
|   sourceType!: Generated<SourceType>; | ||||
| 
 | ||||
|   @DeleteDateColumn() | ||||
|   deletedAt!: Date | null; | ||||
|   deletedAt!: Timestamp | null; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => AssetTable, { | ||||
|     onDelete: 'CASCADE', | ||||
|     onUpdate: 'CASCADE', | ||||
|     indexName: 'IDX_asset_files_assetId', | ||||
|   }) | ||||
|   assetId?: string; | ||||
|   assetId!: string; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column() | ||||
|   type!: AssetFileType; | ||||
| @ -38,5 +40,5 @@ export class AssetFileTable { | ||||
|   path!: string; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_asset_files_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<boolean>; | ||||
| 
 | ||||
|   @Column({ type: 'character varying', nullable: true }) | ||||
|   duration!: string | null; | ||||
| @ -99,10 +101,10 @@ export class AssetTable { | ||||
|   livePhotoVideoId!: string | null; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column({ index: true }) | ||||
|   originalFileName!: string; | ||||
| @ -114,32 +116,32 @@ export class AssetTable { | ||||
|   thumbhash!: Buffer | null; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isOffline!: boolean; | ||||
|   isOffline!: Generated<boolean>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => LibraryTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) | ||||
|   libraryId?: string | null; | ||||
|   libraryId!: string | null; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isExternal!: boolean; | ||||
|   isExternal!: Generated<boolean>; | ||||
| 
 | ||||
|   @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<AssetStatus>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_assets_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| 
 | ||||
|   @Column({ enum: asset_visibility_enum, default: AssetVisibility.TIMELINE }) | ||||
|   visibility!: AssetVisibility; | ||||
|   visibility!: Generated<AssetVisibility>; | ||||
| } | ||||
|  | ||||
| @ -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<number>; | ||||
| 
 | ||||
|   @Column() | ||||
|   entityType!: EntityType; | ||||
| @ -20,5 +20,5 @@ export class AuditTable { | ||||
|   ownerId!: string; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; // 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<Date>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_asset_exif_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @Column() | ||||
|   name!: string; | ||||
| @ -29,17 +31,17 @@ export class LibraryTable { | ||||
|   exclusionPatterns!: string[]; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Date>; | ||||
| 
 | ||||
|   @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<string>; | ||||
| } | ||||
|  | ||||
| @ -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<Date, Date | string, Date | string>; | ||||
| type Generated<T> = | ||||
|   T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>; | ||||
| import { | ||||
|   AfterDeleteTrigger, | ||||
|   CreateDateColumn, | ||||
|   ForeignKeyColumn, | ||||
|   Generated, | ||||
|   Table, | ||||
|   Timestamp, | ||||
|   UpdateDateColumn, | ||||
| } from 'src/sql-tools'; | ||||
| 
 | ||||
| @Table('memories_assets_assets') | ||||
| @UpdatedAtTrigger('memory_assets_updated_at') | ||||
|  | ||||
| @ -1,10 +1,5 @@ | ||||
| import { ColumnType } from 'kysely'; | ||||
| import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; | ||||
| import { Column, CreateDateColumn, Table } from 'src/sql-tools'; | ||||
| 
 | ||||
| type Timestamp = ColumnType<Date, Date | string, Date | string>; | ||||
| type Generated<T> = | ||||
|   T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>; | ||||
| 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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<boolean>; | ||||
| 
 | ||||
|   /** 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<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @Column({ type: 'uuid' }) | ||||
|   entityId!: string; | ||||
|  | ||||
| @ -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<number>; | ||||
| 
 | ||||
|   @Column({ type: 'character varying', length: 50 }) | ||||
|   admin!: string; | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @DeleteDateColumn() | ||||
|   deletedAt?: Date; | ||||
|   deletedAt!: Timestamp | null; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_notifications_update_id' }) | ||||
|   updateId?: string; | ||||
|   updateId!: Generated<string>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) | ||||
|   userId!: string; | ||||
| 
 | ||||
|   @Column({ default: NotificationLevel.Info }) | ||||
|   level!: NotificationLevel; | ||||
|   level!: Generated<NotificationLevel>; | ||||
| 
 | ||||
|   @Column({ default: NotificationLevel.Info }) | ||||
|   type!: NotificationType; | ||||
|   type!: Generated<NotificationType>; | ||||
| 
 | ||||
|   @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; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @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<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -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<Timestamp>; | ||||
| 
 | ||||
|   @CreateIdColumn({ indexName: 'IDX_partners_create_id' }) | ||||
|   createId!: string; | ||||
|   createId!: Generated<string>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   inTimeline!: boolean; | ||||
|   inTimeline!: Generated<boolean>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_partners_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) | ||||
|   ownerId!: string; | ||||
| 
 | ||||
|   @Column({ default: '' }) | ||||
|   name!: string; | ||||
|   name!: Generated<string>; | ||||
| 
 | ||||
|   @Column({ default: '' }) | ||||
|   thumbnailPath!: string; | ||||
|   thumbnailPath!: Generated<string>; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isHidden!: boolean; | ||||
|   isHidden!: Generated<boolean>; | ||||
| 
 | ||||
|   @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<boolean>; | ||||
| 
 | ||||
|   @Column({ type: 'character varying', nullable: true, default: null }) | ||||
|   color?: string | null; | ||||
|   color!: string | null; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_person_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   // TODO convert to byte[]
 | ||||
|   @Column() | ||||
|   token!: string; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<string>; | ||||
| 
 | ||||
|   @Column({ default: '' }) | ||||
|   deviceOS!: string; | ||||
|   deviceOS!: Generated<string>; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_sessions_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| 
 | ||||
|   @Column({ type: 'timestamp with time zone', nullable: true }) | ||||
|   pinExpiresAt!: Date | null; | ||||
|   pinExpiresAt!: Timestamp | null; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @Column({ type: 'character varying', nullable: true }) | ||||
|   description!: string | null; | ||||
| @ -22,10 +31,10 @@ export class SharedLinkTable { | ||||
|   type!: SharedLinkType; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<boolean>; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: true }) | ||||
|   showExif!: boolean; | ||||
|   showExif!: Generated<boolean>; | ||||
| 
 | ||||
|   @Column({ type: 'character varying', nullable: true }) | ||||
|   password!: string | null; | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   //TODO: Add constraint to ensure primary asset exists in the assets array
 | ||||
|   @ForeignKeyColumn(() => AssetTable, { nullable: false, unique: true }) | ||||
|  | ||||
| @ -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<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column() | ||||
|   ack!: string; | ||||
| 
 | ||||
|   @UpdateIdColumn({ indexName: 'IDX_session_sync_checkpoints_update_id' }) | ||||
|   updateId!: string; | ||||
|   updateId!: Generated<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @ForeignKeyColumn(() => UserTable, { | ||||
|     onUpdate: 'CASCADE', | ||||
| @ -29,17 +31,17 @@ export class TagTable { | ||||
|   value!: string; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @UpdateDateColumn() | ||||
|   updatedAt!: Date; | ||||
|   updatedAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @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<string>; | ||||
| } | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @Column({ type: 'uuid' }) | ||||
|   userId!: string; | ||||
| 
 | ||||
|   @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_users_audit_deleted_at' }) | ||||
|   deletedAt!: Date; | ||||
| 
 | ||||
|   @PrimaryGeneratedUuidV7Column() | ||||
|   id!: string; | ||||
|   deletedAt!: Generated<Timestamp>; | ||||
| } | ||||
|  | ||||
| @ -7,16 +7,14 @@ import { | ||||
|   Column, | ||||
|   CreateDateColumn, | ||||
|   DeleteDateColumn, | ||||
|   Generated, | ||||
|   Index, | ||||
|   PrimaryGeneratedColumn, | ||||
|   Table, | ||||
|   Timestamp, | ||||
|   UpdateDateColumn, | ||||
| } from 'src/sql-tools'; | ||||
| 
 | ||||
| type Timestamp = ColumnType<Date, Date | string, Date | string>; | ||||
| type Generated<T> = | ||||
|   T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>; | ||||
| 
 | ||||
| @Table('users') | ||||
| @UpdatedAtTrigger('users_updated_at') | ||||
| @AfterDeleteTrigger({ | ||||
|  | ||||
| @ -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<string>; | ||||
| 
 | ||||
|   @CreateDateColumn() | ||||
|   createdAt!: Date; | ||||
|   createdAt!: Generated<Timestamp>; | ||||
| 
 | ||||
|   @Column() | ||||
|   version!: string; | ||||
|  | ||||
| @ -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<Assets>[] = []; | ||||
|     const assetImports: Insertable<AssetTable>[] = []; | ||||
|     await Promise.all( | ||||
|       job.paths.map((path) => | ||||
|         this.processEntity(path, library.ownerId, job.libraryId) | ||||
|  | ||||
| @ -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<Exif>, | ||||
|     exifInfo: Insertable<ExifTable>, | ||||
|   ): Promise<void> { | ||||
|     if (!exifInfo.livePhotoCID) { | ||||
|       return; | ||||
| @ -240,7 +242,7 @@ export class MetadataService extends BaseService { | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const exifData: Insertable<Exif> = { | ||||
|     const exifData: Insertable<ExifTable> = { | ||||
|       assetId: asset.id, | ||||
| 
 | ||||
|       // dates
 | ||||
| @ -710,10 +712,10 @@ export class MetadataService extends BaseService { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const facesToAdd: (Insertable<AssetFaces> & { assetId: string })[] = []; | ||||
|     const facesToAdd: (Insertable<AssetFaceTable> & { 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<Person> & { ownerId: string })[] = []; | ||||
|     const missing: (Insertable<PersonTable> & { ownerId: string })[] = []; | ||||
|     const missingWithFaceAsset: { id: string; ownerId: string; faceAssetId: string }[] = []; | ||||
| 
 | ||||
|     const adjustedRegionInfo = this.orientRegionInfo(tags.RegionInfo, tags.Orientation); | ||||
|  | ||||
| @ -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<AssetFaces> & { id: string })[] = []; | ||||
|     const embeddings: FaceSearch[] = []; | ||||
|     const facesToAdd: (Insertable<AssetFaceTable> & { id: string })[] = []; | ||||
|     const embeddings: FaceSearchTable[] = []; | ||||
|     const mlFaceIds = new Set<string>(); | ||||
| 
 | ||||
|     for (const face of asset.faces) { | ||||
|  | ||||
| @ -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<string, Insertable<SessionSyncCheckpoints>> = {}; | ||||
|     const checkpoints: Record<string, Insertable<SessionSyncCheckpointTable>> = {}; | ||||
|     for (const ack of dto.acks) { | ||||
|       const { type } = fromAck(ack); | ||||
|       // TODO proper ack validation via class validator
 | ||||
|  | ||||
| @ -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<TagAsset>[] = []; | ||||
|     const items: Insertable<TagAssetTable>[] = []; | ||||
|     for (const tagsId of tagIds) { | ||||
|       for (const assetsId of assetIds) { | ||||
|         items.push({ tagsId, assetsId }); | ||||
|  | ||||
| @ -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<Date, Date | string, Date | string>; | ||||
| export type Generated<T> = | ||||
|   T extends KyselyColumnType<infer S, infer I, infer U> | ||||
|     ? KyselyColumnType<S, I | undefined, U> | ||||
|     : KyselyColumnType<T, T | undefined, T>; | ||||
| export type Int8 = KyselyColumnType<number>; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
							
								
								
									
										1
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							| @ -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', | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -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<S extends BaseService = BaseService> { | ||||
|     return { partner, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newAsset(dto: Partial<Insertable<Assets>> = {}) { | ||||
|   async newAsset(dto: Partial<Insertable<AssetTable>> = {}) { | ||||
|     const asset = mediumFactory.assetInsert(dto); | ||||
|     const result = await this.get(AssetRepository).create(asset); | ||||
|     return { asset, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newMemory(dto: Partial<Insertable<Memories>> = {}) { | ||||
|   async newMemory(dto: Partial<Insertable<MemoryTable>> = {}) { | ||||
|     const memory = mediumFactory.memoryInsert(dto); | ||||
|     const result = await this.get(MemoryRepository).create(memory, new Set<string>()); | ||||
|     return { memory, result }; | ||||
| @ -142,12 +150,12 @@ export class MediumTestContext<S extends BaseService = BaseService> { | ||||
|     return { memoryAsset: dto, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newExif(dto: Insertable<Exif>) { | ||||
|   async newExif(dto: Insertable<ExifTable>) { | ||||
|     const result = await this.get(AssetRepository).upsertExif(dto); | ||||
|     return { result }; | ||||
|   } | ||||
| 
 | ||||
|   async newAlbum(dto: Insertable<Albums>) { | ||||
|   async newAlbum(dto: Insertable<AlbumTable>) { | ||||
|     const album = mediumFactory.albumInsert(dto); | ||||
|     const result = await this.get(AlbumRepository).create(album, [], []); | ||||
|     return { album, result }; | ||||
| @ -164,19 +172,19 @@ export class MediumTestContext<S extends BaseService = BaseService> { | ||||
|     return { albumUser: { albumId, userId, role }, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newJobStatus(dto: Partial<Insertable<AssetJobStatus>> & { assetId: string }) { | ||||
|   async newJobStatus(dto: Partial<Insertable<AssetJobStatusTable>> & { assetId: string }) { | ||||
|     const jobStatus = mediumFactory.assetJobStatusInsert({ assetId: dto.assetId }); | ||||
|     const result = await this.get(AssetRepository).upsertJobStatus(jobStatus); | ||||
|     return { jobStatus, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newPerson(dto: Partial<Insertable<Person>> & { ownerId: string }) { | ||||
|   async newPerson(dto: Partial<Insertable<PersonTable>> & { ownerId: string }) { | ||||
|     const person = mediumFactory.personInsert(dto); | ||||
|     const result = await this.get(PersonRepository).create(person); | ||||
|     return { person, result }; | ||||
|   } | ||||
| 
 | ||||
|   async newSession(dto: Partial<Insertable<Sessions>> & { userId: string }) { | ||||
|   async newSession(dto: Partial<Insertable<SessionTable>> & { userId: string }) { | ||||
|     const session = mediumFactory.sessionInsert(dto); | ||||
|     const result = await this.get(SessionRepository).create(session); | ||||
|     return { session, result }; | ||||
| @ -338,10 +346,10 @@ const newMockRepository = <T>(key: ClassConstructor<T>) => { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const assetInsert = (asset: Partial<Insertable<Assets>> = {}) => { | ||||
| const assetInsert = (asset: Partial<Insertable<AssetTable>> = {}) => { | ||||
|   const id = asset.id || newUuid(); | ||||
|   const now = newDate(); | ||||
|   const defaults: Insertable<Assets> = { | ||||
|   const defaults: Insertable<AssetTable> = { | ||||
|     deviceAssetId: '', | ||||
|     deviceId: '', | ||||
|     originalFileName: '', | ||||
| @ -363,9 +371,9 @@ const assetInsert = (asset: Partial<Insertable<Assets>> = {}) => { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const albumInsert = (album: Partial<Insertable<Albums>> & { ownerId: string }) => { | ||||
| const albumInsert = (album: Partial<Insertable<AlbumTable>> & { ownerId: string }) => { | ||||
|   const id = album.id || newUuid(); | ||||
|   const defaults: Omit<Insertable<Albums>, 'ownerId'> = { | ||||
|   const defaults: Omit<Insertable<AlbumTable>, 'ownerId'> = { | ||||
|     albumName: 'Album', | ||||
|   }; | ||||
| 
 | ||||
| @ -376,7 +384,7 @@ const albumInsert = (album: Partial<Insertable<Albums>> & { ownerId: string }) = | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const faceInsert = (face: Partial<Insertable<FaceSearch>> & { faceId: string }) => { | ||||
| const faceInsert = (face: Partial<Insertable<FaceSearchTable>> & { faceId: string }) => { | ||||
|   const defaults = { | ||||
|     faceId: face.faceId, | ||||
|     embedding: face.embedding || newEmbedding(), | ||||
| @ -409,10 +417,10 @@ const assetFaceInsert = (assetFace: Partial<AssetFace> & { assetId: string }) => | ||||
| }; | ||||
| 
 | ||||
| const assetJobStatusInsert = ( | ||||
|   job: Partial<Insertable<AssetJobStatus>> & { assetId: string }, | ||||
| ): Insertable<AssetJobStatus> => { | ||||
|   job: Partial<Insertable<AssetJobStatusTable>> & { assetId: string }, | ||||
| ): Insertable<AssetJobStatusTable> => { | ||||
|   const date = DateTime.now().minus({ days: 15 }).toISO(); | ||||
|   const defaults: Omit<Insertable<AssetJobStatus>, 'assetId'> = { | ||||
|   const defaults: Omit<Insertable<AssetJobStatusTable>, 'assetId'> = { | ||||
|     duplicatesDetectedAt: date, | ||||
|     facesRecognizedAt: date, | ||||
|     metadataExtractedAt: date, | ||||
| @ -426,7 +434,7 @@ const assetJobStatusInsert = ( | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const personInsert = (person: Partial<Insertable<Person>> & { ownerId: string }) => { | ||||
| const personInsert = (person: Partial<Insertable<PersonTable>> & { ownerId: string }) => { | ||||
|   const defaults = { | ||||
|     birthDate: person.birthDate || null, | ||||
|     color: person.color || null, | ||||
| @ -449,8 +457,12 @@ const personInsert = (person: Partial<Insertable<Person>> & { ownerId: string }) | ||||
| 
 | ||||
| const sha256 = (value: string) => createHash('sha256').update(value).digest('base64'); | ||||
| 
 | ||||
| const sessionInsert = ({ id = newUuid(), userId, ...session }: Partial<Insertable<Sessions>> & { userId: string }) => { | ||||
|   const defaults: Insertable<Sessions> = { | ||||
| const sessionInsert = ({ | ||||
|   id = newUuid(), | ||||
|   userId, | ||||
|   ...session | ||||
| }: Partial<Insertable<SessionTable>> & { userId: string }) => { | ||||
|   const defaults: Insertable<SessionTable> = { | ||||
|     id, | ||||
|     userId, | ||||
|     token: sha256(id), | ||||
| @ -478,11 +490,11 @@ const userInsert = (user: Partial<Insertable<UserTable>> = {}) => { | ||||
|   return { ...defaults, ...user, id }; | ||||
| }; | ||||
| 
 | ||||
| const memoryInsert = (memory: Partial<Insertable<Memories>> = {}) => { | ||||
| const memoryInsert = (memory: Partial<Insertable<MemoryTable>> = {}) => { | ||||
|   const id = memory.id || newUuid(); | ||||
|   const date = newDate(); | ||||
| 
 | ||||
|   const defaults: Insertable<Memories> = { | ||||
|   const defaults: Insertable<MemoryTable> = { | ||||
|     id, | ||||
|     createdAt: date, | ||||
|     updatedAt: date, | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
| 
 | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user