refactor: database types (#19624)

This commit is contained in:
Jason Rasmussen 2025-06-30 13:19:16 -04:00 committed by GitHub
parent 09cbc5d3f4
commit e60bc3c304
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
99 changed files with 518 additions and 889 deletions

View File

@ -29,7 +29,6 @@
"migrations:run": "node ./dist/bin/migrations.js run", "migrations:run": "node ./dist/bin/migrations.js run",
"schema:drop": "node ./dist/bin/migrations.js query 'DROP schema public cascade; CREATE schema public;'", "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", "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:open-api": "node ./dist/bin/sync-open-api.js",
"sync:sql": "node ./dist/bin/sync-sql.js", "sync:sql": "node ./dist/bin/sync-sql.js",
"email:dev": "email dev -p 3050 --dir src/emails", "email:dev": "email dev -p 3050 --dir src/emails",

View File

@ -1,5 +1,4 @@
import { Selectable } from 'kysely'; import { Selectable } from 'kysely';
import { Albums, Exif as DatabaseExif } from 'src/db';
import { MapAsset } from 'src/dtos/asset-response.dto'; import { MapAsset } from 'src/dtos/asset-response.dto';
import { import {
AlbumUserRole, AlbumUserRole,
@ -13,6 +12,8 @@ import {
UserAvatarColor, UserAvatarColor,
UserStatus, UserStatus,
} from 'src/enum'; } from 'src/enum';
import { AlbumTable } from 'src/schema/tables/album.table';
import { ExifTable } from 'src/schema/tables/exif.table';
import { UserMetadataItem } from 'src/types'; import { UserMetadataItem } from 'src/types';
export type AuthUser = { export type AuthUser = {
@ -193,7 +194,7 @@ export type SharedLink = {
userId: string; userId: string;
}; };
export type Album = Selectable<Albums> & { export type Album = Selectable<AlbumTable> & {
owner: User; owner: User;
assets: MapAsset[]; assets: MapAsset[];
}; };
@ -239,7 +240,7 @@ export type Session = {
pinExpiresAt: Date | null; pinExpiresAt: Date | null;
}; };
export type Exif = Omit<Selectable<DatabaseExif>, 'updatedAt' | 'updateId'>; export type Exif = Omit<Selectable<ExifTable>, 'updatedAt' | 'updateId'>;
export type Person = { export type Person = {
createdAt: Date; createdAt: Date;

540
server/src/db.d.ts vendored
View File

@ -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;
}

View File

@ -4,10 +4,10 @@ import { IsArray, IsInt, IsNotEmpty, IsNumber, IsString, Max, Min, ValidateNeste
import { Selectable } from 'kysely'; import { Selectable } from 'kysely';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { AssetFace, Person } from 'src/database'; import { AssetFace, Person } from 'src/database';
import { AssetFaces } from 'src/db';
import { PropertyLifecycle } from 'src/decorators'; import { PropertyLifecycle } from 'src/decorators';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { SourceType } from 'src/enum'; import { SourceType } from 'src/enum';
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
import { asDateString } from 'src/utils/date'; import { asDateString } from 'src/utils/date';
import { import {
IsDateStringFormat, 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 { return {
id: face.id, id: face.id,
imageHeight: face.imageHeight, imageHeight: face.imageHeight,

View File

@ -1,9 +1,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Kysely, sql } from 'kysely'; import { Kysely, sql } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { AlbumUserRole, AssetVisibility } from 'src/enum'; import { AlbumUserRole, AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { asUuid } from 'src/utils/database'; import { asUuid } from 'src/utils/database';
class ActivityAccess { class ActivityAccess {

View File

@ -3,9 +3,10 @@ import { Insertable, Kysely, NotNull, sql } from 'kysely';
import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { Activity, DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetVisibility } from 'src/enum'; import { AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { ActivityTable } from 'src/schema/tables/activity.table';
import { asUuid } from 'src/utils/database'; import { asUuid } from 'src/utils/database';
export interface ActivitySearch { export interface ActivitySearch {
@ -48,7 +49,7 @@ export class ActivityRepository {
} }
@GenerateSql({ params: [{ albumId: DummyValue.UUID, userId: DummyValue.UUID }] }) @GenerateSql({ params: [{ albumId: DummyValue.UUID, userId: DummyValue.UUID }] })
async create(activity: Insertable<Activity>) { async create(activity: Insertable<ActivityTable>) {
return this.db return this.db
.insertInto('activity') .insertInto('activity')
.values(activity) .values(activity)

View File

@ -1,9 +1,10 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, Updateable } from 'kysely'; import { Insertable, Kysely, Updateable } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { AlbumsSharedUsersUsers, DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AlbumUserRole } from 'src/enum'; import { AlbumUserRole } from 'src/enum';
import { DB } from 'src/schema';
import { AlbumUserTable } from 'src/schema/tables/album-user.table';
export type AlbumPermissionId = { export type AlbumPermissionId = {
albumsId: string; albumsId: string;
@ -15,7 +16,7 @@ export class AlbumUserRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} constructor(@InjectKysely() private db: Kysely<DB>) {}
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] }) @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
create(albumUser: Insertable<AlbumsSharedUsersUsers>) { create(albumUser: Insertable<AlbumUserTable>) {
return this.db return this.db
.insertInto('albums_shared_users_users') .insertInto('albums_shared_users_users')
.values(albumUser) .values(albumUser)
@ -24,7 +25,7 @@ export class AlbumUserRepository {
} }
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] }) @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 return this.db
.updateTable('albums_shared_users_users') .updateTable('albums_shared_users_users')
.set(dto) .set(dto)

View File

@ -3,9 +3,10 @@ import { ExpressionBuilder, Insertable, Kysely, NotNull, sql, Updateable } from
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns, Exif } from 'src/database'; import { columns, Exif } from 'src/database';
import { Albums, DB } from 'src/db';
import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { AlbumUserCreateDto } from 'src/dtos/album.dto'; 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'; import { withDefaultVisibility } from 'src/utils/database';
export interface AlbumAssetCount { export interface AlbumAssetCount {
@ -269,7 +270,7 @@ export class AlbumRepository {
await this.addAssets(this.db, albumId, assetIds); 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) => { return this.db.transaction().execute(async (tx) => {
const newAlbum = await tx.insertInto('albums').values(album).returning('albums.id').executeTakeFirst(); 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 return this.db
.updateTable('albums') .updateTable('albums')
.set(album) .set(album)

View File

@ -3,19 +3,20 @@ import { Insertable, Kysely, Updateable } from 'kysely';
import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { ApiKeys, DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; 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'; import { asUuid } from 'src/utils/database';
@Injectable() @Injectable()
export class ApiKeyRepository { export class ApiKeyRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} 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(); 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 return this.db
.updateTable('api_keys') .updateTable('api_keys')
.set(dto) .set(dto)

View File

@ -3,9 +3,9 @@ import { Kysely } from 'kysely';
import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { jsonArrayFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { Asset, columns } from 'src/database'; import { Asset, columns } from 'src/database';
import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetFileType, AssetType, AssetVisibility } from 'src/enum'; import { AssetFileType, AssetType, AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { StorageAsset } from 'src/types'; import { StorageAsset } from 'src/types';
import { import {
anyUuid, anyUuid,

View File

@ -3,9 +3,13 @@ import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql
import { isEmpty, isUndefined, omitBy } from 'lodash'; import { isEmpty, isUndefined, omitBy } from 'lodash';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { Stack } from 'src/database'; import { Stack } from 'src/database';
import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; 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 { import {
anyUuid, anyUuid,
asUuid, asUuid,
@ -110,7 +114,7 @@ interface GetByIdsRelations {
export class AssetRepository { export class AssetRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} 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) }; const value = { ...exif, assetId: asUuid(exif.assetId) };
await this.db await this.db
.insertInto('exif') .insertInto('exif')
@ -157,7 +161,7 @@ export class AssetRepository {
@GenerateSql({ params: [[DummyValue.UUID], { model: DummyValue.STRING }] }) @GenerateSql({ params: [[DummyValue.UUID], { model: DummyValue.STRING }] })
@Chunked() @Chunked()
async updateAllExif(ids: string[], options: Updateable<Exif>): Promise<void> { async updateAllExif(ids: string[], options: Updateable<ExifTable>): Promise<void> {
if (ids.length === 0) { if (ids.length === 0) {
return; return;
} }
@ -165,7 +169,7 @@ export class AssetRepository {
await this.db.updateTable('exif').set(options).where('assetId', 'in', ids).execute(); 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) { if (jobStatus.length === 0) {
return; return;
} }
@ -191,11 +195,11 @@ export class AssetRepository {
.execute(); .execute();
} }
create(asset: Insertable<Assets>) { create(asset: Insertable<AssetTable>) {
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow(); 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(); return this.db.insertInto('assets').values(assets).returningAll().execute();
} }
@ -375,18 +379,18 @@ export class AssetRepository {
@GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] }) @GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] })
@Chunked() @Chunked()
async updateAll(ids: string[], options: Updateable<Assets>): Promise<void> { async updateAll(ids: string[], options: Updateable<AssetTable>): Promise<void> {
if (ids.length === 0) { if (ids.length === 0) {
return; return;
} }
await this.db.updateTable('assets').set(options).where('id', '=', anyUuid(ids)).execute(); 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(); 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); const value = omitBy(asset, isUndefined);
delete value.id; delete value.id;
if (!isEmpty(value)) { if (!isEmpty(value)) {
@ -742,7 +746,7 @@ export class AssetRepository {
.execute(); .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) }; const value = { ...file, assetId: asUuid(file.assetId) };
await this.db await this.db
.insertInto('asset_files') .insertInto('asset_files')
@ -755,7 +759,7 @@ export class AssetRepository {
.execute(); .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) { if (files.length === 0) {
return; return;
} }
@ -772,7 +776,7 @@ export class AssetRepository {
.execute(); .execute();
} }
async deleteFiles(files: Pick<Selectable<AssetFiles>, 'id'>[]): Promise<void> { async deleteFiles(files: Pick<Selectable<AssetFileTable>, 'id'>[]): Promise<void> {
if (files.length === 0) { if (files.length === 0) {
return; return;
} }

View File

@ -1,9 +1,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { DatabaseAction, EntityType } from 'src/enum'; import { DatabaseAction, EntityType } from 'src/enum';
import { DB } from 'src/schema';
export interface AuditSearch { export interface AuditSearch {
action?: DatabaseAction; action?: DatabaseAction;

View File

@ -15,11 +15,11 @@ import {
VECTORCHORD_VERSION_RANGE, VECTORCHORD_VERSION_RANGE,
VECTORS_VERSION_RANGE, VECTORS_VERSION_RANGE,
} from 'src/constants'; } from 'src/constants';
import { DB } from 'src/db';
import { GenerateSql } from 'src/decorators'; import { GenerateSql } from 'src/decorators';
import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum'; import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository'; import { ConfigRepository } from 'src/repositories/config.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { DB } from 'src/schema';
import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types';
import { vectorIndexQuery } from 'src/utils/database'; import { vectorIndexQuery } from 'src/utils/database';
import { isValidInteger } from 'src/validation'; import { isValidInteger } from 'src/validation';

View File

@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { AssetVisibility } from 'src/enum'; import { AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { anyUuid } from 'src/utils/database'; import { anyUuid } from 'src/utils/database';
const builder = (db: Kysely<DB>) => const builder = (db: Kysely<DB>) =>

View File

@ -1,11 +1,11 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Kysely, NotNull, sql } from 'kysely'; import { Kysely, NotNull, sql } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { Chunked, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, DummyValue, GenerateSql } from 'src/decorators';
import { MapAsset } from 'src/dtos/asset-response.dto'; import { MapAsset } from 'src/dtos/asset-response.dto';
import { AssetType, VectorIndex } from 'src/enum'; import { AssetType, VectorIndex } from 'src/enum';
import { probes } from 'src/repositories/database.repository'; import { probes } from 'src/repositories/database.repository';
import { DB } from 'src/schema';
import { anyUuid, asUuid, withDefaultVisibility } from 'src/utils/database'; import { anyUuid, asUuid, withDefaultVisibility } from 'src/utils/database';
interface DuplicateSearch { interface DuplicateSearch {

View File

@ -1,10 +1,11 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { Insertable, Kysely, sql, Updateable } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB, Libraries } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
import { AssetType, AssetVisibility } from 'src/enum'; import { AssetType, AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { LibraryTable } from 'src/schema/tables/library.table';
export enum AssetSyncResult { export enum AssetSyncResult {
DO_NOTHING, DO_NOTHING,
@ -47,7 +48,7 @@ export class LibraryRepository {
.execute(); .execute();
} }
create(library: Insertable<Libraries>) { create(library: Insertable<LibraryTable>) {
return this.db.insertInto('libraries').values(library).returningAll().executeTakeFirstOrThrow(); 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(); 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 return this.db
.updateTable('libraries') .updateTable('libraries')
.set(library) .set(library)

View File

@ -6,12 +6,14 @@ import { createReadStream, existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises'; import { readFile } from 'node:fs/promises';
import readLine from 'node:readline'; import readLine from 'node:readline';
import { citiesFile } from 'src/constants'; import { citiesFile } from 'src/constants';
import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetVisibility, SystemMetadataKey } from 'src/enum'; import { AssetVisibility, SystemMetadataKey } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository'; import { ConfigRepository } from 'src/repositories/config.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.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 { export interface MapMarkerSearchOptions {
isArchived?: boolean; isArchived?: boolean;
@ -38,8 +40,8 @@ export interface MapMarker extends ReverseGeocodeResult {
} }
interface MapDB extends DB { interface MapDB extends DB {
geodata_places_tmp: GeodataPlaces; geodata_places_tmp: GeodataPlacesTable;
naturalearth_countries_tmp: NaturalearthCountries; naturalearth_countries_tmp: NaturalEarthCountriesTable;
} }
@Injectable() @Injectable()
@ -193,11 +195,11 @@ export class MapRepository {
return; return;
} }
const entities: Insertable<NaturalearthCountries>[] = []; const entities: Insertable<NaturalEarthCountriesTable>[] = [];
for (const feature of geoJSONData.features) { for (const feature of geoJSONData.features) {
for (const entry of feature.geometry.coordinates) { for (const entry of feature.geometry.coordinates) {
const coordinates: number[][][] = feature.geometry.type === 'MultiPolygon' ? entry[0] : entry; const coordinates: number[][][] = feature.geometry.type === 'MultiPolygon' ? entry[0] : entry;
const featureRecord: Insertable<NaturalearthCountries> = { const featureRecord: Insertable<NaturalEarthCountriesTable> = {
admin: feature.properties.ADMIN, admin: feature.properties.ADMIN,
admin_a3: feature.properties.ADM0_A3, admin_a3: feature.properties.ADM0_A3,
type: feature.properties.TYPE, type: feature.properties.TYPE,

View File

@ -3,10 +3,11 @@ import { Insertable, Kysely, sql, Updateable } from 'kysely';
import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { jsonArrayFrom } from 'kysely/helpers/postgres';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB, Memories } from 'src/db';
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { MemorySearchDto } from 'src/dtos/memory.dto'; import { MemorySearchDto } from 'src/dtos/memory.dto';
import { AssetVisibility } from 'src/enum'; import { AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { MemoryTable } from 'src/schema/tables/memory.table';
import { IBulkAsset } from 'src/types'; import { IBulkAsset } from 'src/types';
@Injectable() @Injectable()
@ -80,7 +81,7 @@ export class MemoryRepository implements IBulkAsset {
return this.getByIdBuilder(id).executeTakeFirst(); 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 this.db.transaction().execute(async (tx) => {
const { id } = await tx.insertInto('memories').values(memory).returning('id').executeTakeFirstOrThrow(); 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 }] }) @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(); await this.db.updateTable('memories').set(memory).where('id', '=', id).execute();
return this.getByIdBuilder(id).executeTakeFirstOrThrow(); return this.getByIdBuilder(id).executeTakeFirstOrThrow();
} }

View File

@ -1,15 +1,16 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { Insertable, Kysely, sql, Updateable } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB, MoveHistory } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetPathType, PathType } from 'src/enum'; import { AssetPathType, PathType } from 'src/enum';
import { DB } from 'src/schema';
import { MoveTable } from 'src/schema/tables/move.table';
@Injectable() @Injectable()
export class MoveRepository { export class MoveRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} constructor(@InjectKysely() private db: Kysely<DB>) {}
create(entity: Insertable<MoveHistory>) { create(entity: Insertable<MoveTable>) {
return this.db.insertInto('move_history').values(entity).returningAll().executeTakeFirstOrThrow(); return this.db.insertInto('move_history').values(entity).returningAll().executeTakeFirstOrThrow();
} }
@ -23,7 +24,7 @@ export class MoveRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
update(id: string, entity: Updateable<MoveHistory>) { update(id: string, entity: Updateable<MoveTable>) {
return this.db return this.db
.updateTable('move_history') .updateTable('move_history')
.set(entity) .set(entity)

View File

@ -2,9 +2,10 @@ import { Insertable, Kysely, Updateable } from 'kysely';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB, Notifications } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { NotificationSearchDto } from 'src/dtos/notification.dto'; import { NotificationSearchDto } from 'src/dtos/notification.dto';
import { DB } from 'src/schema';
import { NotificationTable } from 'src/schema/tables/notification.table';
export class NotificationRepository { export class NotificationRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} constructor(@InjectKysely() private db: Kysely<DB>) {}
@ -53,7 +54,7 @@ export class NotificationRepository {
.execute(); .execute();
} }
create(notification: Insertable<Notifications>) { create(notification: Insertable<NotificationTable>) {
return this.db return this.db
.insertInto('notifications') .insertInto('notifications')
.values(notification) .values(notification)
@ -70,7 +71,7 @@ export class NotificationRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
update(id: string, notification: Updateable<Notifications>) { update(id: string, notification: Updateable<NotificationTable>) {
return this.db return this.db
.updateTable('notifications') .updateTable('notifications')
.set(notification) .set(notification)
@ -80,7 +81,7 @@ export class NotificationRepository {
.executeTakeFirstOrThrow(); .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(); await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute();
} }

View File

@ -3,8 +3,9 @@ import { ExpressionBuilder, Insertable, Kysely, NotNull, Updateable } from 'kyse
import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB, Partners } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { DB } from 'src/schema';
import { PartnerTable } from 'src/schema/tables/partner.table';
export interface PartnerIds { export interface PartnerIds {
sharedById: string; sharedById: string;
@ -47,7 +48,7 @@ export class PartnerRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
create(values: Insertable<Partners>) { create(values: Insertable<PartnerTable>) {
return this.db return this.db
.insertInto('partners') .insertInto('partners')
.values(values) .values(values)
@ -59,7 +60,7 @@ export class PartnerRepository {
} }
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }, { inTimeline: true }] }) @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 return this.db
.updateTable('partners') .updateTable('partners')
.set(values) .set(values)

View File

@ -2,9 +2,12 @@ import { Injectable } from '@nestjs/common';
import { ExpressionBuilder, Insertable, Kysely, Selectable, sql, Updateable } from 'kysely'; import { ExpressionBuilder, Insertable, Kysely, Selectable, sql, Updateable } from 'kysely';
import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { jsonObjectFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { AssetFaces, DB, FaceSearch, Person } from 'src/db';
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
import { AssetFileType, AssetVisibility, SourceType } from 'src/enum'; 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 { removeUndefinedKeys } from 'src/utils/database';
import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; import { paginationHelper, PaginationOptions } from 'src/utils/pagination';
@ -57,7 +60,7 @@ export interface GetAllFacesOptions {
export type UnassignFacesOptions = DeleteFacesOptions; export type UnassignFacesOptions = DeleteFacesOptions;
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[]; export type SelectFaceOptions = (keyof Selectable<AssetFaceTable>)[];
const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => { const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
return jsonObjectFrom( return jsonObjectFrom(
@ -378,11 +381,11 @@ export class PersonRepository {
.executeTakeFirstOrThrow(); .executeTakeFirstOrThrow();
} }
create(person: Insertable<Person>) { create(person: Insertable<PersonTable>) {
return this.db.insertInto('person').values(person).returningAll().executeTakeFirstOrThrow(); 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) { if (people.length === 0) {
return []; return [];
} }
@ -393,9 +396,9 @@ export class PersonRepository {
@GenerateSql({ params: [[], [], [{ faceId: DummyValue.UUID, embedding: DummyValue.VECTOR }]] }) @GenerateSql({ params: [[], [], [{ faceId: DummyValue.UUID, embedding: DummyValue.VECTOR }]] })
async refreshFaces( async refreshFaces(
facesToAdd: (Insertable<AssetFaces> & { assetId: string })[], facesToAdd: (Insertable<AssetFaceTable> & { assetId: string })[],
faceIdsToRemove: string[], faceIdsToRemove: string[],
embeddingsToAdd?: Insertable<FaceSearch>[], embeddingsToAdd?: Insertable<FaceSearchTable>[],
): Promise<void> { ): Promise<void> {
let query = this.db; let query = this.db;
if (facesToAdd.length > 0) { if (facesToAdd.length > 0) {
@ -415,7 +418,7 @@ export class PersonRepository {
await query.selectFrom(sql`(select 1)`.as('dummy')).execute(); 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 return this.db
.updateTable('person') .updateTable('person')
.set(person) .set(person)
@ -424,7 +427,7 @@ export class PersonRepository {
.executeTakeFirstOrThrow(); .executeTakeFirstOrThrow();
} }
async updateAll(people: Insertable<Person>[]): Promise<void> { async updateAll(people: Insertable<PersonTable>[]): Promise<void> {
if (people.length === 0) { if (people.length === 0) {
return; return;
} }
@ -496,7 +499,7 @@ export class PersonRepository {
return result?.latestDate; 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(); await this.db.insertInto('asset_faces').values(face).execute();
} }

View File

@ -2,11 +2,12 @@ import { Injectable } from '@nestjs/common';
import { Kysely, OrderByDirection, Selectable, sql } from 'kysely'; import { Kysely, OrderByDirection, Selectable, sql } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { randomUUID } from 'node:crypto'; import { randomUUID } from 'node:crypto';
import { DB, Exif } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { MapAsset } from 'src/dtos/asset-response.dto'; import { MapAsset } from 'src/dtos/asset-response.dto';
import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum';
import { probes } from 'src/repositories/database.repository'; 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 { anyUuid, searchAssetBuilder } from 'src/utils/database';
import { paginationHelper } from 'src/utils/pagination'; import { paginationHelper } from 'src/utils/pagination';
import { isValidInteger } from 'src/validation'; import { isValidInteger } from 'src/validation';
@ -385,7 +386,7 @@ export class SearchRepository {
.select((eb) => .select((eb) =>
eb eb
.fn('to_jsonb', [eb.table('exif')]) .fn('to_jsonb', [eb.table('exif')])
.$castTo<Selectable<Exif>>() .$castTo<Selectable<ExifTable>>()
.as('exifInfo'), .as('exifInfo'),
) )
.orderBy('exif.city') .orderBy('exif.city')

View File

@ -4,8 +4,9 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB, Sessions } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; 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'; import { asUuid } from 'src/utils/database';
export type SessionSearchOptions = { updatedBefore: Date }; export type SessionSearchOptions = { updatedBefore: Date };
@ -72,11 +73,11 @@ export class SessionRepository {
.execute(); .execute();
} }
create(dto: Insertable<Sessions>) { create(dto: Insertable<SessionTable>) {
return this.db.insertInto('sessions').values(dto).returningAll().executeTakeFirstOrThrow(); return this.db.insertInto('sessions').values(dto).returningAll().executeTakeFirstOrThrow();
} }
update(id: string, dto: Updateable<Sessions>) { update(id: string, dto: Updateable<SessionTable>) {
return this.db return this.db
.updateTable('sessions') .updateTable('sessions')
.set(dto) .set(dto)

View File

@ -4,10 +4,11 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
import _ from 'lodash'; import _ from 'lodash';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { Album, columns } from 'src/database'; import { Album, columns } from 'src/database';
import { DB, SharedLinks } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { MapAsset } from 'src/dtos/asset-response.dto'; import { MapAsset } from 'src/dtos/asset-response.dto';
import { SharedLinkType } from 'src/enum'; import { SharedLinkType } from 'src/enum';
import { DB } from 'src/schema';
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
export type SharedLinkSearchOptions = { export type SharedLinkSearchOptions = {
userId: string; userId: string;
@ -183,7 +184,7 @@ export class SharedLinkRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
async create(entity: Insertable<SharedLinks> & { assetIds?: string[] }) { async create(entity: Insertable<SharedLinkTable> & { assetIds?: string[] }) {
const { id } = await this.db const { id } = await this.db
.insertInto('shared_links') .insertInto('shared_links')
.values(_.omit(entity, 'assetIds')) .values(_.omit(entity, 'assetIds'))
@ -200,7 +201,7 @@ export class SharedLinkRepository {
return this.getSharedLinks(id); 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 const { id } = await this.db
.updateTable('shared_links') .updateTable('shared_links')
.set(_.omit(entity, 'assets', 'album', 'assetIds')) .set(_.omit(entity, 'assets', 'album', 'assetIds'))

View File

@ -3,8 +3,9 @@ import { ExpressionBuilder, Kysely, Updateable } from 'kysely';
import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { jsonArrayFrom } from 'kysely/helpers/postgres';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { AssetStack, DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; 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'; import { asUuid, withDefaultVisibility } from 'src/utils/database';
export interface StackSearch { export interface StackSearch {
@ -130,7 +131,7 @@ export class StackRepository {
await this.db.deleteFrom('asset_stack').where('id', 'in', ids).execute(); 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 return this.db
.updateTable('asset_stack') .updateTable('asset_stack')
.set(entity) .set(entity)

View File

@ -1,9 +1,10 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Insertable, Kysely } from 'kysely'; import { Insertable, Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB, SessionSyncCheckpoints } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { SyncEntityType } from 'src/enum'; import { SyncEntityType } from 'src/enum';
import { DB } from 'src/schema';
import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table';
@Injectable() @Injectable()
export class SyncCheckpointRepository { export class SyncCheckpointRepository {
@ -18,7 +19,7 @@ export class SyncCheckpointRepository {
.execute(); .execute();
} }
upsertAll(items: Insertable<SessionSyncCheckpoints>[]) { upsertAll(items: Insertable<SessionSyncCheckpointTable>[]) {
return this.db return this.db
.insertInto('session_sync_checkpoints') .insertInto('session_sync_checkpoints')
.values(items) .values(items)

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { Kysely, SelectQueryBuilder, sql } from 'kysely'; import { Kysely, SelectQueryBuilder, sql } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { DB } from 'src/schema';
import { SyncAck } from 'src/types'; import { SyncAck } from 'src/types';
type AuditTables = type AuditTables =

View File

@ -2,11 +2,12 @@ import { Injectable } from '@nestjs/common';
import { Insertable, Kysely } from 'kysely'; import { Insertable, Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { readFile } from 'node:fs/promises'; import { readFile } from 'node:fs/promises';
import { DB, SystemMetadata as DbSystemMetadata } from 'src/db';
import { GenerateSql } from 'src/decorators'; import { GenerateSql } from 'src/decorators';
import { DB } from 'src/schema';
import { SystemMetadataTable } from 'src/schema/tables/system-metadata.table';
import { SystemMetadata } from 'src/types'; import { SystemMetadata } from 'src/types';
type Upsert = Insertable<DbSystemMetadata>; type Upsert = Insertable<SystemMetadataTable>;
@Injectable() @Injectable()
export class SystemMetadataRepository { export class SystemMetadataRepository {

View File

@ -2,9 +2,11 @@ import { Injectable } from '@nestjs/common';
import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { Insertable, Kysely, sql, Updateable } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB, TagAsset, Tags } from 'src/db';
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { LoggingRepository } from 'src/repositories/logging.repository'; 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() @Injectable()
export class TagRepository { export class TagRepository {
@ -72,12 +74,12 @@ export class TagRepository {
} }
@GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] }) @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(); return this.db.insertInto('tags').values(tag).returningAll().executeTakeFirstOrThrow();
} }
@GenerateSql({ params: [DummyValue.UUID, { color: DummyValue.STRING }] }) @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(); 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] }]] }) @GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagsIds: [DummyValue.UUID] }]] })
@Chunked() @Chunked()
upsertAssetIds(items: Insertable<TagAsset>[]) { upsertAssetIds(items: Insertable<TagAssetTable>[]) {
if (items.length === 0) { if (items.length === 0) {
return Promise.resolve([]); return Promise.resolve([]);
} }

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetStatus } from 'src/enum'; import { AssetStatus } from 'src/enum';
import { DB } from 'src/schema';
export class TrashRepository { export class TrashRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {} constructor(@InjectKysely() private db: Kysely<DB>) {}

View File

@ -4,14 +4,15 @@ import { jsonArrayFrom } from 'kysely/helpers/postgres';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { columns } from 'src/database'; import { columns } from 'src/database';
import { DB, UserMetadata as DbUserMetadata } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetType, AssetVisibility, UserStatus } from 'src/enum'; 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 { UserTable } from 'src/schema/tables/user.table';
import { UserMetadata, UserMetadataItem } from 'src/types'; import { UserMetadata, UserMetadataItem } from 'src/types';
import { asUuid } from 'src/utils/database'; import { asUuid } from 'src/utils/database';
type Upsert = Insertable<DbUserMetadata>; type Upsert = Insertable<UserMetadataTable>;
export interface UserListFilter { export interface UserListFilter {
id?: string; id?: string;

View File

@ -1,8 +1,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Insertable, Kysely } from 'kysely'; import { Insertable, Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB, VersionHistory } from 'src/db';
import { GenerateSql } from 'src/decorators'; import { GenerateSql } from 'src/decorators';
import { DB } from 'src/schema';
import { VersionHistoryTable } from 'src/schema/tables/version-history.table';
@Injectable() @Injectable()
export class VersionHistoryRepository { export class VersionHistoryRepository {
@ -18,7 +19,7 @@ export class VersionHistoryRepository {
return this.db.selectFrom('version_history').selectAll().orderBy('createdAt', 'desc').executeTakeFirst(); 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(); return this.db.insertInto('version_history').values(version).returningAll().executeTakeFirstOrThrow();
} }
} }

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { InjectKysely } from 'nestjs-kysely'; import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { DummyValue, GenerateSql } from 'src/decorators'; import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetVisibility } from 'src/enum'; import { AssetVisibility } from 'src/enum';
import { DB } from 'src/schema';
import { asUuid, withExif } from 'src/utils/database'; import { asUuid, withExif } from 'src/utils/database';
export class ViewRepository { export class ViewRepository {

View File

@ -21,7 +21,7 @@ import { AlbumAuditTable } from 'src/schema/tables/album-audit.table';
import { AlbumUserAuditTable } from 'src/schema/tables/album-user-audit.table'; import { AlbumUserAuditTable } from 'src/schema/tables/album-user-audit.table';
import { AlbumUserTable } from 'src/schema/tables/album-user.table'; import { AlbumUserTable } from 'src/schema/tables/album-user.table';
import { AlbumTable } from 'src/schema/tables/album.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 { AssetAuditTable } from 'src/schema/tables/asset-audit.table';
import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
import { AssetFileTable } from 'src/schema/tables/asset-files.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 { SystemMetadataTable } from 'src/schema/tables/system-metadata.table';
import { TagAssetTable } from 'src/schema/tables/tag-asset.table'; import { TagAssetTable } from 'src/schema/tables/tag-asset.table';
import { TagClosureTable } from 'src/schema/tables/tag-closure.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 { UserAuditTable } from 'src/schema/tables/user-audit.table';
import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserMetadataTable } from 'src/schema/tables/user-metadata.table';
import { UserTable } from 'src/schema/tables/user.table'; import { UserTable } from 'src/schema/tables/user.table';
import { VersionHistoryTable } from 'src/schema/tables/version-history.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']) @Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql'])
@Database({ name: 'immich' }) @Database({ name: 'immich' })
@ -68,7 +69,7 @@ export class ImmichDatabase {
AlbumUserAuditTable, AlbumUserAuditTable,
AlbumUserTable, AlbumUserTable,
AlbumTable, AlbumTable,
APIKeyTable, ApiKeyTable,
AssetAuditTable, AssetAuditTable,
AssetFaceTable, AssetFaceTable,
AssetJobStatusTable, AssetJobStatusTable,
@ -96,6 +97,7 @@ export class ImmichDatabase {
StackTable, StackTable,
SessionSyncCheckpointTable, SessionSyncCheckpointTable,
SystemMetadataTable, SystemMetadataTable,
TagTable,
TagAssetTable, TagAssetTable,
TagClosureTable, TagClosureTable,
UserAuditTable, UserAuditTable,
@ -122,3 +124,55 @@ export class ImmichDatabase {
enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum]; 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;
}

View File

@ -7,9 +7,11 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
Index, Index,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -27,13 +29,13 @@ import {
}) })
export class ActivityTable { export class ActivityTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@ForeignKeyColumn(() => AlbumTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) @ForeignKeyColumn(() => AlbumTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
albumId!: string; albumId!: string;
@ -48,8 +50,8 @@ export class ActivityTable {
comment!: string | null; comment!: string | null;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isLiked!: boolean; isLiked!: Generated<boolean>;
@UpdateIdColumn({ indexName: 'IDX_activity_update_id' }) @UpdateIdColumn({ indexName: 'IDX_activity_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -1,11 +1,11 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
import { AlbumTable } from 'src/schema/tables/album.table'; 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') @Table('album_assets_audit')
export class AlbumAssetAuditTable { export class AlbumAssetAuditTable {
@PrimaryGeneratedUuidV7Column() @PrimaryGeneratedUuidV7Column()
id!: string; id!: Generated<string>;
@ForeignKeyColumn(() => AlbumTable, { @ForeignKeyColumn(() => AlbumTable, {
type: 'uuid', type: 'uuid',
@ -19,5 +19,5 @@ export class AlbumAssetAuditTable {
assetId!: string; assetId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_assets_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_assets_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
} }

View File

@ -2,7 +2,15 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
import { album_assets_delete_audit } from 'src/schema/functions'; import { album_assets_delete_audit } from 'src/schema/functions';
import { AlbumTable } from 'src/schema/tables/album.table'; import { AlbumTable } from 'src/schema/tables/album.table';
import { AssetTable } from 'src/schema/tables/asset.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' }) @Table({ name: 'albums_assets_assets', primaryConstraintName: 'PK_c67bc36fa845fb7b18e0e398180' })
@UpdatedAtTrigger('album_assets_updated_at') @UpdatedAtTrigger('album_assets_updated_at')
@ -21,11 +29,11 @@ export class AlbumAssetTable {
assetsId!: string; assetsId!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@UpdateIdColumn({ indexName: 'IDX_album_assets_update_id' }) @UpdateIdColumn({ indexName: 'IDX_album_assets_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -1,10 +1,10 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; 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') @Table('albums_audit')
export class AlbumAuditTable { export class AlbumAuditTable {
@PrimaryGeneratedUuidV7Column() @PrimaryGeneratedUuidV7Column()
id!: string; id!: Generated<string>;
@Column({ type: 'uuid', indexName: 'IDX_albums_audit_album_id' }) @Column({ type: 'uuid', indexName: 'IDX_albums_audit_album_id' })
albumId!: string; albumId!: string;
@ -13,5 +13,5 @@ export class AlbumAuditTable {
userId!: string; userId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_albums_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_albums_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
} }

View File

@ -1,10 +1,10 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; 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') @Table('album_users_audit')
export class AlbumUserAuditTable { export class AlbumUserAuditTable {
@PrimaryGeneratedUuidV7Column() @PrimaryGeneratedUuidV7Column()
id!: string; id!: Generated<string>;
@Column({ type: 'uuid', indexName: 'IDX_album_users_audit_album_id' }) @Column({ type: 'uuid', indexName: 'IDX_album_users_audit_album_id' })
albumId!: string; albumId!: string;
@ -13,5 +13,5 @@ export class AlbumUserAuditTable {
userId!: string; userId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_users_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_users_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
} }

View File

@ -9,8 +9,10 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
Index, Index,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -50,17 +52,17 @@ export class AlbumUserTable {
usersId!: string; usersId!: string;
@Column({ type: 'character varying', default: AlbumUserRole.EDITOR }) @Column({ type: 'character varying', default: AlbumUserRole.EDITOR })
role!: AlbumUserRole; role!: Generated<AlbumUserRole>;
@CreateIdColumn({ indexName: 'IDX_album_users_create_id' }) @CreateIdColumn({ indexName: 'IDX_album_users_create_id' })
createId?: string; createId!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateIdColumn({ indexName: 'IDX_album_users_update_id' }) @UpdateIdColumn({ indexName: 'IDX_album_users_update_id' })
updateId?: string; updateId!: Generated<string>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
} }

View File

@ -9,8 +9,10 @@ import {
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -25,16 +27,16 @@ import {
}) })
export class AlbumTable { export class AlbumTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
ownerId!: string; ownerId!: string;
@Column({ default: 'Untitled Album' }) @Column({ default: 'Untitled Album' })
albumName!: string; albumName!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@ForeignKeyColumn(() => AssetTable, { @ForeignKeyColumn(() => AssetTable, {
nullable: true, nullable: true,
@ -42,23 +44,23 @@ export class AlbumTable {
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
comment: 'Asset ID to be used as thumbnail', comment: 'Asset ID to be used as thumbnail',
}) })
albumThumbnailAssetId!: string; albumThumbnailAssetId!: string | null;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column({ type: 'text', default: '' }) @Column({ type: 'text', default: '' })
description!: string; description!: Generated<string>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt!: Date | null; deletedAt!: Timestamp | null;
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
isActivityEnabled!: boolean; isActivityEnabled!: Generated<boolean>;
@Column({ default: AssetOrder.DESC }) @Column({ default: AssetOrder.DESC })
order!: AssetOrder; order!: Generated<AssetOrder>;
@UpdateIdColumn({ indexName: 'IDX_albums_update_id' }) @UpdateIdColumn({ indexName: 'IDX_albums_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -5,14 +5,19 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@Table('api_keys') @Table('api_keys')
@UpdatedAtTrigger('api_keys_updated_at') @UpdatedAtTrigger('api_keys_updated_at')
export class APIKeyTable { export class ApiKeyTable {
@PrimaryGeneratedColumn()
id!: Generated<string>;
@Column() @Column()
name!: string; name!: string;
@ -23,17 +28,14 @@ export class APIKeyTable {
userId!: string; userId!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@PrimaryGeneratedColumn()
id!: string;
@Column({ array: true, type: 'character varying' }) @Column({ array: true, type: 'character varying' })
permissions!: Permission[]; permissions!: Permission[];
@UpdateIdColumn({ indexName: 'IDX_api_keys_update_id' }) @UpdateIdColumn({ indexName: 'IDX_api_keys_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -1,10 +1,10 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; 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') @Table('assets_audit')
export class AssetAuditTable { export class AssetAuditTable {
@PrimaryGeneratedUuidV7Column() @PrimaryGeneratedUuidV7Column()
id!: string; id!: Generated<string>;
@Column({ type: 'uuid', indexName: 'IDX_assets_audit_asset_id' }) @Column({ type: 'uuid', indexName: 'IDX_assets_audit_asset_id' })
assetId!: string; assetId!: string;
@ -13,5 +13,5 @@ export class AssetAuditTable {
ownerId!: string; ownerId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_assets_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_assets_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
} }

View File

@ -2,12 +2,24 @@ import { SourceType } from 'src/enum';
import { asset_face_source_type } from 'src/schema/enums'; import { asset_face_source_type } from 'src/schema/enums';
import { AssetTable } from 'src/schema/tables/asset.table'; import { AssetTable } from 'src/schema/tables/asset.table';
import { PersonTable } from 'src/schema/tables/person.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' }) @Table({ name: 'asset_faces' })
@Index({ name: 'IDX_asset_faces_assetId_personId', columns: ['assetId', 'personId'] }) @Index({ name: 'IDX_asset_faces_assetId_personId', columns: ['assetId', 'personId'] })
@Index({ columns: ['personId', 'assetId'] }) @Index({ columns: ['personId', 'assetId'] })
export class AssetFaceTable { export class AssetFaceTable {
@PrimaryGeneratedColumn()
id!: Generated<string>;
@ForeignKeyColumn(() => AssetTable, { @ForeignKeyColumn(() => AssetTable, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
@ -26,29 +38,26 @@ export class AssetFaceTable {
personId!: string | null; personId!: string | null;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
imageWidth!: number; imageWidth!: Generated<number>;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
imageHeight!: number; imageHeight!: Generated<number>;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
boundingBoxX1!: number; boundingBoxX1!: Generated<number>;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
boundingBoxY1!: number; boundingBoxY1!: Generated<number>;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
boundingBoxX2!: number; boundingBoxX2!: Generated<number>;
@Column({ default: 0, type: 'integer' }) @Column({ default: 0, type: 'integer' })
boundingBoxY2!: number; boundingBoxY2!: Generated<number>;
@PrimaryGeneratedColumn()
id!: string;
@Column({ default: SourceType.MACHINE_LEARNING, enum: asset_face_source_type }) @Column({ default: SourceType.MACHINE_LEARNING, enum: asset_face_source_type })
sourceType!: SourceType; sourceType!: Generated<SourceType>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt!: Date | null; deletedAt!: Timestamp | null;
} }

View File

@ -5,8 +5,10 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
Unique, Unique,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -16,20 +18,20 @@ import {
@UpdatedAtTrigger('asset_files_updated_at') @UpdatedAtTrigger('asset_files_updated_at')
export class AssetFileTable { export class AssetFileTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@ForeignKeyColumn(() => AssetTable, { @ForeignKeyColumn(() => AssetTable, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
indexName: 'IDX_asset_files_assetId', indexName: 'IDX_asset_files_assetId',
}) })
assetId?: string; assetId!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column() @Column()
type!: AssetFileType; type!: AssetFileType;
@ -38,5 +40,5 @@ export class AssetFileTable {
path!: string; path!: string;
@UpdateIdColumn({ indexName: 'IDX_asset_files_update_id' }) @UpdateIdColumn({ indexName: 'IDX_asset_files_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -1,5 +1,5 @@
import { AssetTable } from 'src/schema/tables/asset.table'; 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') @Table('asset_job_status')
export class AssetJobStatusTable { export class AssetJobStatusTable {
@ -7,17 +7,17 @@ export class AssetJobStatusTable {
assetId!: string; assetId!: string;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
facesRecognizedAt!: Date | null; facesRecognizedAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
metadataExtractedAt!: Date | null; metadataExtractedAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
duplicatesDetectedAt!: Date | null; duplicatesDetectedAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
previewAt!: Date | null; previewAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
thumbnailAt!: Date | null; thumbnailAt!: Timestamp | null;
} }

View File

@ -11,9 +11,11 @@ import {
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
Index, Index,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; 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 // For all assets, each originalpath must be unique per user and library
export class AssetTable { export class AssetTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@Column() @Column()
deviceAssetId!: string; deviceAssetId!: string;
@ -78,13 +80,13 @@ export class AssetTable {
originalPath!: string; originalPath!: string;
@Column({ type: 'timestamp with time zone', indexName: 'idx_asset_file_created_at' }) @Column({ type: 'timestamp with time zone', indexName: 'idx_asset_file_created_at' })
fileCreatedAt!: Date; fileCreatedAt!: Timestamp;
@Column({ type: 'timestamp with time zone' }) @Column({ type: 'timestamp with time zone' })
fileModifiedAt!: Date; fileModifiedAt!: Timestamp;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isFavorite!: boolean; isFavorite!: Generated<boolean>;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
duration!: string | null; duration!: string | null;
@ -99,10 +101,10 @@ export class AssetTable {
livePhotoVideoId!: string | null; livePhotoVideoId!: string | null;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@Column({ index: true }) @Column({ index: true })
originalFileName!: string; originalFileName!: string;
@ -114,32 +116,32 @@ export class AssetTable {
thumbhash!: Buffer | null; thumbhash!: Buffer | null;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isOffline!: boolean; isOffline!: Generated<boolean>;
@ForeignKeyColumn(() => LibraryTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) @ForeignKeyColumn(() => LibraryTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true })
libraryId?: string | null; libraryId!: string | null;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isExternal!: boolean; isExternal!: Generated<boolean>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt!: Date | null; deletedAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone' }) @Column({ type: 'timestamp with time zone' })
localDateTime!: Date; localDateTime!: Timestamp;
@ForeignKeyColumn(() => StackTable, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' }) @ForeignKeyColumn(() => StackTable, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' })
stackId?: string | null; stackId!: string | null;
@Column({ type: 'uuid', nullable: true, indexName: 'IDX_assets_duplicateId' }) @Column({ type: 'uuid', nullable: true, indexName: 'IDX_assets_duplicateId' })
duplicateId!: string | null; duplicateId!: string | null;
@Column({ enum: assets_status_enum, default: AssetStatus.ACTIVE }) @Column({ enum: assets_status_enum, default: AssetStatus.ACTIVE })
status!: AssetStatus; status!: Generated<AssetStatus>;
@UpdateIdColumn({ indexName: 'IDX_assets_update_id' }) @UpdateIdColumn({ indexName: 'IDX_assets_update_id' })
updateId?: string; updateId!: Generated<string>;
@Column({ enum: asset_visibility_enum, default: AssetVisibility.TIMELINE }) @Column({ enum: asset_visibility_enum, default: AssetVisibility.TIMELINE })
visibility!: AssetVisibility; visibility!: Generated<AssetVisibility>;
} }

View File

@ -1,11 +1,11 @@
import { DatabaseAction, EntityType } from 'src/enum'; 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') @Table('audit')
@Index({ name: 'IDX_ownerId_createdAt', columns: ['ownerId', 'createdAt'] }) @Index({ name: 'IDX_ownerId_createdAt', columns: ['ownerId', 'createdAt'] })
export class AuditTable { export class AuditTable {
@PrimaryColumn({ type: 'serial', synchronize: false }) @PrimaryColumn({ type: 'serial', synchronize: false })
id!: number; id!: Generated<number>;
@Column() @Column()
entityType!: EntityType; entityType!: EntityType;
@ -20,5 +20,5 @@ export class AuditTable {
ownerId!: string; ownerId!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
} }

View File

@ -1,6 +1,6 @@
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
import { AssetTable } from 'src/schema/tables/asset.table'; 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') @Table('exif')
@UpdatedAtTrigger('asset_exif_updated_at') @UpdatedAtTrigger('asset_exif_updated_at')
@ -21,16 +21,16 @@ export class ExifTable {
exifImageHeight!: number | null; exifImageHeight!: number | null;
@Column({ type: 'bigint', nullable: true }) @Column({ type: 'bigint', nullable: true })
fileSizeInByte!: number | null; fileSizeInByte!: Int8 | null;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
orientation!: string | null; orientation!: string | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
dateTimeOriginal!: Date | null; dateTimeOriginal!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
modifyDate!: Date | null; modifyDate!: Timestamp | null;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
lensModel!: string | null; lensModel!: string | null;
@ -60,10 +60,10 @@ export class ExifTable {
country!: string | null; country!: string | null;
@Column({ type: 'text', default: '' }) @Column({ type: 'text', default: '' })
description!: string; // or caption description!: Generated<string>; // or caption
@Column({ type: 'double precision', nullable: true }) @Column({ type: 'double precision', nullable: true })
fps?: number | null; fps!: number | null;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
exposureTime!: string | null; exposureTime!: string | null;
@ -93,8 +93,8 @@ export class ExifTable {
rating!: number | null; rating!: number | null;
@UpdateDateColumn({ default: () => 'clock_timestamp()' }) @UpdateDateColumn({ default: () => 'clock_timestamp()' })
updatedAt?: Date; updatedAt!: Generated<Date>;
@UpdateIdColumn({ indexName: 'IDX_asset_exif_update_id' }) @UpdateIdColumn({ indexName: 'IDX_asset_exif_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -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 }) @Table({ name: 'geodata_places', synchronize: false })
@Index({ @Index({
@ -47,20 +47,20 @@ export class GeodataPlacesTable {
countryCode!: string; countryCode!: string;
@Column({ type: 'character varying', length: 20, nullable: true }) @Column({ type: 'character varying', length: 20, nullable: true })
admin1Code!: string; admin1Code!: string | null;
@Column({ type: 'character varying', length: 80, nullable: true }) @Column({ type: 'character varying', length: 80, nullable: true })
admin2Code!: string; admin2Code!: string | null;
@Column({ type: 'date' }) @Column({ type: 'date' })
modificationDate!: Date; modificationDate!: Timestamp;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
admin1Name!: string; admin1Name!: string | null;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
admin2Name!: string; admin2Name!: string | null;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
alternateNames!: string; alternateNames!: string | null;
} }

View File

@ -5,8 +5,10 @@ import {
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -14,7 +16,7 @@ import {
@UpdatedAtTrigger('libraries_updated_at') @UpdatedAtTrigger('libraries_updated_at')
export class LibraryTable { export class LibraryTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@Column() @Column()
name!: string; name!: string;
@ -29,17 +31,17 @@ export class LibraryTable {
exclusionPatterns!: string[]; exclusionPatterns!: string[];
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Date>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt?: Date; deletedAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
refreshedAt!: Date | null; refreshedAt!: Timestamp | null;
@UpdateIdColumn({ indexName: 'IDX_libraries_update_id' }) @UpdateIdColumn({ indexName: 'IDX_libraries_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -1,13 +1,16 @@
import { ColumnType } from 'kysely';
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
import { memory_assets_delete_audit } from 'src/schema/functions'; import { memory_assets_delete_audit } from 'src/schema/functions';
import { AssetTable } from 'src/schema/tables/asset.table'; import { AssetTable } from 'src/schema/tables/asset.table';
import { MemoryTable } from 'src/schema/tables/memory.table'; import { MemoryTable } from 'src/schema/tables/memory.table';
import { AfterDeleteTrigger, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; import {
AfterDeleteTrigger,
type Timestamp = ColumnType<Date, Date | string, Date | string>; CreateDateColumn,
type Generated<T> = ForeignKeyColumn,
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>; Generated,
Table,
Timestamp,
UpdateDateColumn,
} from 'src/sql-tools';
@Table('memories_assets_assets') @Table('memories_assets_assets')
@UpdatedAtTrigger('memory_assets_updated_at') @UpdatedAtTrigger('memory_assets_updated_at')

View File

@ -1,10 +1,5 @@
import { ColumnType } from 'kysely';
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
import { Column, CreateDateColumn, Table } from 'src/sql-tools'; import { Column, CreateDateColumn, Generated, Table, Timestamp } 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('memories_audit') @Table('memories_audit')
export class MemoryAuditTable { export class MemoryAuditTable {
@ -18,5 +13,5 @@ export class MemoryAuditTable {
userId!: string; userId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_memories_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_memories_audit_deleted_at' })
deletedAt!: Timestamp; deletedAt!: Generated<Timestamp>;
} }

View File

@ -8,8 +8,10 @@ import {
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -24,16 +26,16 @@ import {
}) })
export class MemoryTable { export class MemoryTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt?: Date; deletedAt!: Timestamp | null;
@ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
ownerId!: string; ownerId!: string;
@ -46,22 +48,22 @@ export class MemoryTable {
/** unless set to true, will be automatically deleted in the future */ /** unless set to true, will be automatically deleted in the future */
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isSaved!: boolean; isSaved!: Generated<boolean>;
/** memories are sorted in ascending order by this value */ /** memories are sorted in ascending order by this value */
@Column({ type: 'timestamp with time zone' }) @Column({ type: 'timestamp with time zone' })
memoryAt!: Date; memoryAt!: Timestamp;
/** when the user last viewed the memory */ /** when the user last viewed the memory */
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
seenAt?: Date; seenAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
showAt?: Date; showAt!: Timestamp | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
hideAt?: Date; hideAt!: Timestamp | null;
@UpdateIdColumn({ indexName: 'IDX_memories_update_id' }) @UpdateIdColumn({ indexName: 'IDX_memories_update_id' })
updateId?: string; updateId!: Generated<string>;
} }

View File

@ -1,5 +1,5 @@
import { PathType } from 'src/enum'; 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') @Table('move_history')
// path lock (per entity) // path lock (per entity)
@ -8,7 +8,7 @@ import { Column, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools';
@Unique({ name: 'UQ_newPath', columns: ['newPath'] }) @Unique({ name: 'UQ_newPath', columns: ['newPath'] })
export class MoveTable { export class MoveTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@Column({ type: 'uuid' }) @Column({ type: 'uuid' })
entityId!: string; entityId!: string;

View File

@ -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' }) @Table({ name: 'naturalearth_countries', primaryConstraintName: 'naturalearth_countries_pkey' })
export class NaturalEarthCountriesTable { export class NaturalEarthCountriesTable {
@PrimaryGeneratedColumn({ strategy: 'identity' }) @PrimaryGeneratedColumn({ strategy: 'identity' })
id!: number; id!: Generated<number>;
@Column({ type: 'character varying', length: 50 }) @Column({ type: 'character varying', length: 50 })
admin!: string; admin!: string;

View File

@ -6,8 +6,10 @@ import {
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -15,28 +17,28 @@ import {
@UpdatedAtTrigger('notifications_updated_at') @UpdatedAtTrigger('notifications_updated_at')
export class NotificationTable { export class NotificationTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@DeleteDateColumn() @DeleteDateColumn()
deletedAt?: Date; deletedAt!: Timestamp | null;
@UpdateIdColumn({ indexName: 'IDX_notifications_update_id' }) @UpdateIdColumn({ indexName: 'IDX_notifications_update_id' })
updateId?: string; updateId!: Generated<string>;
@ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true })
userId!: string; userId!: string;
@Column({ default: NotificationLevel.Info }) @Column({ default: NotificationLevel.Info })
level!: NotificationLevel; level!: Generated<NotificationLevel>;
@Column({ default: NotificationLevel.Info }) @Column({ default: NotificationLevel.Info })
type!: NotificationType; type!: Generated<NotificationType>;
@Column({ type: 'jsonb', nullable: true }) @Column({ type: 'jsonb', nullable: true })
data!: any | null; data!: any | null;
@ -45,8 +47,8 @@ export class NotificationTable {
title!: string; title!: string;
@Column({ type: 'text', nullable: true }) @Column({ type: 'text', nullable: true })
description!: string; description!: string | null;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
readAt?: Date | null; readAt!: Timestamp | null;
} }

View File

@ -1,10 +1,10 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; 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') @Table('partners_audit')
export class PartnerAuditTable { export class PartnerAuditTable {
@PrimaryGeneratedUuidV7Column() @PrimaryGeneratedUuidV7Column()
id!: string; id!: Generated<string>;
@Column({ type: 'uuid', indexName: 'IDX_partners_audit_shared_by_id' }) @Column({ type: 'uuid', indexName: 'IDX_partners_audit_shared_by_id' })
sharedById!: string; sharedById!: string;
@ -13,5 +13,5 @@ export class PartnerAuditTable {
sharedWithId!: string; sharedWithId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_partners_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_partners_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
} }

View File

@ -1,7 +1,16 @@
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
import { partners_delete_audit } from 'src/schema/functions'; import { partners_delete_audit } from 'src/schema/functions';
import { UserTable } from 'src/schema/tables/user.table'; 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') @Table('partners')
@UpdatedAtTrigger('partners_updated_at') @UpdatedAtTrigger('partners_updated_at')
@ -25,17 +34,17 @@ export class PartnerTable {
sharedWithId!: string; sharedWithId!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@CreateIdColumn({ indexName: 'IDX_partners_create_id' }) @CreateIdColumn({ indexName: 'IDX_partners_create_id' })
createId!: string; createId!: Generated<string>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
inTimeline!: boolean; inTimeline!: Generated<boolean>;
@UpdateIdColumn({ indexName: 'IDX_partners_update_id' }) @UpdateIdColumn({ indexName: 'IDX_partners_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -6,8 +6,10 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -16,38 +18,38 @@ import {
@Check({ name: 'CHK_b0f82b0ed662bfc24fbb58bb45', expression: `"birthDate" <= CURRENT_DATE` }) @Check({ name: 'CHK_b0f82b0ed662bfc24fbb58bb45', expression: `"birthDate" <= CURRENT_DATE` })
export class PersonTable { export class PersonTable {
@PrimaryGeneratedColumn('uuid') @PrimaryGeneratedColumn('uuid')
id!: string; id!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
ownerId!: string; ownerId!: string;
@Column({ default: '' }) @Column({ default: '' })
name!: string; name!: Generated<string>;
@Column({ default: '' }) @Column({ default: '' })
thumbnailPath!: string; thumbnailPath!: Generated<string>;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isHidden!: boolean; isHidden!: Generated<boolean>;
@Column({ type: 'date', nullable: true }) @Column({ type: 'date', nullable: true })
birthDate!: Date | string | null; birthDate!: Timestamp | null;
@ForeignKeyColumn(() => AssetFaceTable, { onDelete: 'SET NULL', nullable: true }) @ForeignKeyColumn(() => AssetFaceTable, { onDelete: 'SET NULL', nullable: true })
faceAssetId!: string | null; faceAssetId!: string | null;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isFavorite!: boolean; isFavorite!: Generated<boolean>;
@Column({ type: 'character varying', nullable: true, default: null }) @Column({ type: 'character varying', nullable: true, default: null })
color?: string | null; color!: string | null;
@UpdateIdColumn({ indexName: 'IDX_person_update_id' }) @UpdateIdColumn({ indexName: 'IDX_person_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -4,8 +4,10 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -13,20 +15,20 @@ import {
@UpdatedAtTrigger('sessions_updated_at') @UpdatedAtTrigger('sessions_updated_at')
export class SessionTable { export class SessionTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
// TODO convert to byte[] // TODO convert to byte[]
@Column() @Column()
token!: string; token!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
expiresAt!: Date | null; expiresAt!: Timestamp | null;
@ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
userId!: string; userId!: string;
@ -35,14 +37,14 @@ export class SessionTable {
parentId!: string | null; parentId!: string | null;
@Column({ default: '' }) @Column({ default: '' })
deviceType!: string; deviceType!: Generated<string>;
@Column({ default: '' }) @Column({ default: '' })
deviceOS!: string; deviceOS!: Generated<string>;
@UpdateIdColumn({ indexName: 'IDX_sessions_update_id' }) @UpdateIdColumn({ indexName: 'IDX_sessions_update_id' })
updateId!: string; updateId!: Generated<string>;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
pinExpiresAt!: Date | null; pinExpiresAt!: Timestamp | null;
} }

View File

@ -1,13 +1,22 @@
import { SharedLinkType } from 'src/enum'; import { SharedLinkType } from 'src/enum';
import { AlbumTable } from 'src/schema/tables/album.table'; import { AlbumTable } from 'src/schema/tables/album.table';
import { UserTable } from 'src/schema/tables/user.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') @Table('shared_links')
@Unique({ name: 'UQ_sharedlink_key', columns: ['key'] }) @Unique({ name: 'UQ_sharedlink_key', columns: ['key'] })
export class SharedLinkTable { export class SharedLinkTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
description!: string | null; description!: string | null;
@ -22,10 +31,10 @@ export class SharedLinkTable {
type!: SharedLinkType; type!: SharedLinkType;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@Column({ type: 'timestamp with time zone', nullable: true }) @Column({ type: 'timestamp with time zone', nullable: true })
expiresAt!: Date | null; expiresAt!: Timestamp | null;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
allowUpload!: boolean; allowUpload!: boolean;
@ -36,13 +45,13 @@ export class SharedLinkTable {
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
indexName: 'IDX_sharedlink_albumId', indexName: 'IDX_sharedlink_albumId',
}) })
albumId!: string; albumId!: string | null;
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
allowDownload!: boolean; allowDownload!: Generated<boolean>;
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
showExif!: boolean; showExif!: Generated<boolean>;
@Column({ type: 'character varying', nullable: true }) @Column({ type: 'character varying', nullable: true })
password!: string | null; password!: string | null;

View File

@ -1,11 +1,11 @@
import { AssetTable } from 'src/schema/tables/asset.table'; import { AssetTable } from 'src/schema/tables/asset.table';
import { UserTable } from 'src/schema/tables/user.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') @Table('asset_stack')
export class StackTable { export class StackTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
//TODO: Add constraint to ensure primary asset exists in the assets array //TODO: Add constraint to ensure primary asset exists in the assets array
@ForeignKeyColumn(() => AssetTable, { nullable: false, unique: true }) @ForeignKeyColumn(() => AssetTable, { nullable: false, unique: true })

View File

@ -1,7 +1,16 @@
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
import { SyncEntityType } from 'src/enum'; import { SyncEntityType } from 'src/enum';
import { SessionTable } from 'src/schema/tables/session.table'; 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') @Table('session_sync_checkpoints')
@UpdatedAtTrigger('session_sync_checkpoints_updated_at') @UpdatedAtTrigger('session_sync_checkpoints_updated_at')
@ -13,14 +22,14 @@ export class SessionSyncCheckpointTable {
type!: SyncEntityType; type!: SyncEntityType;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column() @Column()
ack!: string; ack!: string;
@UpdateIdColumn({ indexName: 'IDX_session_sync_checkpoints_update_id' }) @UpdateIdColumn({ indexName: 'IDX_session_sync_checkpoints_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -4,8 +4,10 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
ForeignKeyColumn, ForeignKeyColumn,
Generated,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
Unique, Unique,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } from 'src/sql-tools';
@ -15,7 +17,7 @@ import {
@Unique({ columns: ['userId', 'value'] }) @Unique({ columns: ['userId', 'value'] })
export class TagTable { export class TagTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@ForeignKeyColumn(() => UserTable, { @ForeignKeyColumn(() => UserTable, {
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
@ -29,17 +31,17 @@ export class TagTable {
value!: string; value!: string;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@UpdateDateColumn() @UpdateDateColumn()
updatedAt!: Date; updatedAt!: Generated<Timestamp>;
@Column({ type: 'character varying', nullable: true, default: null }) @Column({ type: 'character varying', nullable: true, default: null })
color!: string | null; color!: string | null;
@ForeignKeyColumn(() => TagTable, { nullable: true, onDelete: 'CASCADE' }) @ForeignKeyColumn(() => TagTable, { nullable: true, onDelete: 'CASCADE' })
parentId?: string; parentId!: string | null;
@UpdateIdColumn({ indexName: 'IDX_tags_update_id' }) @UpdateIdColumn({ indexName: 'IDX_tags_update_id' })
updateId!: string; updateId!: Generated<string>;
} }

View File

@ -1,14 +1,14 @@
import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; 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') @Table('users_audit')
export class UserAuditTable { export class UserAuditTable {
@PrimaryGeneratedUuidV7Column()
id!: Generated<string>;
@Column({ type: 'uuid' }) @Column({ type: 'uuid' })
userId!: string; userId!: string;
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_users_audit_deleted_at' }) @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_users_audit_deleted_at' })
deletedAt!: Date; deletedAt!: Generated<Timestamp>;
@PrimaryGeneratedUuidV7Column()
id!: string;
} }

View File

@ -7,16 +7,14 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
DeleteDateColumn, DeleteDateColumn,
Generated,
Index, Index,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
Table, Table,
Timestamp,
UpdateDateColumn, UpdateDateColumn,
} from 'src/sql-tools'; } 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') @Table('users')
@UpdatedAtTrigger('users_updated_at') @UpdatedAtTrigger('users_updated_at')
@AfterDeleteTrigger({ @AfterDeleteTrigger({

View File

@ -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') @Table('version_history')
export class VersionHistoryTable { export class VersionHistoryTable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: string; id!: Generated<string>;
@CreateDateColumn() @CreateDateColumn()
createdAt!: Date; createdAt!: Generated<Timestamp>;
@Column() @Column()
version!: string; version!: string;

View File

@ -6,7 +6,6 @@ import path, { basename, isAbsolute, parse } from 'node:path';
import picomatch from 'picomatch'; import picomatch from 'picomatch';
import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants';
import { StorageCore } from 'src/cores/storage.core'; import { StorageCore } from 'src/cores/storage.core';
import { Assets } from 'src/db';
import { OnEvent, OnJob } from 'src/decorators'; import { OnEvent, OnJob } from 'src/decorators';
import { import {
CreateLibraryDto, CreateLibraryDto,
@ -21,6 +20,7 @@ import {
import { AssetStatus, AssetType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; import { AssetStatus, AssetType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum';
import { ArgOf } from 'src/repositories/event.repository'; import { ArgOf } from 'src/repositories/event.repository';
import { AssetSyncResult } from 'src/repositories/library.repository'; import { AssetSyncResult } from 'src/repositories/library.repository';
import { AssetTable } from 'src/schema/tables/asset.table';
import { BaseService } from 'src/services/base.service'; import { BaseService } from 'src/services/base.service';
import { JobOf } from 'src/types'; import { JobOf } from 'src/types';
import { mimeTypes } from 'src/utils/mime-types'; import { mimeTypes } from 'src/utils/mime-types';
@ -237,7 +237,7 @@ export class LibraryService extends BaseService {
return JobStatus.FAILED; return JobStatus.FAILED;
} }
const assetImports: Insertable<Assets>[] = []; const assetImports: Insertable<AssetTable>[] = [];
await Promise.all( await Promise.all(
job.paths.map((path) => job.paths.map((path) =>
this.processEntity(path, library.ownerId, job.libraryId) this.processEntity(path, library.ownerId, job.libraryId)

View File

@ -9,7 +9,6 @@ import path from 'node:path';
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
import { StorageCore } from 'src/cores/storage.core'; import { StorageCore } from 'src/cores/storage.core';
import { Asset, AssetFace } from 'src/database'; import { Asset, AssetFace } from 'src/database';
import { AssetFaces, Exif, Person } from 'src/db';
import { OnEvent, OnJob } from 'src/decorators'; import { OnEvent, OnJob } from 'src/decorators';
import { import {
AssetType, AssetType,
@ -25,6 +24,9 @@ import {
import { ArgOf } from 'src/repositories/event.repository'; import { ArgOf } from 'src/repositories/event.repository';
import { ReverseGeocodeResult } from 'src/repositories/map.repository'; import { ReverseGeocodeResult } from 'src/repositories/map.repository';
import { ImmichTags } from 'src/repositories/metadata.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 { BaseService } from 'src/services/base.service';
import { JobItem, JobOf } from 'src/types'; import { JobItem, JobOf } from 'src/types';
import { isFaceImportEnabled } from 'src/utils/misc'; import { isFaceImportEnabled } from 'src/utils/misc';
@ -162,7 +164,7 @@ export class MetadataService extends BaseService {
private async linkLivePhotos( private async linkLivePhotos(
asset: { id: string; type: AssetType; ownerId: string; libraryId: string | null }, asset: { id: string; type: AssetType; ownerId: string; libraryId: string | null },
exifInfo: Insertable<Exif>, exifInfo: Insertable<ExifTable>,
): Promise<void> { ): Promise<void> {
if (!exifInfo.livePhotoCID) { if (!exifInfo.livePhotoCID) {
return; return;
@ -240,7 +242,7 @@ export class MetadataService extends BaseService {
} }
} }
const exifData: Insertable<Exif> = { const exifData: Insertable<ExifTable> = {
assetId: asset.id, assetId: asset.id,
// dates // dates
@ -710,10 +712,10 @@ export class MetadataService extends BaseService {
return; return;
} }
const facesToAdd: (Insertable<AssetFaces> & { assetId: string })[] = []; const facesToAdd: (Insertable<AssetFaceTable> & { assetId: string })[] = [];
const existingNames = await this.personRepository.getDistinctNames(asset.ownerId, { withHidden: true }); const existingNames = await this.personRepository.getDistinctNames(asset.ownerId, { withHidden: true });
const existingNameMap = new Map(existingNames.map(({ id, name }) => [name.toLowerCase(), id])); 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 missingWithFaceAsset: { id: string; ownerId: string; faceAssetId: string }[] = [];
const adjustedRegionInfo = this.orientRegionInfo(tags.RegionInfo, tags.Orientation); const adjustedRegionInfo = this.orientRegionInfo(tags.RegionInfo, tags.Orientation);

View File

@ -2,7 +2,6 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/comm
import { Insertable, Updateable } from 'kysely'; import { Insertable, Updateable } from 'kysely';
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
import { Person } from 'src/database'; import { Person } from 'src/database';
import { AssetFaces, FaceSearch } from 'src/db';
import { Chunked, OnJob } from 'src/decorators'; import { Chunked, OnJob } from 'src/decorators';
import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
@ -37,6 +36,8 @@ import {
} from 'src/enum'; } from 'src/enum';
import { BoundingBox } from 'src/repositories/machine-learning.repository'; import { BoundingBox } from 'src/repositories/machine-learning.repository';
import { UpdateFacesData } from 'src/repositories/person.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 { BaseService } from 'src/services/base.service';
import { JobItem, JobOf } from 'src/types'; import { JobItem, JobOf } from 'src/types';
import { ImmichFileResponse } from 'src/utils/file'; 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}`); this.logger.debug(`${faces.length} faces detected in ${previewFile.path}`);
const facesToAdd: (Insertable<AssetFaces> & { id: string })[] = []; const facesToAdd: (Insertable<AssetFaceTable> & { id: string })[] = [];
const embeddings: FaceSearch[] = []; const embeddings: FaceSearchTable[] = [];
const mlFaceIds = new Set<string>(); const mlFaceIds = new Set<string>();
for (const face of asset.faces) { for (const face of asset.faces) {

View File

@ -3,7 +3,6 @@ import { Insertable } from 'kysely';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { Writable } from 'node:stream'; import { Writable } from 'node:stream';
import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
import { SessionSyncCheckpoints } from 'src/db';
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { import {
@ -17,6 +16,7 @@ import {
SyncStreamDto, SyncStreamDto,
} from 'src/dtos/sync.dto'; } from 'src/dtos/sync.dto';
import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; 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 { BaseService } from 'src/services/base.service';
import { SyncAck } from 'src/types'; import { SyncAck } from 'src/types';
import { getMyPartnerIds } from 'src/utils/asset.util'; import { getMyPartnerIds } from 'src/utils/asset.util';
@ -90,7 +90,7 @@ export class SyncService extends BaseService {
return throwSessionRequired(); return throwSessionRequired();
} }
const checkpoints: Record<string, Insertable<SessionSyncCheckpoints>> = {}; const checkpoints: Record<string, Insertable<SessionSyncCheckpointTable>> = {};
for (const ack of dto.acks) { for (const ack of dto.acks) {
const { type } = fromAck(ack); const { type } = fromAck(ack);
// TODO proper ack validation via class validator // TODO proper ack validation via class validator

View File

@ -1,6 +1,5 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { Insertable } from 'kysely'; import { Insertable } from 'kysely';
import { TagAsset } from 'src/db';
import { OnJob } from 'src/decorators'; import { OnJob } from 'src/decorators';
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
@ -14,6 +13,7 @@ import {
mapTag, mapTag,
} from 'src/dtos/tag.dto'; } from 'src/dtos/tag.dto';
import { JobName, JobStatus, Permission, QueueName } from 'src/enum'; 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 { BaseService } from 'src/services/base.service';
import { addAssets, removeAssets } from 'src/utils/asset.util'; import { addAssets, removeAssets } from 'src/utils/asset.util';
import { upsertTags } from 'src/utils/tag'; 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 }), 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 tagsId of tagIds) {
for (const assetsId of assetIds) { for (const assetsId of assetIds) {
items.push({ tagsId, assetsId }); items.push({ tagsId, assetsId });

View File

@ -1,4 +1,4 @@
import { Kysely } from 'kysely'; import { Kysely, ColumnType as KyselyColumnType } from 'kysely';
export type PostgresDB = { export type PostgresDB = {
pg_am: { pg_am: {
@ -476,3 +476,10 @@ export enum Reason {
MissingInSource = 'missing in source', MissingInSource = 'missing in source',
MissingInTarget = 'missing in target', 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>;

View File

@ -16,9 +16,9 @@ import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import { parse } from 'pg-connection-string'; import { parse } from 'pg-connection-string';
import postgres, { Notice } from 'postgres'; import postgres, { Notice } from 'postgres';
import { columns, Exif, Person } from 'src/database'; import { columns, Exif, Person } from 'src/database';
import { DB } from 'src/db';
import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum'; import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum';
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
import { DB } from 'src/schema';
import { DatabaseConnectionParams, VectorExtension } from 'src/types'; import { DatabaseConnectionParams, VectorExtension } from 'src/types';
type Ssl = 'require' | 'allow' | 'prefer' | 'verify-full' | boolean | object; type Ssl = 'require' | 'allow' | 'prefer' | 'verify-full' | boolean | object;

View File

@ -35,6 +35,7 @@ export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif
primaryAssetId: assets[0].id, primaryAssetId: assets[0].id,
createdAt: new Date('2023-02-23T05:06:29.716Z'), createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'), updatedAt: new Date('2023-02-23T05:06:29.716Z'),
updateId: 'uuid-v7',
}; };
}; };

View File

@ -4,7 +4,6 @@ import { DateTime } from 'luxon';
import { createHash, randomBytes } from 'node:crypto'; import { createHash, randomBytes } from 'node:crypto';
import { Writable } from 'node:stream'; import { Writable } from 'node:stream';
import { AssetFace } from 'src/database'; 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 { AuthDto, LoginResponseDto } from 'src/dtos/auth.dto';
import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository'; 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 { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { VersionHistoryRepository } from 'src/repositories/version-history.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 { UserTable } from 'src/schema/tables/user.table';
import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service'; import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service';
import { SyncService } from 'src/services/sync.service'; import { SyncService } from 'src/services/sync.service';
@ -125,13 +133,13 @@ export class MediumTestContext<S extends BaseService = BaseService> {
return { partner, result }; return { partner, result };
} }
async newAsset(dto: Partial<Insertable<Assets>> = {}) { async newAsset(dto: Partial<Insertable<AssetTable>> = {}) {
const asset = mediumFactory.assetInsert(dto); const asset = mediumFactory.assetInsert(dto);
const result = await this.get(AssetRepository).create(asset); const result = await this.get(AssetRepository).create(asset);
return { asset, result }; return { asset, result };
} }
async newMemory(dto: Partial<Insertable<Memories>> = {}) { async newMemory(dto: Partial<Insertable<MemoryTable>> = {}) {
const memory = mediumFactory.memoryInsert(dto); const memory = mediumFactory.memoryInsert(dto);
const result = await this.get(MemoryRepository).create(memory, new Set<string>()); const result = await this.get(MemoryRepository).create(memory, new Set<string>());
return { memory, result }; return { memory, result };
@ -142,12 +150,12 @@ export class MediumTestContext<S extends BaseService = BaseService> {
return { memoryAsset: dto, result }; return { memoryAsset: dto, result };
} }
async newExif(dto: Insertable<Exif>) { async newExif(dto: Insertable<ExifTable>) {
const result = await this.get(AssetRepository).upsertExif(dto); const result = await this.get(AssetRepository).upsertExif(dto);
return { result }; return { result };
} }
async newAlbum(dto: Insertable<Albums>) { async newAlbum(dto: Insertable<AlbumTable>) {
const album = mediumFactory.albumInsert(dto); const album = mediumFactory.albumInsert(dto);
const result = await this.get(AlbumRepository).create(album, [], []); const result = await this.get(AlbumRepository).create(album, [], []);
return { album, result }; return { album, result };
@ -164,19 +172,19 @@ export class MediumTestContext<S extends BaseService = BaseService> {
return { albumUser: { albumId, userId, role }, result }; 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 jobStatus = mediumFactory.assetJobStatusInsert({ assetId: dto.assetId });
const result = await this.get(AssetRepository).upsertJobStatus(jobStatus); const result = await this.get(AssetRepository).upsertJobStatus(jobStatus);
return { jobStatus, result }; 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 person = mediumFactory.personInsert(dto);
const result = await this.get(PersonRepository).create(person); const result = await this.get(PersonRepository).create(person);
return { person, result }; 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 session = mediumFactory.sessionInsert(dto);
const result = await this.get(SessionRepository).create(session); const result = await this.get(SessionRepository).create(session);
return { session, result }; 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 id = asset.id || newUuid();
const now = newDate(); const now = newDate();
const defaults: Insertable<Assets> = { const defaults: Insertable<AssetTable> = {
deviceAssetId: '', deviceAssetId: '',
deviceId: '', deviceId: '',
originalFileName: '', 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 id = album.id || newUuid();
const defaults: Omit<Insertable<Albums>, 'ownerId'> = { const defaults: Omit<Insertable<AlbumTable>, 'ownerId'> = {
albumName: 'Album', 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 = { const defaults = {
faceId: face.faceId, faceId: face.faceId,
embedding: face.embedding || newEmbedding(), embedding: face.embedding || newEmbedding(),
@ -409,10 +417,10 @@ const assetFaceInsert = (assetFace: Partial<AssetFace> & { assetId: string }) =>
}; };
const assetJobStatusInsert = ( const assetJobStatusInsert = (
job: Partial<Insertable<AssetJobStatus>> & { assetId: string }, job: Partial<Insertable<AssetJobStatusTable>> & { assetId: string },
): Insertable<AssetJobStatus> => { ): Insertable<AssetJobStatusTable> => {
const date = DateTime.now().minus({ days: 15 }).toISO(); const date = DateTime.now().minus({ days: 15 }).toISO();
const defaults: Omit<Insertable<AssetJobStatus>, 'assetId'> = { const defaults: Omit<Insertable<AssetJobStatusTable>, 'assetId'> = {
duplicatesDetectedAt: date, duplicatesDetectedAt: date,
facesRecognizedAt: date, facesRecognizedAt: date,
metadataExtractedAt: 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 = { const defaults = {
birthDate: person.birthDate || null, birthDate: person.birthDate || null,
color: person.color || 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 sha256 = (value: string) => createHash('sha256').update(value).digest('base64');
const sessionInsert = ({ id = newUuid(), userId, ...session }: Partial<Insertable<Sessions>> & { userId: string }) => { const sessionInsert = ({
const defaults: Insertable<Sessions> = { id = newUuid(),
userId,
...session
}: Partial<Insertable<SessionTable>> & { userId: string }) => {
const defaults: Insertable<SessionTable> = {
id, id,
userId, userId,
token: sha256(id), token: sha256(id),
@ -478,11 +490,11 @@ const userInsert = (user: Partial<Insertable<UserTable>> = {}) => {
return { ...defaults, ...user, id }; return { ...defaults, ...user, id };
}; };
const memoryInsert = (memory: Partial<Insertable<Memories>> = {}) => { const memoryInsert = (memory: Partial<Insertable<MemoryTable>> = {}) => {
const id = memory.id || newUuid(); const id = memory.id || newUuid();
const date = newDate(); const date = newDate();
const defaults: Insertable<Memories> = { const defaults: Insertable<MemoryTable> = {
id, id,
createdAt: date, createdAt: date,
updatedAt: date, updatedAt: date,

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { ConfigRepository } from 'src/repositories/config.repository'; import { ConfigRepository } from 'src/repositories/config.repository';
import { DatabaseRepository } from 'src/repositories/database.repository'; import { DatabaseRepository } from 'src/repositories/database.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { DB } from 'src/schema';
import { getKyselyConfig } from 'src/utils/database'; import { getKyselyConfig } from 'src/utils/database';
import { GenericContainer, Wait } from 'testcontainers'; import { GenericContainer, Wait } from 'testcontainers';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AssetRepository } from 'src/repositories/asset.repository'; import { AssetRepository } from 'src/repositories/asset.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { DB } from 'src/schema';
import { AssetService } from 'src/services/asset.service'; import { AssetService } from 'src/services/asset.service';
import { newMediumService } from 'test/medium.factory'; import { newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';

View File

@ -1,7 +1,6 @@
import { BadRequestException } from '@nestjs/common'; import { BadRequestException } from '@nestjs/common';
import { hash } from 'bcrypt'; import { hash } from 'bcrypt';
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AuthType } from 'src/enum'; import { AuthType } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository'; import { AccessRepository } from 'src/repositories/access.repository';
import { ConfigRepository } from 'src/repositories/config.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 { StorageRepository } from 'src/repositories/storage.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { AuthService } from 'src/services/auth.service'; import { AuthService } from 'src/services/auth.service';
import { mediumFactory, newMediumService } from 'test/medium.factory'; import { mediumFactory, newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';

View File

@ -1,6 +1,5 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { DB } from 'src/db';
import { AssetFileType, MemoryType } from 'src/enum'; import { AssetFileType, MemoryType } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository'; import { AccessRepository } from 'src/repositories/access.repository';
import { AssetRepository } from 'src/repositories/asset.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 { PartnerRepository } from 'src/repositories/partner.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { MemoryService } from 'src/services/memory.service'; import { MemoryService } from 'src/services/memory.service';
import { newMediumService } from 'test/medium.factory'; import { newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';

View File

@ -1,10 +1,10 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AccessRepository } from 'src/repositories/access.repository'; import { AccessRepository } from 'src/repositories/access.repository';
import { DatabaseRepository } from 'src/repositories/database.repository'; import { DatabaseRepository } from 'src/repositories/database.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { PersonRepository } from 'src/repositories/person.repository'; import { PersonRepository } from 'src/repositories/person.repository';
import { StorageRepository } from 'src/repositories/storage.repository'; import { StorageRepository } from 'src/repositories/storage.repository';
import { DB } from 'src/schema';
import { PersonService } from 'src/services/person.service'; import { PersonService } from 'src/services/person.service';
import { newMediumService } from 'test/medium.factory'; import { newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';

View File

@ -1,6 +1,5 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { DB } from 'src/db';
import { ImmichEnvironment, JobName, JobStatus } from 'src/enum'; import { ImmichEnvironment, JobName, JobStatus } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository'; import { ConfigRepository } from 'src/repositories/config.repository';
import { CryptoRepository } from 'src/repositories/crypto.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 { LoggingRepository } from 'src/repositories/logging.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { UserService } from 'src/services/user.service'; import { UserService } from 'src/services/user.service';
import { mediumFactory, newMediumService } from 'test/medium.factory'; import { mediumFactory, newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';

View File

@ -1,11 +1,11 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { serverVersion } from 'src/constants'; import { serverVersion } from 'src/constants';
import { DB } from 'src/db';
import { JobName } from 'src/enum'; import { JobName } from 'src/enum';
import { DatabaseRepository } from 'src/repositories/database.repository'; import { DatabaseRepository } from 'src/repositories/database.repository';
import { JobRepository } from 'src/repositories/job.repository'; import { JobRepository } from 'src/repositories/job.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
import { DB } from 'src/schema';
import { VersionService } from 'src/services/version.service'; import { VersionService } from 'src/services/version.service';
import { newMediumService } from 'test/medium.factory'; import { newMediumService } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,6 +1,6 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,6 +1,6 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum';
import { AlbumRepository } from 'src/repositories/album.repository'; import { AlbumRepository } from 'src/repositories/album.repository';
import { AssetRepository } from 'src/repositories/asset.repository'; import { AssetRepository } from 'src/repositories/asset.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum';
import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum';
import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository';
import { AlbumRepository } from 'src/repositories/album.repository'; import { AlbumRepository } from 'src/repositories/album.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,6 +1,6 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { AssetRepository } from 'src/repositories/asset.repository'; import { AssetRepository } from 'src/repositories/asset.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { MemoryRepository } from 'src/repositories/memory.repository'; import { MemoryRepository } from 'src/repositories/memory.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { MemoryRepository } from 'src/repositories/memory.repository'; import { MemoryRepository } from 'src/repositories/memory.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,6 +1,6 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,9 +1,9 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { AssetRepository } from 'src/repositories/asset.repository'; import { AssetRepository } from 'src/repositories/asset.repository';
import { PartnerRepository } from 'src/repositories/partner.repository'; import { PartnerRepository } from 'src/repositories/partner.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { factory } from 'test/small.factory'; import { factory } from 'test/small.factory';
import { getKyselyDB, wait } from 'test/utils'; import { getKyselyDB, wait } from 'test/utils';

View File

@ -1,8 +1,8 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { PartnerRepository } from 'src/repositories/partner.repository'; import { PartnerRepository } from 'src/repositories/partner.repository';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -1,7 +1,7 @@
import { Kysely } from 'kysely'; import { Kysely } from 'kysely';
import { DB } from 'src/db';
import { SyncEntityType, SyncRequestType } from 'src/enum'; import { SyncEntityType, SyncRequestType } from 'src/enum';
import { UserRepository } from 'src/repositories/user.repository'; import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory'; import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils'; import { getKyselyDB } from 'test/utils';

View File

@ -7,7 +7,6 @@ import { ChildProcessWithoutNullStreams } from 'node:child_process';
import { Writable } from 'node:stream'; import { Writable } from 'node:stream';
import { PNG } from 'pngjs'; import { PNG } from 'pngjs';
import postgres from 'postgres'; import postgres from 'postgres';
import { DB } from 'src/db';
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
import { AuthGuard } from 'src/middleware/auth.guard'; import { AuthGuard } from 'src/middleware/auth.guard';
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; 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 { UserRepository } from 'src/repositories/user.repository';
import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
import { ViewRepository } from 'src/repositories/view-repository'; import { ViewRepository } from 'src/repositories/view-repository';
import { DB } from 'src/schema';
import { AuthService } from 'src/services/auth.service'; import { AuthService } from 'src/services/auth.service';
import { BaseService } from 'src/services/base.service'; import { BaseService } from 'src/services/base.service';
import { RepositoryInterface } from 'src/types'; import { RepositoryInterface } from 'src/types';