1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-03-31 21:29:38 +02:00

refactor: migrate access repo to kysely ()

This commit is contained in:
Alex 2025-01-16 09:25:03 -06:00 committed by GitHub
parent 89f40b311c
commit 378bd3c993
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 367 additions and 477 deletions
server/src

View file

@ -1,180 +1,137 @@
-- NOTE: This file is auto generated by ./sql-generator -- NOTE: This file is auto generated by ./sql-generator
-- AccessRepository.activity.checkOwnerAccess -- AccessRepository.activity.checkOwnerAccess
SELECT select
"ActivityEntity"."id" AS "ActivityEntity_id" "activity"."id"
FROM from
"activity" "ActivityEntity" "activity"
WHERE where
( "activity"."id" in ($1)
("ActivityEntity"."id" IN ($1)) and "activity"."userId" = $2
AND ("ActivityEntity"."userId" = $2)
)
-- AccessRepository.activity.checkAlbumOwnerAccess -- AccessRepository.activity.checkAlbumOwnerAccess
SELECT select
"ActivityEntity"."id" AS "ActivityEntity_id" "activity"."id"
FROM from
"activity" "ActivityEntity" "activity"
LEFT JOIN "albums" "ActivityEntity__ActivityEntity_album" ON "ActivityEntity__ActivityEntity_album"."id" = "ActivityEntity"."albumId" left join "albums" on "activity"."albumId" = "albums"."id"
AND ( and "albums"."deletedAt" is null
"ActivityEntity__ActivityEntity_album"."deletedAt" IS NULL where
) "activity"."id" in ($1)
WHERE and "albums"."ownerId" = $2::uuid
(
("ActivityEntity"."id" IN ($1))
AND (
(
(
"ActivityEntity__ActivityEntity_album"."ownerId" = $2
)
)
)
)
-- AccessRepository.activity.checkCreateAccess -- AccessRepository.activity.checkCreateAccess
SELECT select
"album"."id" AS "album_id" "albums"."id"
FROM from
"albums" "album" "albums"
LEFT JOIN "albums_shared_users_users" "album_albumUsers_users" ON "album_albumUsers_users"."albumsId" = "album"."id" left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
LEFT JOIN "users" "albumUsers" ON "albumUsers"."id" = "album_albumUsers_users"."usersId" left join "users" on "users"."id" = "albumUsers"."usersId"
AND ("albumUsers"."deletedAt" IS NULL) and "users"."deletedAt" is null
WHERE where
( "albums"."id" in ($1)
"album"."id" IN ($1) and "albums"."isActivityEnabled" = $2
AND "album"."isActivityEnabled" = true and (
AND ( "albums"."ownerId" = $3
"album"."ownerId" = $2 or "users"."id" = $4
OR "albumUsers"."id" = $2
)
) )
AND ("album"."deletedAt" IS NULL) and "albums"."deletedAt" is null
-- AccessRepository.album.checkOwnerAccess -- AccessRepository.album.checkOwnerAccess
SELECT select
"AlbumEntity"."id" AS "AlbumEntity_id" "albums"."id"
FROM from
"albums" "AlbumEntity" "albums"
WHERE where
( "albums"."id" in ($1)
( and "albums"."ownerId" = $2
("AlbumEntity"."id" IN ($1)) and "albums"."deletedAt" is null
AND ("AlbumEntity"."ownerId" = $2)
)
)
AND ("AlbumEntity"."deletedAt" IS NULL)
-- AccessRepository.album.checkSharedAlbumAccess -- AccessRepository.album.checkSharedAlbumAccess
SELECT select
"AlbumEntity"."id" AS "AlbumEntity_id" "albums"."id"
FROM from
"albums" "AlbumEntity" "albums"
LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id" left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId" left join "users" on "users"."id" = "albumUsers"."usersId"
AND ( and "users"."deletedAt" is null
"a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL where
) "albums"."id" in ($1)
WHERE and "albums"."deletedAt" is null
( and "users"."id" = $2
( and "albumUsers"."role" in ($3, $4)
("AlbumEntity"."id" IN ($1))
AND (
(
(
(
(
"a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = $2
)
)
)
AND (
"AlbumEntity__AlbumEntity_albumUsers"."role" IN ($3, $4)
)
)
)
)
)
AND ("AlbumEntity"."deletedAt" IS NULL)
-- AccessRepository.album.checkSharedLinkAccess -- AccessRepository.album.checkSharedLinkAccess
SELECT select
"SharedLinkEntity"."albumId" AS "SharedLinkEntity_albumId", "shared_links"."albumId"
"SharedLinkEntity"."id" AS "SharedLinkEntity_id" from
FROM "shared_links"
"shared_links" "SharedLinkEntity" where
WHERE "shared_links"."id" = $1
( and "shared_links"."albumId" in ($2)
("SharedLinkEntity"."id" = $1)
AND ("SharedLinkEntity"."albumId" IN ($2))
)
-- AccessRepository.asset.checkAlbumAccess -- AccessRepository.asset.checkAlbumAccess
SELECT select
"asset"."id" AS "assetId", "assets"."id",
"asset"."livePhotoVideoId" AS "livePhotoVideoId" "assets"."livePhotoVideoId"
FROM from
"albums" "album" "albums"
INNER JOIN "albums_assets_assets" "album_asset" ON "album_asset"."albumsId" = "album"."id" inner join "albums_assets_assets" as "albumAssets" on "albums"."id" = "albumAssets"."albumsId"
INNER JOIN "assets" "asset" ON "asset"."id" = "album_asset"."assetsId" inner join "assets" on "assets"."id" = "albumAssets"."assetsId"
AND ("asset"."deletedAt" IS NULL) and "assets"."deletedAt" is null
LEFT JOIN "albums_shared_users_users" "album_albumUsers_users" ON "album_albumUsers_users"."albumsId" = "album"."id" left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
LEFT JOIN "users" "albumUsers" ON "albumUsers"."id" = "album_albumUsers_users"."usersId" left join "users" on "users"."id" = "albumUsers"."usersId"
AND ("albumUsers"."deletedAt" IS NULL) and "users"."deletedAt" is null
WHERE where
( array["assets"."id", "assets"."livePhotoVideoId"] && array[$1]::uuid []
array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid [] and (
AND ( "albums"."ownerId" = $2
"album"."ownerId" = $2 or "users"."id" = $3
OR "albumUsers"."id" = $2
)
) )
AND ("album"."deletedAt" IS NULL) and "albums"."deletedAt" is null
-- AccessRepository.asset.checkOwnerAccess -- AccessRepository.asset.checkOwnerAccess
SELECT select
"AssetEntity"."id" AS "AssetEntity_id" "assets"."id"
FROM from
"assets" "AssetEntity" "assets"
WHERE where
( "assets"."id" in ($1)
("AssetEntity"."id" IN ($1)) and "assets"."ownerId" = $2
AND ("AssetEntity"."ownerId" = $2)
)
-- AccessRepository.asset.checkPartnerAccess -- AccessRepository.asset.checkPartnerAccess
SELECT select
"asset"."id" AS "assetId" "assets"."id"
FROM from
"partners" "partner" "partners" as "partner"
INNER JOIN "users" "sharedBy" ON "sharedBy"."id" = "partner"."sharedById" inner join "users" as "sharedBy" on "sharedBy"."id" = "partner"."sharedById"
AND ("sharedBy"."deletedAt" IS NULL) and "sharedBy"."deletedAt" is null
INNER JOIN "assets" "asset" ON "asset"."ownerId" = "sharedBy"."id" inner join "assets" on "assets"."ownerId" = "sharedBy"."id"
AND ("asset"."deletedAt" IS NULL) and "assets"."deletedAt" is null
WHERE where
"partner"."sharedWithId" = $1 "partner"."sharedWithId" = $1
AND "asset"."isArchived" = false and "assets"."isArchived" = $2
AND "asset"."id" IN ($2) and "assets"."id" in ($3)
-- AccessRepository.asset.checkSharedLinkAccess -- AccessRepository.asset.checkSharedLinkAccess
SELECT select
"assets"."id" AS "assetId", "assets"."id" as "assetId",
"assets"."livePhotoVideoId" AS "assetLivePhotoVideoId", "assets"."livePhotoVideoId" as "assetLivePhotoVideoId",
"albumAssets"."id" AS "albumAssetId", "albumAssets"."id" as "albumAssetId",
"albumAssets"."livePhotoVideoId" AS "albumAssetLivePhotoVideoId" "albumAssets"."livePhotoVideoId" as "albumAssetLivePhotoVideoId"
FROM from
"shared_links" "sharedLink" "shared_links"
LEFT JOIN "albums" "album" ON "album"."id" = "sharedLink"."albumId" left join "albums" on "albums"."id" = "shared_links"."albumId"
AND ("album"."deletedAt" IS NULL) and "albums"."deletedAt" is null
LEFT JOIN "shared_link__asset" "assets_sharedLink" ON "assets_sharedLink"."sharedLinksId" = "sharedLink"."id" left join "shared_link__asset" on "shared_link__asset"."sharedLinksId" = "shared_links"."id"
LEFT JOIN "assets" "assets" ON "assets"."id" = "assets_sharedLink"."assetsId" left join "assets" on "assets"."id" = "shared_link__asset"."assetsId"
AND ("assets"."deletedAt" IS NULL) and "assets"."deletedAt" is null
LEFT JOIN "albums_assets_assets" "album_albumAssets" ON "album_albumAssets"."albumsId" = "album"."id" left join "albums_assets_assets" on "albums_assets_assets"."albumsId" = "albums"."id"
LEFT JOIN "assets" "albumAssets" ON "albumAssets"."id" = "album_albumAssets"."assetsId" left join "assets" as "albumAssets" on "albumAssets"."id" = "albums_assets_assets"."assetsId"
AND ("albumAssets"."deletedAt" IS NULL) and "albumAssets"."deletedAt" is null
WHERE where
"sharedLink"."id" = $1 "shared_links"."id" = $1
AND array[ and array[
"assets"."id", "assets"."id",
"assets"."livePhotoVideoId", "assets"."livePhotoVideoId",
"albumAssets"."id", "albumAssets"."id",
@ -182,100 +139,76 @@ WHERE
] && array[$2]::uuid [] ] && array[$2]::uuid []
-- AccessRepository.authDevice.checkOwnerAccess -- AccessRepository.authDevice.checkOwnerAccess
SELECT select
"SessionEntity"."id" AS "SessionEntity_id" "sessions"."id"
FROM from
"sessions" "SessionEntity" "sessions"
WHERE where
( "sessions"."userId" = $1
("SessionEntity"."userId" = $1) and "sessions"."id" in ($2)
AND ("SessionEntity"."id" IN ($2))
)
-- AccessRepository.memory.checkOwnerAccess -- AccessRepository.memory.checkOwnerAccess
SELECT select
"MemoryEntity"."id" AS "MemoryEntity_id" "memories"."id"
FROM from
"memories" "MemoryEntity" "memories"
WHERE where
( "memories"."id" in ($1)
( and "memories"."ownerId" = $2
("MemoryEntity"."id" IN ($1)) and "memories"."deletedAt" is null
AND ("MemoryEntity"."ownerId" = $2)
)
)
AND ("MemoryEntity"."deletedAt" IS NULL)
-- AccessRepository.person.checkOwnerAccess -- AccessRepository.person.checkOwnerAccess
SELECT select
"PersonEntity"."id" AS "PersonEntity_id" "person"."id"
FROM from
"person" "PersonEntity" "person"
WHERE where
( "person"."id" in ($1)
("PersonEntity"."id" IN ($1)) and "person"."ownerId" = $2
AND ("PersonEntity"."ownerId" = $2)
)
-- AccessRepository.person.checkFaceOwnerAccess -- AccessRepository.person.checkFaceOwnerAccess
SELECT select
"AssetFaceEntity"."id" AS "AssetFaceEntity_id" "asset_faces"."id"
FROM from
"asset_faces" "AssetFaceEntity" "asset_faces"
LEFT JOIN "assets" "AssetFaceEntity__AssetFaceEntity_asset" ON "AssetFaceEntity__AssetFaceEntity_asset"."id" = "AssetFaceEntity"."assetId" left join "assets" on "assets"."id" = "asset_faces"."assetId"
AND ( and "assets"."deletedAt" is null
"AssetFaceEntity__AssetFaceEntity_asset"."deletedAt" IS NULL where
) "asset_faces"."id" in ($1)
WHERE and "assets"."ownerId" = $2
(
("AssetFaceEntity"."id" IN ($1))
AND (
(
(
"AssetFaceEntity__AssetFaceEntity_asset"."ownerId" = $2
)
)
)
)
-- AccessRepository.partner.checkUpdateAccess -- AccessRepository.partner.checkUpdateAccess
SELECT select
"partner"."sharedById" AS "partner_sharedById", "partners"."sharedById"
"partner"."sharedWithId" AS "partner_sharedWithId" from
FROM "partners"
"partners" "partner" where
WHERE "partners"."sharedById" in ($1)
"partner"."sharedById" IN ($1) and "partners"."sharedWithId" = $2
AND "partner"."sharedWithId" = $2
-- AccessRepository.stack.checkOwnerAccess -- AccessRepository.stack.checkOwnerAccess
SELECT select
"StackEntity"."id" AS "StackEntity_id" "stacks"."id"
FROM from
"asset_stack" "StackEntity" "asset_stack" as "stacks"
WHERE where
( "stacks"."id" in ($1)
("StackEntity"."id" IN ($1)) and "stacks"."ownerId" = $2
AND ("StackEntity"."ownerId" = $2)
)
-- AccessRepository.tag.checkOwnerAccess -- AccessRepository.tag.checkOwnerAccess
SELECT select
"TagEntity"."id" AS "TagEntity_id" "tags"."id"
FROM from
"tags" "TagEntity" "tags"
WHERE where
( "tags"."id" in ($1)
("TagEntity"."id" IN ($1)) and "tags"."userId" = $2
AND ("TagEntity"."userId" = $2)
)
-- AccessRepository.timeline.checkPartnerAccess -- AccessRepository.timeline.checkPartnerAccess
SELECT select
"partner"."sharedById" AS "partner_sharedById", "partners"."sharedById"
"partner"."sharedWithId" AS "partner_sharedWithId" from
FROM "partners"
"partners" "partner" where
WHERE "partners"."sharedById" in ($1)
"partner"."sharedById" IN ($1) and "partners"."sharedWithId" = $2
AND "partner"."sharedWithId" = $2

View file

@ -1,21 +1,12 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { Kysely, sql } from 'kysely';
import { InjectKysely } from 'nestjs-kysely';
import { DB } from 'src/db';
import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
import { ActivityEntity } from 'src/entities/activity.entity';
import { AlbumEntity } from 'src/entities/album.entity';
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { LibraryEntity } from 'src/entities/library.entity';
import { MemoryEntity } from 'src/entities/memory.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { SessionEntity } from 'src/entities/session.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { StackEntity } from 'src/entities/stack.entity';
import { TagEntity } from 'src/entities/tag.entity';
import { AlbumUserRole } from 'src/enum'; import { AlbumUserRole } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAccessRepository } from 'src/interfaces/access.interface';
import { Brackets, In, Repository } from 'typeorm'; import { asUuid } from 'src/utils/database';
type IActivityAccess = IAccessRepository['activity']; type IActivityAccess = IAccessRepository['activity'];
type IAlbumAccess = IAccessRepository['album']; type IAlbumAccess = IAccessRepository['album'];
@ -30,10 +21,7 @@ type ITimelineAccess = IAccessRepository['timeline'];
@Injectable() @Injectable()
class ActivityAccess implements IActivityAccess { class ActivityAccess implements IActivityAccess {
constructor( constructor(private db: Kysely<DB>) {}
private activityRepository: Repository<ActivityEntity>,
private albumRepository: Repository<AlbumEntity>,
) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -42,15 +30,16 @@ class ActivityAccess implements IActivityAccess {
return new Set(); return new Set();
} }
return this.activityRepository return this.db
.find({ .selectFrom('activity')
select: { id: true }, .select('activity.id')
where: { .where('activity.id', 'in', [...activityIds])
id: In([...activityIds]), .where('activity.userId', '=', userId)
userId, .execute()
}, .then((activities) => {
}) console.log('activities', activities);
.then((activities) => new Set(activities.map((activity) => activity.id))); return new Set(activities.map((activity) => activity.id));
});
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ -60,16 +49,13 @@ class ActivityAccess implements IActivityAccess {
return new Set(); return new Set();
} }
return this.activityRepository return this.db
.find({ .selectFrom('activity')
select: { id: true }, .select('activity.id')
where: { .leftJoin('albums', (join) => join.onRef('activity.albumId', '=', 'albums.id').on('albums.deletedAt', 'is', null))
id: In([...activityIds]), .where('activity.id', 'in', [...activityIds])
album: { .whereRef('albums.ownerId', '=', asUuid(userId))
ownerId: userId, .execute()
},
},
})
.then((activities) => new Set(activities.map((activity) => activity.id))); .then((activities) => new Set(activities.map((activity) => activity.id)));
} }
@ -80,28 +66,22 @@ class ActivityAccess implements IActivityAccess {
return new Set(); return new Set();
} }
return this.albumRepository return this.db
.createQueryBuilder('album') .selectFrom('albums')
.select('album.id') .select('albums.id')
.leftJoin('album.albumUsers', 'album_albumUsers_users') .leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
.leftJoin('album_albumUsers_users.user', 'albumUsers') .leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
.where('album.id IN (:...albumIds)', { albumIds: [...albumIds] }) .where('albums.id', 'in', [...albumIds])
.andWhere('album.isActivityEnabled = true') .where('albums.isActivityEnabled', '=', true)
.andWhere( .where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
new Brackets((qb) => { .where('albums.deletedAt', 'is', null)
qb.where('album.ownerId = :userId', { userId }).orWhere('albumUsers.id = :userId', { userId }); .execute()
}),
)
.getMany()
.then((albums) => new Set(albums.map((album) => album.id))); .then((albums) => new Set(albums.map((album) => album.id)));
} }
} }
class AlbumAccess implements IAlbumAccess { class AlbumAccess implements IAlbumAccess {
constructor( constructor(private db: Kysely<DB>) {}
private albumRepository: Repository<AlbumEntity>,
private sharedLinkRepository: Repository<SharedLinkEntity>,
) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -110,14 +90,13 @@ class AlbumAccess implements IAlbumAccess {
return new Set(); return new Set();
} }
return this.albumRepository return this.db
.find({ .selectFrom('albums')
select: { id: true }, .select('albums.id')
where: { .where('albums.id', 'in', [...albumIds])
id: In([...albumIds]), .where('albums.ownerId', '=', userId)
ownerId: userId, .where('albums.deletedAt', 'is', null)
}, .execute()
})
.then((albums) => new Set(albums.map((album) => album.id))); .then((albums) => new Set(albums.map((album) => album.id)));
} }
@ -128,19 +107,19 @@ class AlbumAccess implements IAlbumAccess {
return new Set(); return new Set();
} }
return this.albumRepository const accessRole =
.find({ access === AlbumUserRole.EDITOR ? [AlbumUserRole.EDITOR] : [AlbumUserRole.EDITOR, AlbumUserRole.VIEWER];
select: { id: true },
where: { return this.db
id: In([...albumIds]), .selectFrom('albums')
albumUsers: { .select('albums.id')
user: { id: userId }, .leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
// If editor access is needed we check for it, otherwise both are accepted .leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
role: .where('albums.id', 'in', [...albumIds])
access === AlbumUserRole.EDITOR ? AlbumUserRole.EDITOR : In([AlbumUserRole.EDITOR, AlbumUserRole.VIEWER]), .where('albums.deletedAt', 'is', null)
}, .where('users.id', '=', userId)
}, .where('albumUsers.role', 'in', [...accessRole])
}) .execute()
.then((albums) => new Set(albums.map((album) => album.id))); .then((albums) => new Set(albums.map((album) => album.id)));
} }
@ -151,14 +130,12 @@ class AlbumAccess implements IAlbumAccess {
return new Set(); return new Set();
} }
return this.sharedLinkRepository return this.db
.find({ .selectFrom('shared_links')
select: { albumId: true }, .select('shared_links.albumId')
where: { .where('shared_links.id', '=', sharedLinkId)
id: sharedLinkId, .where('shared_links.albumId', 'in', [...albumIds])
albumId: In([...albumIds]), .execute()
},
})
.then( .then(
(sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))), (sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
); );
@ -166,12 +143,7 @@ class AlbumAccess implements IAlbumAccess {
} }
class AssetAccess implements IAssetAccess { class AssetAccess implements IAssetAccess {
constructor( constructor(private db: Kysely<DB>) {}
private albumRepository: Repository<AlbumEntity>,
private assetRepository: Repository<AssetEntity>,
private partnerRepository: Repository<PartnerEntity>,
private sharedLinkRepository: Repository<SharedLinkEntity>,
) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -180,30 +152,31 @@ class AssetAccess implements IAssetAccess {
return new Set(); return new Set();
} }
return this.albumRepository return this.db
.createQueryBuilder('album') .selectFrom('albums')
.innerJoin('album.assets', 'asset') .innerJoin('albums_assets_assets as albumAssets', 'albums.id', 'albumAssets.albumsId')
.leftJoin('album.albumUsers', 'album_albumUsers_users') .innerJoin('assets', (join) =>
.leftJoin('album_albumUsers_users.user', 'albumUsers') join.onRef('assets.id', '=', 'albumAssets.assetsId').on('assets.deletedAt', 'is', null),
.select('asset.id', 'assetId')
.addSelect('asset.livePhotoVideoId', 'livePhotoVideoId')
.where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', {
assetIds: [...assetIds],
})
.andWhere(
new Brackets((qb) => {
qb.where('album.ownerId = :userId', { userId }).orWhere('albumUsers.id = :userId', { userId });
}),
) )
.getRawMany() .leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
.then((rows) => { .leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
.select(['assets.id', 'assets.livePhotoVideoId'])
.where(
sql`array["assets"."id", "assets"."livePhotoVideoId"]`,
'&&',
sql`array[${sql.join([...assetIds])}]::uuid[] `,
)
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
.where('albums.deletedAt', 'is', null)
.execute()
.then((assets) => {
const allowedIds = new Set<string>(); const allowedIds = new Set<string>();
for (const row of rows) { for (const asset of assets) {
if (row.assetId && assetIds.has(row.assetId)) { if (asset.id && assetIds.has(asset.id)) {
allowedIds.add(row.assetId); allowedIds.add(asset.id);
} }
if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) { if (asset.livePhotoVideoId && assetIds.has(asset.livePhotoVideoId)) {
allowedIds.add(row.livePhotoVideoId); allowedIds.add(asset.livePhotoVideoId);
} }
} }
return allowedIds; return allowedIds;
@ -217,15 +190,12 @@ class AssetAccess implements IAssetAccess {
return new Set(); return new Set();
} }
return this.assetRepository return this.db
.find({ .selectFrom('assets')
select: { id: true }, .select('assets.id')
where: { .where('assets.id', 'in', [...assetIds])
id: In([...assetIds]), .where('assets.ownerId', '=', userId)
ownerId: userId, .execute()
},
withDeleted: true,
})
.then((assets) => new Set(assets.map((asset) => asset.id))); .then((assets) => new Set(assets.map((asset) => asset.id)));
} }
@ -236,16 +206,20 @@ class AssetAccess implements IAssetAccess {
return new Set(); return new Set();
} }
return this.partnerRepository return this.db
.createQueryBuilder('partner') .selectFrom('partners as partner')
.innerJoin('partner.sharedBy', 'sharedBy') .innerJoin('users as sharedBy', (join) =>
.innerJoin('sharedBy.assets', 'asset') join.onRef('sharedBy.id', '=', 'partner.sharedById').on('sharedBy.deletedAt', 'is', null),
.select('asset.id', 'assetId') )
.where('partner.sharedWithId = :userId', { userId }) .innerJoin('assets', (join) =>
.andWhere('asset.isArchived = false') join.onRef('assets.ownerId', '=', 'sharedBy.id').on('assets.deletedAt', 'is', null),
.andWhere('asset.id IN (:...assetIds)', { assetIds: [...assetIds] }) )
.getRawMany() .select('assets.id')
.then((rows) => new Set(rows.map((row) => row.assetId))); .where('partner.sharedWithId', '=', userId)
.where('assets.isArchived', '=', false)
.where('assets.id', 'in', [...assetIds])
.execute()
.then((assets) => new Set(assets.map((asset) => asset.id)));
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ -255,23 +229,32 @@ class AssetAccess implements IAssetAccess {
return new Set(); return new Set();
} }
return this.sharedLinkRepository return this.db
.createQueryBuilder('sharedLink') .selectFrom('shared_links')
.leftJoin('sharedLink.album', 'album') .leftJoin('albums', (join) =>
.leftJoin('sharedLink.assets', 'assets') join.onRef('albums.id', '=', 'shared_links.albumId').on('albums.deletedAt', 'is', null),
.leftJoin('album.assets', 'albumAssets')
.select('assets.id', 'assetId')
.addSelect('albumAssets.id', 'albumAssetId')
.addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId')
.addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId')
.where('sharedLink.id = :sharedLinkId', { sharedLinkId })
.andWhere(
'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]',
{
assetIds: [...assetIds],
},
) )
.getRawMany() .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),
)
.select([
'assets.id as assetId',
'assets.livePhotoVideoId as assetLivePhotoVideoId',
'albumAssets.id as albumAssetId',
'albumAssets.livePhotoVideoId as albumAssetLivePhotoVideoId',
])
.where('shared_links.id', '=', sharedLinkId)
.where(
sql`array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"]`,
'&&',
sql`array[${sql.join([...assetIds])}]::uuid[] `,
)
.execute()
.then((rows) => { .then((rows) => {
const allowedIds = new Set<string>(); const allowedIds = new Set<string>();
for (const row of rows) { for (const row of rows) {
@ -294,7 +277,7 @@ class AssetAccess implements IAssetAccess {
} }
class AuthDeviceAccess implements IAuthDeviceAccess { class AuthDeviceAccess implements IAuthDeviceAccess {
constructor(private sessionRepository: Repository<SessionEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -303,20 +286,18 @@ class AuthDeviceAccess implements IAuthDeviceAccess {
return new Set(); return new Set();
} }
return this.sessionRepository return this.db
.find({ .selectFrom('sessions')
select: { id: true }, .select('sessions.id')
where: { .where('sessions.userId', '=', userId)
userId, .where('sessions.id', 'in', [...deviceIds])
id: In([...deviceIds]), .execute()
},
})
.then((tokens) => new Set(tokens.map((token) => token.id))); .then((tokens) => new Set(tokens.map((token) => token.id)));
} }
} }
class StackAccess implements IStackAccess { class StackAccess implements IStackAccess {
constructor(private stackRepository: Repository<StackEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -325,20 +306,18 @@ class StackAccess implements IStackAccess {
return new Set(); return new Set();
} }
return this.stackRepository return this.db
.find({ .selectFrom('asset_stack as stacks')
select: { id: true }, .select('stacks.id')
where: { .where('stacks.id', 'in', [...stackIds])
id: In([...stackIds]), .where('stacks.ownerId', '=', userId)
ownerId: userId, .execute()
},
})
.then((stacks) => new Set(stacks.map((stack) => stack.id))); .then((stacks) => new Set(stacks.map((stack) => stack.id)));
} }
} }
class TimelineAccess implements ITimelineAccess { class TimelineAccess implements ITimelineAccess {
constructor(private partnerRepository: Repository<PartnerEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -347,18 +326,18 @@ class TimelineAccess implements ITimelineAccess {
return new Set(); return new Set();
} }
return this.partnerRepository return this.db
.createQueryBuilder('partner') .selectFrom('partners')
.select('partner.sharedById') .select('partners.sharedById')
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] }) .where('partners.sharedById', 'in', [...partnerIds])
.andWhere('partner.sharedWithId = :userId', { userId }) .where('partners.sharedWithId', '=', userId)
.getMany() .execute()
.then((partners) => new Set(partners.map((partner) => partner.sharedById))); .then((partners) => new Set(partners.map((partner) => partner.sharedById)));
} }
} }
class MemoryAccess implements IMemoryAccess { class MemoryAccess implements IMemoryAccess {
constructor(private memoryRepository: Repository<MemoryEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -367,23 +346,19 @@ class MemoryAccess implements IMemoryAccess {
return new Set(); return new Set();
} }
return this.memoryRepository return this.db
.find({ .selectFrom('memories')
select: { id: true }, .select('memories.id')
where: { .where('memories.id', 'in', [...memoryIds])
id: In([...memoryIds]), .where('memories.ownerId', '=', userId)
ownerId: userId, .where('memories.deletedAt', 'is', null)
}, .execute()
})
.then((memories) => new Set(memories.map((memory) => memory.id))); .then((memories) => new Set(memories.map((memory) => memory.id)));
} }
} }
class PersonAccess implements IPersonAccess { class PersonAccess implements IPersonAccess {
constructor( constructor(private db: Kysely<DB>) {}
private assetFaceRepository: Repository<AssetFaceEntity>,
private personRepository: Repository<PersonEntity>,
) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -392,14 +367,12 @@ class PersonAccess implements IPersonAccess {
return new Set(); return new Set();
} }
return this.personRepository return this.db
.find({ .selectFrom('person')
select: { id: true }, .select('person.id')
where: { .where('person.id', 'in', [...personIds])
id: In([...personIds]), .where('person.ownerId', '=', userId)
ownerId: userId, .execute()
},
})
.then((persons) => new Set(persons.map((person) => person.id))); .then((persons) => new Set(persons.map((person) => person.id)));
} }
@ -410,22 +383,21 @@ class PersonAccess implements IPersonAccess {
return new Set(); return new Set();
} }
return this.assetFaceRepository return this.db
.find({ .selectFrom('asset_faces')
select: { id: true }, .select('asset_faces.id')
where: { .leftJoin('assets', (join) =>
id: In([...assetFaceIds]), join.onRef('assets.id', '=', 'asset_faces.assetId').on('assets.deletedAt', 'is', null),
asset: { )
ownerId: userId, .where('asset_faces.id', 'in', [...assetFaceIds])
}, .where('assets.ownerId', '=', userId)
}, .execute()
})
.then((faces) => new Set(faces.map((face) => face.id))); .then((faces) => new Set(faces.map((face) => face.id)));
} }
} }
class PartnerAccess implements IPartnerAccess { class PartnerAccess implements IPartnerAccess {
constructor(private partnerRepository: Repository<PartnerEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -434,18 +406,18 @@ class PartnerAccess implements IPartnerAccess {
return new Set(); return new Set();
} }
return this.partnerRepository return this.db
.createQueryBuilder('partner') .selectFrom('partners')
.select('partner.sharedById') .select('partners.sharedById')
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] }) .where('partners.sharedById', 'in', [...partnerIds])
.andWhere('partner.sharedWithId = :userId', { userId }) .where('partners.sharedWithId', '=', userId)
.getMany() .execute()
.then((partners) => new Set(partners.map((partner) => partner.sharedById))); .then((partners) => new Set(partners.map((partner) => partner.sharedById)));
} }
} }
class TagAccess implements ITagAccess { class TagAccess implements ITagAccess {
constructor(private tagRepository: Repository<TagEntity>) {} constructor(private db: Kysely<DB>) {}
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
@ChunkedSet({ paramIndex: 1 }) @ChunkedSet({ paramIndex: 1 })
@ -454,14 +426,12 @@ class TagAccess implements ITagAccess {
return new Set(); return new Set();
} }
return this.tagRepository return this.db
.find({ .selectFrom('tags')
select: { id: true }, .select('tags.id')
where: { .where('tags.id', 'in', [...tagIds])
id: In([...tagIds]), .where('tags.userId', '=', userId)
userId, .execute()
},
})
.then((tags) => new Set(tags.map((tag) => tag.id))); .then((tags) => new Set(tags.map((tag) => tag.id)));
} }
} }
@ -478,29 +448,16 @@ export class AccessRepository implements IAccessRepository {
tag: ITagAccess; tag: ITagAccess;
timeline: ITimelineAccess; timeline: ITimelineAccess;
constructor( constructor(@InjectKysely() db: Kysely<DB>) {
@InjectRepository(ActivityEntity) activityRepository: Repository<ActivityEntity>, this.activity = new ActivityAccess(db);
@InjectRepository(AssetEntity) assetRepository: Repository<AssetEntity>, this.album = new AlbumAccess(db);
@InjectRepository(AlbumEntity) albumRepository: Repository<AlbumEntity>, this.asset = new AssetAccess(db);
@InjectRepository(LibraryEntity) libraryRepository: Repository<LibraryEntity>, this.authDevice = new AuthDeviceAccess(db);
@InjectRepository(MemoryEntity) memoryRepository: Repository<MemoryEntity>, this.memory = new MemoryAccess(db);
@InjectRepository(PartnerEntity) partnerRepository: Repository<PartnerEntity>, this.person = new PersonAccess(db);
@InjectRepository(PersonEntity) personRepository: Repository<PersonEntity>, this.partner = new PartnerAccess(db);
@InjectRepository(AssetFaceEntity) assetFaceRepository: Repository<AssetFaceEntity>, this.stack = new StackAccess(db);
@InjectRepository(SharedLinkEntity) sharedLinkRepository: Repository<SharedLinkEntity>, this.tag = new TagAccess(db);
@InjectRepository(SessionEntity) sessionRepository: Repository<SessionEntity>, this.timeline = new TimelineAccess(db);
@InjectRepository(StackEntity) stackRepository: Repository<StackEntity>,
@InjectRepository(TagEntity) tagRepository: Repository<TagEntity>,
) {
this.activity = new ActivityAccess(activityRepository, albumRepository);
this.album = new AlbumAccess(albumRepository, sharedLinkRepository);
this.asset = new AssetAccess(albumRepository, assetRepository, partnerRepository, sharedLinkRepository);
this.authDevice = new AuthDeviceAccess(sessionRepository);
this.memory = new MemoryAccess(memoryRepository);
this.person = new PersonAccess(assetFaceRepository, personRepository);
this.partner = new PartnerAccess(partnerRepository);
this.stack = new StackAccess(stackRepository);
this.tag = new TagAccess(tagRepository);
this.timeline = new TimelineAccess(partnerRepository);
} }
} }