mirror of
https://github.com/immich-app/immich.git
synced 2026-03-04 09:57:33 +03:00
refactor: migrate shared-link repository to kysely (#15289)
* refactor: migrate shared-link repository to kysely * fix duplicate individual shared link return in getAll when there are more than 1 asset in the shared link * using correct order condition * using eb.table --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
@@ -1,90 +1,256 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Insertable, Kysely, sql, Updateable } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import _ from 'lodash';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB, SharedLinks } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||
import { SharedLinkType } from 'src/enum';
|
||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class SharedLinkRepository implements ISharedLinkRepository {
|
||||
constructor(@InjectRepository(SharedLinkEntity) private repository: Repository<SharedLinkEntity>) {}
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||
get(userId: string, id: string): Promise<SharedLinkEntity | null> {
|
||||
return this.repository.findOne({
|
||||
where: {
|
||||
id,
|
||||
userId,
|
||||
},
|
||||
relations: {
|
||||
assets: {
|
||||
exifInfo: true,
|
||||
},
|
||||
album: {
|
||||
assets: {
|
||||
exifInfo: true,
|
||||
},
|
||||
owner: true,
|
||||
},
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
assets: {
|
||||
fileCreatedAt: 'ASC',
|
||||
},
|
||||
album: {
|
||||
assets: {
|
||||
fileCreatedAt: 'ASC',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
get(userId: string, id: string): Promise<SharedLinkEntity | undefined> {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.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')
|
||||
.innerJoinLateral(
|
||||
(eb) => eb.selectFrom('exif').selectAll('exif').whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('exifInfo').as('exifInfo'))
|
||||
.orderBy('assets.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')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.selectAll('assets')
|
||||
.whereRef('albums_assets_assets.assetsId', '=', 'assets.id')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('exif')
|
||||
.selectAll('exif')
|
||||
.whereRef('exif.assetId', '=', 'assets.id')
|
||||
.as('assets_exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('assets_exifInfo')).as('exifInfo'))
|
||||
.orderBy('assets.fileCreatedAt', 'asc')
|
||||
.as('assets'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.selectAll('users')
|
||||
.whereRef('users.id', '=', 'albums.ownerId')
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.as('owner'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) =>
|
||||
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".*`])
|
||||
.as('album'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.coalesce(eb.fn.jsonAgg('a').filterWhere('a.id', 'is not', null), sql`'[]'`).as('assets'))
|
||||
.groupBy(['shared_links.id', sql`"album".*`])
|
||||
.select((eb) => eb.fn.toJson('album').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')
|
||||
.executeTakeFirst() as Promise<SharedLinkEntity | undefined>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
getAll(userId: string): Promise<SharedLinkEntity[]> {
|
||||
return this.repository.find({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
relations: {
|
||||
assets: true,
|
||||
album: {
|
||||
owner: true,
|
||||
},
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
},
|
||||
});
|
||||
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')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.whereRef('assets.id', '=', 'shared_link__asset.assetsId')
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.selectAll('assets')
|
||||
.as('assets'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('albums')
|
||||
.selectAll('albums')
|
||||
.whereRef('albums.id', '=', 'shared_links.albumId')
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.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',
|
||||
])
|
||||
.whereRef('users.id', '=', 'albums.ownerId')
|
||||
.where('users.deletedAt', 'is', null)
|
||||
.as('owner'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('owner').as('owner'))
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.as('album'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) => eb.fn.toJson('album').as('album'))
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)]))
|
||||
.orderBy('shared_links.createdAt', 'desc')
|
||||
.distinctOn(['shared_links.createdAt'])
|
||||
.execute() as unknown as Promise<SharedLinkEntity[]>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.BUFFER] })
|
||||
async getByKey(key: Buffer): Promise<SharedLinkEntity | null> {
|
||||
return await this.repository.findOne({
|
||||
where: {
|
||||
key,
|
||||
},
|
||||
relations: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
async getByKey(key: Buffer): Promise<SharedLinkEntity | undefined> {
|
||||
return this.db
|
||||
.selectFrom('shared_links')
|
||||
.selectAll('shared_links')
|
||||
.where('shared_links.key', '=', key)
|
||||
.leftJoin('albums', 'albums.id', 'shared_links.albumId')
|
||||
.where('albums.deletedAt', 'is', null)
|
||||
.select((eb) =>
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('users')
|
||||
.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',
|
||||
])
|
||||
.whereRef('users.id', '=', 'shared_links.userId'),
|
||||
).as('user'),
|
||||
)
|
||||
.where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('albums.id', 'is not', null)]))
|
||||
.executeTakeFirst() as Promise<SharedLinkEntity | undefined>;
|
||||
}
|
||||
|
||||
create(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
|
||||
return this.save(entity);
|
||||
async create(entity: Insertable<SharedLinks> & { assetIds?: string[] }): Promise<SharedLinkEntity> {
|
||||
const { id } = await this.db
|
||||
.insertInto('shared_links')
|
||||
.values(_.omit(entity, 'assetIds'))
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (entity.assetIds && entity.assetIds.length > 0) {
|
||||
await this.db
|
||||
.insertInto('shared_link__asset')
|
||||
.values(entity.assetIds!.map((assetsId) => ({ assetsId, sharedLinksId: id })))
|
||||
.execute();
|
||||
}
|
||||
|
||||
return this.getSharedLinks(id);
|
||||
}
|
||||
|
||||
update(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
|
||||
return this.save(entity);
|
||||
async update(entity: Updateable<SharedLinks> & { id: string; assetIds?: string[] }): Promise<SharedLinkEntity> {
|
||||
const { id } = await this.db
|
||||
.updateTable('shared_links')
|
||||
.set(_.omit(entity, 'assets', 'album', 'assetIds'))
|
||||
.where('shared_links.id', '=', entity.id)
|
||||
.returningAll()
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (entity.assetIds && entity.assetIds.length > 0) {
|
||||
await this.db
|
||||
.insertInto('shared_link__asset')
|
||||
.values(entity.assetIds!.map((assetsId) => ({ assetsId, sharedLinksId: id })))
|
||||
.execute();
|
||||
}
|
||||
|
||||
return this.getSharedLinks(id);
|
||||
}
|
||||
|
||||
async remove(entity: SharedLinkEntity): Promise<void> {
|
||||
await this.repository.remove(entity);
|
||||
await this.db.deleteFrom('shared_links').where('shared_links.id', '=', entity.id).execute();
|
||||
}
|
||||
|
||||
private async save(entity: Partial<SharedLinkEntity>): Promise<SharedLinkEntity> {
|
||||
await this.repository.save(entity);
|
||||
return this.repository.findOneOrFail({ where: { id: entity.id } });
|
||||
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')
|
||||
.leftJoinLateral(
|
||||
(eb) =>
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.whereRef('assets.id', '=', 'shared_link__asset.assetsId')
|
||||
.selectAll('assets')
|
||||
.innerJoinLateral(
|
||||
(eb) => eb.selectFrom('exif').whereRef('exif.assetId', '=', 'assets.id').selectAll().as('exif'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.as('assets'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.select((eb) =>
|
||||
eb.fn.coalesce(eb.fn.jsonAgg('assets').filterWhere('assets.id', 'is not', null), sql`'[]'`).as('assets'),
|
||||
)
|
||||
.groupBy('shared_links.id')
|
||||
.executeTakeFirstOrThrow() as Promise<SharedLinkEntity>;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user