|
|
|
|
@ -1,9 +1,11 @@
|
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
|
import { Insertable, Kysely, Selectable, UpdateResult, Updateable, sql } from 'kysely';
|
|
|
|
|
import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely';
|
|
|
|
|
import { isEmpty, isUndefined, omitBy } from 'lodash';
|
|
|
|
|
import { InjectKysely } from 'nestjs-kysely';
|
|
|
|
|
import { Stack } from 'src/database';
|
|
|
|
|
import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
|
|
|
|
|
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
|
|
|
|
import { MapAsset } from 'src/dtos/asset-response.dto';
|
|
|
|
|
import {
|
|
|
|
|
AssetEntity,
|
|
|
|
|
hasPeople,
|
|
|
|
|
@ -23,7 +25,7 @@ import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
|
|
|
|
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository';
|
|
|
|
|
import { anyUuid, asUuid, removeUndefinedKeys, unnest } from 'src/utils/database';
|
|
|
|
|
import { globToSqlPattern } from 'src/utils/misc';
|
|
|
|
|
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
|
|
|
|
import { PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
|
|
|
|
|
|
|
|
|
export type AssetStats = Record<AssetType, number>;
|
|
|
|
|
|
|
|
|
|
@ -141,12 +143,12 @@ export interface GetByIdsRelations {
|
|
|
|
|
|
|
|
|
|
export interface DuplicateGroup {
|
|
|
|
|
duplicateId: string;
|
|
|
|
|
assets: AssetEntity[];
|
|
|
|
|
assets: MapAsset[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface DayOfYearAssets {
|
|
|
|
|
yearsAgo: number;
|
|
|
|
|
assets: AssetEntity[];
|
|
|
|
|
assets: MapAsset[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
@ -234,12 +236,12 @@ export class AssetRepository {
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create(asset: Insertable<Assets>): Promise<AssetEntity> {
|
|
|
|
|
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirst() as any as Promise<AssetEntity>;
|
|
|
|
|
create(asset: Insertable<Assets>) {
|
|
|
|
|
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createAll(assets: Insertable<Assets>[]): Promise<AssetEntity[]> {
|
|
|
|
|
return this.db.insertInto('assets').values(assets).returningAll().execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
createAll(assets: Insertable<Assets>[]) {
|
|
|
|
|
return this.db.insertInto('assets').values(assets).returningAll().execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
|
|
|
|
|
@ -299,20 +301,13 @@ export class AssetRepository {
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [[DummyValue.UUID]] })
|
|
|
|
|
@ChunkedArray()
|
|
|
|
|
getByIds(ids: string[]): Promise<AssetEntity[]> {
|
|
|
|
|
return (
|
|
|
|
|
this.db
|
|
|
|
|
//
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
.where('assets.id', '=', anyUuid(ids))
|
|
|
|
|
.execute() as Promise<AssetEntity[]>
|
|
|
|
|
);
|
|
|
|
|
getByIds(ids: string[]) {
|
|
|
|
|
return this.db.selectFrom('assets').selectAll('assets').where('assets.id', '=', anyUuid(ids)).execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [[DummyValue.UUID]] })
|
|
|
|
|
@ChunkedArray()
|
|
|
|
|
getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]> {
|
|
|
|
|
getByIdsWithAllRelationsButStacks(ids: string[]) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -320,23 +315,8 @@ export class AssetRepository {
|
|
|
|
|
.select(withTags)
|
|
|
|
|
.$call(withExif)
|
|
|
|
|
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
|
|
|
|
.leftJoinLateral(
|
|
|
|
|
(eb) =>
|
|
|
|
|
eb
|
|
|
|
|
.selectFrom('assets as stacked')
|
|
|
|
|
.selectAll('asset_stack')
|
|
|
|
|
.select((eb) => eb.fn('array_agg', [eb.table('stacked')]).as('assets'))
|
|
|
|
|
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
|
|
|
|
.whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId')
|
|
|
|
|
.where('stacked.deletedAt', 'is', null)
|
|
|
|
|
.where('stacked.isArchived', '=', false)
|
|
|
|
|
.groupBy('asset_stack.id')
|
|
|
|
|
.as('stacked_assets'),
|
|
|
|
|
(join) => join.on('asset_stack.id', 'is not', null),
|
|
|
|
|
)
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack'))
|
|
|
|
|
.where('assets.id', '=', anyUuid(ids))
|
|
|
|
|
.execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID] })
|
|
|
|
|
@ -356,36 +336,29 @@ export class AssetRepository {
|
|
|
|
|
return assets.map((asset) => asset.deviceAssetId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getByUserId(
|
|
|
|
|
pagination: PaginationOptions,
|
|
|
|
|
userId: string,
|
|
|
|
|
options: Omit<AssetSearchOptions, 'userIds'> = {},
|
|
|
|
|
): Paginated<AssetEntity> {
|
|
|
|
|
getByUserId(pagination: PaginationOptions, userId: string, options: Omit<AssetSearchOptions, 'userIds'> = {}) {
|
|
|
|
|
return this.getAll(pagination, { ...options, userIds: [userId] });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
|
|
|
|
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise<AssetEntity | undefined> {
|
|
|
|
|
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
.where('libraryId', '=', asUuid(libraryId))
|
|
|
|
|
.where('originalPath', '=', originalPath)
|
|
|
|
|
.limit(1)
|
|
|
|
|
.executeTakeFirst() as any as Promise<AssetEntity | undefined>;
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getAll(
|
|
|
|
|
pagination: PaginationOptions,
|
|
|
|
|
{ orderDirection, ...options }: AssetSearchOptions = {},
|
|
|
|
|
): Paginated<AssetEntity> {
|
|
|
|
|
async getAll(pagination: PaginationOptions, { orderDirection, ...options }: AssetSearchOptions = {}) {
|
|
|
|
|
const builder = searchAssetBuilder(this.db, options)
|
|
|
|
|
.select(withFiles)
|
|
|
|
|
.orderBy('assets.createdAt', orderDirection ?? 'asc')
|
|
|
|
|
.limit(pagination.take + 1)
|
|
|
|
|
.offset(pagination.skip ?? 0);
|
|
|
|
|
const items = await builder.execute();
|
|
|
|
|
return paginationHelper(items as any as AssetEntity[], pagination.take);
|
|
|
|
|
return paginationHelper(items, pagination.take);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -420,23 +393,22 @@ export class AssetRepository {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID] })
|
|
|
|
|
getById(
|
|
|
|
|
id: string,
|
|
|
|
|
{ exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {},
|
|
|
|
|
): Promise<AssetEntity | undefined> {
|
|
|
|
|
getById(id: string, { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
.where('assets.id', '=', asUuid(id))
|
|
|
|
|
.$if(!!exifInfo, withExif)
|
|
|
|
|
.$if(!!faces, (qb) => qb.select(faces?.person ? withFacesAndPeople : withFaces))
|
|
|
|
|
.$if(!!faces, (qb) => qb.select(faces?.person ? withFacesAndPeople : withFaces).$narrowType<{ faces: NotNull }>())
|
|
|
|
|
.$if(!!library, (qb) => qb.select(withLibrary))
|
|
|
|
|
.$if(!!owner, (qb) => qb.select(withOwner))
|
|
|
|
|
.$if(!!smartSearch, withSmartSearch)
|
|
|
|
|
.$if(!!stack, (qb) =>
|
|
|
|
|
qb
|
|
|
|
|
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
|
|
|
|
.$if(!stack!.assets, (qb) => qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).as('stack')))
|
|
|
|
|
.$if(!stack!.assets, (qb) =>
|
|
|
|
|
qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).$castTo<Stack | null>().as('stack')),
|
|
|
|
|
)
|
|
|
|
|
.$if(!!stack!.assets, (qb) =>
|
|
|
|
|
qb
|
|
|
|
|
.leftJoinLateral(
|
|
|
|
|
@ -453,13 +425,13 @@ export class AssetRepository {
|
|
|
|
|
.as('stacked_assets'),
|
|
|
|
|
(join) => join.on('asset_stack.id', 'is not', null),
|
|
|
|
|
)
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')),
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack')),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.$if(!!files, (qb) => qb.select(withFiles))
|
|
|
|
|
.$if(!!tags, (qb) => qb.select(withTags))
|
|
|
|
|
.limit(1)
|
|
|
|
|
.executeTakeFirst() as any as Promise<AssetEntity | undefined>;
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] })
|
|
|
|
|
@ -488,7 +460,7 @@ export class AssetRepository {
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async update(asset: Updateable<Assets> & { id: string }): Promise<AssetEntity> {
|
|
|
|
|
async update(asset: Updateable<Assets> & { id: string }) {
|
|
|
|
|
const value = omitBy(asset, isUndefined);
|
|
|
|
|
delete value.id;
|
|
|
|
|
if (!isEmpty(value)) {
|
|
|
|
|
@ -498,10 +470,10 @@ export class AssetRepository {
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
.$call(withExif)
|
|
|
|
|
.$call((qb) => qb.select(withFacesAndPeople))
|
|
|
|
|
.executeTakeFirst() as Promise<AssetEntity>;
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.getById(asset.id, { exifInfo: true, faces: { person: true } }) as Promise<AssetEntity>;
|
|
|
|
|
return this.getById(asset.id, { exifInfo: true, faces: { person: true } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async remove(asset: { id: string }): Promise<void> {
|
|
|
|
|
@ -509,7 +481,7 @@ export class AssetRepository {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [{ ownerId: DummyValue.UUID, libraryId: DummyValue.UUID, checksum: DummyValue.BUFFER }] })
|
|
|
|
|
getByChecksum({ ownerId, libraryId, checksum }: AssetGetByChecksumOptions): Promise<AssetEntity | undefined> {
|
|
|
|
|
getByChecksum({ ownerId, libraryId, checksum }: AssetGetByChecksumOptions) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -517,7 +489,7 @@ export class AssetRepository {
|
|
|
|
|
.where('checksum', '=', checksum)
|
|
|
|
|
.$call((qb) => (libraryId ? qb.where('libraryId', '=', asUuid(libraryId)) : qb.where('libraryId', 'is', null)))
|
|
|
|
|
.limit(1)
|
|
|
|
|
.executeTakeFirst() as Promise<AssetEntity | undefined>;
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.BUFFER]] })
|
|
|
|
|
@ -544,7 +516,7 @@ export class AssetRepository {
|
|
|
|
|
return asset?.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | undefined> {
|
|
|
|
|
findLivePhotoMatch(options: LivePhotoSearchOptions) {
|
|
|
|
|
const { ownerId, otherAssetId, livePhotoCID, type } = options;
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
@ -555,7 +527,7 @@ export class AssetRepository {
|
|
|
|
|
.where('type', '=', type)
|
|
|
|
|
.where('exif.livePhotoCID', '=', livePhotoCID)
|
|
|
|
|
.limit(1)
|
|
|
|
|
.executeTakeFirst() as Promise<AssetEntity | undefined>;
|
|
|
|
|
.executeTakeFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql(
|
|
|
|
|
@ -564,7 +536,7 @@ export class AssetRepository {
|
|
|
|
|
params: [DummyValue.PAGINATION, property],
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
async getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity> {
|
|
|
|
|
async getWithout(pagination: PaginationOptions, property: WithoutProperty) {
|
|
|
|
|
const items = await this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -626,7 +598,7 @@ export class AssetRepository {
|
|
|
|
|
.orderBy('createdAt')
|
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
|
|
return paginationHelper(items as any as AssetEntity[], pagination.take);
|
|
|
|
|
return paginationHelper(items, pagination.take);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
|
|
|
|
|
@ -645,7 +617,7 @@ export class AssetRepository {
|
|
|
|
|
.executeTakeFirstOrThrow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getRandom(userIds: string[], take: number): Promise<AssetEntity[]> {
|
|
|
|
|
getRandom(userIds: string[], take: number) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -655,7 +627,7 @@ export class AssetRepository {
|
|
|
|
|
.where('deletedAt', 'is', null)
|
|
|
|
|
.orderBy((eb) => eb.fn('random'))
|
|
|
|
|
.limit(take)
|
|
|
|
|
.execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [{ size: TimeBucketSize.MONTH }] })
|
|
|
|
|
@ -708,7 +680,7 @@ export class AssetRepository {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH, withStacked: true }] })
|
|
|
|
|
async getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
|
|
|
|
|
async getTimeBucket(timeBucket: string, options: TimeBucketOptions) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -741,7 +713,7 @@ export class AssetRepository {
|
|
|
|
|
.as('stacked_assets'),
|
|
|
|
|
(join) => join.on('asset_stack.id', 'is not', null),
|
|
|
|
|
)
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')),
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo<Stack | null>()).as('stack')),
|
|
|
|
|
)
|
|
|
|
|
.$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!))
|
|
|
|
|
.$if(options.isDuplicate !== undefined, (qb) =>
|
|
|
|
|
@ -753,11 +725,11 @@ export class AssetRepository {
|
|
|
|
|
.where('assets.isVisible', '=', true)
|
|
|
|
|
.where(truncatedDate(options.size), '=', timeBucket.replace(/^[+-]/, ''))
|
|
|
|
|
.orderBy('assets.localDateTime', options.order ?? 'desc')
|
|
|
|
|
.execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [DummyValue.UUID] })
|
|
|
|
|
getDuplicates(userId: string): Promise<DuplicateGroup[]> {
|
|
|
|
|
getDuplicates(userId: string) {
|
|
|
|
|
return (
|
|
|
|
|
this.db
|
|
|
|
|
.with('duplicates', (qb) =>
|
|
|
|
|
@ -774,9 +746,15 @@ export class AssetRepository {
|
|
|
|
|
(join) => join.onTrue(),
|
|
|
|
|
)
|
|
|
|
|
.select('assets.duplicateId')
|
|
|
|
|
.select((eb) => eb.fn('jsonb_agg', [eb.table('asset')]).as('assets'))
|
|
|
|
|
.select((eb) =>
|
|
|
|
|
eb
|
|
|
|
|
.fn('jsonb_agg', [eb.table('asset')])
|
|
|
|
|
.$castTo<MapAsset[]>()
|
|
|
|
|
.as('assets'),
|
|
|
|
|
)
|
|
|
|
|
.where('assets.ownerId', '=', asUuid(userId))
|
|
|
|
|
.where('assets.duplicateId', 'is not', null)
|
|
|
|
|
.$narrowType<{ duplicateId: NotNull }>()
|
|
|
|
|
.where('assets.deletedAt', 'is', null)
|
|
|
|
|
.where('assets.isVisible', '=', true)
|
|
|
|
|
.where('assets.stackId', 'is', null)
|
|
|
|
|
@ -801,7 +779,7 @@ export class AssetRepository {
|
|
|
|
|
.where(({ not, exists }) =>
|
|
|
|
|
not(exists((eb) => eb.selectFrom('unique').whereRef('unique.duplicateId', '=', 'duplicates.duplicateId'))),
|
|
|
|
|
)
|
|
|
|
|
.execute() as any as Promise<DuplicateGroup[]>
|
|
|
|
|
.execute()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -845,7 +823,7 @@ export class AssetRepository {
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]> {
|
|
|
|
|
getAllForUserFullSync(options: AssetFullSyncOptions) {
|
|
|
|
|
const { ownerId, lastId, updatedUntil, limit } = options;
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
@ -863,18 +841,18 @@ export class AssetRepository {
|
|
|
|
|
.as('stacked_assets'),
|
|
|
|
|
(join) => join.on('asset_stack.id', 'is not', null),
|
|
|
|
|
)
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack'))
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack'))
|
|
|
|
|
.where('assets.ownerId', '=', asUuid(ownerId))
|
|
|
|
|
.where('assets.isVisible', '=', true)
|
|
|
|
|
.where('assets.updatedAt', '<=', updatedUntil)
|
|
|
|
|
.$if(!!lastId, (qb) => qb.where('assets.id', '>', lastId!))
|
|
|
|
|
.orderBy('assets.id')
|
|
|
|
|
.limit(limit)
|
|
|
|
|
.execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE, limit: 100 }] })
|
|
|
|
|
async getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]> {
|
|
|
|
|
async getChangedDeltaSync(options: AssetDeltaSyncOptions) {
|
|
|
|
|
return this.db
|
|
|
|
|
.selectFrom('assets')
|
|
|
|
|
.selectAll('assets')
|
|
|
|
|
@ -891,12 +869,12 @@ export class AssetRepository {
|
|
|
|
|
.as('stacked_assets'),
|
|
|
|
|
(join) => join.on('asset_stack.id', 'is not', null),
|
|
|
|
|
)
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack'))
|
|
|
|
|
.select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo<Stack | null>()).as('stack'))
|
|
|
|
|
.where('assets.ownerId', '=', anyUuid(options.userIds))
|
|
|
|
|
.where('assets.isVisible', '=', true)
|
|
|
|
|
.where('assets.updatedAt', '>', options.updatedAfter)
|
|
|
|
|
.limit(options.limit)
|
|
|
|
|
.execute() as any as Promise<AssetEntity[]>;
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async upsertFile(file: Pick<Insertable<AssetFiles>, 'assetId' | 'path' | 'type'>): Promise<void> {
|
|
|
|
|
|