diff --git a/server/src/db.d.ts b/server/src/db.d.ts index 6242914bee..c902398233 100644 --- a/server/src/db.d.ts +++ b/server/src/db.d.ts @@ -3,21 +3,16 @@ * Please do not edit it manually. */ -import type { ColumnType } from "kysely"; +import type { ColumnType } from 'kysely'; -export type ArrayType = ArrayTypeImpl extends (infer U)[] - ? U[] - : ArrayTypeImpl; +export type ArrayType = ArrayTypeImpl extends (infer U)[] ? U[] : ArrayTypeImpl; -export type ArrayTypeImpl = T extends ColumnType - ? ColumnType - : T[]; +export type ArrayTypeImpl = T extends ColumnType ? ColumnType : T[]; -export type AssetsStatusEnum = "active" | "deleted" | "trashed"; +export type AssetsStatusEnum = 'active' | 'deleted' | 'trashed'; -export type Generated = T extends ColumnType - ? ColumnType - : ColumnType; +export type Generated = + T extends ColumnType ? ColumnType : ColumnType; export type Int8 = ColumnType; @@ -33,7 +28,7 @@ export type JsonPrimitive = boolean | number | string | null; export type JsonValue = JsonArray | JsonObject | JsonPrimitive; -export type Sourcetype = "exif" | "machine-learning"; +export type Sourcetype = 'exif' | 'machine-learning'; export type Timestamp = ColumnType; @@ -154,6 +149,12 @@ export interface AssetStack { primaryAssetId: string; } +export interface AssetUser { + assetId: string; + createdAt: Timestamp; + userId: string; +} + export interface Audit { action: string; createdAt: Generated; @@ -413,6 +414,7 @@ export interface DB { asset_files: AssetFiles; asset_job_status: AssetJobStatus; asset_stack: AssetStack; + asset_user: AssetUser; assets: Assets; audit: Audit; exif: Exif; @@ -438,6 +440,6 @@ export interface DB { tags_closure: TagsClosure; user_metadata: UserMetadata; users: Users; - "vectors.pg_vector_index_stat": VectorsPgVectorIndexStat; + 'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat; version_history: VersionHistory; } diff --git a/server/src/entities/asset-user.entity.ts b/server/src/entities/asset-user.entity.ts new file mode 100644 index 0000000000..05045130e7 --- /dev/null +++ b/server/src/entities/asset-user.entity.ts @@ -0,0 +1,22 @@ +import { AssetEntity } from 'src/entities/asset.entity'; +import { UserEntity } from 'src/entities/user.entity'; +import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm'; + +@Entity('asset_user') +@Index('IDX_assetId_userId', ['assetId', 'userId']) +export class AssetUserEntity { + @PrimaryColumn() + assetId!: string; + + @ManyToOne(() => AssetEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) + asset!: AssetEntity; + + @PrimaryColumn() + userId!: string; + + @ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false }) + user!: UserEntity; + + @Column() + createdAt!: Date; +} diff --git a/server/src/entities/index.ts b/server/src/entities/index.ts index 75e92038ac..f495a8e059 100644 --- a/server/src/entities/index.ts +++ b/server/src/entities/index.ts @@ -5,6 +5,7 @@ import { APIKeyEntity } from 'src/entities/api-key.entity'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetFileEntity } from 'src/entities/asset-files.entity'; import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; +import { AssetUserEntity } from 'src/entities/asset-user.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { AuditEntity } from 'src/entities/audit.entity'; import { ExifEntity } from 'src/entities/exif.entity'; @@ -34,6 +35,7 @@ export const entities = [ AssetEntity, AssetFaceEntity, AssetFileEntity, + AssetUserEntity, AssetJobStatusEntity, AuditEntity, ExifEntity, diff --git a/server/src/migrations/1738099775096-AddAssetUserTable.ts b/server/src/migrations/1738099775096-AddAssetUserTable.ts new file mode 100644 index 0000000000..baf59c0883 --- /dev/null +++ b/server/src/migrations/1738099775096-AddAssetUserTable.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddAssetUserTable1738099775096 implements MigrationInterface { + name = 'AddAssetUserTable1738099775096' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "asset_user" ("assetId" uuid NOT NULL, "userId" uuid NOT NULL, "createdAt" TIMESTAMP NOT NULL, CONSTRAINT "PK_f3d7f17ab93d60e007282726058" PRIMARY KEY ("assetId", "userId"))`); + await queryRunner.query(`CREATE INDEX "IDX_assetId_userId" ON "asset_user" ("assetId", "userId") `); + await queryRunner.query(`ALTER TABLE "asset_user" ADD CONSTRAINT "FK_07c8478e0936e78b553aaeceedb" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "asset_user" ADD CONSTRAINT "FK_85e2ef24493bdf649dfdfb769a2" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "asset_user" DROP CONSTRAINT "FK_85e2ef24493bdf649dfdfb769a2"`); + await queryRunner.query(`ALTER TABLE "asset_user" DROP CONSTRAINT "FK_07c8478e0936e78b553aaeceedb"`); + await queryRunner.query(`DROP INDEX "public"."IDX_assetId_userId"`); + await queryRunner.query(`DROP TABLE "asset_user"`); + } + +} diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 1f9f8f997f..b85b208619 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -81,7 +81,24 @@ export class AssetRepository implements IAssetRepository { } create(asset: Insertable): Promise { - return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirst() as any as Promise; + return this.db.transaction().execute(async (tx) => { + const newAsset = (await tx + .insertInto('assets') + .values(asset) + .returningAll() + .executeTakeFirst()) as any as AssetEntity; + + await tx + .insertInto('asset_user') + .values({ + assetId: newAsset.id, + userId: newAsset.ownerId, + createdAt: new Date(), + }) + .execute(); + + return newAsset; + }); } @GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })