mirror of
https://github.com/immich-app/immich.git
synced 2026-03-04 09:57:33 +03:00
feat: rename schema (#19891)
This commit is contained in:
@@ -35,9 +35,9 @@ class ActivityAccess {
|
||||
return this.db
|
||||
.selectFrom('activity')
|
||||
.select('activity.id')
|
||||
.leftJoin('albums', (join) => join.onRef('activity.albumId', '=', 'albums.id').on('albums.deletedAt', 'is', null))
|
||||
.leftJoin('album', (join) => join.onRef('activity.albumId', '=', 'album.id').on('album.deletedAt', 'is', null))
|
||||
.where('activity.id', 'in', [...activityIds])
|
||||
.whereRef('albums.ownerId', '=', asUuid(userId))
|
||||
.whereRef('album.ownerId', '=', asUuid(userId))
|
||||
.execute()
|
||||
.then((activities) => new Set(activities.map((activity) => activity.id)));
|
||||
}
|
||||
@@ -50,14 +50,14 @@ class ActivityAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.select('albums.id')
|
||||
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||
.where('albums.id', 'in', [...albumIds])
|
||||
.where('albums.isActivityEnabled', '=', true)
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.selectFrom('album')
|
||||
.select('album.id')
|
||||
.leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id')
|
||||
.leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null))
|
||||
.where('album.id', 'in', [...albumIds])
|
||||
.where('album.isActivityEnabled', '=', true)
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)]))
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.execute()
|
||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||
}
|
||||
@@ -74,11 +74,11 @@ class AlbumAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.select('albums.id')
|
||||
.where('albums.id', 'in', [...albumIds])
|
||||
.where('albums.ownerId', '=', userId)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.selectFrom('album')
|
||||
.select('album.id')
|
||||
.where('album.id', 'in', [...albumIds])
|
||||
.where('album.ownerId', '=', userId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.execute()
|
||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||
}
|
||||
@@ -94,14 +94,14 @@ class AlbumAccess {
|
||||
access === AlbumUserRole.EDITOR ? [AlbumUserRole.EDITOR] : [AlbumUserRole.EDITOR, AlbumUserRole.VIEWER];
|
||||
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.select('albums.id')
|
||||
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||
.where('albums.id', 'in', [...albumIds])
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.where('users.id', '=', userId)
|
||||
.where('albumUsers.role', 'in', [...accessRole])
|
||||
.selectFrom('album')
|
||||
.select('album.id')
|
||||
.leftJoin('album_user', 'album_user.albumsId', 'album.id')
|
||||
.leftJoin('user', (join) => join.onRef('user.id', '=', 'album_user.usersId').on('user.deletedAt', 'is', null))
|
||||
.where('album.id', 'in', [...albumIds])
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.where('user.id', '=', userId)
|
||||
.where('album_user.role', 'in', [...accessRole])
|
||||
.execute()
|
||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||
}
|
||||
@@ -114,10 +114,10 @@ class AlbumAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.select('shared_links.albumId')
|
||||
.where('shared_links.id', '=', sharedLinkId)
|
||||
.where('shared_links.albumId', 'in', [...albumIds])
|
||||
.selectFrom('shared_link')
|
||||
.select('shared_link.albumId')
|
||||
.where('shared_link.id', '=', sharedLinkId)
|
||||
.where('shared_link.albumId', 'in', [...albumIds])
|
||||
.execute()
|
||||
.then(
|
||||
(sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
|
||||
@@ -136,21 +136,21 @@ class AssetAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.innerJoin('albums_assets_assets as albumAssets', 'albums.id', 'albumAssets.albumsId')
|
||||
.innerJoin('assets', (join) =>
|
||||
join.onRef('assets.id', '=', 'albumAssets.assetsId').on('assets.deletedAt', 'is', null),
|
||||
.selectFrom('album')
|
||||
.innerJoin('album_asset as albumAssets', 'album.id', 'albumAssets.albumsId')
|
||||
.innerJoin('asset', (join) =>
|
||||
join.onRef('asset.id', '=', 'albumAssets.assetsId').on('asset.deletedAt', 'is', null),
|
||||
)
|
||||
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||
.select(['assets.id', 'assets.livePhotoVideoId'])
|
||||
.leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id')
|
||||
.leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null))
|
||||
.select(['asset.id', 'asset.livePhotoVideoId'])
|
||||
.where(
|
||||
sql`array["assets"."id", "assets"."livePhotoVideoId"]`,
|
||||
sql`array["asset"."id", "asset"."livePhotoVideoId"]`,
|
||||
'&&',
|
||||
sql`array[${sql.join([...assetIds])}]::uuid[] `,
|
||||
)
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)]))
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.execute()
|
||||
.then((assets) => {
|
||||
const allowedIds = new Set<string>();
|
||||
@@ -174,11 +174,11 @@ class AssetAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select('assets.id')
|
||||
.where('assets.id', 'in', [...assetIds])
|
||||
.where('assets.ownerId', '=', userId)
|
||||
.$if(!hasElevatedPermission, (eb) => eb.where('assets.visibility', '!=', AssetVisibility.LOCKED))
|
||||
.selectFrom('asset')
|
||||
.select('asset.id')
|
||||
.where('asset.id', 'in', [...assetIds])
|
||||
.where('asset.ownerId', '=', userId)
|
||||
.$if(!hasElevatedPermission, (eb) => eb.where('asset.visibility', '!=', AssetVisibility.LOCKED))
|
||||
.execute()
|
||||
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
||||
}
|
||||
@@ -191,23 +191,21 @@ class AssetAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('partners as partner')
|
||||
.innerJoin('users as sharedBy', (join) =>
|
||||
.selectFrom('partner')
|
||||
.innerJoin('user as sharedBy', (join) =>
|
||||
join.onRef('sharedBy.id', '=', 'partner.sharedById').on('sharedBy.deletedAt', 'is', null),
|
||||
)
|
||||
.innerJoin('assets', (join) =>
|
||||
join.onRef('assets.ownerId', '=', 'sharedBy.id').on('assets.deletedAt', 'is', null),
|
||||
)
|
||||
.select('assets.id')
|
||||
.innerJoin('asset', (join) => join.onRef('asset.ownerId', '=', 'sharedBy.id').on('asset.deletedAt', 'is', null))
|
||||
.select('asset.id')
|
||||
.where('partner.sharedWithId', '=', userId)
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE)),
|
||||
eb('assets.visibility', '=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
eb('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE)),
|
||||
eb('asset.visibility', '=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
|
||||
.where('assets.id', 'in', [...assetIds])
|
||||
.where('asset.id', 'in', [...assetIds])
|
||||
.execute()
|
||||
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
||||
}
|
||||
@@ -220,27 +218,25 @@ class AssetAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.leftJoin('albums', (join) =>
|
||||
join.onRef('albums.id', '=', 'shared_links.albumId').on('albums.deletedAt', 'is', null),
|
||||
.selectFrom('shared_link')
|
||||
.leftJoin('album', (join) => join.onRef('album.id', '=', 'shared_link.albumId').on('album.deletedAt', 'is', null))
|
||||
.leftJoin('shared_link_asset', 'shared_link_asset.sharedLinksId', 'shared_link.id')
|
||||
.leftJoin('asset', (join) =>
|
||||
join.onRef('asset.id', '=', 'shared_link_asset.assetsId').on('asset.deletedAt', 'is', null),
|
||||
)
|
||||
.leftJoin('shared_link__asset', 'shared_link__asset.sharedLinksId', 'shared_links.id')
|
||||
.leftJoin('assets', (join) =>
|
||||
join.onRef('assets.id', '=', 'shared_link__asset.assetsId').on('assets.deletedAt', 'is', null),
|
||||
)
|
||||
.leftJoin('albums_assets_assets', 'albums_assets_assets.albumsId', 'albums.id')
|
||||
.leftJoin('assets as albumAssets', (join) =>
|
||||
join.onRef('albumAssets.id', '=', 'albums_assets_assets.assetsId').on('albumAssets.deletedAt', 'is', null),
|
||||
.leftJoin('album_asset', 'album_asset.albumsId', 'album.id')
|
||||
.leftJoin('asset as albumAssets', (join) =>
|
||||
join.onRef('albumAssets.id', '=', 'album_asset.assetsId').on('albumAssets.deletedAt', 'is', null),
|
||||
)
|
||||
.select([
|
||||
'assets.id as assetId',
|
||||
'assets.livePhotoVideoId as assetLivePhotoVideoId',
|
||||
'asset.id as assetId',
|
||||
'asset.livePhotoVideoId as assetLivePhotoVideoId',
|
||||
'albumAssets.id as albumAssetId',
|
||||
'albumAssets.livePhotoVideoId as albumAssetLivePhotoVideoId',
|
||||
])
|
||||
.where('shared_links.id', '=', sharedLinkId)
|
||||
.where('shared_link.id', '=', sharedLinkId)
|
||||
.where(
|
||||
sql`array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"]`,
|
||||
sql`array["asset"."id", "asset"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"]`,
|
||||
'&&',
|
||||
sql`array[${sql.join([...assetIds])}]::uuid[] `,
|
||||
)
|
||||
@@ -277,10 +273,10 @@ class AuthDeviceAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.select('sessions.id')
|
||||
.where('sessions.userId', '=', userId)
|
||||
.where('sessions.id', 'in', [...deviceIds])
|
||||
.selectFrom('session')
|
||||
.select('session.id')
|
||||
.where('session.userId', '=', userId)
|
||||
.where('session.id', 'in', [...deviceIds])
|
||||
.execute()
|
||||
.then((tokens) => new Set(tokens.map((token) => token.id)));
|
||||
}
|
||||
@@ -297,10 +293,10 @@ class NotificationAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('notifications')
|
||||
.select('notifications.id')
|
||||
.where('notifications.id', 'in', [...notificationIds])
|
||||
.where('notifications.userId', '=', userId)
|
||||
.selectFrom('notification')
|
||||
.select('notification.id')
|
||||
.where('notification.id', 'in', [...notificationIds])
|
||||
.where('notification.userId', '=', userId)
|
||||
.execute()
|
||||
.then((stacks) => new Set(stacks.map((stack) => stack.id)));
|
||||
}
|
||||
@@ -317,10 +313,10 @@ class SessionAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.select('sessions.id')
|
||||
.where('sessions.id', 'in', [...sessionIds])
|
||||
.where('sessions.userId', '=', userId)
|
||||
.selectFrom('session')
|
||||
.select('session.id')
|
||||
.where('session.id', 'in', [...sessionIds])
|
||||
.where('session.userId', '=', userId)
|
||||
.execute()
|
||||
.then((sessions) => new Set(sessions.map((session) => session.id)));
|
||||
}
|
||||
@@ -336,10 +332,10 @@ class StackAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('asset_stack as stacks')
|
||||
.select('stacks.id')
|
||||
.where('stacks.id', 'in', [...stackIds])
|
||||
.where('stacks.ownerId', '=', userId)
|
||||
.selectFrom('stack')
|
||||
.select('stack.id')
|
||||
.where('stack.id', 'in', [...stackIds])
|
||||
.where('stack.ownerId', '=', userId)
|
||||
.execute()
|
||||
.then((stacks) => new Set(stacks.map((stack) => stack.id)));
|
||||
}
|
||||
@@ -356,10 +352,10 @@ class TimelineAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('partners')
|
||||
.select('partners.sharedById')
|
||||
.where('partners.sharedById', 'in', [...partnerIds])
|
||||
.where('partners.sharedWithId', '=', userId)
|
||||
.selectFrom('partner')
|
||||
.select('partner.sharedById')
|
||||
.where('partner.sharedById', 'in', [...partnerIds])
|
||||
.where('partner.sharedWithId', '=', userId)
|
||||
.execute()
|
||||
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
||||
}
|
||||
@@ -376,11 +372,11 @@ class MemoryAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('memories')
|
||||
.select('memories.id')
|
||||
.where('memories.id', 'in', [...memoryIds])
|
||||
.where('memories.ownerId', '=', userId)
|
||||
.where('memories.deletedAt', 'is', null)
|
||||
.selectFrom('memory')
|
||||
.select('memory.id')
|
||||
.where('memory.id', 'in', [...memoryIds])
|
||||
.where('memory.ownerId', '=', userId)
|
||||
.where('memory.deletedAt', 'is', null)
|
||||
.execute()
|
||||
.then((memories) => new Set(memories.map((memory) => memory.id)));
|
||||
}
|
||||
@@ -413,13 +409,11 @@ class PersonAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.select('asset_faces.id')
|
||||
.leftJoin('assets', (join) =>
|
||||
join.onRef('assets.id', '=', 'asset_faces.assetId').on('assets.deletedAt', 'is', null),
|
||||
)
|
||||
.where('asset_faces.id', 'in', [...assetFaceIds])
|
||||
.where('assets.ownerId', '=', userId)
|
||||
.selectFrom('asset_face')
|
||||
.select('asset_face.id')
|
||||
.leftJoin('asset', (join) => join.onRef('asset.id', '=', 'asset_face.assetId').on('asset.deletedAt', 'is', null))
|
||||
.where('asset_face.id', 'in', [...assetFaceIds])
|
||||
.where('asset.ownerId', '=', userId)
|
||||
.execute()
|
||||
.then((faces) => new Set(faces.map((face) => face.id)));
|
||||
}
|
||||
@@ -436,10 +430,10 @@ class PartnerAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('partners')
|
||||
.select('partners.sharedById')
|
||||
.where('partners.sharedById', 'in', [...partnerIds])
|
||||
.where('partners.sharedWithId', '=', userId)
|
||||
.selectFrom('partner')
|
||||
.select('partner.sharedById')
|
||||
.where('partner.sharedById', 'in', [...partnerIds])
|
||||
.where('partner.sharedWithId', '=', userId)
|
||||
.execute()
|
||||
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
||||
}
|
||||
@@ -456,10 +450,10 @@ class TagAccess {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('tags')
|
||||
.select('tags.id')
|
||||
.where('tags.id', 'in', [...tagIds])
|
||||
.where('tags.userId', '=', userId)
|
||||
.selectFrom('tag')
|
||||
.select('tag.id')
|
||||
.where('tag.id', 'in', [...tagIds])
|
||||
.where('tag.userId', '=', userId)
|
||||
.execute()
|
||||
.then((tags) => new Set(tags.map((tag) => tag.id)));
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ export class ActivityRepository {
|
||||
return this.db
|
||||
.selectFrom('activity')
|
||||
.selectAll('activity')
|
||||
.innerJoin('users', (join) => join.onRef('users.id', '=', 'activity.userId').on('users.deletedAt', 'is', null))
|
||||
.innerJoin('user as user2', (join) =>
|
||||
join.onRef('user2.id', '=', 'activity.userId').on('user2.deletedAt', 'is', null),
|
||||
)
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
@@ -37,13 +39,13 @@ export class ActivityRepository {
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('user').as('user'))
|
||||
.leftJoin('assets', 'assets.id', 'activity.assetId')
|
||||
.leftJoin('asset', 'asset.id', 'activity.assetId')
|
||||
.$if(!!userId, (qb) => qb.where('activity.userId', '=', userId!))
|
||||
.$if(assetId === null, (qb) => qb.where('assetId', 'is', null))
|
||||
.$if(!!assetId, (qb) => qb.where('activity.assetId', '=', assetId!))
|
||||
.$if(!!albumId, (qb) => qb.where('activity.albumId', '=', albumId!))
|
||||
.$if(isLiked !== undefined, (qb) => qb.where('activity.isLiked', '=', isLiked!))
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.orderBy('activity.createdAt', 'asc')
|
||||
.execute();
|
||||
}
|
||||
@@ -55,7 +57,7 @@ export class ActivityRepository {
|
||||
.values(activity)
|
||||
.returningAll()
|
||||
.returning((eb) =>
|
||||
jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'activity.userId').select(columns.user)).as(
|
||||
jsonObjectFrom(eb.selectFrom('user').whereRef('user.id', '=', 'activity.userId').select(columns.user)).as(
|
||||
'user',
|
||||
),
|
||||
)
|
||||
@@ -82,14 +84,14 @@ export class ActivityRepository {
|
||||
eb.fn.countAll<number>().filterWhere('activity.isLiked', '=', false).as('comments'),
|
||||
eb.fn.countAll<number>().filterWhere('activity.isLiked', '=', true).as('likes'),
|
||||
])
|
||||
.innerJoin('users', (join) => join.onRef('users.id', '=', 'activity.userId').on('users.deletedAt', 'is', null))
|
||||
.leftJoin('assets', 'assets.id', 'activity.assetId')
|
||||
.innerJoin('user', (join) => join.onRef('user.id', '=', 'activity.userId').on('user.deletedAt', 'is', null))
|
||||
.leftJoin('asset', 'asset.id', 'activity.assetId')
|
||||
.$if(!!assetId, (qb) => qb.where('activity.assetId', '=', assetId!))
|
||||
.where('activity.albumId', '=', albumId)
|
||||
.where(({ or, and, eb }) =>
|
||||
or([
|
||||
and([eb('assets.deletedAt', 'is', null), eb('assets.visibility', '!=', sql.lit(AssetVisibility.LOCKED))]),
|
||||
eb('assets.id', 'is', null),
|
||||
and([eb('asset.deletedAt', 'is', null), eb('asset.visibility', '!=', sql.lit(AssetVisibility.LOCKED))]),
|
||||
eb('asset.id', 'is', null),
|
||||
]),
|
||||
)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
@@ -18,7 +18,7 @@ export class AlbumUserRepository {
|
||||
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
|
||||
create(albumUser: Insertable<AlbumUserTable>) {
|
||||
return this.db
|
||||
.insertInto('albums_shared_users_users')
|
||||
.insertInto('album_user')
|
||||
.values(albumUser)
|
||||
.returning(['usersId', 'albumsId', 'role'])
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -27,7 +27,7 @@ export class AlbumUserRepository {
|
||||
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] })
|
||||
update({ usersId, albumsId }: AlbumPermissionId, dto: Updateable<AlbumUserTable>) {
|
||||
return this.db
|
||||
.updateTable('albums_shared_users_users')
|
||||
.updateTable('album_user')
|
||||
.set(dto)
|
||||
.where('usersId', '=', usersId)
|
||||
.where('albumsId', '=', albumsId)
|
||||
@@ -37,10 +37,6 @@ export class AlbumUserRepository {
|
||||
|
||||
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
|
||||
async delete({ usersId, albumsId }: AlbumPermissionId): Promise<void> {
|
||||
await this.db
|
||||
.deleteFrom('albums_shared_users_users')
|
||||
.where('usersId', '=', usersId)
|
||||
.where('albumsId', '=', albumsId)
|
||||
.execute();
|
||||
await this.db.deleteFrom('album_user').where('usersId', '=', usersId).where('albumsId', '=', albumsId).execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,47 +21,47 @@ export interface AlbumInfoOptions {
|
||||
withAssets: boolean;
|
||||
}
|
||||
|
||||
const withOwner = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
||||
return jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'albums.ownerId'))
|
||||
const withOwner = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
return jsonObjectFrom(eb.selectFrom('user').select(columns.user).whereRef('user.id', '=', 'album.ownerId'))
|
||||
.$notNull()
|
||||
.as('owner');
|
||||
};
|
||||
|
||||
const withAlbumUsers = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
||||
const withAlbumUsers = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
return jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.select('album_users.role')
|
||||
.selectFrom('album_user')
|
||||
.select('album_user.role')
|
||||
.select((eb) =>
|
||||
jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'album_users.usersId'))
|
||||
jsonObjectFrom(eb.selectFrom('user').select(columns.user).whereRef('user.id', '=', 'album_user.usersId'))
|
||||
.$notNull()
|
||||
.as('user'),
|
||||
)
|
||||
.whereRef('album_users.albumsId', '=', 'albums.id'),
|
||||
.whereRef('album_user.albumsId', '=', 'album.id'),
|
||||
)
|
||||
.$notNull()
|
||||
.as('albumUsers');
|
||||
};
|
||||
|
||||
const withSharedLink = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
||||
return jsonArrayFrom(eb.selectFrom('shared_links').selectAll().whereRef('shared_links.albumId', '=', 'albums.id')).as(
|
||||
const withSharedLink = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
return jsonArrayFrom(eb.selectFrom('shared_link').selectAll().whereRef('shared_link.albumId', '=', 'album.id')).as(
|
||||
'sharedLinks',
|
||||
);
|
||||
};
|
||||
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
return eb
|
||||
.selectFrom((eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.leftJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.select((eb) => eb.table('exif').$castTo<Exif>().as('exifInfo'))
|
||||
.innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id')
|
||||
.whereRef('albums_assets_assets.albumsId', '=', 'albums.id')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.leftJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.select((eb) => eb.table('asset_exif').$castTo<Exif>().as('exifInfo'))
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset.id')
|
||||
.whereRef('album_asset.albumsId', '=', 'album.id')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.$call(withDefaultVisibility)
|
||||
.orderBy('assets.fileCreatedAt', 'desc')
|
||||
.orderBy('asset.fileCreatedAt', 'desc')
|
||||
.as('asset'),
|
||||
)
|
||||
.select((eb) => eb.fn.jsonAgg('asset').as('assets'))
|
||||
@@ -75,10 +75,10 @@ export class AlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, { withAssets: true }] })
|
||||
async getById(id: string, options: AlbumInfoOptions) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.where('albums.id', '=', id)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.where('album.id', '=', id)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select(withOwner)
|
||||
.select(withAlbumUsers)
|
||||
.select(withSharedLink)
|
||||
@@ -90,26 +90,26 @@ export class AlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
async getByAssetId(ownerId: string, assetId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.innerJoin('album_asset', 'album_asset.albumsId', 'album.id')
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('albums.ownerId', '=', ownerId),
|
||||
eb('album.ownerId', '=', ownerId),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.whereRef('album_users.albumsId', '=', 'albums.id')
|
||||
.where('album_users.usersId', '=', ownerId),
|
||||
.selectFrom('album_user')
|
||||
.whereRef('album_user.albumsId', '=', 'album.id')
|
||||
.where('album_user.usersId', '=', ownerId),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.where('album_assets.assetsId', '=', assetId)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.orderBy('albums.createdAt', 'desc')
|
||||
.where('album_asset.assetsId', '=', assetId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.select(withOwner)
|
||||
.select(withAlbumUsers)
|
||||
.orderBy('albums.createdAt', 'desc')
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -123,18 +123,18 @@ export class AlbumRepository {
|
||||
|
||||
return (
|
||||
this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.$call(withDefaultVisibility)
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.assetsId', 'assets.id')
|
||||
.select('album_assets.albumsId as albumId')
|
||||
.select((eb) => eb.fn.min(sql<Date>`("assets"."localDateTime" AT TIME ZONE 'UTC'::text)::date`).as('startDate'))
|
||||
.select((eb) => eb.fn.max(sql<Date>`("assets"."localDateTime" AT TIME ZONE 'UTC'::text)::date`).as('endDate'))
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset.id')
|
||||
.select('album_asset.albumsId as albumId')
|
||||
.select((eb) => eb.fn.min(sql<Date>`("asset"."localDateTime" AT TIME ZONE 'UTC'::text)::date`).as('startDate'))
|
||||
.select((eb) => eb.fn.max(sql<Date>`("asset"."localDateTime" AT TIME ZONE 'UTC'::text)::date`).as('endDate'))
|
||||
// lastModifiedAssetTimestamp is only used in mobile app, please remove if not need
|
||||
.select((eb) => eb.fn.max('assets.updatedAt').as('lastModifiedAssetTimestamp'))
|
||||
.select((eb) => sql<number>`${eb.fn.count('assets.id')}::int`.as('assetCount'))
|
||||
.where('album_assets.albumsId', 'in', ids)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.groupBy('album_assets.albumsId')
|
||||
.select((eb) => eb.fn.max('asset.updatedAt').as('lastModifiedAssetTimestamp'))
|
||||
.select((eb) => sql<number>`${eb.fn.count('asset.id')}::int`.as('assetCount'))
|
||||
.where('album_asset.albumsId', 'in', ids)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.groupBy('album_asset.albumsId')
|
||||
.execute()
|
||||
);
|
||||
}
|
||||
@@ -142,14 +142,14 @@ export class AlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getOwned(ownerId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.select(withOwner)
|
||||
.select(withAlbumUsers)
|
||||
.select(withSharedLink)
|
||||
.where('albums.ownerId', '=', ownerId)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.orderBy('albums.createdAt', 'desc')
|
||||
.where('album.ownerId', '=', ownerId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -159,29 +159,29 @@ export class AlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getShared(ownerId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.whereRef('album_users.albumsId', '=', 'albums.id')
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', ownerId), eb('album_users.usersId', '=', ownerId)])),
|
||||
.selectFrom('album_user')
|
||||
.whereRef('album_user.albumsId', '=', 'album.id')
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', ownerId), eb('album_user.usersId', '=', ownerId)])),
|
||||
),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('shared_links')
|
||||
.whereRef('shared_links.albumId', '=', 'albums.id')
|
||||
.where('shared_links.userId', '=', ownerId),
|
||||
.selectFrom('shared_link')
|
||||
.whereRef('shared_link.albumId', '=', 'album.id')
|
||||
.where('shared_link.userId', '=', ownerId),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select(withAlbumUsers)
|
||||
.select(withOwner)
|
||||
.select(withSharedLink)
|
||||
.orderBy('albums.createdAt', 'desc')
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -191,43 +191,33 @@ export class AlbumRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getNotShared(ownerId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.where('albums.ownerId', '=', ownerId)
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.where((eb) =>
|
||||
eb.not(
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.whereRef('album_users.albumsId', '=', 'albums.id'),
|
||||
),
|
||||
),
|
||||
)
|
||||
.where((eb) =>
|
||||
eb.not(eb.exists(eb.selectFrom('shared_links').whereRef('shared_links.albumId', '=', 'albums.id'))),
|
||||
)
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.where('album.ownerId', '=', ownerId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.where((eb) => eb.not(eb.exists(eb.selectFrom('album_user').whereRef('album_user.albumsId', '=', 'album.id'))))
|
||||
.where((eb) => eb.not(eb.exists(eb.selectFrom('shared_link').whereRef('shared_link.albumId', '=', 'album.id'))))
|
||||
.select(withOwner)
|
||||
.orderBy('albums.createdAt', 'desc')
|
||||
.orderBy('album.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
async restoreAll(userId: string): Promise<void> {
|
||||
await this.db.updateTable('albums').set({ deletedAt: null }).where('ownerId', '=', userId).execute();
|
||||
await this.db.updateTable('album').set({ deletedAt: null }).where('ownerId', '=', userId).execute();
|
||||
}
|
||||
|
||||
async softDeleteAll(userId: string): Promise<void> {
|
||||
await this.db.updateTable('albums').set({ deletedAt: new Date() }).where('ownerId', '=', userId).execute();
|
||||
await this.db.updateTable('album').set({ deletedAt: new Date() }).where('ownerId', '=', userId).execute();
|
||||
}
|
||||
|
||||
async deleteAll(userId: string): Promise<void> {
|
||||
await this.db.deleteFrom('albums').where('ownerId', '=', userId).execute();
|
||||
await this.db.deleteFrom('album').where('ownerId', '=', userId).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID]] })
|
||||
@Chunked()
|
||||
async removeAssetsFromAll(assetIds: string[]): Promise<void> {
|
||||
await this.db.deleteFrom('albums_assets_assets').where('albums_assets_assets.assetsId', 'in', assetIds).execute();
|
||||
await this.db.deleteFrom('album_asset').where('album_asset.assetsId', 'in', assetIds).execute();
|
||||
}
|
||||
|
||||
@Chunked({ paramIndex: 1 })
|
||||
@@ -237,9 +227,9 @@ export class AlbumRepository {
|
||||
}
|
||||
|
||||
await this.db
|
||||
.deleteFrom('albums_assets_assets')
|
||||
.where('albums_assets_assets.albumsId', '=', albumId)
|
||||
.where('albums_assets_assets.assetsId', 'in', assetIds)
|
||||
.deleteFrom('album_asset')
|
||||
.where('album_asset.albumsId', '=', albumId)
|
||||
.where('album_asset.assetsId', 'in', assetIds)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -258,10 +248,10 @@ export class AlbumRepository {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('albums_assets_assets')
|
||||
.selectFrom('album_asset')
|
||||
.selectAll()
|
||||
.where('albums_assets_assets.albumsId', '=', albumId)
|
||||
.where('albums_assets_assets.assetsId', 'in', assetIds)
|
||||
.where('album_asset.albumsId', '=', albumId)
|
||||
.where('album_asset.assetsId', 'in', assetIds)
|
||||
.execute()
|
||||
.then((results) => new Set(results.map(({ assetsId }) => assetsId)));
|
||||
}
|
||||
@@ -272,7 +262,7 @@ export class AlbumRepository {
|
||||
|
||||
create(album: Insertable<AlbumTable>, assetIds: string[], albumUsers: AlbumUserCreateDto[]) {
|
||||
return this.db.transaction().execute(async (tx) => {
|
||||
const newAlbum = await tx.insertInto('albums').values(album).returning('albums.id').executeTakeFirst();
|
||||
const newAlbum = await tx.insertInto('album').values(album).returning('album.id').executeTakeFirst();
|
||||
|
||||
if (!newAlbum) {
|
||||
throw new Error('Failed to create album');
|
||||
@@ -284,7 +274,7 @@ export class AlbumRepository {
|
||||
|
||||
if (albumUsers.length > 0) {
|
||||
await tx
|
||||
.insertInto('albums_shared_users_users')
|
||||
.insertInto('album_user')
|
||||
.values(
|
||||
albumUsers.map((albumUser) => ({ albumsId: newAlbum.id, usersId: albumUser.userId, role: albumUser.role })),
|
||||
)
|
||||
@@ -292,7 +282,7 @@ export class AlbumRepository {
|
||||
}
|
||||
|
||||
return tx
|
||||
.selectFrom('albums')
|
||||
.selectFrom('album')
|
||||
.selectAll()
|
||||
.where('id', '=', newAlbum.id)
|
||||
.select(withOwner)
|
||||
@@ -305,10 +295,10 @@ export class AlbumRepository {
|
||||
|
||||
update(id: string, album: Updateable<AlbumTable>) {
|
||||
return this.db
|
||||
.updateTable('albums')
|
||||
.updateTable('album')
|
||||
.set(album)
|
||||
.where('id', '=', id)
|
||||
.returningAll('albums')
|
||||
.returningAll('album')
|
||||
.returning(withOwner)
|
||||
.returning(withSharedLink)
|
||||
.returning(withAlbumUsers)
|
||||
@@ -316,7 +306,7 @@ export class AlbumRepository {
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
await this.db.deleteFrom('albums').where('id', '=', id).execute();
|
||||
await this.db.deleteFrom('album').where('id', '=', id).execute();
|
||||
}
|
||||
|
||||
@Chunked({ paramIndex: 2, chunkSize: 30_000 })
|
||||
@@ -326,7 +316,7 @@ export class AlbumRepository {
|
||||
}
|
||||
|
||||
await db
|
||||
.insertInto('albums_assets_assets')
|
||||
.insertInto('album_asset')
|
||||
.values(assetIds.map((assetId) => ({ albumsId: albumId, assetsId: assetId })))
|
||||
.execute();
|
||||
}
|
||||
@@ -343,11 +333,11 @@ export class AlbumRepository {
|
||||
// Subquery for getting a new thumbnail.
|
||||
|
||||
const result = await this.db
|
||||
.updateTable('albums')
|
||||
.updateTable('album')
|
||||
.set((eb) => ({
|
||||
albumThumbnailAssetId: this.updateThumbnailBuilder(eb)
|
||||
.select('album_assets.assetsId')
|
||||
.orderBy('assets.fileCreatedAt', 'desc')
|
||||
.select('album_asset.assetsId')
|
||||
.orderBy('asset.fileCreatedAt', 'desc')
|
||||
.limit(1),
|
||||
}))
|
||||
.where((eb) =>
|
||||
@@ -362,7 +352,7 @@ export class AlbumRepository {
|
||||
eb.exists(
|
||||
this.updateThumbnailBuilder(eb)
|
||||
.select(sql`1`.as('1'))
|
||||
.whereRef('albums.albumThumbnailAssetId', '=', 'album_assets.assetsId'), // Has invalid assets
|
||||
.whereRef('album.albumThumbnailAssetId', '=', 'album_asset.assetsId'), // Has invalid assets
|
||||
),
|
||||
),
|
||||
]),
|
||||
@@ -373,12 +363,12 @@ export class AlbumRepository {
|
||||
return Number(result[0].numUpdatedRows);
|
||||
}
|
||||
|
||||
private updateThumbnailBuilder(eb: ExpressionBuilder<DB, 'albums'>) {
|
||||
private updateThumbnailBuilder(eb: ExpressionBuilder<DB, 'album'>) {
|
||||
return eb
|
||||
.selectFrom('albums_assets_assets as album_assets')
|
||||
.innerJoin('assets', (join) =>
|
||||
join.onRef('album_assets.assetsId', '=', 'assets.id').on('assets.deletedAt', 'is', null),
|
||||
.selectFrom('album_asset')
|
||||
.innerJoin('asset', (join) =>
|
||||
join.onRef('album_asset.assetsId', '=', 'asset.id').on('asset.deletedAt', 'is', null),
|
||||
)
|
||||
.whereRef('album_assets.albumsId', '=', 'albums.id');
|
||||
.whereRef('album_asset.albumsId', '=', 'album.id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,45 +13,45 @@ export class ApiKeyRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
create(dto: Insertable<ApiKeyTable>) {
|
||||
return this.db.insertInto('api_keys').values(dto).returning(columns.apiKey).executeTakeFirstOrThrow();
|
||||
return this.db.insertInto('api_key').values(dto).returning(columns.apiKey).executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
async update(userId: string, id: string, dto: Updateable<ApiKeyTable>) {
|
||||
return this.db
|
||||
.updateTable('api_keys')
|
||||
.updateTable('api_key')
|
||||
.set(dto)
|
||||
.where('api_keys.userId', '=', userId)
|
||||
.where('api_key.userId', '=', userId)
|
||||
.where('id', '=', asUuid(id))
|
||||
.returning(columns.apiKey)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
async delete(userId: string, id: string) {
|
||||
await this.db.deleteFrom('api_keys').where('userId', '=', userId).where('id', '=', asUuid(id)).execute();
|
||||
await this.db.deleteFrom('api_key').where('userId', '=', userId).where('id', '=', asUuid(id)).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.STRING] })
|
||||
getKey(hashedToken: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.selectFrom('api_key')
|
||||
.select((eb) => [
|
||||
...columns.authApiKey,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.authUser)
|
||||
.whereRef('users.id', '=', 'api_keys.userId')
|
||||
.where('users.deletedAt', 'is', null),
|
||||
.whereRef('user.id', '=', 'api_key.userId')
|
||||
.where('user.deletedAt', 'is', null),
|
||||
).as('user'),
|
||||
])
|
||||
.where('api_keys.key', '=', hashedToken)
|
||||
.where('api_key.key', '=', hashedToken)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
getById(userId: string, id: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.selectFrom('api_key')
|
||||
.select(columns.apiKey)
|
||||
.where('id', '=', asUuid(id))
|
||||
.where('userId', '=', userId)
|
||||
@@ -61,7 +61,7 @@ export class ApiKeyRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getByUserId(userId: string) {
|
||||
return this.db
|
||||
.selectFrom('api_keys')
|
||||
.selectFrom('api_key')
|
||||
.select(columns.apiKey)
|
||||
.where('userId', '=', userId)
|
||||
.orderBy('createdAt', 'desc')
|
||||
|
||||
@@ -26,9 +26,9 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForSearchDuplicatesJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.where('assets.id', '=', asUuid(id))
|
||||
.leftJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.selectFrom('asset')
|
||||
.where('asset.id', '=', asUuid(id))
|
||||
.leftJoin('smart_search', 'asset.id', 'smart_search.assetId')
|
||||
.select(['id', 'type', 'ownerId', 'duplicateId', 'stackId', 'visibility', 'smart_search.embedding'])
|
||||
.limit(1)
|
||||
.executeTakeFirst();
|
||||
@@ -37,18 +37,18 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForSidecarWriteJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.where('assets.id', '=', asUuid(id))
|
||||
.selectFrom('asset')
|
||||
.where('asset.id', '=', asUuid(id))
|
||||
.select((eb) => [
|
||||
'id',
|
||||
'sidecarPath',
|
||||
'originalPath',
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('tags')
|
||||
.select(['tags.value'])
|
||||
.innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId')
|
||||
.whereRef('assets.id', '=', 'tag_asset.assetsId'),
|
||||
.selectFrom('tag')
|
||||
.select(['tag.value'])
|
||||
.innerJoin('tag_asset', 'tag.id', 'tag_asset.tagsId')
|
||||
.whereRef('asset.id', '=', 'tag_asset.assetsId'),
|
||||
).as('tags'),
|
||||
])
|
||||
.limit(1)
|
||||
@@ -58,20 +58,20 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [false], stream: true })
|
||||
streamForThumbnailJob(force: boolean) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.thumbhash'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.thumbhash'])
|
||||
.select(withFiles)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
// If there aren't any entries, metadata extraction hasn't run yet which is required for thumbnails
|
||||
.innerJoin('asset_job_status', 'asset_job_status.assetId', 'assets.id')
|
||||
.innerJoin('asset_job_status', 'asset_job_status.assetId', 'asset.id')
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('asset_job_status.previewAt', 'is', null),
|
||||
eb('asset_job_status.thumbnailAt', 'is', null),
|
||||
eb('assets.thumbhash', 'is', null),
|
||||
eb('asset.thumbhash', 'is', null),
|
||||
]),
|
||||
),
|
||||
)
|
||||
@@ -81,72 +81,72 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForMigrationJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.ownerId', 'assets.encodedVideoPath'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.ownerId', 'asset.encodedVideoPath'])
|
||||
.select(withFiles)
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForGenerateThumbnailJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.visibility',
|
||||
'assets.originalFileName',
|
||||
'assets.originalPath',
|
||||
'assets.ownerId',
|
||||
'assets.thumbhash',
|
||||
'assets.type',
|
||||
'asset.id',
|
||||
'asset.visibility',
|
||||
'asset.originalFileName',
|
||||
'asset.originalPath',
|
||||
'asset.ownerId',
|
||||
'asset.thumbhash',
|
||||
'asset.type',
|
||||
])
|
||||
.select(withFiles)
|
||||
.$call(withExifInner)
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForMetadataExtraction(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(columns.asset)
|
||||
.select(withFaces)
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, AssetFileType.THUMBNAIL] })
|
||||
getAlbumThumbnailFiles(id: string, fileType?: AssetFileType) {
|
||||
return this.db
|
||||
.selectFrom('asset_files')
|
||||
.selectFrom('asset_file')
|
||||
.select(columns.assetFiles)
|
||||
.where('asset_files.assetId', '=', id)
|
||||
.$if(!!fileType, (qb) => qb.where('asset_files.type', '=', fileType!))
|
||||
.where('asset_file.assetId', '=', id)
|
||||
.$if(!!fileType, (qb) => qb.where('asset_file.type', '=', fileType!))
|
||||
.execute();
|
||||
}
|
||||
|
||||
private assetsWithPreviews() {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.innerJoin('asset_job_status as job_status', 'assetId', 'assets.id')
|
||||
.selectFrom('asset')
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.innerJoin('asset_job_status as job_status', 'assetId', 'asset.id')
|
||||
.where('job_status.previewAt', 'is not', null);
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
streamForSearchDuplicates(force?: boolean) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id'])
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id'])
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.innerJoin('smart_search', 'asset.id', 'smart_search.assetId')
|
||||
.$call(withDefaultVisibility)
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
.innerJoin('asset_job_status as job_status', 'job_status.assetId', 'assets.id')
|
||||
.innerJoin('asset_job_status as job_status', 'job_status.assetId', 'asset.id')
|
||||
.where('job_status.duplicatesDetectedAt', 'is', null),
|
||||
)
|
||||
.stream();
|
||||
@@ -155,11 +155,9 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
streamForEncodeClip(force?: boolean) {
|
||||
return this.assetsWithPreviews()
|
||||
.select(['assets.id'])
|
||||
.select(['asset.id'])
|
||||
.$if(!force, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id'))),
|
||||
),
|
||||
qb.where((eb) => eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'asset.id')))),
|
||||
)
|
||||
.stream();
|
||||
}
|
||||
@@ -167,142 +165,142 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForClipEncoding(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.visibility'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.visibility'])
|
||||
.select((eb) => withFiles(eb, AssetFileType.PREVIEW))
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForDetectFacesJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.visibility'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.visibility'])
|
||||
.$call(withExifInner)
|
||||
.select((eb) => withFaces(eb, true))
|
||||
.select((eb) => withFiles(eb, AssetFileType.PREVIEW))
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID]] })
|
||||
getForSyncAssets(ids: string[]) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.isOffline',
|
||||
'assets.libraryId',
|
||||
'assets.originalPath',
|
||||
'assets.status',
|
||||
'assets.fileModifiedAt',
|
||||
'asset.id',
|
||||
'asset.isOffline',
|
||||
'asset.libraryId',
|
||||
'asset.originalPath',
|
||||
'asset.status',
|
||||
'asset.fileModifiedAt',
|
||||
])
|
||||
.where('assets.id', '=', anyUuid(ids))
|
||||
.where('asset.id', '=', anyUuid(ids))
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForAssetDeletion(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.visibility',
|
||||
'assets.libraryId',
|
||||
'assets.ownerId',
|
||||
'assets.livePhotoVideoId',
|
||||
'assets.sidecarPath',
|
||||
'assets.encodedVideoPath',
|
||||
'assets.originalPath',
|
||||
'asset.id',
|
||||
'asset.visibility',
|
||||
'asset.libraryId',
|
||||
'asset.ownerId',
|
||||
'asset.livePhotoVideoId',
|
||||
'asset.sidecarPath',
|
||||
'asset.encodedVideoPath',
|
||||
'asset.originalPath',
|
||||
])
|
||||
.$call(withExif)
|
||||
.select(withFacesAndPeople)
|
||||
.select(withFiles)
|
||||
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
||||
.leftJoin('stack', 'stack.id', 'asset.stackId')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets as stacked')
|
||||
.select(['asset_stack.id', 'asset_stack.primaryAssetId'])
|
||||
.selectFrom('asset as stacked')
|
||||
.select(['stack.id', 'stack.primaryAssetId'])
|
||||
.select((eb) => eb.fn<Asset[]>('array_agg', [eb.table('stacked')]).as('assets'))
|
||||
.where('stacked.deletedAt', 'is not', null)
|
||||
.where('stacked.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.groupBy('asset_stack.id')
|
||||
.whereRef('stacked.stackId', '=', 'stack.id')
|
||||
.groupBy('stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
(join) => join.on('stack.id', 'is not', null),
|
||||
)
|
||||
.select((eb) => toJson(eb, 'stacked_assets').as('stack'))
|
||||
.where('assets.id', '=', id)
|
||||
.where('asset.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
streamForVideoConversion(force?: boolean) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id'])
|
||||
.where('assets.type', '=', AssetType.VIDEO)
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id'])
|
||||
.where('asset.type', '=', AssetType.VIDEO)
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
.where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')]))
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN),
|
||||
.where((eb) => eb.or([eb('asset.encodedVideoPath', 'is', null), eb('asset.encodedVideoPath', '=', '')]))
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN),
|
||||
)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForVideoConversion(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.ownerId', 'assets.originalPath', 'assets.encodedVideoPath'])
|
||||
.where('assets.id', '=', id)
|
||||
.where('assets.type', '=', AssetType.VIDEO)
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.ownerId', 'asset.originalPath', 'asset.encodedVideoPath'])
|
||||
.where('asset.id', '=', id)
|
||||
.where('asset.type', '=', AssetType.VIDEO)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
streamForMetadataExtraction(force?: boolean) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id'])
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
.leftJoin('asset_job_status', 'asset_job_status.assetId', 'assets.id')
|
||||
.leftJoin('asset_job_status', 'asset_job_status.assetId', 'asset.id')
|
||||
.where((eb) =>
|
||||
eb.or([eb('asset_job_status.metadataExtractedAt', 'is', null), eb('asset_job_status.assetId', 'is', null)]),
|
||||
),
|
||||
)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.stream();
|
||||
}
|
||||
|
||||
private storageTemplateAssetQuery() {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.ownerId',
|
||||
'assets.type',
|
||||
'assets.checksum',
|
||||
'assets.originalPath',
|
||||
'assets.isExternal',
|
||||
'assets.sidecarPath',
|
||||
'assets.originalFileName',
|
||||
'assets.livePhotoVideoId',
|
||||
'assets.fileCreatedAt',
|
||||
'exif.timeZone',
|
||||
'exif.fileSizeInByte',
|
||||
'asset.id',
|
||||
'asset.ownerId',
|
||||
'asset.type',
|
||||
'asset.checksum',
|
||||
'asset.originalPath',
|
||||
'asset.isExternal',
|
||||
'asset.sidecarPath',
|
||||
'asset.originalFileName',
|
||||
'asset.livePhotoVideoId',
|
||||
'asset.fileCreatedAt',
|
||||
'asset_exif.timeZone',
|
||||
'asset_exif.fileSizeInByte',
|
||||
])
|
||||
.where('assets.deletedAt', 'is', null);
|
||||
.where('asset.deletedAt', 'is', null);
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForStorageTemplateJob(id: string): Promise<StorageAsset | undefined> {
|
||||
return this.storageTemplateAssetQuery().where('assets.id', '=', id).executeTakeFirst() as Promise<
|
||||
return this.storageTemplateAssetQuery().where('asset.id', '=', id).executeTakeFirst() as Promise<
|
||||
StorageAsset | undefined
|
||||
>;
|
||||
}
|
||||
@@ -315,21 +313,21 @@ export class AssetJobRepository {
|
||||
@GenerateSql({ params: [DummyValue.DATE], stream: true })
|
||||
streamForDeletedJob(trashedBefore: Date) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(['id', 'isOffline'])
|
||||
.where('assets.deletedAt', '<=', trashedBefore)
|
||||
.where('asset.deletedAt', '<=', trashedBefore)
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
streamForSidecar(force?: boolean) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id'])
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id'])
|
||||
.$if(!force, (qb) =>
|
||||
qb.where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])),
|
||||
qb.where((eb) => eb.or([eb('asset.sidecarPath', '=', ''), eb('asset.sidecarPath', 'is', null)])),
|
||||
)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.stream();
|
||||
}
|
||||
|
||||
@@ -337,13 +335,13 @@ export class AssetJobRepository {
|
||||
streamForDetectFacesJob(force?: boolean) {
|
||||
return this.assetsWithPreviews()
|
||||
.$if(!force, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null))
|
||||
.select(['assets.id'])
|
||||
.orderBy('assets.createdAt', 'desc')
|
||||
.select(['asset.id'])
|
||||
.orderBy('asset.createdAt', 'desc')
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.DATE], stream: true })
|
||||
streamForMigrationJob() {
|
||||
return this.db.selectFrom('assets').select(['id']).where('assets.deletedAt', 'is', null).stream();
|
||||
return this.db.selectFrom('asset').select(['id']).where('asset.deletedAt', 'is', null).stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import { Stack } from 'src/database';
|
||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { DB } from 'src/schema';
|
||||
import { AssetFileTable } from 'src/schema/tables/asset-files.table';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
|
||||
import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { ExifTable } from 'src/schema/tables/exif.table';
|
||||
import {
|
||||
anyUuid,
|
||||
asUuid,
|
||||
@@ -114,10 +114,10 @@ interface GetByIdsRelations {
|
||||
export class AssetRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
async upsertExif(exif: Insertable<ExifTable>): Promise<void> {
|
||||
async upsertExif(exif: Insertable<AssetExifTable>): Promise<void> {
|
||||
const value = { ...exif, assetId: asUuid(exif.assetId) };
|
||||
await this.db
|
||||
.insertInto('exif')
|
||||
.insertInto('asset_exif')
|
||||
.values(value)
|
||||
.onConflict((oc) =>
|
||||
oc.column('assetId').doUpdateSet((eb) =>
|
||||
@@ -161,12 +161,12 @@ export class AssetRepository {
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID], { model: DummyValue.STRING }] })
|
||||
@Chunked()
|
||||
async updateAllExif(ids: string[], options: Updateable<ExifTable>): Promise<void> {
|
||||
async updateAllExif(ids: string[], options: Updateable<AssetExifTable>): Promise<void> {
|
||||
if (ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.db.updateTable('exif').set(options).where('assetId', 'in', ids).execute();
|
||||
await this.db.updateTable('asset_exif').set(options).where('assetId', 'in', ids).execute();
|
||||
}
|
||||
|
||||
async upsertJobStatus(...jobStatus: Insertable<AssetJobStatusTable>[]): Promise<void> {
|
||||
@@ -196,11 +196,11 @@ export class AssetRepository {
|
||||
}
|
||||
|
||||
create(asset: Insertable<AssetTable>) {
|
||||
return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow();
|
||||
return this.db.insertInto('asset').values(asset).returningAll().executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
createAll(assets: Insertable<AssetTable>[]) {
|
||||
return this.db.insertInto('assets').values(assets).returningAll().execute();
|
||||
return this.db.insertInto('asset').values(assets).returningAll().execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
|
||||
@@ -213,7 +213,7 @@ export class AssetRepository {
|
||||
.selectFrom((eb) =>
|
||||
eb
|
||||
.fn('generate_series', [
|
||||
sql`(select date_part('year', min(("localDateTime" at time zone 'UTC')::date))::int from assets)`,
|
||||
sql`(select date_part('year', min(("localDateTime" at time zone 'UTC')::date))::int from asset)`,
|
||||
sql`date_part('year', current_date)::int - 1`,
|
||||
])
|
||||
.as('year'),
|
||||
@@ -224,30 +224,30 @@ export class AssetRepository {
|
||||
.innerJoinLateral(
|
||||
(qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.innerJoin('asset_job_status', 'assets.id', 'asset_job_status.assetId')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.innerJoin('asset_job_status', 'asset.id', 'asset_job_status.assetId')
|
||||
.where('asset_job_status.previewAt', 'is not', null)
|
||||
.where(sql`(assets."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`)
|
||||
.where('assets.ownerId', '=', anyUuid(ownerIds))
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where(sql`(asset."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`)
|
||||
.where('asset.ownerId', '=', anyUuid(ownerIds))
|
||||
.where('asset.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where((eb) =>
|
||||
eb.exists((qb) =>
|
||||
qb
|
||||
.selectFrom('asset_files')
|
||||
.whereRef('assetId', '=', 'assets.id')
|
||||
.where('asset_files.type', '=', AssetFileType.PREVIEW),
|
||||
.selectFrom('asset_file')
|
||||
.whereRef('assetId', '=', 'asset.id')
|
||||
.where('asset_file.type', '=', AssetFileType.PREVIEW),
|
||||
),
|
||||
)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.orderBy(sql`(assets."localDateTime" at time zone 'UTC')::date`, 'desc')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.orderBy(sql`(asset."localDateTime" at time zone 'UTC')::date`, 'desc')
|
||||
.limit(20)
|
||||
.as('a'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.innerJoin('exif', 'a.id', 'exif.assetId')
|
||||
.innerJoin('asset_exif', 'a.id', 'asset_exif.assetId')
|
||||
.selectAll('a')
|
||||
.select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo')),
|
||||
.select((eb) => eb.fn.toJson(eb.table('asset_exif')).as('exifInfo')),
|
||||
)
|
||||
.selectFrom('res')
|
||||
.select(sql<number>`date_part('year', ("localDateTime" at time zone 'UTC')::date)::int`.as('year'))
|
||||
@@ -260,30 +260,30 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [[DummyValue.UUID]] })
|
||||
@ChunkedArray()
|
||||
getByIds(ids: string[]) {
|
||||
return this.db.selectFrom('assets').selectAll('assets').where('assets.id', '=', anyUuid(ids)).execute();
|
||||
return this.db.selectFrom('asset').selectAll('asset').where('asset.id', '=', anyUuid(ids)).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID]] })
|
||||
@ChunkedArray()
|
||||
getByIdsWithAllRelationsButStacks(ids: string[]) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.select(withFacesAndPeople)
|
||||
.select(withTags)
|
||||
.$call(withExif)
|
||||
.where('assets.id', '=', anyUuid(ids))
|
||||
.where('asset.id', '=', anyUuid(ids))
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async deleteAll(ownerId: string): Promise<void> {
|
||||
await this.db.deleteFrom('assets').where('ownerId', '=', ownerId).execute();
|
||||
await this.db.deleteFrom('asset').where('ownerId', '=', ownerId).execute();
|
||||
}
|
||||
|
||||
async getByDeviceIds(ownerId: string, deviceId: string, deviceAssetIds: string[]): Promise<string[]> {
|
||||
const assets = await this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(['deviceAssetId'])
|
||||
.where('deviceAssetId', 'in', deviceAssetIds)
|
||||
.where('deviceId', '=', deviceId)
|
||||
@@ -296,8 +296,8 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.where('originalPath', '=', originalPath)
|
||||
.limit(1)
|
||||
@@ -314,7 +314,7 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
async getAllByDeviceId(ownerId: string, deviceId: string): Promise<string[]> {
|
||||
const items = await this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(['deviceAssetId'])
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('deviceId', '=', deviceId)
|
||||
@@ -328,7 +328,7 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getLivePhotoCount(motionId: string): Promise<number> {
|
||||
const [{ count }] = await this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||
.where('livePhotoVideoId', '=', asUuid(motionId))
|
||||
.execute();
|
||||
@@ -338,9 +338,9 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getById(id: string, { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.where('assets.id', '=', asUuid(id))
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.where('asset.id', '=', asUuid(id))
|
||||
.$if(!!exifInfo, withExif)
|
||||
.$if(!!faces, (qb) => qb.select(faces?.person ? withFacesAndPeople : withFaces).$narrowType<{ faces: NotNull }>())
|
||||
.$if(!!library, (qb) => qb.select(withLibrary))
|
||||
@@ -348,25 +348,25 @@ export class AssetRepository {
|
||||
.$if(!!smartSearch, withSmartSearch)
|
||||
.$if(!!stack, (qb) =>
|
||||
qb
|
||||
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
||||
.leftJoin('stack', 'stack.id', 'asset.stackId')
|
||||
.$if(!stack!.assets, (qb) =>
|
||||
qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).$castTo<Stack | null>().as('stack')),
|
||||
qb.select((eb) => eb.fn.toJson(eb.table('stack')).$castTo<Stack | null>().as('stack')),
|
||||
)
|
||||
.$if(!!stack!.assets, (qb) =>
|
||||
qb
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets as stacked')
|
||||
.selectAll('asset_stack')
|
||||
.selectFrom('asset as stacked')
|
||||
.selectAll('stack')
|
||||
.select((eb) => eb.fn('array_agg', [eb.table('stacked')]).as('assets'))
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId')
|
||||
.whereRef('stacked.stackId', '=', 'stack.id')
|
||||
.whereRef('stacked.id', '!=', 'stack.primaryAssetId')
|
||||
.where('stacked.deletedAt', 'is', null)
|
||||
.where('stacked.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.groupBy('asset_stack.id')
|
||||
.groupBy('stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
(join) => join.on('stack.id', 'is not', null),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack')),
|
||||
),
|
||||
@@ -383,11 +383,11 @@ export class AssetRepository {
|
||||
if (ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
await this.db.updateTable('assets').set(options).where('id', '=', anyUuid(ids)).execute();
|
||||
await this.db.updateTable('asset').set(options).where('id', '=', anyUuid(ids)).execute();
|
||||
}
|
||||
|
||||
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('asset').set(options).where('libraryId', '=', asUuid(libraryId)).execute();
|
||||
}
|
||||
|
||||
async update(asset: Updateable<AssetTable> & { id: string }) {
|
||||
@@ -395,9 +395,9 @@ export class AssetRepository {
|
||||
delete value.id;
|
||||
if (!isEmpty(value)) {
|
||||
return this.db
|
||||
.with('assets', (qb) => qb.updateTable('assets').set(asset).where('id', '=', asUuid(asset.id)).returningAll())
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.with('asset', (qb) => qb.updateTable('asset').set(asset).where('id', '=', asUuid(asset.id)).returningAll())
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.$call(withExif)
|
||||
.$call((qb) => qb.select(withFacesAndPeople))
|
||||
.executeTakeFirst();
|
||||
@@ -407,14 +407,14 @@ export class AssetRepository {
|
||||
}
|
||||
|
||||
async remove(asset: { id: string }): Promise<void> {
|
||||
await this.db.deleteFrom('assets').where('id', '=', asUuid(asset.id)).execute();
|
||||
await this.db.deleteFrom('asset').where('id', '=', asUuid(asset.id)).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ ownerId: DummyValue.UUID, libraryId: DummyValue.UUID, checksum: DummyValue.BUFFER }] })
|
||||
getByChecksum({ ownerId, libraryId, checksum }: AssetGetByChecksumOptions) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('checksum', '=', checksum)
|
||||
.$call((qb) => (libraryId ? qb.where('libraryId', '=', asUuid(libraryId)) : qb.where('libraryId', 'is', null)))
|
||||
@@ -425,7 +425,7 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.BUFFER]] })
|
||||
getByChecksums(userId: string, checksums: Buffer[]) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(['id', 'checksum', 'deletedAt'])
|
||||
.where('ownerId', '=', asUuid(userId))
|
||||
.where('checksum', 'in', checksums)
|
||||
@@ -435,7 +435,7 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] })
|
||||
async getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined> {
|
||||
const asset = await this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select('id')
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('checksum', '=', checksum)
|
||||
@@ -449,37 +449,37 @@ export class AssetRepository {
|
||||
findLivePhotoMatch(options: LivePhotoSearchOptions) {
|
||||
const { ownerId, otherAssetId, livePhotoCID, type } = options;
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.ownerId'])
|
||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.selectFrom('asset')
|
||||
.select(['asset.id', 'asset.ownerId'])
|
||||
.innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.where('id', '!=', asUuid(otherAssetId))
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('type', '=', type)
|
||||
.where('exif.livePhotoCID', '=', livePhotoCID)
|
||||
.where('asset_exif.livePhotoCID', '=', livePhotoCID)
|
||||
.limit(1)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
getStatistics(ownerId: string, { visibility, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.AUDIO).as(AssetType.AUDIO))
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.IMAGE).as(AssetType.IMAGE))
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.VIDEO).as(AssetType.VIDEO))
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.OTHER).as(AssetType.OTHER))
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.$if(visibility === undefined, withDefaultVisibility)
|
||||
.$if(!!visibility, (qb) => qb.where('assets.visibility', '=', visibility!))
|
||||
.$if(!!visibility, (qb) => qb.where('asset.visibility', '=', visibility!))
|
||||
.$if(isFavorite !== undefined, (qb) => qb.where('isFavorite', '=', isFavorite!))
|
||||
.$if(!!isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.$if(!!isTrashed, (qb) => qb.where('asset.status', '!=', AssetStatus.DELETED))
|
||||
.where('deletedAt', isTrashed ? 'is not' : 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
getRandom(userIds: string[], take: number) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.$call(withExif)
|
||||
.$call(withDefaultVisibility)
|
||||
.where('ownerId', '=', anyUuid(userIds))
|
||||
@@ -492,38 +492,36 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [{}] })
|
||||
async getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]> {
|
||||
return this.db
|
||||
.with('assets', (qb) =>
|
||||
.with('asset', (qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(truncatedDate<Date>().as('timeBucket'))
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('asset.status', '!=', AssetStatus.DELETED))
|
||||
.where('asset.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.$if(options.visibility === undefined, withDefaultVisibility)
|
||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||
.$if(!!options.visibility, (qb) => qb.where('asset.visibility', '=', options.visibility!))
|
||||
.$if(!!options.albumId, (qb) =>
|
||||
qb
|
||||
.innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId')
|
||||
.where('albums_assets_assets.albumsId', '=', asUuid(options.albumId!)),
|
||||
.innerJoin('album_asset', 'asset.id', 'album_asset.assetsId')
|
||||
.where('album_asset.albumsId', '=', asUuid(options.albumId!)),
|
||||
)
|
||||
.$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!]))
|
||||
.$if(!!options.withStacked, (qb) =>
|
||||
qb
|
||||
.leftJoin('asset_stack', (join) =>
|
||||
join
|
||||
.onRef('asset_stack.id', '=', 'assets.stackId')
|
||||
.onRef('asset_stack.primaryAssetId', '=', 'assets.id'),
|
||||
.leftJoin('stack', (join) =>
|
||||
join.onRef('stack.id', '=', 'asset.stackId').onRef('stack.primaryAssetId', '=', 'asset.id'),
|
||||
)
|
||||
.where((eb) => eb.or([eb('assets.stackId', 'is', null), eb(eb.table('asset_stack'), 'is not', null)])),
|
||||
.where((eb) => eb.or([eb('asset.stackId', 'is', null), eb(eb.table('stack'), 'is not', null)])),
|
||||
)
|
||||
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!))
|
||||
.$if(!!options.userIds, (qb) => qb.where('asset.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('asset.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.assetType, (qb) => qb.where('asset.type', '=', options.assetType!))
|
||||
.$if(options.isDuplicate !== undefined, (qb) =>
|
||||
qb.where('assets.duplicateId', options.isDuplicate ? 'is not' : 'is', null),
|
||||
qb.where('asset.duplicateId', options.isDuplicate ? 'is not' : 'is', null),
|
||||
)
|
||||
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)),
|
||||
)
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(sql<string>`("timeBucket" AT TIME ZONE 'UTC')::date::text`.as('timeBucket'))
|
||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||
.groupBy('timeBucket')
|
||||
@@ -538,75 +536,75 @@ export class AssetRepository {
|
||||
const query = this.db
|
||||
.with('cte', (qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.select((eb) => [
|
||||
'assets.duration',
|
||||
'assets.id',
|
||||
'assets.visibility',
|
||||
'assets.isFavorite',
|
||||
sql`assets.type = 'IMAGE'`.as('isImage'),
|
||||
sql`assets."deletedAt" is not null`.as('isTrashed'),
|
||||
'assets.livePhotoVideoId',
|
||||
sql`extract(epoch from (assets."localDateTime" - assets."fileCreatedAt" at time zone 'UTC'))::real / 3600`.as(
|
||||
'asset.duration',
|
||||
'asset.id',
|
||||
'asset.visibility',
|
||||
'asset.isFavorite',
|
||||
sql`asset.type = 'IMAGE'`.as('isImage'),
|
||||
sql`asset."deletedAt" is not null`.as('isTrashed'),
|
||||
'asset.livePhotoVideoId',
|
||||
sql`extract(epoch from (asset."localDateTime" - asset."fileCreatedAt" at time zone 'UTC'))::real / 3600`.as(
|
||||
'localOffsetHours',
|
||||
),
|
||||
'assets.ownerId',
|
||||
'assets.status',
|
||||
sql`assets."fileCreatedAt" at time zone 'utc'`.as('fileCreatedAt'),
|
||||
eb.fn('encode', ['assets.thumbhash', sql.lit('base64')]).as('thumbhash'),
|
||||
'exif.city',
|
||||
'exif.country',
|
||||
'exif.projectionType',
|
||||
'asset.ownerId',
|
||||
'asset.status',
|
||||
sql`asset."fileCreatedAt" at time zone 'utc'`.as('fileCreatedAt'),
|
||||
eb.fn('encode', ['asset.thumbhash', sql.lit('base64')]).as('thumbhash'),
|
||||
'asset_exif.city',
|
||||
'asset_exif.country',
|
||||
'asset_exif.projectionType',
|
||||
eb.fn
|
||||
.coalesce(
|
||||
eb
|
||||
.case()
|
||||
.when(sql`exif."exifImageHeight" = 0 or exif."exifImageWidth" = 0`)
|
||||
.when(sql`asset_exif."exifImageHeight" = 0 or asset_exif."exifImageWidth" = 0`)
|
||||
.then(eb.lit(1))
|
||||
.when('exif.orientation', 'in', sql<string>`('5', '6', '7', '8', '-90', '90')`)
|
||||
.then(sql`round(exif."exifImageHeight"::numeric / exif."exifImageWidth"::numeric, 3)`)
|
||||
.else(sql`round(exif."exifImageWidth"::numeric / exif."exifImageHeight"::numeric, 3)`)
|
||||
.when('asset_exif.orientation', 'in', sql<string>`('5', '6', '7', '8', '-90', '90')`)
|
||||
.then(sql`round(asset_exif."exifImageHeight"::numeric / asset_exif."exifImageWidth"::numeric, 3)`)
|
||||
.else(sql`round(asset_exif."exifImageWidth"::numeric / asset_exif."exifImageHeight"::numeric, 3)`)
|
||||
.end(),
|
||||
eb.lit(1),
|
||||
)
|
||||
.as('ratio'),
|
||||
])
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.where('asset.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.$if(options.visibility == undefined, withDefaultVisibility)
|
||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||
.$if(!!options.visibility, (qb) => qb.where('asset.visibility', '=', options.visibility!))
|
||||
.where(truncatedDate(), '=', timeBucket.replace(/^[+-]/, ''))
|
||||
.$if(!!options.albumId, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('albums_assets_assets')
|
||||
.whereRef('albums_assets_assets.assetsId', '=', 'assets.id')
|
||||
.where('albums_assets_assets.albumsId', '=', asUuid(options.albumId!)),
|
||||
.selectFrom('album_asset')
|
||||
.whereRef('album_asset.assetsId', '=', 'asset.id')
|
||||
.where('album_asset.albumsId', '=', asUuid(options.albumId!)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!]))
|
||||
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.userIds, (qb) => qb.where('asset.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('asset.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.withStacked, (qb) =>
|
||||
qb
|
||||
.where((eb) =>
|
||||
eb.not(
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('asset_stack')
|
||||
.whereRef('asset_stack.id', '=', 'assets.stackId')
|
||||
.whereRef('asset_stack.primaryAssetId', '!=', 'assets.id'),
|
||||
.selectFrom('stack')
|
||||
.whereRef('stack.id', '=', 'asset.stackId')
|
||||
.whereRef('stack.primaryAssetId', '!=', 'asset.id'),
|
||||
),
|
||||
),
|
||||
)
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets as stacked')
|
||||
.selectFrom('asset as stacked')
|
||||
.select(sql`array[stacked."stackId"::text, count('stacked')::text]`.as('stack'))
|
||||
.whereRef('stacked.stackId', '=', 'assets.stackId')
|
||||
.whereRef('stacked.stackId', '=', 'asset.stackId')
|
||||
.where('stacked.deletedAt', 'is', null)
|
||||
.where('stacked.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.groupBy('stacked.stackId')
|
||||
@@ -615,13 +613,13 @@ export class AssetRepository {
|
||||
)
|
||||
.select('stack'),
|
||||
)
|
||||
.$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!))
|
||||
.$if(!!options.assetType, (qb) => qb.where('asset.type', '=', options.assetType!))
|
||||
.$if(options.isDuplicate !== undefined, (qb) =>
|
||||
qb.where('assets.duplicateId', options.isDuplicate ? 'is not' : 'is', null),
|
||||
qb.where('asset.duplicateId', options.isDuplicate ? 'is not' : 'is', null),
|
||||
)
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('asset.status', '!=', AssetStatus.DELETED))
|
||||
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!))
|
||||
.orderBy('assets.fileCreatedAt', options.order ?? 'desc'),
|
||||
.orderBy('asset.fileCreatedAt', options.order ?? 'desc'),
|
||||
)
|
||||
.with('agg', (qb) =>
|
||||
qb
|
||||
@@ -660,17 +658,17 @@ export class AssetRepository {
|
||||
const items = await this.db
|
||||
.with('cities', (qb) =>
|
||||
qb
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select('city')
|
||||
.where('city', 'is not', null)
|
||||
.groupBy('city')
|
||||
.having((eb) => eb.fn('count', [eb.ref('assetId')]), '>=', minAssetsPerField),
|
||||
)
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.innerJoin('cities', 'exif.city', 'cities.city')
|
||||
.distinctOn('exif.city')
|
||||
.select(['assetId as data', 'exif.city as value'])
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.innerJoin('cities', 'asset_exif.city', 'cities.city')
|
||||
.distinctOn('asset_exif.city')
|
||||
.select(['assetId as data', 'asset_exif.city as value'])
|
||||
.$narrowType<{ value: NotNull }>()
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
@@ -695,27 +693,27 @@ export class AssetRepository {
|
||||
getAllForUserFullSync(options: AssetFullSyncOptions) {
|
||||
const { ownerId, lastId, updatedUntil, limit } = options;
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.$call(withExif)
|
||||
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
||||
.leftJoin('stack', 'stack.id', 'asset.stackId')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets as stacked')
|
||||
.selectAll('asset_stack')
|
||||
.selectFrom('asset as stacked')
|
||||
.selectAll('stack')
|
||||
.select((eb) => eb.fn.count(eb.table('stacked')).as('assetCount'))
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.groupBy('asset_stack.id')
|
||||
.whereRef('stacked.stackId', '=', 'stack.id')
|
||||
.groupBy('stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
(join) => join.on('stack.id', 'is not', null),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack'))
|
||||
.where('assets.ownerId', '=', asUuid(ownerId))
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.updatedAt', '<=', updatedUntil)
|
||||
.$if(!!lastId, (qb) => qb.where('assets.id', '>', lastId!))
|
||||
.orderBy('assets.id')
|
||||
.where('asset.ownerId', '=', asUuid(ownerId))
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.updatedAt', '<=', updatedUntil)
|
||||
.$if(!!lastId, (qb) => qb.where('asset.id', '>', lastId!))
|
||||
.orderBy('asset.id')
|
||||
.limit(limit)
|
||||
.execute();
|
||||
}
|
||||
@@ -723,25 +721,25 @@ export class AssetRepository {
|
||||
@GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE, limit: 100 }] })
|
||||
async getChangedDeltaSync(options: AssetDeltaSyncOptions) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.$call(withExif)
|
||||
.leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId')
|
||||
.leftJoin('stack', 'stack.id', 'asset.stackId')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets as stacked')
|
||||
.selectAll('asset_stack')
|
||||
.selectFrom('asset as stacked')
|
||||
.selectAll('stack')
|
||||
.select((eb) => eb.fn.count(eb.table('stacked')).as('assetCount'))
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.groupBy('asset_stack.id')
|
||||
.whereRef('stacked.stackId', '=', 'stack.id')
|
||||
.groupBy('stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
(join) => join.on('stack.id', 'is not', null),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo<Stack | null>()).as('stack'))
|
||||
.where('assets.ownerId', '=', anyUuid(options.userIds))
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.updatedAt', '>', options.updatedAfter)
|
||||
.where('asset.ownerId', '=', anyUuid(options.userIds))
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.updatedAt', '>', options.updatedAfter)
|
||||
.limit(options.limit)
|
||||
.execute();
|
||||
}
|
||||
@@ -749,7 +747,7 @@ export class AssetRepository {
|
||||
async upsertFile(file: Pick<Insertable<AssetFileTable>, 'assetId' | 'path' | 'type'>): Promise<void> {
|
||||
const value = { ...file, assetId: asUuid(file.assetId) };
|
||||
await this.db
|
||||
.insertInto('asset_files')
|
||||
.insertInto('asset_file')
|
||||
.values(value)
|
||||
.onConflict((oc) =>
|
||||
oc.columns(['assetId', 'type']).doUpdateSet((eb) => ({
|
||||
@@ -766,7 +764,7 @@ export class AssetRepository {
|
||||
|
||||
const values = files.map((row) => ({ ...row, assetId: asUuid(row.assetId) }));
|
||||
await this.db
|
||||
.insertInto('asset_files')
|
||||
.insertInto('asset_file')
|
||||
.values(values)
|
||||
.onConflict((oc) =>
|
||||
oc.columns(['assetId', 'type']).doUpdateSet((eb) => ({
|
||||
@@ -782,7 +780,7 @@ export class AssetRepository {
|
||||
}
|
||||
|
||||
await this.db
|
||||
.deleteFrom('asset_files')
|
||||
.deleteFrom('asset_file')
|
||||
.where('id', '=', anyUuid(files.map((file) => file.id)))
|
||||
.execute();
|
||||
}
|
||||
@@ -797,7 +795,7 @@ export class AssetRepository {
|
||||
const exclusions = exclusionPatterns.map((pattern) => globToSqlPattern(pattern));
|
||||
|
||||
return this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({
|
||||
isOffline: true,
|
||||
deletedAt: new Date(),
|
||||
@@ -823,9 +821,9 @@ export class AssetRepository {
|
||||
eb.not(
|
||||
eb.exists(
|
||||
this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select('originalPath')
|
||||
.whereRef('assets.originalPath', '=', eb.ref('path'))
|
||||
.whereRef('asset.originalPath', '=', eb.ref('path'))
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.where('isExternal', '=', true),
|
||||
),
|
||||
@@ -838,7 +836,7 @@ export class AssetRepository {
|
||||
|
||||
async getLibraryAssetCount(libraryId: string): Promise<number> {
|
||||
const { count } = await this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
@@ -7,34 +7,34 @@ import { anyUuid } from 'src/utils/database';
|
||||
|
||||
const builder = (db: Kysely<DB>) =>
|
||||
db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', 'assetId', 'id')
|
||||
.select(['assets.id', 'assets.livePhotoVideoId', 'exif.fileSizeInByte as size'])
|
||||
.where('assets.deletedAt', 'is', null);
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', 'assetId', 'id')
|
||||
.select(['asset.id', 'asset.livePhotoVideoId', 'asset_exif.fileSizeInByte as size'])
|
||||
.where('asset.deletedAt', 'is', null);
|
||||
|
||||
@Injectable()
|
||||
export class DownloadRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
downloadAssetIds(ids: string[]) {
|
||||
return builder(this.db).where('assets.id', '=', anyUuid(ids)).stream();
|
||||
return builder(this.db).where('asset.id', '=', anyUuid(ids)).stream();
|
||||
}
|
||||
|
||||
downloadMotionAssetIds(ids: string[]) {
|
||||
return builder(this.db).select(['assets.originalPath']).where('assets.id', '=', anyUuid(ids)).stream();
|
||||
return builder(this.db).select(['asset.originalPath']).where('asset.id', '=', anyUuid(ids)).stream();
|
||||
}
|
||||
|
||||
downloadAlbumId(albumId: string) {
|
||||
return builder(this.db)
|
||||
.innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId')
|
||||
.where('albums_assets_assets.albumsId', '=', albumId)
|
||||
.innerJoin('album_asset', 'asset.id', 'album_asset.assetsId')
|
||||
.where('album_asset.albumsId', '=', albumId)
|
||||
.stream();
|
||||
}
|
||||
|
||||
downloadUserId(userId: string) {
|
||||
return builder(this.db)
|
||||
.where('assets.ownerId', '=', userId)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('asset.ownerId', '=', userId)
|
||||
.where('asset.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,28 +32,28 @@ export class DuplicateRepository {
|
||||
this.db
|
||||
.with('duplicates', (qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.$call(withDefaultVisibility)
|
||||
.leftJoinLateral(
|
||||
(qb) =>
|
||||
qb
|
||||
.selectFrom('exif')
|
||||
.selectAll('assets')
|
||||
.select((eb) => eb.table('exif').as('exifInfo'))
|
||||
.whereRef('exif.assetId', '=', 'assets.id')
|
||||
.as('asset'),
|
||||
.selectFrom('asset_exif')
|
||||
.selectAll('asset')
|
||||
.select((eb) => eb.table('asset_exif').as('exifInfo'))
|
||||
.whereRef('asset_exif.assetId', '=', 'asset.id')
|
||||
.as('asset2'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select('assets.duplicateId')
|
||||
.select('asset.duplicateId')
|
||||
.select((eb) =>
|
||||
eb.fn.jsonAgg('asset').orderBy('assets.localDateTime', 'asc').$castTo<MapAsset[]>().as('assets'),
|
||||
eb.fn.jsonAgg('asset2').orderBy('asset.localDateTime', 'asc').$castTo<MapAsset[]>().as('assets'),
|
||||
)
|
||||
.where('assets.ownerId', '=', asUuid(userId))
|
||||
.where('assets.duplicateId', 'is not', null)
|
||||
.where('asset.ownerId', '=', asUuid(userId))
|
||||
.where('asset.duplicateId', 'is not', null)
|
||||
.$narrowType<{ duplicateId: NotNull }>()
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.stackId', 'is', null)
|
||||
.groupBy('assets.duplicateId'),
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.where('asset.stackId', 'is', null)
|
||||
.groupBy('asset.duplicateId'),
|
||||
)
|
||||
.with('unique', (qb) =>
|
||||
qb
|
||||
@@ -63,10 +63,10 @@ export class DuplicateRepository {
|
||||
)
|
||||
.with('removed_unique', (qb) =>
|
||||
qb
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({ duplicateId: null })
|
||||
.from('unique')
|
||||
.whereRef('assets.duplicateId', '=', 'unique.duplicateId'),
|
||||
.whereRef('asset.duplicateId', '=', 'unique.duplicateId'),
|
||||
)
|
||||
.selectFrom('duplicates')
|
||||
.selectAll()
|
||||
@@ -81,7 +81,7 @@ export class DuplicateRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
async delete(userId: string, id: string): Promise<void> {
|
||||
await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({ duplicateId: null })
|
||||
.where('ownerId', '=', userId)
|
||||
.where('duplicateId', '=', id)
|
||||
@@ -96,7 +96,7 @@ export class DuplicateRepository {
|
||||
}
|
||||
|
||||
await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({ duplicateId: null })
|
||||
.where('ownerId', '=', userId)
|
||||
.where('duplicateId', 'in', ids)
|
||||
@@ -120,19 +120,19 @@ export class DuplicateRepository {
|
||||
return await trx
|
||||
.with('cte', (qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.$call(withDefaultVisibility)
|
||||
.select([
|
||||
'assets.id as assetId',
|
||||
'assets.duplicateId',
|
||||
'asset.id as assetId',
|
||||
'asset.duplicateId',
|
||||
sql<number>`smart_search.embedding <=> ${embedding}`.as('distance'),
|
||||
])
|
||||
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.type', '=', type)
|
||||
.where('assets.id', '!=', asUuid(assetId))
|
||||
.where('assets.stackId', 'is', null)
|
||||
.innerJoin('smart_search', 'asset.id', 'smart_search.assetId')
|
||||
.where('asset.ownerId', '=', anyUuid(userIds))
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.where('asset.type', '=', type)
|
||||
.where('asset.id', '!=', asUuid(assetId))
|
||||
.where('asset.stackId', 'is', null)
|
||||
.orderBy('distance')
|
||||
.limit(64),
|
||||
)
|
||||
@@ -148,7 +148,7 @@ export class DuplicateRepository {
|
||||
})
|
||||
async merge(options: DuplicateMergeOptions): Promise<void> {
|
||||
await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({ duplicateId: options.targetId })
|
||||
.where((eb) =>
|
||||
eb.or([eb('duplicateId', '=', anyUuid(options.sourceIds)), eb('id', '=', anyUuid(options.assetIds))]),
|
||||
|
||||
@@ -21,50 +21,50 @@ export class LibraryRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
get(id: string, withDeleted = false) {
|
||||
return this.db
|
||||
.selectFrom('libraries')
|
||||
.selectAll('libraries')
|
||||
.where('libraries.id', '=', id)
|
||||
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||
.selectFrom('library')
|
||||
.selectAll('library')
|
||||
.where('library.id', '=', id)
|
||||
.$if(!withDeleted, (qb) => qb.where('library.deletedAt', 'is', null))
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [] })
|
||||
getAll(withDeleted = false) {
|
||||
return this.db
|
||||
.selectFrom('libraries')
|
||||
.selectAll('libraries')
|
||||
.selectFrom('library')
|
||||
.selectAll('library')
|
||||
.orderBy('createdAt', 'asc')
|
||||
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||
.$if(!withDeleted, (qb) => qb.where('library.deletedAt', 'is', null))
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql()
|
||||
getAllDeleted() {
|
||||
return this.db
|
||||
.selectFrom('libraries')
|
||||
.selectAll('libraries')
|
||||
.where('libraries.deletedAt', 'is not', null)
|
||||
.selectFrom('library')
|
||||
.selectAll('library')
|
||||
.where('library.deletedAt', 'is not', null)
|
||||
.orderBy('createdAt', 'asc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
create(library: Insertable<LibraryTable>) {
|
||||
return this.db.insertInto('libraries').values(library).returningAll().executeTakeFirstOrThrow();
|
||||
return this.db.insertInto('library').values(library).returningAll().executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
await this.db.deleteFrom('libraries').where('libraries.id', '=', id).execute();
|
||||
await this.db.deleteFrom('library').where('library.id', '=', id).execute();
|
||||
}
|
||||
|
||||
async softDelete(id: string) {
|
||||
await this.db.updateTable('libraries').set({ deletedAt: new Date() }).where('libraries.id', '=', id).execute();
|
||||
await this.db.updateTable('library').set({ deletedAt: new Date() }).where('library.id', '=', id).execute();
|
||||
}
|
||||
|
||||
update(id: string, library: Updateable<LibraryTable>) {
|
||||
return this.db
|
||||
.updateTable('libraries')
|
||||
.updateTable('library')
|
||||
.set(library)
|
||||
.where('libraries.id', '=', id)
|
||||
.where('library.id', '=', id)
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
@@ -72,14 +72,14 @@ export class LibraryRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined> {
|
||||
const stats = await this.db
|
||||
.selectFrom('libraries')
|
||||
.innerJoin('assets', 'assets.libraryId', 'libraries.id')
|
||||
.leftJoin('exif', 'exif.assetId', 'assets.id')
|
||||
.selectFrom('library')
|
||||
.innerJoin('asset', 'asset.libraryId', 'library.id')
|
||||
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||
.select((eb) =>
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
eb.and([eb('asset.type', '=', AssetType.IMAGE), eb('asset.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
)
|
||||
.as('photos'),
|
||||
)
|
||||
@@ -87,25 +87,25 @@ export class LibraryRepository {
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
eb.and([eb('asset.type', '=', AssetType.VIDEO), eb('asset.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
)
|
||||
.as('videos'),
|
||||
)
|
||||
.select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('exif.fileSizeInByte'), eb.val(0)).as('usage'))
|
||||
.groupBy('libraries.id')
|
||||
.where('libraries.id', '=', id)
|
||||
.select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('asset_exif.fileSizeInByte'), eb.val(0)).as('usage'))
|
||||
.groupBy('library.id')
|
||||
.where('library.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
|
||||
// possibly a new library with 0 assets
|
||||
if (!stats) {
|
||||
const zero = sql<number>`0::int`;
|
||||
return this.db
|
||||
.selectFrom('libraries')
|
||||
.selectFrom('library')
|
||||
.select(zero.as('photos'))
|
||||
.select(zero.as('videos'))
|
||||
.select(zero.as('usage'))
|
||||
.select(zero.as('total'))
|
||||
.where('libraries.id', '=', id)
|
||||
.where('library.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@@ -118,6 +118,6 @@ export class LibraryRepository {
|
||||
}
|
||||
|
||||
streamAssetIds(libraryId: string) {
|
||||
return this.db.selectFrom('assets').select(['id']).where('libraryId', '=', libraryId).stream();
|
||||
return this.db.selectFrom('asset').select(['id']).where('libraryId', '=', libraryId).stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,25 +83,32 @@ export class MapRepository {
|
||||
{ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore }: MapMarkerSearchOptions = {},
|
||||
) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', (builder) =>
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', (builder) =>
|
||||
builder
|
||||
.onRef('assets.id', '=', 'exif.assetId')
|
||||
.on('exif.latitude', 'is not', null)
|
||||
.on('exif.longitude', 'is not', null),
|
||||
.onRef('asset.id', '=', 'asset_exif.assetId')
|
||||
.on('asset_exif.latitude', 'is not', null)
|
||||
.on('asset_exif.longitude', 'is not', null),
|
||||
)
|
||||
.select(['id', 'exif.latitude as lat', 'exif.longitude as lon', 'exif.city', 'exif.state', 'exif.country'])
|
||||
.select([
|
||||
'id',
|
||||
'asset_exif.latitude as lat',
|
||||
'asset_exif.longitude as lon',
|
||||
'asset_exif.city',
|
||||
'asset_exif.state',
|
||||
'asset_exif.country',
|
||||
])
|
||||
.$narrowType<{ lat: NotNull; lon: NotNull }>()
|
||||
.$if(isArchived === true, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.or([
|
||||
eb('assets.visibility', '=', AssetVisibility.TIMELINE),
|
||||
eb('assets.visibility', '=', AssetVisibility.ARCHIVE),
|
||||
eb('asset.visibility', '=', AssetVisibility.TIMELINE),
|
||||
eb('asset.visibility', '=', AssetVisibility.ARCHIVE),
|
||||
]),
|
||||
),
|
||||
)
|
||||
.$if(isArchived === false || isArchived === undefined, (qb) =>
|
||||
qb.where('assets.visibility', '=', AssetVisibility.TIMELINE),
|
||||
qb.where('asset.visibility', '=', AssetVisibility.TIMELINE),
|
||||
)
|
||||
.$if(isFavorite !== undefined, (q) => q.where('isFavorite', '=', isFavorite!))
|
||||
.$if(fileCreatedAfter !== undefined, (q) => q.where('fileCreatedAt', '>=', fileCreatedAfter!))
|
||||
@@ -118,9 +125,9 @@ export class MapRepository {
|
||||
expression.push(
|
||||
eb.exists((eb) =>
|
||||
eb
|
||||
.selectFrom('albums_assets_assets')
|
||||
.whereRef('assets.id', '=', 'albums_assets_assets.assetsId')
|
||||
.where('albums_assets_assets.albumsId', 'in', albumIds),
|
||||
.selectFrom('album_asset')
|
||||
.whereRef('asset.id', '=', 'album_asset.assetsId')
|
||||
.where('album_asset.albumsId', 'in', albumIds),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ export class MemoryRepository implements IBulkAsset {
|
||||
|
||||
async cleanup() {
|
||||
await this.db
|
||||
.deleteFrom('memories_assets_assets')
|
||||
.using('assets')
|
||||
.whereRef('memories_assets_assets.assetsId', '=', 'assets.id')
|
||||
.where('assets.visibility', '!=', AssetVisibility.TIMELINE)
|
||||
.deleteFrom('memory_asset')
|
||||
.using('asset')
|
||||
.whereRef('memory_asset.assetsId', '=', 'asset.id')
|
||||
.where('asset.visibility', '!=', AssetVisibility.TIMELINE)
|
||||
.execute();
|
||||
|
||||
return this.db
|
||||
.deleteFrom('memories')
|
||||
.deleteFrom('memory')
|
||||
.where('createdAt', '<', DateTime.now().minus({ days: 30 }).toJSDate())
|
||||
.where('isSaved', '=', false)
|
||||
.execute();
|
||||
@@ -31,7 +31,7 @@ export class MemoryRepository implements IBulkAsset {
|
||||
|
||||
searchBuilder(ownerId: string, dto: MemorySearchDto) {
|
||||
return this.db
|
||||
.selectFrom('memories')
|
||||
.selectFrom('memory')
|
||||
.$if(dto.isSaved !== undefined, (qb) => qb.where('isSaved', '=', dto.isSaved!))
|
||||
.$if(dto.type !== undefined, (qb) => qb.where('type', '=', dto.type!))
|
||||
.$if(dto.for !== undefined, (qb) =>
|
||||
@@ -62,16 +62,16 @@ export class MemoryRepository implements IBulkAsset {
|
||||
.select((eb) =>
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId')
|
||||
.whereRef('memories_assets_assets.memoriesId', '=', 'memories.id')
|
||||
.orderBy('assets.fileCreatedAt', 'asc')
|
||||
.where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('assets.deletedAt', 'is', null),
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.innerJoin('memory_asset', 'asset.id', 'memory_asset.assetsId')
|
||||
.whereRef('memory_asset.memoriesId', '=', 'memory.id')
|
||||
.orderBy('asset.fileCreatedAt', 'asc')
|
||||
.where('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('asset.deletedAt', 'is', null),
|
||||
).as('assets'),
|
||||
)
|
||||
.selectAll('memories')
|
||||
.selectAll('memory')
|
||||
.orderBy('memoryAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
@@ -83,11 +83,11 @@ export class MemoryRepository implements IBulkAsset {
|
||||
|
||||
async create(memory: Insertable<MemoryTable>, assetIds: Set<string>) {
|
||||
const id = await this.db.transaction().execute(async (tx) => {
|
||||
const { id } = await tx.insertInto('memories').values(memory).returning('id').executeTakeFirstOrThrow();
|
||||
const { id } = await tx.insertInto('memory').values(memory).returning('id').executeTakeFirstOrThrow();
|
||||
|
||||
if (assetIds.size > 0) {
|
||||
const values = [...assetIds].map((assetId) => ({ memoriesId: id, assetsId: assetId }));
|
||||
await tx.insertInto('memories_assets_assets').values(values).execute();
|
||||
await tx.insertInto('memory_asset').values(values).execute();
|
||||
}
|
||||
|
||||
return id;
|
||||
@@ -98,13 +98,13 @@ export class MemoryRepository implements IBulkAsset {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { ownerId: DummyValue.UUID, isSaved: true }] })
|
||||
async update(id: string, memory: Updateable<MemoryTable>) {
|
||||
await this.db.updateTable('memories').set(memory).where('id', '=', id).execute();
|
||||
await this.db.updateTable('memory').set(memory).where('id', '=', id).execute();
|
||||
return this.getByIdBuilder(id).executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async delete(id: string) {
|
||||
await this.db.deleteFrom('memories').where('id', '=', id).execute();
|
||||
await this.db.deleteFrom('memory').where('id', '=', id).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||
@@ -115,7 +115,7 @@ export class MemoryRepository implements IBulkAsset {
|
||||
}
|
||||
|
||||
const results = await this.db
|
||||
.selectFrom('memories_assets_assets')
|
||||
.selectFrom('memory_asset')
|
||||
.select(['assetsId'])
|
||||
.where('memoriesId', '=', id)
|
||||
.where('assetsId', 'in', assetIds)
|
||||
@@ -131,7 +131,7 @@ export class MemoryRepository implements IBulkAsset {
|
||||
}
|
||||
|
||||
await this.db
|
||||
.insertInto('memories_assets_assets')
|
||||
.insertInto('memory_asset')
|
||||
.values(assetIds.map((assetId) => ({ memoriesId: id, assetsId: assetId })))
|
||||
.execute();
|
||||
}
|
||||
@@ -143,27 +143,23 @@ export class MemoryRepository implements IBulkAsset {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.db
|
||||
.deleteFrom('memories_assets_assets')
|
||||
.where('memoriesId', '=', id)
|
||||
.where('assetsId', 'in', assetIds)
|
||||
.execute();
|
||||
await this.db.deleteFrom('memory_asset').where('memoriesId', '=', id).where('assetsId', 'in', assetIds).execute();
|
||||
}
|
||||
|
||||
private getByIdBuilder(id: string) {
|
||||
return this.db
|
||||
.selectFrom('memories')
|
||||
.selectAll('memories')
|
||||
.selectFrom('memory')
|
||||
.selectAll('memory')
|
||||
.select((eb) =>
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId')
|
||||
.whereRef('memories_assets_assets.memoriesId', '=', 'memories.id')
|
||||
.orderBy('assets.fileCreatedAt', 'asc')
|
||||
.where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('assets.deletedAt', 'is', null),
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.innerJoin('memory_asset', 'asset.id', 'memory_asset.assetsId')
|
||||
.whereRef('memory_asset.memoriesId', '=', 'memory.id')
|
||||
.orderBy('asset.fileCreatedAt', 'asc')
|
||||
.where('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('asset.deletedAt', 'is', null),
|
||||
).as('assets'),
|
||||
)
|
||||
.where('id', '=', id)
|
||||
|
||||
@@ -45,7 +45,7 @@ export class MoveRepository {
|
||||
eb(
|
||||
'move_history.entityId',
|
||||
'not in',
|
||||
eb.selectFrom('assets').select('id').whereRef('assets.id', '=', 'move_history.entityId'),
|
||||
eb.selectFrom('asset').select('id').whereRef('asset.id', '=', 'move_history.entityId'),
|
||||
),
|
||||
)
|
||||
.where('move_history.pathType', '=', sql.lit(AssetPathType.ORIGINAL))
|
||||
|
||||
@@ -12,7 +12,7 @@ export class NotificationRepository {
|
||||
|
||||
cleanup() {
|
||||
return this.db
|
||||
.deleteFrom('notifications')
|
||||
.deleteFrom('notification')
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
// remove soft-deleted notifications
|
||||
@@ -38,7 +38,7 @@ export class NotificationRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, {}] }, { name: 'unread', params: [DummyValue.UUID, { unread: true }] })
|
||||
search(userId: string, dto: NotificationSearchDto) {
|
||||
return this.db
|
||||
.selectFrom('notifications')
|
||||
.selectFrom('notification')
|
||||
.select(columns.notification)
|
||||
.where((qb) =>
|
||||
qb.and({
|
||||
@@ -56,7 +56,7 @@ export class NotificationRepository {
|
||||
|
||||
create(notification: Insertable<NotificationTable>) {
|
||||
return this.db
|
||||
.insertInto('notifications')
|
||||
.insertInto('notification')
|
||||
.values(notification)
|
||||
.returning(columns.notification)
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -64,7 +64,7 @@ export class NotificationRepository {
|
||||
|
||||
get(id: string) {
|
||||
return this.db
|
||||
.selectFrom('notifications')
|
||||
.selectFrom('notification')
|
||||
.select(columns.notification)
|
||||
.where('id', '=', id)
|
||||
.where('deletedAt', 'is not', null)
|
||||
@@ -73,7 +73,7 @@ export class NotificationRepository {
|
||||
|
||||
update(id: string, notification: Updateable<NotificationTable>) {
|
||||
return this.db
|
||||
.updateTable('notifications')
|
||||
.updateTable('notification')
|
||||
.set(notification)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where('id', '=', id)
|
||||
@@ -82,12 +82,12 @@ export class NotificationRepository {
|
||||
}
|
||||
|
||||
async updateAll(ids: string[], notification: Updateable<NotificationTable>) {
|
||||
await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute();
|
||||
await this.db.updateTable('notification').set(notification).where('id', 'in', ids).execute();
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
await this.db
|
||||
.updateTable('notifications')
|
||||
.updateTable('notification')
|
||||
.set({ deletedAt: DateTime.now().toJSDate() })
|
||||
.where('id', '=', id)
|
||||
.execute();
|
||||
@@ -95,7 +95,7 @@ export class NotificationRepository {
|
||||
|
||||
async deleteAll(ids: string[]) {
|
||||
await this.db
|
||||
.updateTable('notifications')
|
||||
.updateTable('notification')
|
||||
.set({ deletedAt: DateTime.now().toJSDate() })
|
||||
.where('id', 'in', ids)
|
||||
.execute();
|
||||
|
||||
@@ -17,15 +17,15 @@ export enum PartnerDirection {
|
||||
SharedWith = 'shared-with',
|
||||
}
|
||||
|
||||
const withSharedBy = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
||||
const withSharedBy = (eb: ExpressionBuilder<DB, 'partner'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('users as sharedBy').select(columns.user).whereRef('sharedBy.id', '=', 'partners.sharedById'),
|
||||
eb.selectFrom('user as sharedBy').select(columns.user).whereRef('sharedBy.id', '=', 'partner.sharedById'),
|
||||
).as('sharedBy');
|
||||
};
|
||||
|
||||
const withSharedWith = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
||||
const withSharedWith = (eb: ExpressionBuilder<DB, 'partner'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('users as sharedWith').select(columns.user).whereRef('sharedWith.id', '=', 'partners.sharedWithId'),
|
||||
eb.selectFrom('user as sharedWith').select(columns.user).whereRef('sharedWith.id', '=', 'partner.sharedWithId'),
|
||||
).as('sharedWith');
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ export class PartnerRepository {
|
||||
|
||||
create(values: Insertable<PartnerTable>) {
|
||||
return this.db
|
||||
.insertInto('partners')
|
||||
.insertInto('partner')
|
||||
.values(values)
|
||||
.returningAll()
|
||||
.returning(withSharedBy)
|
||||
@@ -62,7 +62,7 @@ export class PartnerRepository {
|
||||
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }, { inTimeline: true }] })
|
||||
update({ sharedWithId, sharedById }: PartnerIds, values: Updateable<PartnerTable>) {
|
||||
return this.db
|
||||
.updateTable('partners')
|
||||
.updateTable('partner')
|
||||
.set(values)
|
||||
.where('sharedWithId', '=', sharedWithId)
|
||||
.where('sharedById', '=', sharedById)
|
||||
@@ -76,7 +76,7 @@ export class PartnerRepository {
|
||||
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }] })
|
||||
async remove({ sharedWithId, sharedById }: PartnerIds) {
|
||||
await this.db
|
||||
.deleteFrom('partners')
|
||||
.deleteFrom('partner')
|
||||
.where('sharedWithId', '=', sharedWithId)
|
||||
.where('sharedById', '=', sharedById)
|
||||
.execute();
|
||||
@@ -84,14 +84,14 @@ export class PartnerRepository {
|
||||
|
||||
private builder() {
|
||||
return this.db
|
||||
.selectFrom('partners')
|
||||
.innerJoin('users as sharedBy', (join) =>
|
||||
join.onRef('partners.sharedById', '=', 'sharedBy.id').on('sharedBy.deletedAt', 'is', null),
|
||||
.selectFrom('partner')
|
||||
.innerJoin('user as sharedBy', (join) =>
|
||||
join.onRef('partner.sharedById', '=', 'sharedBy.id').on('sharedBy.deletedAt', 'is', null),
|
||||
)
|
||||
.innerJoin('users as sharedWith', (join) =>
|
||||
join.onRef('partners.sharedWithId', '=', 'sharedWith.id').on('sharedWith.deletedAt', 'is', null),
|
||||
.innerJoin('user as sharedWith', (join) =>
|
||||
join.onRef('partner.sharedWithId', '=', 'sharedWith.id').on('sharedWith.deletedAt', 'is', null),
|
||||
)
|
||||
.selectAll('partners')
|
||||
.selectAll('partner')
|
||||
.select(withSharedBy)
|
||||
.select(withSharedWith);
|
||||
}
|
||||
|
||||
@@ -62,21 +62,21 @@ export type UnassignFacesOptions = DeleteFacesOptions;
|
||||
|
||||
export type SelectFaceOptions = (keyof Selectable<AssetFaceTable>)[];
|
||||
|
||||
const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||
const withPerson = (eb: ExpressionBuilder<DB, 'asset_face'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('person').selectAll('person').whereRef('person.id', '=', 'asset_faces.personId'),
|
||||
eb.selectFrom('person').selectAll('person').whereRef('person.id', '=', 'asset_face.personId'),
|
||||
).as('person');
|
||||
};
|
||||
|
||||
const withAsset = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('assets').selectAll('assets').whereRef('assets.id', '=', 'asset_faces.assetId'),
|
||||
).as('asset');
|
||||
const withAsset = (eb: ExpressionBuilder<DB, 'asset_face'>) => {
|
||||
return jsonObjectFrom(eb.selectFrom('asset').selectAll('asset').whereRef('asset.id', '=', 'asset_face.assetId')).as(
|
||||
'asset',
|
||||
);
|
||||
};
|
||||
|
||||
const withFaceSearch = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||
const withFaceSearch = (eb: ExpressionBuilder<DB, 'asset_face'>) => {
|
||||
return jsonObjectFrom(
|
||||
eb.selectFrom('face_search').selectAll('face_search').whereRef('face_search.faceId', '=', 'asset_faces.id'),
|
||||
eb.selectFrom('face_search').selectAll('face_search').whereRef('face_search.faceId', '=', 'asset_face.id'),
|
||||
).as('faceSearch');
|
||||
};
|
||||
|
||||
@@ -87,10 +87,10 @@ export class PersonRepository {
|
||||
@GenerateSql({ params: [{ oldPersonId: DummyValue.UUID, newPersonId: DummyValue.UUID }] })
|
||||
async reassignFaces({ oldPersonId, faceIds, newPersonId }: UpdateFacesData): Promise<number> {
|
||||
const result = await this.db
|
||||
.updateTable('asset_faces')
|
||||
.updateTable('asset_face')
|
||||
.set({ personId: newPersonId })
|
||||
.$if(!!oldPersonId, (qb) => qb.where('asset_faces.personId', '=', oldPersonId!))
|
||||
.$if(!!faceIds, (qb) => qb.where('asset_faces.id', 'in', faceIds!))
|
||||
.$if(!!oldPersonId, (qb) => qb.where('asset_face.personId', '=', oldPersonId!))
|
||||
.$if(!!faceIds, (qb) => qb.where('asset_face.id', 'in', faceIds!))
|
||||
.executeTakeFirst();
|
||||
|
||||
return Number(result.numChangedRows ?? 0);
|
||||
@@ -98,9 +98,9 @@ export class PersonRepository {
|
||||
|
||||
async unassignFaces({ sourceType }: UnassignFacesOptions): Promise<void> {
|
||||
await this.db
|
||||
.updateTable('asset_faces')
|
||||
.updateTable('asset_face')
|
||||
.set({ personId: null })
|
||||
.where('asset_faces.sourceType', '=', sourceType)
|
||||
.where('asset_face.sourceType', '=', sourceType)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -115,18 +115,18 @@ export class PersonRepository {
|
||||
}
|
||||
|
||||
async deleteFaces({ sourceType }: DeleteFacesOptions): Promise<void> {
|
||||
await this.db.deleteFrom('asset_faces').where('asset_faces.sourceType', '=', sourceType).execute();
|
||||
await this.db.deleteFrom('asset_face').where('asset_face.sourceType', '=', sourceType).execute();
|
||||
}
|
||||
|
||||
getAllFaces(options: GetAllFacesOptions = {}) {
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.selectAll('asset_faces')
|
||||
.$if(options.personId === null, (qb) => qb.where('asset_faces.personId', 'is', null))
|
||||
.$if(!!options.personId, (qb) => qb.where('asset_faces.personId', '=', options.personId!))
|
||||
.$if(!!options.sourceType, (qb) => qb.where('asset_faces.sourceType', '=', options.sourceType!))
|
||||
.$if(!!options.assetId, (qb) => qb.where('asset_faces.assetId', '=', options.assetId!))
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.selectFrom('asset_face')
|
||||
.selectAll('asset_face')
|
||||
.$if(options.personId === null, (qb) => qb.where('asset_face.personId', 'is', null))
|
||||
.$if(!!options.personId, (qb) => qb.where('asset_face.personId', '=', options.personId!))
|
||||
.$if(!!options.sourceType, (qb) => qb.where('asset_face.sourceType', '=', options.sourceType!))
|
||||
.$if(!!options.assetId, (qb) => qb.where('asset_face.assetId', '=', options.assetId!))
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.stream();
|
||||
}
|
||||
|
||||
@@ -147,21 +147,21 @@ export class PersonRepository {
|
||||
const items = await this.db
|
||||
.selectFrom('person')
|
||||
.selectAll('person')
|
||||
.innerJoin('asset_faces', 'asset_faces.personId', 'person.id')
|
||||
.innerJoin('assets', (join) =>
|
||||
.innerJoin('asset_face', 'asset_face.personId', 'person.id')
|
||||
.innerJoin('asset', (join) =>
|
||||
join
|
||||
.onRef('asset_faces.assetId', '=', 'assets.id')
|
||||
.on('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.on('assets.deletedAt', 'is', null),
|
||||
.onRef('asset_face.assetId', '=', 'asset.id')
|
||||
.on('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.on('asset.deletedAt', 'is', null),
|
||||
)
|
||||
.where('person.ownerId', '=', userId)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.orderBy('person.isHidden', 'asc')
|
||||
.orderBy('person.isFavorite', 'desc')
|
||||
.having((eb) =>
|
||||
eb.or([
|
||||
eb('person.name', '!=', ''),
|
||||
eb((innerEb) => innerEb.fn.count('asset_faces.assetId'), '>=', options?.minimumFaceCount || 1),
|
||||
eb((innerEb) => innerEb.fn.count('asset_face.assetId'), '>=', options?.minimumFaceCount || 1),
|
||||
]),
|
||||
)
|
||||
.groupBy('person.id')
|
||||
@@ -185,7 +185,7 @@ export class PersonRepository {
|
||||
.$if(!options?.closestFaceAssetId, (qb) =>
|
||||
qb
|
||||
.orderBy(sql`NULLIF(person.name, '') is null`, 'asc')
|
||||
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
||||
.orderBy((eb) => eb.fn.count('asset_face.assetId'), 'desc')
|
||||
.orderBy(sql`NULLIF(person.name, '')`, (om) => om.asc().nullsLast())
|
||||
.orderBy('person.createdAt'),
|
||||
)
|
||||
@@ -202,9 +202,9 @@ export class PersonRepository {
|
||||
return this.db
|
||||
.selectFrom('person')
|
||||
.selectAll('person')
|
||||
.leftJoin('asset_faces', 'asset_faces.personId', 'person.id')
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.having((eb) => eb.fn.count('asset_faces.assetId'), '=', 0)
|
||||
.leftJoin('asset_face', 'asset_face.personId', 'person.id')
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.having((eb) => eb.fn.count('asset_face.assetId'), '=', 0)
|
||||
.groupBy('person.id')
|
||||
.execute();
|
||||
}
|
||||
@@ -212,12 +212,12 @@ export class PersonRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getFaces(assetId: string) {
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.selectAll('asset_faces')
|
||||
.selectFrom('asset_face')
|
||||
.selectAll('asset_face')
|
||||
.select(withPerson)
|
||||
.where('asset_faces.assetId', '=', assetId)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.orderBy('asset_faces.boundingBoxX1', 'asc')
|
||||
.where('asset_face.assetId', '=', assetId)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.orderBy('asset_face.boundingBoxX1', 'asc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -225,30 +225,30 @@ export class PersonRepository {
|
||||
getFaceById(id: string) {
|
||||
// TODO return null instead of find or fail
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.selectAll('asset_faces')
|
||||
.selectFrom('asset_face')
|
||||
.selectAll('asset_face')
|
||||
.select(withPerson)
|
||||
.where('asset_faces.id', '=', id)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.where('asset_face.id', '=', id)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getFaceForFacialRecognitionJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.select(['asset_faces.id', 'asset_faces.personId', 'asset_faces.sourceType'])
|
||||
.selectFrom('asset_face')
|
||||
.select(['asset_face.id', 'asset_face.personId', 'asset_face.sourceType'])
|
||||
.select((eb) =>
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.select(['assets.ownerId', 'assets.visibility', 'assets.fileCreatedAt'])
|
||||
.whereRef('assets.id', '=', 'asset_faces.assetId'),
|
||||
.selectFrom('asset')
|
||||
.select(['asset.ownerId', 'asset.visibility', 'asset.fileCreatedAt'])
|
||||
.whereRef('asset.id', '=', 'asset_face.assetId'),
|
||||
).as('asset'),
|
||||
)
|
||||
.select(withFaceSearch)
|
||||
.where('asset_faces.id', '=', id)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.where('asset_face.id', '=', id)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@@ -256,40 +256,40 @@ export class PersonRepository {
|
||||
getDataForThumbnailGenerationJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('person')
|
||||
.innerJoin('asset_faces', 'asset_faces.id', 'person.faceAssetId')
|
||||
.innerJoin('assets', 'asset_faces.assetId', 'assets.id')
|
||||
.leftJoin('exif', 'exif.assetId', 'assets.id')
|
||||
.innerJoin('asset_face', 'asset_face.id', 'person.faceAssetId')
|
||||
.innerJoin('asset', 'asset_face.assetId', 'asset.id')
|
||||
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||
.select([
|
||||
'person.ownerId',
|
||||
'asset_faces.boundingBoxX1 as x1',
|
||||
'asset_faces.boundingBoxY1 as y1',
|
||||
'asset_faces.boundingBoxX2 as x2',
|
||||
'asset_faces.boundingBoxY2 as y2',
|
||||
'asset_faces.imageWidth as oldWidth',
|
||||
'asset_faces.imageHeight as oldHeight',
|
||||
'assets.type',
|
||||
'assets.originalPath',
|
||||
'exif.orientation as exifOrientation',
|
||||
'asset_face.boundingBoxX1 as x1',
|
||||
'asset_face.boundingBoxY1 as y1',
|
||||
'asset_face.boundingBoxX2 as x2',
|
||||
'asset_face.boundingBoxY2 as y2',
|
||||
'asset_face.imageWidth as oldWidth',
|
||||
'asset_face.imageHeight as oldHeight',
|
||||
'asset.type',
|
||||
'asset.originalPath',
|
||||
'asset_exif.orientation as exifOrientation',
|
||||
])
|
||||
.select((eb) =>
|
||||
eb
|
||||
.selectFrom('asset_files')
|
||||
.select('asset_files.path')
|
||||
.whereRef('asset_files.assetId', '=', 'assets.id')
|
||||
.where('asset_files.type', '=', sql.lit(AssetFileType.PREVIEW))
|
||||
.selectFrom('asset_file')
|
||||
.select('asset_file.path')
|
||||
.whereRef('asset_file.assetId', '=', 'asset.id')
|
||||
.where('asset_file.type', '=', sql.lit(AssetFileType.PREVIEW))
|
||||
.as('previewPath'),
|
||||
)
|
||||
.where('person.id', '=', id)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
async reassignFace(assetFaceId: string, newPersonId: string): Promise<number> {
|
||||
const result = await this.db
|
||||
.updateTable('asset_faces')
|
||||
.updateTable('asset_face')
|
||||
.set({ personId: newPersonId })
|
||||
.where('asset_faces.id', '=', assetFaceId)
|
||||
.where('asset_face.id', '=', assetFaceId)
|
||||
.executeTakeFirst();
|
||||
|
||||
return Number(result.numChangedRows ?? 0);
|
||||
@@ -336,16 +336,16 @@ export class PersonRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getStatistics(personId: string): Promise<PersonStatistics> {
|
||||
const result = await this.db
|
||||
.selectFrom('asset_faces')
|
||||
.leftJoin('assets', (join) =>
|
||||
.selectFrom('asset_face')
|
||||
.leftJoin('asset', (join) =>
|
||||
join
|
||||
.onRef('assets.id', '=', 'asset_faces.assetId')
|
||||
.on('asset_faces.personId', '=', personId)
|
||||
.on('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.on('assets.deletedAt', 'is', null),
|
||||
.onRef('asset.id', '=', 'asset_face.assetId')
|
||||
.on('asset_face.personId', '=', personId)
|
||||
.on('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.on('asset.deletedAt', 'is', null),
|
||||
)
|
||||
.select((eb) => eb.fn.count(eb.fn('distinct', ['assets.id'])).as('count'))
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.select((eb) => eb.fn.count(eb.fn('distinct', ['asset.id'])).as('count'))
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
|
||||
return {
|
||||
@@ -361,16 +361,16 @@ export class PersonRepository {
|
||||
.where((eb) =>
|
||||
eb.exists((eb) =>
|
||||
eb
|
||||
.selectFrom('asset_faces')
|
||||
.whereRef('asset_faces.personId', '=', 'person.id')
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.selectFrom('asset_face')
|
||||
.whereRef('asset_face.personId', '=', 'person.id')
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.where((eb) =>
|
||||
eb.exists((eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.whereRef('assets.id', '=', 'asset_faces.assetId')
|
||||
.where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('assets.deletedAt', 'is', null),
|
||||
.selectFrom('asset')
|
||||
.whereRef('asset.id', '=', 'asset_face.assetId')
|
||||
.where('asset.visibility', '=', sql.lit(AssetVisibility.TIMELINE))
|
||||
.where('asset.deletedAt', 'is', null),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -402,12 +402,12 @@ export class PersonRepository {
|
||||
): Promise<void> {
|
||||
let query = this.db;
|
||||
if (facesToAdd.length > 0) {
|
||||
(query as any) = query.with('added', (db) => db.insertInto('asset_faces').values(facesToAdd));
|
||||
(query as any) = query.with('added', (db) => db.insertInto('asset_face').values(facesToAdd));
|
||||
}
|
||||
|
||||
if (faceIdsToRemove.length > 0) {
|
||||
(query as any) = query.with('removed', (db) =>
|
||||
db.deleteFrom('asset_faces').where('asset_faces.id', '=', (eb) => eb.fn.any(eb.val(faceIdsToRemove))),
|
||||
db.deleteFrom('asset_face').where('asset_face.id', '=', (eb) => eb.fn.any(eb.val(faceIdsToRemove))),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -469,23 +469,23 @@ export class PersonRepository {
|
||||
}
|
||||
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.selectAll('asset_faces')
|
||||
.selectFrom('asset_face')
|
||||
.selectAll('asset_face')
|
||||
.select(withAsset)
|
||||
.select(withPerson)
|
||||
.where('asset_faces.assetId', 'in', assetIds)
|
||||
.where('asset_faces.personId', 'in', personIds)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.where('asset_face.assetId', 'in', assetIds)
|
||||
.where('asset_face.personId', 'in', personIds)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getRandomFace(personId: string) {
|
||||
return this.db
|
||||
.selectFrom('asset_faces')
|
||||
.selectAll('asset_faces')
|
||||
.where('asset_faces.personId', '=', personId)
|
||||
.where('asset_faces.deletedAt', 'is', null)
|
||||
.selectFrom('asset_face')
|
||||
.selectAll('asset_face')
|
||||
.where('asset_face.personId', '=', personId)
|
||||
.where('asset_face.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@@ -500,22 +500,22 @@ export class PersonRepository {
|
||||
}
|
||||
|
||||
async createAssetFace(face: Insertable<AssetFaceTable>): Promise<void> {
|
||||
await this.db.insertInto('asset_faces').values(face).execute();
|
||||
await this.db.insertInto('asset_face').values(face).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async deleteAssetFace(id: string): Promise<void> {
|
||||
await this.db.deleteFrom('asset_faces').where('asset_faces.id', '=', id).execute();
|
||||
await this.db.deleteFrom('asset_face').where('asset_face.id', '=', id).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async softDeleteAssetFaces(id: string): Promise<void> {
|
||||
await this.db.updateTable('asset_faces').set({ deletedAt: new Date() }).where('asset_faces.id', '=', id).execute();
|
||||
await this.db.updateTable('asset_face').set({ deletedAt: new Date() }).where('asset_face.id', '=', id).execute();
|
||||
}
|
||||
|
||||
async vacuum({ reindexVectors }: { reindexVectors: boolean }): Promise<void> {
|
||||
await sql`VACUUM ANALYZE asset_faces, face_search, person`.execute(this.db);
|
||||
await sql`REINDEX TABLE asset_faces`.execute(this.db);
|
||||
await sql`VACUUM ANALYZE asset_face, face_search, person`.execute(this.db);
|
||||
await sql`REINDEX TABLE asset_face`.execute(this.db);
|
||||
await sql`REINDEX TABLE person`.execute(this.db);
|
||||
if (reindexVectors) {
|
||||
await sql`REINDEX TABLE face_search`.execute(this.db);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum';
|
||||
import { probes } from 'src/repositories/database.repository';
|
||||
import { DB } from 'src/schema';
|
||||
import { ExifTable } from 'src/schema/tables/exif.table';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { anyUuid, searchAssetBuilder } from 'src/utils/database';
|
||||
import { paginationHelper } from 'src/utils/pagination';
|
||||
import { isValidInteger } from 'src/validation';
|
||||
@@ -183,8 +183,8 @@ export class SearchRepository {
|
||||
async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions) {
|
||||
const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection;
|
||||
const items = await searchAssetBuilder(this.db, options)
|
||||
.selectAll('assets')
|
||||
.orderBy('assets.fileCreatedAt', orderDirection)
|
||||
.selectAll('asset')
|
||||
.orderBy('asset.fileCreatedAt', orderDirection)
|
||||
.limit(pagination.size + 1)
|
||||
.offset((pagination.page - 1) * pagination.size)
|
||||
.execute();
|
||||
@@ -224,13 +224,13 @@ export class SearchRepository {
|
||||
const uuid = randomUUID();
|
||||
const builder = searchAssetBuilder(this.db, options);
|
||||
const lessThan = builder
|
||||
.selectAll('assets')
|
||||
.where('assets.id', '<', uuid)
|
||||
.selectAll('asset')
|
||||
.where('asset.id', '<', uuid)
|
||||
.orderBy(sql`random()`)
|
||||
.limit(size);
|
||||
const greaterThan = builder
|
||||
.selectAll('assets')
|
||||
.where('assets.id', '>', uuid)
|
||||
.selectAll('asset')
|
||||
.where('asset.id', '>', uuid)
|
||||
.orderBy(sql`random()`)
|
||||
.limit(size);
|
||||
const { rows } = await sql<MapAsset>`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db);
|
||||
@@ -258,8 +258,8 @@ export class SearchRepository {
|
||||
return this.db.transaction().execute(async (trx) => {
|
||||
await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.CLIP])}`.execute(trx);
|
||||
const items = await searchAssetBuilder(trx, options)
|
||||
.selectAll('assets')
|
||||
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.selectAll('asset')
|
||||
.innerJoin('smart_search', 'asset.id', 'smart_search.assetId')
|
||||
.orderBy(sql`smart_search.embedding <=> ${options.embedding}`)
|
||||
.limit(pagination.size + 1)
|
||||
.offset((pagination.page - 1) * pagination.size)
|
||||
@@ -288,18 +288,18 @@ export class SearchRepository {
|
||||
return await trx
|
||||
.with('cte', (qb) =>
|
||||
qb
|
||||
.selectFrom('asset_faces')
|
||||
.selectFrom('asset_face')
|
||||
.select([
|
||||
'asset_faces.id',
|
||||
'asset_faces.personId',
|
||||
'asset_face.id',
|
||||
'asset_face.personId',
|
||||
sql<number>`face_search.embedding <=> ${embedding}`.as('distance'),
|
||||
])
|
||||
.innerJoin('assets', 'assets.id', 'asset_faces.assetId')
|
||||
.innerJoin('face_search', 'face_search.faceId', 'asset_faces.id')
|
||||
.leftJoin('person', 'person.id', 'asset_faces.personId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null))
|
||||
.innerJoin('asset', 'asset.id', 'asset_face.assetId')
|
||||
.innerJoin('face_search', 'face_search.faceId', 'asset_face.id')
|
||||
.leftJoin('person', 'person.id', 'asset_face.personId')
|
||||
.where('asset.ownerId', '=', anyUuid(userIds))
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.$if(!!hasPerson, (qb) => qb.where('asset_face.personId', 'is not', null))
|
||||
.$if(!!minBirthDate, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.or([eb('person.birthDate', 'is', null), eb('person.birthDate', '<=', minBirthDate!)]),
|
||||
@@ -347,13 +347,13 @@ export class SearchRepository {
|
||||
return this.db
|
||||
.withRecursive('cte', (qb) => {
|
||||
const base = qb
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(['city', 'assetId'])
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('assets.type', '=', AssetType.IMAGE)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.innerJoin('asset', 'asset.id', 'asset_exif.assetId')
|
||||
.where('asset.ownerId', '=', anyUuid(userIds))
|
||||
.where('asset.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('asset.type', '=', AssetType.IMAGE)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.orderBy('city')
|
||||
.limit(1);
|
||||
|
||||
@@ -363,14 +363,14 @@ export class SearchRepository {
|
||||
.innerJoinLateral(
|
||||
(qb) =>
|
||||
qb
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(['city', 'assetId'])
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('assets.type', '=', AssetType.IMAGE)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.whereRef('exif.city', '>', 'cte.city')
|
||||
.innerJoin('asset', 'asset.id', 'asset_exif.assetId')
|
||||
.where('asset.ownerId', '=', anyUuid(userIds))
|
||||
.where('asset.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('asset.type', '=', AssetType.IMAGE)
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.whereRef('asset_exif.city', '>', 'cte.city')
|
||||
.orderBy('city')
|
||||
.limit(1)
|
||||
.as('l'),
|
||||
@@ -379,17 +379,17 @@ export class SearchRepository {
|
||||
|
||||
return sql<{ city: string; assetId: string }>`(${base} union all ${recursive})`;
|
||||
})
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', 'assets.id', 'exif.assetId')
|
||||
.innerJoin('cte', 'assets.id', 'cte.assetId')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.innerJoin('cte', 'asset.id', 'cte.assetId')
|
||||
.selectAll('asset')
|
||||
.select((eb) =>
|
||||
eb
|
||||
.fn('to_jsonb', [eb.table('exif')])
|
||||
.$castTo<Selectable<ExifTable>>()
|
||||
.fn('to_jsonb', [eb.table('asset_exif')])
|
||||
.$castTo<Selectable<AssetExifTable>>()
|
||||
.as('exifInfo'),
|
||||
)
|
||||
.orderBy('exif.city')
|
||||
.orderBy('asset_exif.city')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -445,10 +445,10 @@ export class SearchRepository {
|
||||
|
||||
private getExifField<K extends 'city' | 'state' | 'country' | 'make' | 'model'>(field: K, userIds: string[]) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(field)
|
||||
.distinctOn(field)
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.innerJoin('asset', 'asset.id', 'asset_exif.assetId')
|
||||
.where('ownerId', '=', anyUuid(userIds))
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('deletedAt', 'is', null)
|
||||
|
||||
@@ -17,7 +17,7 @@ export class SessionRepository {
|
||||
|
||||
cleanup() {
|
||||
return this.db
|
||||
.deleteFrom('sessions')
|
||||
.deleteFrom('session')
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('updatedAt', '<=', DateTime.now().minus({ days: 90 }).toJSDate()),
|
||||
@@ -31,7 +31,7 @@ export class SessionRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
get(id: string) {
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.selectFrom('session')
|
||||
.select(['id', 'expiresAt', 'pinExpiresAt'])
|
||||
.where('id', '=', id)
|
||||
.executeTakeFirst();
|
||||
@@ -40,20 +40,20 @@ export class SessionRepository {
|
||||
@GenerateSql({ params: [DummyValue.STRING] })
|
||||
getByToken(token: string) {
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.selectFrom('session')
|
||||
.select((eb) => [
|
||||
...columns.authSession,
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.authUser)
|
||||
.whereRef('users.id', '=', 'sessions.userId')
|
||||
.where('users.deletedAt', 'is', null),
|
||||
.whereRef('user.id', '=', 'session.userId')
|
||||
.where('user.deletedAt', 'is', null),
|
||||
).as('user'),
|
||||
])
|
||||
.where('sessions.token', '=', token)
|
||||
.where('session.token', '=', token)
|
||||
.where((eb) =>
|
||||
eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]),
|
||||
eb.or([eb('session.expiresAt', 'is', null), eb('session.expiresAt', '>', DateTime.now().toJSDate())]),
|
||||
)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
@@ -61,47 +61,47 @@ export class SessionRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getByUserId(userId: string) {
|
||||
return this.db
|
||||
.selectFrom('sessions')
|
||||
.innerJoin('users', (join) => join.onRef('users.id', '=', 'sessions.userId').on('users.deletedAt', 'is', null))
|
||||
.selectAll('sessions')
|
||||
.where('sessions.userId', '=', userId)
|
||||
.selectFrom('session')
|
||||
.innerJoin('user', (join) => join.onRef('user.id', '=', 'session.userId').on('user.deletedAt', 'is', null))
|
||||
.selectAll('session')
|
||||
.where('session.userId', '=', userId)
|
||||
.where((eb) =>
|
||||
eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]),
|
||||
eb.or([eb('session.expiresAt', 'is', null), eb('session.expiresAt', '>', DateTime.now().toJSDate())]),
|
||||
)
|
||||
.orderBy('sessions.updatedAt', 'desc')
|
||||
.orderBy('sessions.createdAt', 'desc')
|
||||
.orderBy('session.updatedAt', 'desc')
|
||||
.orderBy('session.createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
create(dto: Insertable<SessionTable>) {
|
||||
return this.db.insertInto('sessions').values(dto).returningAll().executeTakeFirstOrThrow();
|
||||
return this.db.insertInto('session').values(dto).returningAll().executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
update(id: string, dto: Updateable<SessionTable>) {
|
||||
return this.db
|
||||
.updateTable('sessions')
|
||||
.updateTable('session')
|
||||
.set(dto)
|
||||
.where('sessions.id', '=', asUuid(id))
|
||||
.where('session.id', '=', asUuid(id))
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async delete(id: string) {
|
||||
await this.db.deleteFrom('sessions').where('id', '=', asUuid(id)).execute();
|
||||
await this.db.deleteFrom('session').where('id', '=', asUuid(id)).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async lockAll(userId: string) {
|
||||
await this.db.updateTable('sessions').set({ pinExpiresAt: null }).where('userId', '=', userId).execute();
|
||||
await this.db.updateTable('session').set({ pinExpiresAt: null }).where('userId', '=', userId).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async resetSyncProgress(sessionId: string) {
|
||||
await this.db.transaction().execute((tx) => {
|
||||
return Promise.all([
|
||||
tx.updateTable('sessions').set({ isPendingSyncReset: false }).where('id', '=', sessionId).execute(),
|
||||
tx.deleteFrom('session_sync_checkpoints').where('sessionId', '=', sessionId).execute(),
|
||||
tx.updateTable('session').set({ isPendingSyncReset: false }).where('id', '=', sessionId).execute(),
|
||||
tx.deleteFrom('session_sync_checkpoint').where('sessionId', '=', sessionId).execute(),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,61 +22,66 @@ export class SharedLinkRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
get(userId: string, id: string) {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.selectFrom('shared_link')
|
||||
.selectAll('shared_link')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('shared_link__asset')
|
||||
.whereRef('shared_links.id', '=', 'shared_link__asset.sharedLinksId')
|
||||
.innerJoin('assets', 'assets.id', 'shared_link__asset.assetsId')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.selectAll('assets')
|
||||
.selectFrom('shared_link_asset')
|
||||
.whereRef('shared_link.id', '=', 'shared_link_asset.sharedLinksId')
|
||||
.innerJoin('asset', 'asset.id', 'shared_link_asset.assetsId')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.selectAll('asset')
|
||||
.innerJoinLateral(
|
||||
(eb) => eb.selectFrom('exif').selectAll('exif').whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'),
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('asset_exif')
|
||||
.selectAll('asset_exif')
|
||||
.whereRef('asset_exif.assetId', '=', 'asset.id')
|
||||
.as('exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('exifInfo').as('exifInfo'))
|
||||
.orderBy('assets.fileCreatedAt', 'asc')
|
||||
.orderBy('asset.fileCreatedAt', 'asc')
|
||||
.as('a'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.whereRef('albums.id', '=', 'shared_links.albumId')
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.leftJoin('albums_assets_assets', 'albums_assets_assets.albumsId', 'albums.id')
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.whereRef('album.id', '=', 'shared_link.albumId')
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.leftJoin('album_asset', 'album_asset.albumsId', 'album.id')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.whereRef('albums_assets_assets.assetsId', '=', 'assets.id')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.whereRef('album_asset.assetsId', '=', 'asset.id')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('exif')
|
||||
.selectAll('exif')
|
||||
.whereRef('exif.assetId', '=', 'assets.id')
|
||||
.as('assets_exifInfo'),
|
||||
.selectFrom('asset_exif')
|
||||
.selectAll('asset_exif')
|
||||
.whereRef('asset_exif.assetId', '=', 'asset.id')
|
||||
.as('exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('assets_exifInfo')).as('exifInfo'))
|
||||
.orderBy('assets.fileCreatedAt', 'asc')
|
||||
.select((eb) => eb.fn.toJson(eb.table('exifInfo')).as('exifInfo'))
|
||||
.orderBy('asset.fileCreatedAt', 'asc')
|
||||
.as('assets'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectAll('users')
|
||||
.whereRef('users.id', '=', 'albums.ownerId')
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.selectFrom('user')
|
||||
.selectAll('user')
|
||||
.whereRef('user.id', '=', 'album.ownerId')
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.as('owner'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
@@ -84,7 +89,7 @@ export class SharedLinkRepository {
|
||||
eb.fn.coalesce(eb.fn.jsonAgg('assets').filterWhere('assets.id', 'is not', null), sql`'[]'`).as('assets'),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('owner').as('owner'))
|
||||
.groupBy(['albums.id', sql`"owner".*`])
|
||||
.groupBy(['album.id', sql`"owner".*`])
|
||||
.as('album'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
@@ -94,29 +99,29 @@ export class SharedLinkRepository {
|
||||
.$castTo<MapAsset[]>()
|
||||
.as('assets'),
|
||||
)
|
||||
.groupBy(['shared_links.id', sql`"album".*`])
|
||||
.groupBy(['shared_link.id', sql`"album".*`])
|
||||
.select((eb) => eb.fn.toJson('album').$castTo<Album | null>().as('album'))
|
||||
.where('shared_links.id', '=', id)
|
||||
.where('shared_links.userId', '=', userId)
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.orderBy('shared_links.createdAt', 'desc')
|
||||
.where('shared_link.id', '=', id)
|
||||
.where('shared_link.userId', '=', userId)
|
||||
.where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.orderBy('shared_link.createdAt', 'desc')
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ userId: DummyValue.UUID, albumId: DummyValue.UUID }] })
|
||||
getAll({ userId, albumId }: SharedLinkSearchOptions) {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.where('shared_links.userId', '=', userId)
|
||||
.leftJoin('shared_link__asset', 'shared_link__asset.sharedLinksId', 'shared_links.id')
|
||||
.selectFrom('shared_link')
|
||||
.selectAll('shared_link')
|
||||
.where('shared_link.userId', '=', userId)
|
||||
.leftJoin('shared_link_asset', 'shared_link_asset.sharedLinksId', 'shared_link.id')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.select((eb) => eb.fn.jsonAgg('assets').as('assets'))
|
||||
.whereRef('assets.id', '=', 'shared_link__asset.assetsId')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.selectFrom('asset')
|
||||
.select((eb) => eb.fn.jsonAgg('asset').as('assets'))
|
||||
.whereRef('asset.id', '=', 'shared_link_asset.assetsId')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.as('assets'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
@@ -125,75 +130,75 @@ export class SharedLinkRepository {
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.whereRef('albums.id', '=', 'shared_links.albumId')
|
||||
.selectFrom('album')
|
||||
.selectAll('album')
|
||||
.whereRef('album.id', '=', 'shared_link.albumId')
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select([
|
||||
'users.id',
|
||||
'users.email',
|
||||
'users.createdAt',
|
||||
'users.profileImagePath',
|
||||
'users.isAdmin',
|
||||
'users.shouldChangePassword',
|
||||
'users.deletedAt',
|
||||
'users.oauthId',
|
||||
'users.updatedAt',
|
||||
'users.storageLabel',
|
||||
'users.name',
|
||||
'users.quotaSizeInBytes',
|
||||
'users.quotaUsageInBytes',
|
||||
'users.status',
|
||||
'users.profileChangedAt',
|
||||
'user.id',
|
||||
'user.email',
|
||||
'user.createdAt',
|
||||
'user.profileImagePath',
|
||||
'user.isAdmin',
|
||||
'user.shouldChangePassword',
|
||||
'user.deletedAt',
|
||||
'user.oauthId',
|
||||
'user.updatedAt',
|
||||
'user.storageLabel',
|
||||
'user.name',
|
||||
'user.quotaSizeInBytes',
|
||||
'user.quotaUsageInBytes',
|
||||
'user.status',
|
||||
'user.profileChangedAt',
|
||||
])
|
||||
.whereRef('users.id', '=', 'albums.ownerId')
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.whereRef('user.id', '=', 'album.ownerId')
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.as('owner'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('owner').as('owner'))
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.as('album'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('album').$castTo<Album | null>().as('album'))
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.$if(!!albumId, (eb) => eb.where('shared_links.albumId', '=', albumId!))
|
||||
.orderBy('shared_links.createdAt', 'desc')
|
||||
.distinctOn(['shared_links.createdAt'])
|
||||
.where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.$if(!!albumId, (eb) => eb.where('shared_link.albumId', '=', albumId!))
|
||||
.orderBy('shared_link.createdAt', 'desc')
|
||||
.distinctOn(['shared_link.createdAt'])
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.BUFFER] })
|
||||
async getByKey(key: Buffer) {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.where('shared_links.key', '=', key)
|
||||
.leftJoin('albums', 'albums.id', 'shared_links.albumId')
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.selectFrom('shared_link')
|
||||
.where('shared_link.key', '=', key)
|
||||
.leftJoin('album', 'album.id', 'shared_link.albumId')
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.select((eb) => [
|
||||
...columns.authSharedLink,
|
||||
jsonObjectFrom(
|
||||
eb.selectFrom('users').select(columns.authUser).whereRef('users.id', '=', 'shared_links.userId'),
|
||||
eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'),
|
||||
).as('user'),
|
||||
])
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('albums.id', 'is not', null)]))
|
||||
.where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async create(entity: Insertable<SharedLinkTable> & { assetIds?: string[] }) {
|
||||
const { id } = await this.db
|
||||
.insertInto('shared_links')
|
||||
.insertInto('shared_link')
|
||||
.values(_.omit(entity, 'assetIds'))
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (entity.assetIds && entity.assetIds.length > 0) {
|
||||
await this.db
|
||||
.insertInto('shared_link__asset')
|
||||
.insertInto('shared_link_asset')
|
||||
.values(entity.assetIds!.map((assetsId) => ({ assetsId, sharedLinksId: id })))
|
||||
.execute();
|
||||
}
|
||||
@@ -203,15 +208,15 @@ export class SharedLinkRepository {
|
||||
|
||||
async update(entity: Updateable<SharedLinkTable> & { id: string; assetIds?: string[] }) {
|
||||
const { id } = await this.db
|
||||
.updateTable('shared_links')
|
||||
.updateTable('shared_link')
|
||||
.set(_.omit(entity, 'assets', 'album', 'assetIds'))
|
||||
.where('shared_links.id', '=', entity.id)
|
||||
.where('shared_link.id', '=', entity.id)
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (entity.assetIds && entity.assetIds.length > 0) {
|
||||
await this.db
|
||||
.insertInto('shared_link__asset')
|
||||
.insertInto('shared_link_asset')
|
||||
.values(entity.assetIds!.map((assetsId) => ({ assetsId, sharedLinksId: id })))
|
||||
.execute();
|
||||
}
|
||||
@@ -220,23 +225,24 @@ export class SharedLinkRepository {
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.db.deleteFrom('shared_links').where('shared_links.id', '=', id).execute();
|
||||
await this.db.deleteFrom('shared_link').where('shared_link.id', '=', id).execute();
|
||||
}
|
||||
|
||||
private getSharedLinks(id: string) {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.where('shared_links.id', '=', id)
|
||||
.leftJoin('shared_link__asset', 'shared_link__asset.sharedLinksId', 'shared_links.id')
|
||||
.selectFrom('shared_link')
|
||||
.selectAll('shared_link')
|
||||
.where('shared_link.id', '=', id)
|
||||
.leftJoin('shared_link_asset', 'shared_link_asset.sharedLinksId', 'shared_link.id')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.whereRef('assets.id', '=', 'shared_link__asset.assetsId')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.whereRef('asset.id', '=', 'shared_link_asset.assetsId')
|
||||
.selectAll('asset')
|
||||
.innerJoinLateral(
|
||||
(eb) => eb.selectFrom('exif').whereRef('exif.assetId', '=', 'assets.id').selectAll().as('exif'),
|
||||
(eb) =>
|
||||
eb.selectFrom('asset_exif').whereRef('asset_exif.assetId', '=', 'asset.id').selectAll().as('exif'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.as('assets'),
|
||||
@@ -248,7 +254,7 @@ export class SharedLinkRepository {
|
||||
.$castTo<MapAsset[]>()
|
||||
.as('assets'),
|
||||
)
|
||||
.groupBy('shared_links.id')
|
||||
.groupBy('shared_link.id')
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,29 +13,34 @@ export interface StackSearch {
|
||||
primaryAssetId?: string;
|
||||
}
|
||||
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false) => {
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'stack'>, withTags = false) => {
|
||||
return jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.innerJoinLateral(
|
||||
(eb) => eb.selectFrom('exif').select(columns.exif).whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'),
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('asset_exif')
|
||||
.select(columns.exif)
|
||||
.whereRef('asset_exif.assetId', '=', 'asset.id')
|
||||
.as('exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.$if(withTags, (eb) =>
|
||||
eb.select((eb) =>
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('tags')
|
||||
.selectFrom('tag')
|
||||
.select(columns.tag)
|
||||
.innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId')
|
||||
.whereRef('tag_asset.assetsId', '=', 'assets.id'),
|
||||
.innerJoin('tag_asset', 'tag.id', 'tag_asset.tagsId')
|
||||
.whereRef('tag_asset.assetsId', '=', 'asset.id'),
|
||||
).as('tags'),
|
||||
),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('exifInfo').as('exifInfo'))
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.whereRef('assets.stackId', '=', 'asset_stack.id')
|
||||
.where('asset.deletedAt', 'is', null)
|
||||
.whereRef('asset.stackId', '=', 'stack.id')
|
||||
.$call(withDefaultVisibility),
|
||||
).as('assets');
|
||||
};
|
||||
@@ -47,28 +52,28 @@ export class StackRepository {
|
||||
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
||||
search(query: StackSearch) {
|
||||
return this.db
|
||||
.selectFrom('asset_stack')
|
||||
.selectAll('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.selectAll('stack')
|
||||
.select(withAssets)
|
||||
.where('asset_stack.ownerId', '=', query.ownerId)
|
||||
.$if(!!query.primaryAssetId, (eb) => eb.where('asset_stack.primaryAssetId', '=', query.primaryAssetId!))
|
||||
.where('stack.ownerId', '=', query.ownerId)
|
||||
.$if(!!query.primaryAssetId, (eb) => eb.where('stack.primaryAssetId', '=', query.primaryAssetId!))
|
||||
.execute();
|
||||
}
|
||||
|
||||
async create(entity: Omit<Insertable<StackTable>, 'primaryAssetId'>, assetIds: string[]) {
|
||||
return this.db.transaction().execute(async (tx) => {
|
||||
const stacks = await tx
|
||||
.selectFrom('asset_stack')
|
||||
.where('asset_stack.ownerId', '=', entity.ownerId)
|
||||
.where('asset_stack.primaryAssetId', 'in', assetIds)
|
||||
.select('asset_stack.id')
|
||||
.selectFrom('stack')
|
||||
.where('stack.ownerId', '=', entity.ownerId)
|
||||
.where('stack.primaryAssetId', 'in', assetIds)
|
||||
.select('stack.id')
|
||||
.select((eb) =>
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.select('assets.id')
|
||||
.whereRef('assets.stackId', '=', 'asset_stack.id')
|
||||
.where('assets.deletedAt', 'is', null),
|
||||
.selectFrom('asset')
|
||||
.select('asset.id')
|
||||
.whereRef('asset.stackId', '=', 'stack.id')
|
||||
.where('asset.deletedAt', 'is', null),
|
||||
).as('assets'),
|
||||
)
|
||||
.execute();
|
||||
@@ -86,7 +91,7 @@ export class StackRepository {
|
||||
|
||||
if (stacks.length > 0) {
|
||||
await tx
|
||||
.deleteFrom('asset_stack')
|
||||
.deleteFrom('stack')
|
||||
.where(
|
||||
'id',
|
||||
'in',
|
||||
@@ -96,13 +101,13 @@ export class StackRepository {
|
||||
}
|
||||
|
||||
const newRecord = await tx
|
||||
.insertInto('asset_stack')
|
||||
.insertInto('stack')
|
||||
.values({ ...entity, primaryAssetId: assetIds[0] })
|
||||
.returning('id')
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
await tx
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.set({
|
||||
stackId: newRecord.id,
|
||||
updatedAt: new Date(),
|
||||
@@ -111,8 +116,8 @@ export class StackRepository {
|
||||
.execute();
|
||||
|
||||
return tx
|
||||
.selectFrom('asset_stack')
|
||||
.selectAll('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.selectAll('stack')
|
||||
.select(withAssets)
|
||||
.where('id', '=', newRecord.id)
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -121,19 +126,19 @@ export class StackRepository {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async delete(id: string): Promise<void> {
|
||||
await this.db.deleteFrom('asset_stack').where('id', '=', asUuid(id)).execute();
|
||||
await this.db.deleteFrom('stack').where('id', '=', asUuid(id)).execute();
|
||||
}
|
||||
|
||||
async deleteAll(ids: string[]): Promise<void> {
|
||||
await this.db.deleteFrom('asset_stack').where('id', 'in', ids).execute();
|
||||
await this.db.deleteFrom('stack').where('id', 'in', ids).execute();
|
||||
}
|
||||
|
||||
update(id: string, entity: Updateable<StackTable>) {
|
||||
return this.db
|
||||
.updateTable('asset_stack')
|
||||
.updateTable('stack')
|
||||
.set(entity)
|
||||
.where('id', '=', asUuid(id))
|
||||
.returningAll('asset_stack')
|
||||
.returningAll('stack')
|
||||
.returning((eb) => withAssets(eb, true))
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
@@ -141,7 +146,7 @@ export class StackRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getById(id: string) {
|
||||
return this.db
|
||||
.selectFrom('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.selectAll()
|
||||
.select((eb) => withAssets(eb, true))
|
||||
.where('id', '=', asUuid(id))
|
||||
|
||||
@@ -13,7 +13,7 @@ export class SyncCheckpointRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getAll(sessionId: string) {
|
||||
return this.db
|
||||
.selectFrom('session_sync_checkpoints')
|
||||
.selectFrom('session_sync_checkpoint')
|
||||
.select(['type', 'ack'])
|
||||
.where('sessionId', '=', sessionId)
|
||||
.execute();
|
||||
@@ -21,7 +21,7 @@ export class SyncCheckpointRepository {
|
||||
|
||||
upsertAll(items: Insertable<SessionSyncCheckpointTable>[]) {
|
||||
return this.db
|
||||
.insertInto('session_sync_checkpoints')
|
||||
.insertInto('session_sync_checkpoint')
|
||||
.values(items)
|
||||
.onConflict((oc) =>
|
||||
oc.columns(['sessionId', 'type']).doUpdateSet((eb) => ({
|
||||
@@ -34,7 +34,7 @@ export class SyncCheckpointRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
deleteAll(sessionId: string, types?: SyncEntityType[]) {
|
||||
return this.db
|
||||
.deleteFrom('session_sync_checkpoints')
|
||||
.deleteFrom('session_sync_checkpoint')
|
||||
.where('sessionId', '=', sessionId)
|
||||
.$if(!!types, (qb) => qb.where('type', 'in', types!))
|
||||
.execute();
|
||||
|
||||
@@ -7,27 +7,27 @@ import { DB } from 'src/schema';
|
||||
import { SyncAck } from 'src/types';
|
||||
|
||||
type AuditTables =
|
||||
| 'users_audit'
|
||||
| 'partners_audit'
|
||||
| 'assets_audit'
|
||||
| 'albums_audit'
|
||||
| 'album_users_audit'
|
||||
| 'album_assets_audit'
|
||||
| 'memories_audit'
|
||||
| 'memory_assets_audit'
|
||||
| 'stacks_audit'
|
||||
| 'user_audit'
|
||||
| 'partner_audit'
|
||||
| 'asset_audit'
|
||||
| 'album_audit'
|
||||
| 'album_user_audit'
|
||||
| 'album_asset_audit'
|
||||
| 'memory_audit'
|
||||
| 'memory_asset_audit'
|
||||
| 'stack_audit'
|
||||
| 'person_audit'
|
||||
| 'user_metadata_audit';
|
||||
type UpsertTables =
|
||||
| 'users'
|
||||
| 'partners'
|
||||
| 'assets'
|
||||
| 'exif'
|
||||
| 'albums'
|
||||
| 'albums_shared_users_users'
|
||||
| 'memories'
|
||||
| 'memories_assets_assets'
|
||||
| 'asset_stack'
|
||||
| 'user'
|
||||
| 'partner'
|
||||
| 'asset'
|
||||
| 'asset_exif'
|
||||
| 'album'
|
||||
| 'album_user'
|
||||
| 'memory'
|
||||
| 'memory_asset'
|
||||
| 'stack'
|
||||
| 'person'
|
||||
| 'user_metadata';
|
||||
|
||||
@@ -100,7 +100,7 @@ class AlbumSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
getCreatedAfter(userId: string, afterCreateId?: string) {
|
||||
return this.db
|
||||
.selectFrom('albums_shared_users_users')
|
||||
.selectFrom('album_user')
|
||||
.select(['albumsId as id', 'createId'])
|
||||
.where('usersId', '=', userId)
|
||||
.$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!))
|
||||
@@ -112,7 +112,7 @@ class AlbumSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('albums_audit')
|
||||
.selectFrom('album_audit')
|
||||
.select(['id', 'albumId'])
|
||||
.where('userId', '=', userId)
|
||||
.$call(this.auditTableFilters(ack))
|
||||
@@ -122,24 +122,24 @@ class AlbumSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('albums')
|
||||
.distinctOn(['albums.id', 'albums.updateId'])
|
||||
.where('albums.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('albums.updateId', '>', ack!.updateId))
|
||||
.orderBy('albums.updateId', 'asc')
|
||||
.leftJoin('albums_shared_users_users as album_users', 'albums.id', 'album_users.albumsId')
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('album_users.usersId', '=', userId)]))
|
||||
.selectFrom('album')
|
||||
.distinctOn(['album.id', 'album.updateId'])
|
||||
.where('album.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('album.updateId', '>', ack!.updateId))
|
||||
.orderBy('album.updateId', 'asc')
|
||||
.leftJoin('album_user as album_users', 'album.id', 'album_users.albumsId')
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_users.usersId', '=', userId)]))
|
||||
.select([
|
||||
'albums.id',
|
||||
'albums.ownerId',
|
||||
'albums.albumName as name',
|
||||
'albums.description',
|
||||
'albums.createdAt',
|
||||
'albums.updatedAt',
|
||||
'albums.albumThumbnailAssetId as thumbnailAssetId',
|
||||
'albums.isActivityEnabled',
|
||||
'albums.order',
|
||||
'albums.updateId',
|
||||
'album.id',
|
||||
'album.ownerId',
|
||||
'album.albumName as name',
|
||||
'album.description',
|
||||
'album.createdAt',
|
||||
'album.updatedAt',
|
||||
'album.albumThumbnailAssetId as thumbnailAssetId',
|
||||
'album.isActivityEnabled',
|
||||
'album.order',
|
||||
'album.updateId',
|
||||
])
|
||||
.stream();
|
||||
}
|
||||
@@ -149,31 +149,31 @@ class AlbumAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.assetsId', 'assets.id')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset.id')
|
||||
.select(columns.syncAsset)
|
||||
.select('assets.updateId')
|
||||
.where('album_assets.albumsId', '=', albumId)
|
||||
.where('assets.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('assets.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('assets.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('assets.updateId', 'asc')
|
||||
.select('asset.updateId')
|
||||
.where('album_asset.albumsId', '=', albumId)
|
||||
.where('asset.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('asset.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('asset.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('asset.updateId', 'asc')
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.assetsId', 'assets.id')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset.id')
|
||||
.select(columns.syncAsset)
|
||||
.select('assets.updateId')
|
||||
.where('assets.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('assets.updateId', '>', ack!.updateId))
|
||||
.orderBy('assets.updateId', 'asc')
|
||||
.innerJoin('albums', 'albums.id', 'album_assets.albumsId')
|
||||
.leftJoin('albums_shared_users_users as album_users', 'album_users.albumsId', 'album_assets.albumsId')
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('album_users.usersId', '=', userId)]))
|
||||
.select('asset.updateId')
|
||||
.where('asset.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('asset.updateId', '>', ack!.updateId))
|
||||
.orderBy('asset.updateId', 'asc')
|
||||
.innerJoin('album', 'album.id', 'album_asset.albumsId')
|
||||
.leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId')
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)]))
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
@@ -182,31 +182,31 @@ class AlbumAssetExifSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.assetsId', 'exif.assetId')
|
||||
.selectFrom('asset_exif')
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId')
|
||||
.select(columns.syncAssetExif)
|
||||
.select('exif.updateId')
|
||||
.where('album_assets.albumsId', '=', albumId)
|
||||
.where('exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('exif.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('exif.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('exif.updateId', 'asc')
|
||||
.select('asset_exif.updateId')
|
||||
.where('album_asset.albumsId', '=', albumId)
|
||||
.where('asset_exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('asset_exif.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('asset_exif.updateId', 'asc')
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.innerJoin('albums_assets_assets as album_assets', 'album_assets.assetsId', 'exif.assetId')
|
||||
.selectFrom('asset_exif')
|
||||
.innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId')
|
||||
.select(columns.syncAssetExif)
|
||||
.select('exif.updateId')
|
||||
.where('exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('exif.updateId', '>', ack!.updateId))
|
||||
.orderBy('exif.updateId', 'asc')
|
||||
.innerJoin('albums', 'albums.id', 'album_assets.albumsId')
|
||||
.leftJoin('albums_shared_users_users as album_users', 'album_users.albumsId', 'album_assets.albumsId')
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('album_users.usersId', '=', userId)]))
|
||||
.select('asset_exif.updateId')
|
||||
.where('asset_exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('asset_exif.updateId', '>', ack!.updateId))
|
||||
.orderBy('asset_exif.updateId', 'asc')
|
||||
.innerJoin('album', 'album.id', 'album_asset.albumsId')
|
||||
.leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId')
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)]))
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ class AlbumToAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums_assets_assets as album_assets')
|
||||
.selectFrom('album_asset as album_assets')
|
||||
.select(['album_assets.assetsId as assetId', 'album_assets.albumsId as albumId', 'album_assets.updateId'])
|
||||
.where('album_assets.albumsId', '=', albumId)
|
||||
.where('album_assets.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
@@ -228,22 +228,22 @@ class AlbumToAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('album_assets_audit')
|
||||
.selectFrom('album_asset_audit')
|
||||
.select(['id', 'assetId', 'albumId'])
|
||||
.where((eb) =>
|
||||
eb(
|
||||
'albumId',
|
||||
'in',
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectFrom('album')
|
||||
.select(['id'])
|
||||
.where('ownerId', '=', userId)
|
||||
.union((eb) =>
|
||||
eb.parens(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as albumUsers')
|
||||
.select(['albumUsers.albumsId as id'])
|
||||
.where('albumUsers.usersId', '=', userId),
|
||||
.selectFrom('album_user')
|
||||
.select(['album_user.albumsId as id'])
|
||||
.where('album_user.usersId', '=', userId),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -255,14 +255,14 @@ class AlbumToAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('albums_assets_assets as album_assets')
|
||||
.select(['album_assets.assetsId as assetId', 'album_assets.albumsId as albumId', 'album_assets.updateId'])
|
||||
.where('album_assets.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('album_assets.updateId', '>', ack!.updateId))
|
||||
.orderBy('album_assets.updateId', 'asc')
|
||||
.innerJoin('albums', 'albums.id', 'album_assets.albumsId')
|
||||
.leftJoin('albums_shared_users_users as album_users', 'album_users.albumsId', 'album_assets.albumsId')
|
||||
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('album_users.usersId', '=', userId)]))
|
||||
.selectFrom('album_asset')
|
||||
.select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId'])
|
||||
.where('album_asset.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId))
|
||||
.orderBy('album_asset.updateId', 'asc')
|
||||
.innerJoin('album', 'album.id', 'album_asset.albumsId')
|
||||
.leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId')
|
||||
.where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)]))
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
@@ -271,9 +271,9 @@ class AlbumUserSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.selectFrom('album_user')
|
||||
.select(columns.syncAlbumUser)
|
||||
.select('album_users.updateId')
|
||||
.select('album_user.updateId')
|
||||
.where('albumsId', '=', albumId)
|
||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('updateId', '<=', beforeUpdateId)
|
||||
@@ -285,22 +285,22 @@ class AlbumUserSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('album_users_audit')
|
||||
.selectFrom('album_user_audit')
|
||||
.select(['id', 'userId', 'albumId'])
|
||||
.where((eb) =>
|
||||
eb(
|
||||
'albumId',
|
||||
'in',
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectFrom('album')
|
||||
.select(['id'])
|
||||
.where('ownerId', '=', userId)
|
||||
.union((eb) =>
|
||||
eb.parens(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as albumUsers')
|
||||
.select(['albumUsers.albumsId as id'])
|
||||
.where('albumUsers.usersId', '=', userId),
|
||||
.selectFrom('album_user')
|
||||
.select(['album_user.albumsId as id'])
|
||||
.where('album_user.usersId', '=', userId),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -312,24 +312,24 @@ class AlbumUserSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('albums_shared_users_users as album_users')
|
||||
.selectFrom('album_user')
|
||||
.select(columns.syncAlbumUser)
|
||||
.select('album_users.updateId')
|
||||
.where('album_users.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('album_users.updateId', '>', ack!.updateId))
|
||||
.orderBy('album_users.updateId', 'asc')
|
||||
.select('album_user.updateId')
|
||||
.where('album_user.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.$if(!!ack, (qb) => qb.where('album_user.updateId', '>', ack!.updateId))
|
||||
.orderBy('album_user.updateId', 'asc')
|
||||
.where((eb) =>
|
||||
eb(
|
||||
'album_users.albumsId',
|
||||
'album_user.albumsId',
|
||||
'in',
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectFrom('album')
|
||||
.select(['id'])
|
||||
.where('ownerId', '=', userId)
|
||||
.union((eb) =>
|
||||
eb.parens(
|
||||
eb
|
||||
.selectFrom('albums_shared_users_users as albumUsers')
|
||||
.selectFrom('album_user as albumUsers')
|
||||
.select(['albumUsers.albumsId as id'])
|
||||
.where('albumUsers.usersId', '=', userId),
|
||||
),
|
||||
@@ -344,7 +344,7 @@ class AssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('assets_audit')
|
||||
.selectFrom('asset_audit')
|
||||
.select(['id', 'assetId'])
|
||||
.where('ownerId', '=', userId)
|
||||
.$call(this.auditTableFilters(ack))
|
||||
@@ -354,9 +354,9 @@ class AssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(columns.syncAsset)
|
||||
.select('assets.updateId')
|
||||
.select('asset.updateId')
|
||||
.where('ownerId', '=', userId)
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
@@ -402,10 +402,10 @@ class AssetExifSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(columns.syncAssetExif)
|
||||
.select('exif.updateId')
|
||||
.where('assetId', 'in', (eb) => eb.selectFrom('assets').select('id').where('ownerId', '=', userId))
|
||||
.select('asset_exif.updateId')
|
||||
.where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', userId))
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
}
|
||||
@@ -415,7 +415,7 @@ class MemorySync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('memories_audit')
|
||||
.selectFrom('memory_audit')
|
||||
.select(['id', 'memoryId'])
|
||||
.where('userId', '=', userId)
|
||||
.$call(this.auditTableFilters(ack))
|
||||
@@ -425,7 +425,7 @@ class MemorySync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('memories')
|
||||
.selectFrom('memory')
|
||||
.select([
|
||||
'id',
|
||||
'createdAt',
|
||||
@@ -451,9 +451,9 @@ class MemoryToAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('memory_assets_audit')
|
||||
.selectFrom('memory_asset_audit')
|
||||
.select(['id', 'memoryId', 'assetId'])
|
||||
.where('memoryId', 'in', (eb) => eb.selectFrom('memories').select('id').where('ownerId', '=', userId))
|
||||
.where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId))
|
||||
.$call(this.auditTableFilters(ack))
|
||||
.stream();
|
||||
}
|
||||
@@ -461,10 +461,10 @@ class MemoryToAssetSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('memories_assets_assets')
|
||||
.selectFrom('memory_asset')
|
||||
.select(['memoriesId as memoryId', 'assetsId as assetId'])
|
||||
.select('updateId')
|
||||
.where('memoriesId', 'in', (eb) => eb.selectFrom('memories').select('id').where('ownerId', '=', userId))
|
||||
.where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId))
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
}
|
||||
@@ -474,19 +474,19 @@ class PartnerSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
getCreatedAfter(userId: string, afterCreateId?: string) {
|
||||
return this.db
|
||||
.selectFrom('partners')
|
||||
.selectFrom('partner')
|
||||
.select(['sharedById', 'createId'])
|
||||
.where('sharedWithId', '=', userId)
|
||||
.$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!))
|
||||
.where('createdAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.orderBy('partners.createId', 'asc')
|
||||
.orderBy('partner.createId', 'asc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('partners_audit')
|
||||
.selectFrom('partner_audit')
|
||||
.select(['id', 'sharedById', 'sharedWithId'])
|
||||
.where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)]))
|
||||
.$call(this.auditTableFilters(ack))
|
||||
@@ -496,7 +496,7 @@ class PartnerSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('partners')
|
||||
.selectFrom('partner')
|
||||
.select(['sharedById', 'sharedWithId', 'inTimeline', 'updateId'])
|
||||
.where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)]))
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
@@ -508,9 +508,9 @@ class PartnerAssetsSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(columns.syncAsset)
|
||||
.select('assets.updateId')
|
||||
.select('asset.updateId')
|
||||
.where('ownerId', '=', partnerId)
|
||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('updateId', '<=', beforeUpdateId)
|
||||
@@ -522,10 +522,10 @@ class PartnerAssetsSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('assets_audit')
|
||||
.selectFrom('asset_audit')
|
||||
.select(['id', 'assetId'])
|
||||
.where('ownerId', 'in', (eb) =>
|
||||
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
)
|
||||
.$call(this.auditTableFilters(ack))
|
||||
.stream();
|
||||
@@ -534,11 +534,11 @@ class PartnerAssetsSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select(columns.syncAsset)
|
||||
.select('assets.updateId')
|
||||
.select('asset.updateId')
|
||||
.where('ownerId', 'in', (eb) =>
|
||||
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
)
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
@@ -549,30 +549,30 @@ class PartnerAssetExifsSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(columns.syncAssetExif)
|
||||
.select('exif.updateId')
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', partnerId)
|
||||
.where('exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('exif.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('exif.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('exif.updateId', 'asc')
|
||||
.select('asset_exif.updateId')
|
||||
.innerJoin('asset', 'asset.id', 'asset_exif.assetId')
|
||||
.where('asset.ownerId', '=', partnerId)
|
||||
.where('asset_exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('asset_exif.updateId', '<=', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('asset_exif.updateId', 'asc')
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('exif')
|
||||
.selectFrom('asset_exif')
|
||||
.select(columns.syncAssetExif)
|
||||
.select('exif.updateId')
|
||||
.select('asset_exif.updateId')
|
||||
.where('assetId', 'in', (eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectFrom('asset')
|
||||
.select('id')
|
||||
.where('ownerId', 'in', (eb) =>
|
||||
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
),
|
||||
)
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
@@ -584,7 +584,7 @@ class StackSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('stacks_audit')
|
||||
.selectFrom('stack_audit')
|
||||
.select(['id', 'stackId'])
|
||||
.where('userId', '=', userId)
|
||||
.$call(this.auditTableFilters(ack))
|
||||
@@ -594,7 +594,7 @@ class StackSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.select(columns.syncStack)
|
||||
.select('updateId')
|
||||
.where('ownerId', '=', userId)
|
||||
@@ -607,11 +607,9 @@ class PartnerStackSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getDeletes(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('stacks_audit')
|
||||
.selectFrom('stack_audit')
|
||||
.select(['id', 'stackId'])
|
||||
.where('userId', 'in', (eb) =>
|
||||
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
)
|
||||
.where('userId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId))
|
||||
.$call(this.auditTableFilters(ack))
|
||||
.stream();
|
||||
}
|
||||
@@ -619,7 +617,7 @@ class PartnerStackSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||
getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||
return this.db
|
||||
.selectFrom('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.select(columns.syncStack)
|
||||
.select('updateId')
|
||||
.where('ownerId', '=', partnerId)
|
||||
@@ -633,11 +631,11 @@ class PartnerStackSync extends BaseSync {
|
||||
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||
getUpserts(userId: string, ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('asset_stack')
|
||||
.selectFrom('stack')
|
||||
.select(columns.syncStack)
|
||||
.select('updateId')
|
||||
.where('ownerId', 'in', (eb) =>
|
||||
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||
)
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
@@ -647,13 +645,13 @@ class PartnerStackSync extends BaseSync {
|
||||
class UserSync extends BaseSync {
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
getDeletes(ack?: SyncAck) {
|
||||
return this.db.selectFrom('users_audit').select(['id', 'userId']).$call(this.auditTableFilters(ack)).stream();
|
||||
return this.db.selectFrom('user_audit').select(['id', 'userId']).$call(this.auditTableFilters(ack)).stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [], stream: true })
|
||||
getUpserts(ack?: SyncAck) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(['id', 'name', 'email', 'deletedAt', 'updateId'])
|
||||
.$call(this.upsertTableFilters(ack))
|
||||
.stream();
|
||||
|
||||
@@ -19,13 +19,13 @@ export class TagRepository {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
get(id: string) {
|
||||
return this.db.selectFrom('tags').select(columns.tag).where('id', '=', id).executeTakeFirst();
|
||||
return this.db.selectFrom('tag').select(columns.tag).where('id', '=', id).executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
getByValue(userId: string, value: string) {
|
||||
return this.db
|
||||
.selectFrom('tags')
|
||||
.selectFrom('tag')
|
||||
.select(columns.tag)
|
||||
.where('userId', '=', userId)
|
||||
.where('value', '=', value)
|
||||
@@ -37,7 +37,7 @@ export class TagRepository {
|
||||
const parentId = _parentId ?? null;
|
||||
return this.db.transaction().execute(async (tx) => {
|
||||
const tag = await this.db
|
||||
.insertInto('tags')
|
||||
.insertInto('tag')
|
||||
.values({ userId, value, parentId })
|
||||
.onConflict((oc) => oc.columns(['userId', 'value']).doUpdateSet({ parentId }))
|
||||
.returning(columns.tag)
|
||||
@@ -45,18 +45,18 @@ export class TagRepository {
|
||||
|
||||
// update closure table
|
||||
await tx
|
||||
.insertInto('tags_closure')
|
||||
.insertInto('tag_closure')
|
||||
.values({ id_ancestor: tag.id, id_descendant: tag.id })
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
|
||||
if (parentId) {
|
||||
await tx
|
||||
.insertInto('tags_closure')
|
||||
.insertInto('tag_closure')
|
||||
.columns(['id_ancestor', 'id_descendant'])
|
||||
.expression(
|
||||
this.db
|
||||
.selectFrom('tags_closure')
|
||||
.selectFrom('tag_closure')
|
||||
.select(['id_ancestor', sql.raw<string>(`'${tag.id}'`).as('id_descendant')])
|
||||
.where('id_descendant', '=', parentId),
|
||||
)
|
||||
@@ -70,22 +70,22 @@ export class TagRepository {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getAll(userId: string) {
|
||||
return this.db.selectFrom('tags').select(columns.tag).where('userId', '=', userId).orderBy('value').execute();
|
||||
return this.db.selectFrom('tag').select(columns.tag).where('userId', '=', userId).orderBy('value').execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] })
|
||||
create(tag: Insertable<TagTable>) {
|
||||
return this.db.insertInto('tags').values(tag).returningAll().executeTakeFirstOrThrow();
|
||||
return this.db.insertInto('tag').values(tag).returningAll().executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { color: DummyValue.STRING }] })
|
||||
update(id: string, dto: Updateable<TagTable>) {
|
||||
return this.db.updateTable('tags').set(dto).where('id', '=', id).returningAll().executeTakeFirstOrThrow();
|
||||
return this.db.updateTable('tag').set(dto).where('id', '=', id).returningAll().executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async delete(id: string) {
|
||||
await this.db.deleteFrom('tags').where('id', '=', id).execute();
|
||||
await this.db.deleteFrom('tag').where('id', '=', id).execute();
|
||||
}
|
||||
|
||||
@ChunkedSet({ paramIndex: 1 })
|
||||
@@ -166,17 +166,17 @@ export class TagRepository {
|
||||
// TODO rewrite as a single statement
|
||||
await this.db.transaction().execute(async (tx) => {
|
||||
const result = await tx
|
||||
.selectFrom('assets')
|
||||
.innerJoin('tag_asset', 'tag_asset.assetsId', 'assets.id')
|
||||
.innerJoin('tags_closure', 'tags_closure.id_descendant', 'tag_asset.tagsId')
|
||||
.innerJoin('tags', 'tags.id', 'tags_closure.id_descendant')
|
||||
.select((eb) => ['tags.id', eb.fn.count<number>('assets.id').as('count')])
|
||||
.groupBy('tags.id')
|
||||
.selectFrom('asset')
|
||||
.innerJoin('tag_asset', 'tag_asset.assetsId', 'asset.id')
|
||||
.innerJoin('tag_closure', 'tag_closure.id_descendant', 'tag_asset.tagsId')
|
||||
.innerJoin('tag', 'tag.id', 'tag_closure.id_descendant')
|
||||
.select((eb) => ['tag.id', eb.fn.count<number>('asset.id').as('count')])
|
||||
.groupBy('tag.id')
|
||||
.execute();
|
||||
|
||||
const ids = result.filter(({ count }) => count === 0).map(({ id }) => id);
|
||||
if (ids.length > 0) {
|
||||
await this.db.deleteFrom('tags').where('id', 'in', ids).execute();
|
||||
await this.db.deleteFrom('tag').where('id', 'in', ids).execute();
|
||||
this.logger.log(`Deleted ${ids.length} empty tags`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,13 +8,13 @@ export class TrashRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
getDeletedIds(): AsyncIterableIterator<{ id: string }> {
|
||||
return this.db.selectFrom('assets').select(['id']).where('status', '=', AssetStatus.DELETED).stream();
|
||||
return this.db.selectFrom('asset').select(['id']).where('status', '=', AssetStatus.DELETED).stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async restore(userId: string): Promise<number> {
|
||||
const { numUpdatedRows } = await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.where('ownerId', '=', userId)
|
||||
.where('status', '=', AssetStatus.TRASHED)
|
||||
.set({ status: AssetStatus.ACTIVE, deletedAt: null })
|
||||
@@ -26,7 +26,7 @@ export class TrashRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async empty(userId: string): Promise<number> {
|
||||
const { numUpdatedRows } = await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.where('ownerId', '=', userId)
|
||||
.where('status', '=', AssetStatus.TRASHED)
|
||||
.set({ status: AssetStatus.DELETED })
|
||||
@@ -42,7 +42,7 @@ export class TrashRepository {
|
||||
}
|
||||
|
||||
const { numUpdatedRows } = await this.db
|
||||
.updateTable('assets')
|
||||
.updateTable('asset')
|
||||
.where('status', '=', AssetStatus.TRASHED)
|
||||
.where('id', 'in', ids)
|
||||
.set({ status: AssetStatus.ACTIVE, deletedAt: null })
|
||||
|
||||
@@ -34,12 +34,12 @@ export interface UserFindOptions {
|
||||
withDeleted?: boolean;
|
||||
}
|
||||
|
||||
const withMetadata = (eb: ExpressionBuilder<DB, 'users'>) => {
|
||||
const withMetadata = (eb: ExpressionBuilder<DB, 'user'>) => {
|
||||
return jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('user_metadata')
|
||||
.select(['user_metadata.key', 'user_metadata.value'])
|
||||
.whereRef('users.id', '=', 'user_metadata.userId'),
|
||||
.whereRef('user.id', '=', 'user_metadata.userId'),
|
||||
).as('metadata');
|
||||
};
|
||||
|
||||
@@ -52,11 +52,11 @@ export class UserRepository {
|
||||
options = options || {};
|
||||
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.select(withMetadata)
|
||||
.where('users.id', '=', userId)
|
||||
.$if(!options.withDeleted, (eb) => eb.where('users.deletedAt', 'is', null))
|
||||
.where('user.id', '=', userId)
|
||||
.$if(!options.withDeleted, (eb) => eb.where('user.deletedAt', 'is', null))
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@@ -71,21 +71,21 @@ export class UserRepository {
|
||||
@GenerateSql()
|
||||
getAdmin() {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.select(withMetadata)
|
||||
.where('users.isAdmin', '=', true)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.isAdmin', '=', true)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql()
|
||||
async hasAdmin(): Promise<boolean> {
|
||||
const admin = await this.db
|
||||
.selectFrom('users')
|
||||
.select('users.id')
|
||||
.where('users.isAdmin', '=', true)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.selectFrom('user')
|
||||
.select('user.id')
|
||||
.where('user.isAdmin', '=', true)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
|
||||
return !!admin;
|
||||
@@ -94,59 +94,59 @@ export class UserRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForPinCode(id: string) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.select(['users.pinCode', 'users.password'])
|
||||
.where('users.id', '=', id)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.selectFrom('user')
|
||||
.select(['user.pinCode', 'user.password'])
|
||||
.where('user.id', '=', id)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getForChangePassword(id: string) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.select(['users.id', 'users.password'])
|
||||
.where('users.id', '=', id)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.selectFrom('user')
|
||||
.select(['user.id', 'user.password'])
|
||||
.where('user.id', '=', id)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.EMAIL] })
|
||||
getByEmail(email: string, options?: { withPassword?: boolean }) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.select(withMetadata)
|
||||
.$if(!!options?.withPassword, (eb) => eb.select('password'))
|
||||
.where('email', '=', email)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.STRING] })
|
||||
getByStorageLabel(storageLabel: string) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.where('users.storageLabel', '=', storageLabel)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.storageLabel', '=', storageLabel)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.STRING] })
|
||||
getByOAuthId(oauthId: string) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.select(withMetadata)
|
||||
.where('users.oauthId', '=', oauthId)
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.oauthId', '=', oauthId)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DateTime.now().minus({ years: 1 })] })
|
||||
getDeletedAfter(target: DateTime) {
|
||||
return this.db.selectFrom('users').select(['id']).where('users.deletedAt', '<', target.toJSDate()).execute();
|
||||
return this.db.selectFrom('user').select(['id']).where('user.deletedAt', '<', target.toJSDate()).execute();
|
||||
}
|
||||
|
||||
@GenerateSql(
|
||||
@@ -155,18 +155,18 @@ export class UserRepository {
|
||||
)
|
||||
getList({ id, withDeleted }: UserListFilter = {}) {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.selectFrom('user')
|
||||
.select(columns.userAdmin)
|
||||
.select(withMetadata)
|
||||
.$if(!withDeleted, (eb) => eb.where('users.deletedAt', 'is', null))
|
||||
.$if(!!id, (eb) => eb.where('users.id', '=', id!))
|
||||
.$if(!withDeleted, (eb) => eb.where('user.deletedAt', 'is', null))
|
||||
.$if(!!id, (eb) => eb.where('user.id', '=', id!))
|
||||
.orderBy('createdAt', 'desc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
async create(dto: Insertable<UserTable>) {
|
||||
return this.db
|
||||
.insertInto('users')
|
||||
.insertInto('user')
|
||||
.values(dto)
|
||||
.returning(columns.userAdmin)
|
||||
.returning(withMetadata)
|
||||
@@ -175,10 +175,10 @@ export class UserRepository {
|
||||
|
||||
update(id: string, dto: Updateable<UserTable>) {
|
||||
return this.db
|
||||
.updateTable('users')
|
||||
.updateTable('user')
|
||||
.set(dto)
|
||||
.where('users.id', '=', asUuid(id))
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.id', '=', asUuid(id))
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.returning(columns.userAdmin)
|
||||
.returning(withMetadata)
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -186,9 +186,9 @@ export class UserRepository {
|
||||
|
||||
restore(id: string) {
|
||||
return this.db
|
||||
.updateTable('users')
|
||||
.updateTable('user')
|
||||
.set({ status: UserStatus.ACTIVE, deletedAt: null })
|
||||
.where('users.id', '=', asUuid(id))
|
||||
.where('user.id', '=', asUuid(id))
|
||||
.returning(columns.userAdmin)
|
||||
.returning(withMetadata)
|
||||
.executeTakeFirstOrThrow();
|
||||
@@ -213,24 +213,24 @@ export class UserRepository {
|
||||
|
||||
delete(user: { id: string }, hard?: boolean) {
|
||||
return hard
|
||||
? this.db.deleteFrom('users').where('id', '=', user.id).execute()
|
||||
: this.db.updateTable('users').set({ deletedAt: new Date() }).where('id', '=', user.id).execute();
|
||||
? this.db.deleteFrom('user').where('id', '=', user.id).execute()
|
||||
: this.db.updateTable('user').set({ deletedAt: new Date() }).where('id', '=', user.id).execute();
|
||||
}
|
||||
|
||||
@GenerateSql()
|
||||
getUserStats() {
|
||||
return this.db
|
||||
.selectFrom('users')
|
||||
.leftJoin('assets', (join) => join.onRef('assets.ownerId', '=', 'users.id').on('assets.deletedAt', 'is', null))
|
||||
.leftJoin('exif', 'exif.assetId', 'assets.id')
|
||||
.select(['users.id as userId', 'users.name as userName', 'users.quotaSizeInBytes'])
|
||||
.selectFrom('user')
|
||||
.leftJoin('asset', (join) => join.onRef('asset.ownerId', '=', 'user.id').on('asset.deletedAt', 'is', null))
|
||||
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||
.select(['user.id as userId', 'user.name as userName', 'user.quotaSizeInBytes'])
|
||||
.select((eb) => [
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([
|
||||
eb('assets.type', '=', sql.lit(AssetType.IMAGE)),
|
||||
eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
eb('asset.type', '=', sql.lit(AssetType.IMAGE)),
|
||||
eb('asset.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
.as('photos'),
|
||||
@@ -238,20 +238,23 @@ export class UserRepository {
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([
|
||||
eb('assets.type', '=', sql.lit(AssetType.VIDEO)),
|
||||
eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
eb('asset.type', '=', sql.lit(AssetType.VIDEO)),
|
||||
eb('asset.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
.as('videos'),
|
||||
eb.fn
|
||||
.coalesce(eb.fn.sum<number>('exif.fileSizeInByte').filterWhere('assets.libraryId', 'is', null), eb.lit(0))
|
||||
.coalesce(
|
||||
eb.fn.sum<number>('asset_exif.fileSizeInByte').filterWhere('asset.libraryId', 'is', null),
|
||||
eb.lit(0),
|
||||
)
|
||||
.as('usage'),
|
||||
eb.fn
|
||||
.coalesce(
|
||||
eb.fn
|
||||
.sum<number>('exif.fileSizeInByte')
|
||||
.sum<number>('asset_exif.fileSizeInByte')
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.libraryId', 'is', null), eb('assets.type', '=', sql.lit(AssetType.IMAGE))]),
|
||||
eb.and([eb('asset.libraryId', 'is', null), eb('asset.type', '=', sql.lit(AssetType.IMAGE))]),
|
||||
),
|
||||
eb.lit(0),
|
||||
)
|
||||
@@ -259,45 +262,45 @@ export class UserRepository {
|
||||
eb.fn
|
||||
.coalesce(
|
||||
eb.fn
|
||||
.sum<number>('exif.fileSizeInByte')
|
||||
.sum<number>('asset_exif.fileSizeInByte')
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.libraryId', 'is', null), eb('assets.type', '=', sql.lit(AssetType.VIDEO))]),
|
||||
eb.and([eb('asset.libraryId', 'is', null), eb('asset.type', '=', sql.lit(AssetType.VIDEO))]),
|
||||
),
|
||||
eb.lit(0),
|
||||
)
|
||||
.as('usageVideos'),
|
||||
])
|
||||
.groupBy('users.id')
|
||||
.orderBy('users.createdAt', 'asc')
|
||||
.groupBy('user.id')
|
||||
.orderBy('user.createdAt', 'asc')
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] })
|
||||
async updateUsage(id: string, delta: number): Promise<void> {
|
||||
await this.db
|
||||
.updateTable('users')
|
||||
.updateTable('user')
|
||||
.set({ quotaUsageInBytes: sql`"quotaUsageInBytes" + ${delta}`, updatedAt: new Date() })
|
||||
.where('id', '=', asUuid(id))
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async syncUsage(id?: string) {
|
||||
const query = this.db
|
||||
.updateTable('users')
|
||||
.updateTable('user')
|
||||
.set({
|
||||
quotaUsageInBytes: (eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.leftJoin('exif', 'exif.assetId', 'assets.id')
|
||||
.select((eb) => eb.fn.coalesce(eb.fn.sum<number>('exif.fileSizeInByte'), eb.lit(0)).as('usage'))
|
||||
.where('assets.libraryId', 'is', null)
|
||||
.where('assets.ownerId', '=', eb.ref('users.id')),
|
||||
.selectFrom('asset')
|
||||
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||
.select((eb) => eb.fn.coalesce(eb.fn.sum<number>('asset_exif.fileSizeInByte'), eb.lit(0)).as('usage'))
|
||||
.where('asset.libraryId', 'is', null)
|
||||
.where('asset.ownerId', '=', eb.ref('user.id')),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.$if(id != undefined, (eb) => eb.where('users.id', '=', asUuid(id!)));
|
||||
.where('user.deletedAt', 'is', null)
|
||||
.$if(id != undefined, (eb) => eb.where('user.id', '=', asUuid(id!)));
|
||||
|
||||
await query.execute();
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ export class ViewRepository {
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getUniqueOriginalPaths(userId: string) {
|
||||
const results = await this.db
|
||||
.selectFrom('assets')
|
||||
.select((eb) => eb.fn<string>('substring', ['assets.originalPath', eb.val('^(.*/)[^/]*$')]).as('directoryPath'))
|
||||
.selectFrom('asset')
|
||||
.select((eb) => eb.fn<string>('substring', ['asset.originalPath', eb.val('^(.*/)[^/]*$')]).as('directoryPath'))
|
||||
.distinct()
|
||||
.where('ownerId', '=', asUuid(userId))
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
@@ -30,8 +30,8 @@ export class ViewRepository {
|
||||
const normalizedPath = partialPath.replaceAll(/\/$/g, '');
|
||||
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.selectFrom('asset')
|
||||
.selectAll('asset')
|
||||
.$call(withExif)
|
||||
.where('ownerId', '=', asUuid(userId))
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
@@ -42,7 +42,7 @@ export class ViewRepository {
|
||||
.where('originalPath', 'like', `%${normalizedPath}/%`)
|
||||
.where('originalPath', 'not like', `%${normalizedPath}/%/%`)
|
||||
.orderBy(
|
||||
(eb) => eb.fn('regexp_replace', ['assets.originalPath', eb.val('.*/(.+)'), eb.val(String.raw`\1`)]),
|
||||
(eb) => eb.fn('regexp_replace', ['asset.originalPath', eb.val('.*/(.+)'), eb.val(String.raw`\1`)]),
|
||||
'asc',
|
||||
)
|
||||
.execute();
|
||||
|
||||
Reference in New Issue
Block a user