mirror of
https://github.com/immich-app/immich.git
synced 2026-05-19 22:12:16 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 59384b90b0 |
@@ -3,6 +3,7 @@ import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import {
|
||||
AlbumUserRole,
|
||||
AssetFileType,
|
||||
AssetOrder,
|
||||
AssetType,
|
||||
AssetVisibility,
|
||||
ChecksumAlgorithm,
|
||||
@@ -195,6 +196,7 @@ export type SharedLink = {
|
||||
};
|
||||
|
||||
export type Album = Selectable<AlbumTable> & {
|
||||
order: AssetOrder;
|
||||
assets: ShallowDehydrateObject<Selectable<AssetTable>>[];
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { AlbumUserRole, AssetOrder } from 'src/enum';
|
||||
import { DB } from 'src/schema';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
@@ -82,6 +82,23 @@ const isAlbumOwned = (ownerId: string) => (eb: ExpressionBuilder<DB, 'album'>) =
|
||||
.where('album_user.userId', '=', ownerId),
|
||||
);
|
||||
|
||||
const withOrder = (authUserId?: string) => (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
const defaultOrder = sql<AssetOrder>`${AssetOrder.Desc}`;
|
||||
|
||||
return authUserId
|
||||
? eb.fn
|
||||
.coalesce(
|
||||
eb
|
||||
.selectFrom('album_user')
|
||||
.select('album_user.order')
|
||||
.whereRef('album_user.albumId', '=', 'album.id')
|
||||
.where('album_user.userId', '=', authUserId),
|
||||
defaultOrder,
|
||||
)
|
||||
.as('order')
|
||||
: defaultOrder.as('order');
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AlbumRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
@@ -95,6 +112,7 @@ export class AlbumRepository {
|
||||
.where('album.id', '=', id)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select(withAlbumUsers(authUserId))
|
||||
.select(withOrder(authUserId))
|
||||
.select(withSharedLink)
|
||||
.$if(options.withAssets, (eb) => eb.select(withAssets))
|
||||
.$narrowType<{ assets: NotNull }>()
|
||||
@@ -118,6 +136,7 @@ export class AlbumRepository {
|
||||
.where('album_asset.assetId', '=', assetId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select(withAlbumUsers(ownerId))
|
||||
.select(withOrder(ownerId))
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
@@ -195,6 +214,7 @@ export class AlbumRepository {
|
||||
.on('album_user.role', '=', sql.lit(AlbumUserRole.Owner)),
|
||||
)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select('album_user.order as order')
|
||||
.select(withAlbumUsers(ownerId))
|
||||
.select(withSharedLink)
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
@@ -239,6 +259,7 @@ export class AlbumRepository {
|
||||
)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select(withAlbumUsers(ownerId))
|
||||
.select(withOrder(ownerId))
|
||||
.select(withSharedLink)
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
@@ -271,6 +292,7 @@ export class AlbumRepository {
|
||||
.where(({ not, exists, selectFrom }) =>
|
||||
not(exists(selectFrom('shared_link').whereRef('shared_link.albumId', '=', 'album.id'))),
|
||||
)
|
||||
.select('album_user.order as order')
|
||||
.select(withSharedLink)
|
||||
.select(withAlbumUsers(ownerId))
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
@@ -350,13 +372,13 @@ export class AlbumRepository {
|
||||
params: [
|
||||
{ albumName: DummyValue.STRING },
|
||||
[],
|
||||
[{ userId: DummyValue.UUID, role: AlbumUserRole.Owner }, DummyValue.UUID],
|
||||
[{ userId: DummyValue.UUID, role: AlbumUserRole.Owner, order: AssetOrder.Desc }, DummyValue.UUID],
|
||||
],
|
||||
})
|
||||
async create(
|
||||
album: Insertable<AlbumTable>,
|
||||
assetIds: string[],
|
||||
albumUsers: AlbumUserCreateDto[],
|
||||
albumUsers: (AlbumUserCreateDto & { order?: AssetOrder })[],
|
||||
authUserId: string,
|
||||
) {
|
||||
if (!albumUsers.some((u) => u.role === AlbumUserRole.Owner)) {
|
||||
@@ -365,12 +387,14 @@ export class AlbumRepository {
|
||||
|
||||
const userIds = albumUsers.map((u) => u.userId);
|
||||
const roles = albumUsers.map((u) => u.role);
|
||||
const orders = albumUsers.map((u) => u.order ?? AssetOrder.Desc);
|
||||
|
||||
const result = await this.db
|
||||
.with('album', (db) => db.insertInto('album').values(album).returningAll())
|
||||
.with('album_user', (db) =>
|
||||
db
|
||||
.insertInto('album_user')
|
||||
.columns(['albumId', 'userId', 'role', 'order'])
|
||||
.expression((eb) =>
|
||||
eb
|
||||
.selectFrom('album')
|
||||
@@ -378,13 +402,15 @@ export class AlbumRepository {
|
||||
ref('album.id').as('albumId'),
|
||||
sql`unnest(${userIds}::uuid[])`.as('userId'),
|
||||
sql`unnest(${roles}::album_user_role_enum[])`.as('role'),
|
||||
sql`unnest(${orders}::varchar[])`.as('order'),
|
||||
]),
|
||||
)
|
||||
.returning(['album_user.albumId', 'album_user.userId', 'album_user.role']),
|
||||
.returning(['album_user.albumId', 'album_user.userId', 'album_user.role', 'album_user.order']),
|
||||
)
|
||||
.with('album_asset', (db) =>
|
||||
db
|
||||
.insertInto('album_asset')
|
||||
.columns(['albumId', 'assetId'])
|
||||
.expression((eb) =>
|
||||
eb
|
||||
.selectFrom('album')
|
||||
@@ -396,6 +422,7 @@ export class AlbumRepository {
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.select(withAlbumUsers(authUserId))
|
||||
.select(withOrder(authUserId))
|
||||
.select(withAssets)
|
||||
.$narrowType<{ assets: NotNull }>()
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -403,15 +430,27 @@ export class AlbumRepository {
|
||||
return result;
|
||||
}
|
||||
|
||||
update(id: string, album: Updateable<AlbumTable>, authUserId: string) {
|
||||
return this.db
|
||||
.updateTable('album')
|
||||
.set(album)
|
||||
.where('album.id', '=', id)
|
||||
.returningAll('album')
|
||||
.returning(withSharedLink)
|
||||
.returning(withAlbumUsers(authUserId))
|
||||
.executeTakeFirstOrThrow();
|
||||
update(id: string, album: Updateable<AlbumTable>, authUserId: string, order?: AssetOrder) {
|
||||
return this.db.transaction().execute(async (db) => {
|
||||
if (order !== undefined) {
|
||||
await db
|
||||
.updateTable('album_user')
|
||||
.set({ order })
|
||||
.where('albumId', '=', id)
|
||||
.where('userId', '=', authUserId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
return db
|
||||
.updateTable('album')
|
||||
.set(album)
|
||||
.where('album.id', '=', id)
|
||||
.returningAll('album')
|
||||
.returning(withSharedLink)
|
||||
.returning(withAlbumUsers(authUserId))
|
||||
.returning(withOrder(authUserId))
|
||||
.executeTakeFirstOrThrow();
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import _ from 'lodash';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { Album, columns } from 'src/database';
|
||||
import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AlbumUserRole, SharedLinkType } from 'src/enum';
|
||||
import { AlbumUserRole, AssetOrder, SharedLinkType } from 'src/enum';
|
||||
import { DB } from 'src/schema';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
@@ -55,7 +55,15 @@ const withAlbumOwner = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
const withSharedLinkAlbum = (eb: ExpressionBuilder<DB, 'shared_link'>) => {
|
||||
return eb
|
||||
.selectFrom('album')
|
||||
.leftJoin('album_user as album_order', (join) =>
|
||||
join
|
||||
.onRef('album_order.albumId', '=', 'album.id')
|
||||
.onRef('album_order.userId', '=', 'shared_link.userId'),
|
||||
)
|
||||
.selectAll('album')
|
||||
.select((eb) =>
|
||||
eb.fn.coalesce('album_order.order', sql<AssetOrder>`${AssetOrder.Desc}`).as('order'),
|
||||
)
|
||||
.whereRef('album.id', '=', 'shared_link.albumId')
|
||||
.where('album.deletedAt', 'is', null);
|
||||
};
|
||||
@@ -107,7 +115,7 @@ export class SharedLinkRepository {
|
||||
.as('assets'),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('owner').as('owner'))
|
||||
.groupBy(['album.id', sql`"owner".*`])
|
||||
.groupBy(['album.id', 'album_order.order', sql`"owner".*`])
|
||||
.as('album'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
|
||||
@@ -170,7 +170,7 @@ class AlbumSync extends BaseSync {
|
||||
const userId = options.userId;
|
||||
return this.upsertQuery('album', options)
|
||||
.distinctOn(['album.id', 'album.updateId'])
|
||||
.leftJoin('album_user as album_users', 'album.id', 'album_users.albumId')
|
||||
.innerJoin('album_user as album_users', 'album.id', 'album_users.albumId')
|
||||
.where('album_users.userId', '=', userId)
|
||||
.select([
|
||||
'album.id',
|
||||
@@ -180,7 +180,7 @@ class AlbumSync extends BaseSync {
|
||||
'album.updatedAt',
|
||||
'album.albumThumbnailAssetId as thumbnailAssetId',
|
||||
'album.isActivityEnabled',
|
||||
'album.order',
|
||||
'album_users.order as order',
|
||||
'album.updateId',
|
||||
])
|
||||
.stream();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Kysely, sql } from 'kysely';
|
||||
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await sql`ALTER TABLE "album_user" ADD "order" character varying NOT NULL DEFAULT 'desc';`.execute(db);
|
||||
await sql`UPDATE "album_user" SET "order" = "album"."order" FROM "album" WHERE "album_user"."albumId" = "album"."id";`.execute(
|
||||
db,
|
||||
);
|
||||
await sql`ALTER TABLE "album" DROP COLUMN "order";`.execute(db);
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await sql`ALTER TABLE "album" ADD "order" character varying NOT NULL DEFAULT 'desc';`.execute(db);
|
||||
await sql`
|
||||
UPDATE "album"
|
||||
SET "order" = "album_user"."order"
|
||||
FROM "album_user"
|
||||
WHERE "album_user"."albumId" = "album"."id"
|
||||
AND "album_user"."role" = 'owner';
|
||||
`.execute(db);
|
||||
await sql`ALTER TABLE "album_user" DROP COLUMN "order";`.execute(db);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from '@immich/sql-tools';
|
||||
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { AlbumUserRole, AssetOrder } from 'src/enum';
|
||||
import { album_user_role_enum } from 'src/schema/enums';
|
||||
import { album_user_after_insert, album_user_delete_audit } from 'src/schema/functions';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
@@ -58,6 +58,9 @@ export class AlbumUserTable {
|
||||
@Column({ enum: album_user_role_enum, default: AlbumUserRole.Editor })
|
||||
role!: Generated<AlbumUserRole>;
|
||||
|
||||
@Column({ default: AssetOrder.Desc })
|
||||
order!: Generated<AssetOrder>;
|
||||
|
||||
@CreateIdColumn({ index: true })
|
||||
createId!: Generated<string>;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@Table({ name: 'album' })
|
||||
@@ -45,9 +44,6 @@ export class AlbumTable {
|
||||
@Column({ type: 'boolean', default: true })
|
||||
isActivityEnabled!: Generated<boolean>;
|
||||
|
||||
@Column({ default: AssetOrder.Desc })
|
||||
order!: Generated<AssetOrder>;
|
||||
|
||||
@UpdateIdColumn({ index: true })
|
||||
updateId!: Generated<string>;
|
||||
}
|
||||
|
||||
@@ -182,19 +182,19 @@ describe(AlbumService.name, () => {
|
||||
{
|
||||
albumName: 'test',
|
||||
description: 'description',
|
||||
order: album.order,
|
||||
albumThumbnailAssetId: assetId,
|
||||
},
|
||||
[assetId],
|
||||
[
|
||||
{ userId: owner.id, role: AlbumUserRole.Owner },
|
||||
{ userId: albumUser.userId, role: AlbumUserRole.Editor },
|
||||
{ userId: owner.id, role: AlbumUserRole.Owner, order: AssetOrder.Desc },
|
||||
{ userId: albumUser.userId, role: AlbumUserRole.Editor, order: AssetOrder.Desc },
|
||||
],
|
||||
owner.id,
|
||||
);
|
||||
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(albumUser.userId, {});
|
||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(owner.id);
|
||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(albumUser.userId);
|
||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([assetId]), false);
|
||||
expect(mocks.event.emit).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||
@@ -238,16 +238,19 @@ describe(AlbumService.name, () => {
|
||||
{
|
||||
albumName: album.albumName,
|
||||
description: album.description,
|
||||
order: 'asc',
|
||||
albumThumbnailAssetId: assetId,
|
||||
},
|
||||
[assetId],
|
||||
[{ userId: owner.id, role: AlbumUserRole.Owner }, albumUser],
|
||||
[
|
||||
{ userId: owner.id, role: AlbumUserRole.Owner, order: 'asc' },
|
||||
{ ...albumUser, order: 'asc' },
|
||||
],
|
||||
owner.id,
|
||||
);
|
||||
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(albumUser.userId, {});
|
||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(owner.id);
|
||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(albumUser.userId);
|
||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([assetId]), false);
|
||||
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||
id: album.id,
|
||||
@@ -290,11 +293,10 @@ describe(AlbumService.name, () => {
|
||||
{
|
||||
albumName: album.albumName,
|
||||
description: album.description,
|
||||
order: 'desc',
|
||||
albumThumbnailAssetId: assetId,
|
||||
},
|
||||
[assetId],
|
||||
[{ userId: owner.id, role: AlbumUserRole.Owner }],
|
||||
[{ userId: owner.id, role: AlbumUserRole.Owner, order: 'desc' }],
|
||||
owner.id,
|
||||
);
|
||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(owner.id, new Set([assetId, 'asset-2']), false);
|
||||
@@ -364,10 +366,24 @@ describe(AlbumService.name, () => {
|
||||
expect(mocks.album.update).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.album.update).toHaveBeenCalledWith(
|
||||
album.id,
|
||||
{ id: album.id, albumName: 'new album name' },
|
||||
expect.objectContaining({ id: album.id, albumName: 'new album name' }),
|
||||
owner.id,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the album order for the auth user', async () => {
|
||||
const album = AlbumFactory.create({ order: AssetOrder.Desc });
|
||||
const { user: owner } = album.albumUsers.find(({ role }) => role === AlbumUserRole.Owner)!;
|
||||
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([album.id]));
|
||||
mocks.album.getById.mockResolvedValue(getForAlbum(album));
|
||||
mocks.album.update.mockResolvedValue(getForAlbum({ ...album, order: AssetOrder.Asc }));
|
||||
|
||||
await sut.update(AuthFactory.create(owner), album.id, { order: AssetOrder.Asc });
|
||||
|
||||
expect(mocks.album.update).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.album.update).toHaveBeenCalledWith(album.id, { id: album.id }, owner.id, AssetOrder.Asc);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
@@ -464,6 +480,7 @@ describe(AlbumService.name, () => {
|
||||
mocks.album.getById.mockResolvedValue(getForAlbum(album));
|
||||
mocks.album.update.mockResolvedValue(getForAlbum(album));
|
||||
mocks.user.get.mockResolvedValue(user);
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.albumUser.create.mockResolvedValue(AlbumUserFactory.from().album(album).user(user).build());
|
||||
|
||||
await sut.addUsers(AuthFactory.create(owner), album.id, { albumUsers: [{ userId: user.id }] });
|
||||
@@ -471,7 +488,9 @@ describe(AlbumService.name, () => {
|
||||
expect(mocks.albumUser.create).toHaveBeenCalledWith({
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
order: AssetOrder.Desc,
|
||||
});
|
||||
expect(mocks.user.getMetadata).toHaveBeenCalledWith(user.id);
|
||||
expect(mocks.event.emit).toHaveBeenCalledWith('AlbumInvite', {
|
||||
id: album.id,
|
||||
userId: user.id,
|
||||
|
||||
@@ -124,16 +124,22 @@ export class AlbumService extends BaseService {
|
||||
const assetIds = [...allowedAssetIdsSet].map((id) => id);
|
||||
|
||||
const userMetadata = await this.userRepository.getMetadata(auth.user.id);
|
||||
const ownerOrder = getPreferences(userMetadata).albums.defaultAssetOrder;
|
||||
const albumUsersWithOrder = await Promise.all(
|
||||
albumUsers.map(async (albumUser) => {
|
||||
const userMetadata = await this.userRepository.getMetadata(albumUser.userId);
|
||||
return { ...albumUser, order: getPreferences(userMetadata).albums.defaultAssetOrder };
|
||||
}),
|
||||
);
|
||||
|
||||
const album = await this.albumRepository.create(
|
||||
{
|
||||
albumName: dto.albumName,
|
||||
description: dto.description,
|
||||
albumThumbnailAssetId: assetIds[0] || null,
|
||||
order: getPreferences(userMetadata).albums.defaultAssetOrder,
|
||||
},
|
||||
assetIds,
|
||||
[{ userId: auth.user.id, role: AlbumUserRole.Owner }, ...albumUsers],
|
||||
[{ userId: auth.user.id, role: AlbumUserRole.Owner, order: ownerOrder }, ...albumUsersWithOrder],
|
||||
auth.user.id,
|
||||
);
|
||||
|
||||
@@ -155,6 +161,7 @@ export class AlbumService extends BaseService {
|
||||
throw new BadRequestException('Invalid album thumbnail');
|
||||
}
|
||||
}
|
||||
|
||||
const updatedAlbum = await this.albumRepository.update(
|
||||
album.id,
|
||||
{
|
||||
@@ -163,9 +170,9 @@ export class AlbumService extends BaseService {
|
||||
description: dto.description,
|
||||
albumThumbnailAssetId: dto.albumThumbnailAssetId,
|
||||
isActivityEnabled: dto.isActivityEnabled,
|
||||
order: dto.order,
|
||||
},
|
||||
auth.user.id,
|
||||
dto.order,
|
||||
);
|
||||
|
||||
return mapAlbum({ ...updatedAlbum, assets: album.assets });
|
||||
@@ -307,7 +314,10 @@ export class AlbumService extends BaseService {
|
||||
throw new BadRequestException('Invalid user');
|
||||
}
|
||||
|
||||
await this.albumUserRepository.create({ userId, albumId: id, role });
|
||||
const userMetadata = await this.userRepository.getMetadata(userId);
|
||||
const order = getPreferences(userMetadata).albums.defaultAssetOrder;
|
||||
|
||||
await this.albumUserRepository.create({ userId, albumId: id, role, order });
|
||||
await this.eventRepository.emit('AlbumInvite', { id, userId, senderName: auth.user.name });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { AlbumUserRole, AssetOrder } from 'src/enum';
|
||||
import { AlbumUserTable } from 'src/schema/tables/album-user.table';
|
||||
import { AlbumFactory } from 'test/factories/album.factory';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
@@ -24,6 +24,7 @@ export class AlbumUserFactory {
|
||||
albumId: newUuid(),
|
||||
userId: newUuid(),
|
||||
role: AlbumUserRole.Editor,
|
||||
order: AssetOrder.Desc,
|
||||
createId: newUuidV7(),
|
||||
createdAt: newDate(),
|
||||
updateId: newUuidV7(),
|
||||
|
||||
@@ -15,7 +15,7 @@ export class AlbumFactory {
|
||||
#albumUsers: AlbumUserFactory[] = [];
|
||||
#assets: AssetFactory[] = [];
|
||||
|
||||
private constructor(private readonly value: Selectable<AlbumTable>) {}
|
||||
private constructor(private readonly value: Selectable<AlbumTable> & { order: AssetOrder }) {}
|
||||
|
||||
static create(dto: AlbumLike = {}) {
|
||||
return AlbumFactory.from(dto).build();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { OAuthProfile } from 'src/repositories/oauth.repository';
|
||||
import { ActivityTable } from 'src/schema/tables/activity.table';
|
||||
import { AlbumUserTable } from 'src/schema/tables/album-user.table';
|
||||
@@ -23,7 +24,7 @@ export type AssetLike = Partial<Selectable<AssetTable>>;
|
||||
export type AssetExifLike = Partial<Selectable<AssetExifTable>>;
|
||||
export type AssetEditLike = Partial<Selectable<AssetEditTable>>;
|
||||
export type AssetFileLike = Partial<Selectable<AssetFileTable>>;
|
||||
export type AlbumLike = Partial<Selectable<AlbumTable>>;
|
||||
export type AlbumLike = Partial<Selectable<AlbumTable> & { order: AssetOrder }>;
|
||||
export type AlbumUserLike = Partial<Selectable<AlbumUserTable>>;
|
||||
export type SharedLinkLike = Partial<Selectable<SharedLinkTable>>;
|
||||
export type UserLike = Partial<Selectable<UserTable>>;
|
||||
|
||||
Reference in New Issue
Block a user