mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:39:37 -05:00 
			
		
		
		
	feat(server): sql access checks (#6635)
This commit is contained in:
		
							parent
							
								
									bd87eb309c
								
							
						
					
					
						commit
						7fc4abba72
					
				@ -4,14 +4,17 @@ export const GENERATE_SQL_KEY = 'generate-sql-key';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface GenerateSqlQueries {
 | 
					export interface GenerateSqlQueries {
 | 
				
			||||||
  name?: string;
 | 
					  name?: string;
 | 
				
			||||||
  params?: any[];
 | 
					  params: unknown[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Decorator to enable versioning/tracking of generated Sql */
 | 
					/** Decorator to enable versioning/tracking of generated Sql */
 | 
				
			||||||
export const GenerateSql = (...options: GenerateSqlQueries[]) => SetMetadata(GENERATE_SQL_KEY, options);
 | 
					export const GenerateSql = (...options: GenerateSqlQueries[]) => SetMetadata(GENERATE_SQL_KEY, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const UUID = '00000000-0000-4000-a000-000000000000';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DummyValue = {
 | 
					export const DummyValue = {
 | 
				
			||||||
  UUID: '00000000-0000-4000-a000-000000000000',
 | 
					  UUID,
 | 
				
			||||||
 | 
					  UUID_SET: new Set([UUID]),
 | 
				
			||||||
  PAGINATION: { take: 10, skip: 0 },
 | 
					  PAGINATION: { take: 10, skip: 0 },
 | 
				
			||||||
  EMAIL: 'user@immich.app',
 | 
					  EMAIL: 'user@immich.app',
 | 
				
			||||||
  STRING: 'abcdefghi',
 | 
					  STRING: 'abcdefghi',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
import { IAccessRepository } from '@app/domain';
 | 
					import { IAccessRepository, chunks, setUnion } from '@app/domain';
 | 
				
			||||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
					import { InjectRepository } from '@nestjs/typeorm';
 | 
				
			||||||
import { Brackets, In, Repository } from 'typeorm';
 | 
					import { Brackets, In, Repository } from 'typeorm';
 | 
				
			||||||
import { chunks, setUnion } from '../../domain/domain.util';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ActivityEntity,
 | 
					  ActivityEntity,
 | 
				
			||||||
  AlbumEntity,
 | 
					  AlbumEntity,
 | 
				
			||||||
@ -13,426 +12,503 @@ import {
 | 
				
			|||||||
  SharedLinkEntity,
 | 
					  SharedLinkEntity,
 | 
				
			||||||
  UserTokenEntity,
 | 
					  UserTokenEntity,
 | 
				
			||||||
} from '../entities';
 | 
					} from '../entities';
 | 
				
			||||||
import { DATABASE_PARAMETER_CHUNK_SIZE } from '../infra.util';
 | 
					import { DATABASE_PARAMETER_CHUNK_SIZE, DummyValue, GenerateSql } from '../infra.util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AccessRepository implements IAccessRepository {
 | 
					type IActivityAccess = IAccessRepository['activity'];
 | 
				
			||||||
 | 
					type IAlbumAccess = IAccessRepository['album'];
 | 
				
			||||||
 | 
					type IAssetAccess = IAccessRepository['asset'];
 | 
				
			||||||
 | 
					type IAuthDeviceAccess = IAccessRepository['authDevice'];
 | 
				
			||||||
 | 
					type ILibraryAccess = IAccessRepository['library'];
 | 
				
			||||||
 | 
					type ITimelineAccess = IAccessRepository['timeline'];
 | 
				
			||||||
 | 
					type IPersonAccess = IAccessRepository['person'];
 | 
				
			||||||
 | 
					type IPartnerAccess = IAccessRepository['partner'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ActivityAccess implements IActivityAccess {
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    @InjectRepository(ActivityEntity) private activityRepository: Repository<ActivityEntity>,
 | 
					    private activityRepository: Repository<ActivityEntity>,
 | 
				
			||||||
    @InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
 | 
					    private albumRepository: Repository<AlbumEntity>,
 | 
				
			||||||
    @InjectRepository(AlbumEntity) private albumRepository: Repository<AlbumEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(LibraryEntity) private libraryRepository: Repository<LibraryEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(PersonEntity) private personRepository: Repository<PersonEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(AssetFaceEntity) private assetFaceRepository: Repository<AssetFaceEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(SharedLinkEntity) private sharedLinkRepository: Repository<SharedLinkEntity>,
 | 
					 | 
				
			||||||
    @InjectRepository(UserTokenEntity) private tokenRepository: Repository<UserTokenEntity>,
 | 
					 | 
				
			||||||
  ) {}
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  activity = {
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
    checkOwnerAccess: async (userId: string, activityIds: Set<string>): Promise<Set<string>> => {
 | 
					  async checkOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
      if (activityIds.size === 0) {
 | 
					    if (activityIds.size === 0) {
 | 
				
			||||||
        return new Set();
 | 
					      return new Set();
 | 
				
			||||||
      }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return Promise.all(
 | 
					    return Promise.all(
 | 
				
			||||||
        chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					      chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
          this.activityRepository
 | 
					        this.activityRepository
 | 
				
			||||||
            .find({
 | 
					          .find({
 | 
				
			||||||
              select: { id: true },
 | 
					            select: { id: true },
 | 
				
			||||||
              where: {
 | 
					            where: {
 | 
				
			||||||
                id: In(idChunk),
 | 
					              id: In(idChunk),
 | 
				
			||||||
                userId,
 | 
					              userId,
 | 
				
			||||||
              },
 | 
					            },
 | 
				
			||||||
            })
 | 
					          })
 | 
				
			||||||
            .then((activities) => new Set(activities.map((activity) => activity.id))),
 | 
					          .then((activities) => new Set(activities.map((activity) => activity.id))),
 | 
				
			||||||
        ),
 | 
					      ),
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    checkAlbumOwnerAccess: async (userId: string, activityIds: Set<string>): Promise<Set<string>> => {
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
      if (activityIds.size === 0) {
 | 
					  async checkAlbumOwnerAccess(userId: string, activityIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
        return new Set();
 | 
					    if (activityIds.size === 0) {
 | 
				
			||||||
      }
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return Promise.all(
 | 
					    return Promise.all(
 | 
				
			||||||
        chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					      chunks(activityIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
          this.activityRepository
 | 
					        this.activityRepository
 | 
				
			||||||
            .find({
 | 
					          .find({
 | 
				
			||||||
              select: { id: true },
 | 
					            select: { id: true },
 | 
				
			||||||
              where: {
 | 
					            where: {
 | 
				
			||||||
                id: In(idChunk),
 | 
					              id: In(idChunk),
 | 
				
			||||||
                album: {
 | 
					              album: {
 | 
				
			||||||
                  ownerId: userId,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .then((activities) => new Set(activities.map((activity) => activity.id))),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    checkCreateAccess: async (userId: string, albumIds: Set<string>): Promise<Set<string>> => {
 | 
					 | 
				
			||||||
      if (albumIds.size === 0) {
 | 
					 | 
				
			||||||
        return new Set();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Promise.all(
 | 
					 | 
				
			||||||
        chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					 | 
				
			||||||
          this.albumRepository
 | 
					 | 
				
			||||||
            .createQueryBuilder('album')
 | 
					 | 
				
			||||||
            .select('album.id')
 | 
					 | 
				
			||||||
            .leftJoin('album.sharedUsers', 'sharedUsers')
 | 
					 | 
				
			||||||
            .where('album.id IN (:...albumIds)', { albumIds: idChunk })
 | 
					 | 
				
			||||||
            .andWhere('album.isActivityEnabled = true')
 | 
					 | 
				
			||||||
            .andWhere(
 | 
					 | 
				
			||||||
              new Brackets((qb) => {
 | 
					 | 
				
			||||||
                qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
 | 
					 | 
				
			||||||
              }),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .getMany()
 | 
					 | 
				
			||||||
            .then((albums) => new Set(albums.map((album) => album.id))),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  library = {
 | 
					 | 
				
			||||||
    checkOwnerAccess: async (userId: string, libraryIds: Set<string>): Promise<Set<string>> => {
 | 
					 | 
				
			||||||
      if (libraryIds.size === 0) {
 | 
					 | 
				
			||||||
        return new Set();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Promise.all(
 | 
					 | 
				
			||||||
        chunks(libraryIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					 | 
				
			||||||
          this.libraryRepository
 | 
					 | 
				
			||||||
            .find({
 | 
					 | 
				
			||||||
              select: { id: true },
 | 
					 | 
				
			||||||
              where: {
 | 
					 | 
				
			||||||
                id: In(idChunk),
 | 
					 | 
				
			||||||
                ownerId: userId,
 | 
					                ownerId: userId,
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            })
 | 
					            },
 | 
				
			||||||
            .then((libraries) => new Set(libraries.map((library) => library.id))),
 | 
					          })
 | 
				
			||||||
        ),
 | 
					          .then((activities) => new Set(activities.map((activity) => activity.id))),
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					      ),
 | 
				
			||||||
    },
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    checkPartnerAccess: async (userId: string, partnerIds: Set<string>): Promise<Set<string>> => {
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
      if (partnerIds.size === 0) {
 | 
					  async checkCreateAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
        return new Set();
 | 
					    if (albumIds.size === 0) {
 | 
				
			||||||
      }
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return Promise.all(
 | 
					    return Promise.all(
 | 
				
			||||||
        chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					      chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
          this.partnerRepository
 | 
					        this.albumRepository
 | 
				
			||||||
            .createQueryBuilder('partner')
 | 
					          .createQueryBuilder('album')
 | 
				
			||||||
            .select('partner.sharedById')
 | 
					          .select('album.id')
 | 
				
			||||||
            .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
					          .leftJoin('album.sharedUsers', 'sharedUsers')
 | 
				
			||||||
            .andWhere('partner.sharedWithId = :userId', { userId })
 | 
					          .where('album.id IN (:...albumIds)', { albumIds: idChunk })
 | 
				
			||||||
            .getMany()
 | 
					          .andWhere('album.isActivityEnabled = true')
 | 
				
			||||||
            .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
					          .andWhere(
 | 
				
			||||||
        ),
 | 
					            new Brackets((qb) => {
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					              qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  timeline = {
 | 
					 | 
				
			||||||
    checkPartnerAccess: async (userId: string, partnerIds: Set<string>): Promise<Set<string>> => {
 | 
					 | 
				
			||||||
      if (partnerIds.size === 0) {
 | 
					 | 
				
			||||||
        return new Set();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Promise.all(
 | 
					 | 
				
			||||||
        chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					 | 
				
			||||||
          this.partnerRepository
 | 
					 | 
				
			||||||
            .createQueryBuilder('partner')
 | 
					 | 
				
			||||||
            .select('partner.sharedById')
 | 
					 | 
				
			||||||
            .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
					 | 
				
			||||||
            .andWhere('partner.sharedWithId = :userId', { userId })
 | 
					 | 
				
			||||||
            .getMany()
 | 
					 | 
				
			||||||
            .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  asset = {
 | 
					 | 
				
			||||||
    checkAlbumAccess: async (userId: string, assetIds: Set<string>): Promise<Set<string>> => {
 | 
					 | 
				
			||||||
      if (assetIds.size === 0) {
 | 
					 | 
				
			||||||
        return new Set();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Promise.all(
 | 
					 | 
				
			||||||
        chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					 | 
				
			||||||
          this.albumRepository
 | 
					 | 
				
			||||||
            .createQueryBuilder('album')
 | 
					 | 
				
			||||||
            .innerJoin('album.assets', 'asset')
 | 
					 | 
				
			||||||
            .leftJoin('album.sharedUsers', 'sharedUsers')
 | 
					 | 
				
			||||||
            .select('asset.id', 'assetId')
 | 
					 | 
				
			||||||
            .addSelect('asset.livePhotoVideoId', 'livePhotoVideoId')
 | 
					 | 
				
			||||||
            .where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', {
 | 
					 | 
				
			||||||
              assetIds: idChunk,
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .andWhere(
 | 
					 | 
				
			||||||
              new Brackets((qb) => {
 | 
					 | 
				
			||||||
                qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
 | 
					 | 
				
			||||||
              }),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .getRawMany()
 | 
					 | 
				
			||||||
            .then((rows) => {
 | 
					 | 
				
			||||||
              const allowedIds = new Set<string>();
 | 
					 | 
				
			||||||
              for (const row of rows) {
 | 
					 | 
				
			||||||
                if (row.assetId && assetIds.has(row.assetId)) {
 | 
					 | 
				
			||||||
                  allowedIds.add(row.assetId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) {
 | 
					 | 
				
			||||||
                  allowedIds.add(row.livePhotoVideoId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
              return allowedIds;
 | 
					 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
        ),
 | 
					          )
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					          .getMany()
 | 
				
			||||||
    },
 | 
					          .then((albums) => new Set(albums.map((album) => album.id))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    checkOwnerAccess: async (userId: string, assetIds: Set<string>): Promise<Set<string>> => {
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
      if (assetIds.size === 0) {
 | 
					  }
 | 
				
			||||||
        return new Set();
 | 
					}
 | 
				
			||||||
      }
 | 
					
 | 
				
			||||||
 | 
					class AlbumAccess implements IAlbumAccess {
 | 
				
			||||||
      return Promise.all(
 | 
					  constructor(
 | 
				
			||||||
        chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					    private albumRepository: Repository<AlbumEntity>,
 | 
				
			||||||
          this.assetRepository
 | 
					    private sharedLinkRepository: Repository<SharedLinkEntity>,
 | 
				
			||||||
            .find({
 | 
					  ) {}
 | 
				
			||||||
              select: { id: true },
 | 
					
 | 
				
			||||||
              where: {
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
                id: In(idChunk),
 | 
					  async checkOwnerAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
                ownerId: userId,
 | 
					    if (albumIds.size === 0) {
 | 
				
			||||||
              },
 | 
					      return new Set();
 | 
				
			||||||
              withDeleted: true,
 | 
					    }
 | 
				
			||||||
            })
 | 
					
 | 
				
			||||||
            .then((assets) => new Set(assets.map((asset) => asset.id))),
 | 
					    return Promise.all(
 | 
				
			||||||
        ),
 | 
					      chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					        this.albumRepository
 | 
				
			||||||
    },
 | 
					          .find({
 | 
				
			||||||
 | 
					            select: { id: true },
 | 
				
			||||||
    checkPartnerAccess: async (userId: string, assetIds: Set<string>): Promise<Set<string>> => {
 | 
					            where: {
 | 
				
			||||||
      if (assetIds.size === 0) {
 | 
					              id: In(idChunk),
 | 
				
			||||||
        return new Set();
 | 
					              ownerId: userId,
 | 
				
			||||||
      }
 | 
					            },
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
      return Promise.all(
 | 
					          .then((albums) => new Set(albums.map((album) => album.id))),
 | 
				
			||||||
        chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					      ),
 | 
				
			||||||
          this.partnerRepository
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
            .createQueryBuilder('partner')
 | 
					  }
 | 
				
			||||||
            .innerJoin('partner.sharedBy', 'sharedBy')
 | 
					
 | 
				
			||||||
            .innerJoin('sharedBy.assets', 'asset')
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
            .select('asset.id', 'assetId')
 | 
					  async checkSharedAlbumAccess(userId: string, albumIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
            .where('partner.sharedWithId = :userId', { userId })
 | 
					    if (albumIds.size === 0) {
 | 
				
			||||||
            .andWhere('asset.id IN (:...assetIds)', { assetIds: idChunk })
 | 
					      return new Set();
 | 
				
			||||||
            .getRawMany()
 | 
					    }
 | 
				
			||||||
            .then((rows) => new Set(rows.map((row) => row.assetId))),
 | 
					
 | 
				
			||||||
        ),
 | 
					    return Promise.all(
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					      chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
    },
 | 
					        this.albumRepository
 | 
				
			||||||
 | 
					          .find({
 | 
				
			||||||
    checkSharedLinkAccess: async (sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>> => {
 | 
					            select: { id: true },
 | 
				
			||||||
      if (assetIds.size === 0) {
 | 
					            where: {
 | 
				
			||||||
        return new Set();
 | 
					              id: In(idChunk),
 | 
				
			||||||
      }
 | 
					              sharedUsers: {
 | 
				
			||||||
 | 
					                id: userId,
 | 
				
			||||||
      return Promise.all(
 | 
					              },
 | 
				
			||||||
        chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					            },
 | 
				
			||||||
          this.sharedLinkRepository
 | 
					          })
 | 
				
			||||||
            .createQueryBuilder('sharedLink')
 | 
					          .then((albums) => new Set(albums.map((album) => album.id))),
 | 
				
			||||||
            .leftJoin('sharedLink.album', 'album')
 | 
					      ),
 | 
				
			||||||
            .leftJoin('sharedLink.assets', 'assets')
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
            .leftJoin('album.assets', 'albumAssets')
 | 
					  }
 | 
				
			||||||
            .select('assets.id', 'assetId')
 | 
					
 | 
				
			||||||
            .addSelect('albumAssets.id', 'albumAssetId')
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
            .addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId')
 | 
					  async checkSharedLinkAccess(sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
            .addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId')
 | 
					    if (albumIds.size === 0) {
 | 
				
			||||||
            .where('sharedLink.id = :sharedLinkId', { sharedLinkId })
 | 
					      return new Set();
 | 
				
			||||||
            .andWhere(
 | 
					    }
 | 
				
			||||||
              'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]',
 | 
					
 | 
				
			||||||
              {
 | 
					    return Promise.all(
 | 
				
			||||||
                assetIds: idChunk,
 | 
					      chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
              },
 | 
					        this.sharedLinkRepository
 | 
				
			||||||
            )
 | 
					          .find({
 | 
				
			||||||
            .getRawMany()
 | 
					            select: { albumId: true },
 | 
				
			||||||
            .then((rows) => {
 | 
					            where: {
 | 
				
			||||||
              const allowedIds = new Set<string>();
 | 
					              id: sharedLinkId,
 | 
				
			||||||
              for (const row of rows) {
 | 
					              albumId: In(idChunk),
 | 
				
			||||||
                if (row.assetId && assetIds.has(row.assetId)) {
 | 
					            },
 | 
				
			||||||
                  allowedIds.add(row.assetId);
 | 
					          })
 | 
				
			||||||
                }
 | 
					          .then(
 | 
				
			||||||
                if (row.assetLivePhotoVideoId && assetIds.has(row.assetLivePhotoVideoId)) {
 | 
					            (sharedLinks) =>
 | 
				
			||||||
                  allowedIds.add(row.assetLivePhotoVideoId);
 | 
					              new Set(sharedLinks.flatMap((sharedLink) => (!!sharedLink.albumId ? [sharedLink.albumId] : []))),
 | 
				
			||||||
                }
 | 
					          ),
 | 
				
			||||||
                if (row.albumAssetId && assetIds.has(row.albumAssetId)) {
 | 
					      ),
 | 
				
			||||||
                  allowedIds.add(row.albumAssetId);
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
                }
 | 
					  }
 | 
				
			||||||
                if (row.albumAssetLivePhotoVideoId && assetIds.has(row.albumAssetLivePhotoVideoId)) {
 | 
					}
 | 
				
			||||||
                  allowedIds.add(row.albumAssetLivePhotoVideoId);
 | 
					
 | 
				
			||||||
                }
 | 
					class AssetAccess implements IAssetAccess {
 | 
				
			||||||
              }
 | 
					  constructor(
 | 
				
			||||||
              return allowedIds;
 | 
					    private albumRepository: Repository<AlbumEntity>,
 | 
				
			||||||
            }),
 | 
					    private assetRepository: Repository<AssetEntity>,
 | 
				
			||||||
        ),
 | 
					    private partnerRepository: Repository<PartnerEntity>,
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					    private sharedLinkRepository: Repository<SharedLinkEntity>,
 | 
				
			||||||
    },
 | 
					  ) {}
 | 
				
			||||||
  };
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
  authDevice = {
 | 
					  async checkAlbumAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
    checkOwnerAccess: async (userId: string, deviceIds: Set<string>): Promise<Set<string>> => {
 | 
					    if (assetIds.size === 0) {
 | 
				
			||||||
      if (deviceIds.size === 0) {
 | 
					      return new Set();
 | 
				
			||||||
        return new Set();
 | 
					    }
 | 
				
			||||||
      }
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
      return Promise.all(
 | 
					      chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
        chunks(deviceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					        this.albumRepository
 | 
				
			||||||
          this.tokenRepository
 | 
					          .createQueryBuilder('album')
 | 
				
			||||||
            .find({
 | 
					          .innerJoin('album.assets', 'asset')
 | 
				
			||||||
              select: { id: true },
 | 
					          .leftJoin('album.sharedUsers', 'sharedUsers')
 | 
				
			||||||
              where: {
 | 
					          .select('asset.id', 'assetId')
 | 
				
			||||||
                userId,
 | 
					          .addSelect('asset.livePhotoVideoId', 'livePhotoVideoId')
 | 
				
			||||||
                id: In(idChunk),
 | 
					          .where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', {
 | 
				
			||||||
              },
 | 
					            assetIds: idChunk,
 | 
				
			||||||
            })
 | 
					          })
 | 
				
			||||||
            .then((tokens) => new Set(tokens.map((token) => token.id))),
 | 
					          .andWhere(
 | 
				
			||||||
        ),
 | 
					            new Brackets((qb) => {
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					              qb.where('album.ownerId = :userId', { userId }).orWhere('sharedUsers.id = :userId', { userId });
 | 
				
			||||||
    },
 | 
					            }),
 | 
				
			||||||
  };
 | 
					          )
 | 
				
			||||||
 | 
					          .getRawMany()
 | 
				
			||||||
  album = {
 | 
					          .then((rows) => {
 | 
				
			||||||
    checkOwnerAccess: async (userId: string, albumIds: Set<string>): Promise<Set<string>> => {
 | 
					            const allowedIds = new Set<string>();
 | 
				
			||||||
      if (albumIds.size === 0) {
 | 
					            for (const row of rows) {
 | 
				
			||||||
        return new Set();
 | 
					              if (row.assetId && assetIds.has(row.assetId)) {
 | 
				
			||||||
      }
 | 
					                allowedIds.add(row.assetId);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
      return Promise.all(
 | 
					              if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) {
 | 
				
			||||||
        chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					                allowedIds.add(row.livePhotoVideoId);
 | 
				
			||||||
          this.albumRepository
 | 
					              }
 | 
				
			||||||
            .find({
 | 
					            }
 | 
				
			||||||
              select: { id: true },
 | 
					            return allowedIds;
 | 
				
			||||||
              where: {
 | 
					          }),
 | 
				
			||||||
                id: In(idChunk),
 | 
					      ),
 | 
				
			||||||
                ownerId: userId,
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
              },
 | 
					  }
 | 
				
			||||||
            })
 | 
					
 | 
				
			||||||
            .then((albums) => new Set(albums.map((album) => album.id))),
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
        ),
 | 
					  async checkOwnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					    if (assetIds.size === 0) {
 | 
				
			||||||
    },
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    checkSharedAlbumAccess: async (userId: string, albumIds: Set<string>): Promise<Set<string>> => {
 | 
					
 | 
				
			||||||
      if (albumIds.size === 0) {
 | 
					    return Promise.all(
 | 
				
			||||||
        return new Set();
 | 
					      chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
      }
 | 
					        this.assetRepository
 | 
				
			||||||
 | 
					          .find({
 | 
				
			||||||
      return Promise.all(
 | 
					            select: { id: true },
 | 
				
			||||||
        chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					            where: {
 | 
				
			||||||
          this.albumRepository
 | 
					              id: In(idChunk),
 | 
				
			||||||
            .find({
 | 
					              ownerId: userId,
 | 
				
			||||||
              select: { id: true },
 | 
					            },
 | 
				
			||||||
              where: {
 | 
					            withDeleted: true,
 | 
				
			||||||
                id: In(idChunk),
 | 
					          })
 | 
				
			||||||
                sharedUsers: {
 | 
					          .then((assets) => new Set(assets.map((asset) => asset.id))),
 | 
				
			||||||
                  id: userId,
 | 
					      ),
 | 
				
			||||||
                },
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
              },
 | 
					  }
 | 
				
			||||||
            })
 | 
					
 | 
				
			||||||
            .then((albums) => new Set(albums.map((album) => album.id))),
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
        ),
 | 
					  async checkPartnerAccess(userId: string, assetIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					    if (assetIds.size === 0) {
 | 
				
			||||||
    },
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    checkSharedLinkAccess: async (sharedLinkId: string, albumIds: Set<string>): Promise<Set<string>> => {
 | 
					
 | 
				
			||||||
      if (albumIds.size === 0) {
 | 
					    return Promise.all(
 | 
				
			||||||
        return new Set();
 | 
					      chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
      }
 | 
					        this.partnerRepository
 | 
				
			||||||
 | 
					          .createQueryBuilder('partner')
 | 
				
			||||||
      return Promise.all(
 | 
					          .innerJoin('partner.sharedBy', 'sharedBy')
 | 
				
			||||||
        chunks(albumIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					          .innerJoin('sharedBy.assets', 'asset')
 | 
				
			||||||
          this.sharedLinkRepository
 | 
					          .select('asset.id', 'assetId')
 | 
				
			||||||
            .find({
 | 
					          .where('partner.sharedWithId = :userId', { userId })
 | 
				
			||||||
              select: { albumId: true },
 | 
					          .andWhere('asset.id IN (:...assetIds)', { assetIds: idChunk })
 | 
				
			||||||
              where: {
 | 
					          .getRawMany()
 | 
				
			||||||
                id: sharedLinkId,
 | 
					          .then((rows) => new Set(rows.map((row) => row.assetId))),
 | 
				
			||||||
                albumId: In(idChunk),
 | 
					      ),
 | 
				
			||||||
              },
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
            })
 | 
					  }
 | 
				
			||||||
            .then(
 | 
					
 | 
				
			||||||
              (sharedLinks) =>
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
                new Set(sharedLinks.flatMap((sharedLink) => (!!sharedLink.albumId ? [sharedLink.albumId] : []))),
 | 
					  async checkSharedLinkAccess(sharedLinkId: string, assetIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
            ),
 | 
					    if (assetIds.size === 0) {
 | 
				
			||||||
        ),
 | 
					      return new Set();
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					    }
 | 
				
			||||||
    },
 | 
					
 | 
				
			||||||
  };
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(assetIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
  person = {
 | 
					        this.sharedLinkRepository
 | 
				
			||||||
    checkOwnerAccess: async (userId: string, personIds: Set<string>): Promise<Set<string>> => {
 | 
					          .createQueryBuilder('sharedLink')
 | 
				
			||||||
      if (personIds.size === 0) {
 | 
					          .leftJoin('sharedLink.album', 'album')
 | 
				
			||||||
        return new Set();
 | 
					          .leftJoin('sharedLink.assets', 'assets')
 | 
				
			||||||
      }
 | 
					          .leftJoin('album.assets', 'albumAssets')
 | 
				
			||||||
 | 
					          .select('assets.id', 'assetId')
 | 
				
			||||||
      return Promise.all(
 | 
					          .addSelect('albumAssets.id', 'albumAssetId')
 | 
				
			||||||
        chunks(personIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					          .addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId')
 | 
				
			||||||
          this.personRepository
 | 
					          .addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId')
 | 
				
			||||||
            .find({
 | 
					          .where('sharedLink.id = :sharedLinkId', { sharedLinkId })
 | 
				
			||||||
              select: { id: true },
 | 
					          .andWhere(
 | 
				
			||||||
              where: {
 | 
					            'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]',
 | 
				
			||||||
                id: In(idChunk),
 | 
					            {
 | 
				
			||||||
                ownerId: userId,
 | 
					              assetIds: idChunk,
 | 
				
			||||||
              },
 | 
					            },
 | 
				
			||||||
            })
 | 
					          )
 | 
				
			||||||
            .then((persons) => new Set(persons.map((person) => person.id))),
 | 
					          .getRawMany()
 | 
				
			||||||
        ),
 | 
					          .then((rows) => {
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					            const allowedIds = new Set<string>();
 | 
				
			||||||
    },
 | 
					            for (const row of rows) {
 | 
				
			||||||
 | 
					              if (row.assetId && assetIds.has(row.assetId)) {
 | 
				
			||||||
    checkFaceOwnerAccess: async (userId: string, assetFaceIds: Set<string>): Promise<Set<string>> => {
 | 
					                allowedIds.add(row.assetId);
 | 
				
			||||||
      if (assetFaceIds.size === 0) {
 | 
					              }
 | 
				
			||||||
        return new Set();
 | 
					              if (row.assetLivePhotoVideoId && assetIds.has(row.assetLivePhotoVideoId)) {
 | 
				
			||||||
      }
 | 
					                allowedIds.add(row.assetLivePhotoVideoId);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
      return Promise.all(
 | 
					              if (row.albumAssetId && assetIds.has(row.albumAssetId)) {
 | 
				
			||||||
        chunks(assetFaceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					                allowedIds.add(row.albumAssetId);
 | 
				
			||||||
          this.assetFaceRepository
 | 
					              }
 | 
				
			||||||
            .find({
 | 
					              if (row.albumAssetLivePhotoVideoId && assetIds.has(row.albumAssetLivePhotoVideoId)) {
 | 
				
			||||||
              select: { id: true },
 | 
					                allowedIds.add(row.albumAssetLivePhotoVideoId);
 | 
				
			||||||
              where: {
 | 
					              }
 | 
				
			||||||
                id: In(idChunk),
 | 
					            }
 | 
				
			||||||
                asset: {
 | 
					            return allowedIds;
 | 
				
			||||||
                  ownerId: userId,
 | 
					          }),
 | 
				
			||||||
                },
 | 
					      ),
 | 
				
			||||||
              },
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
            })
 | 
					  }
 | 
				
			||||||
            .then((faces) => new Set(faces.map((face) => face.id))),
 | 
					}
 | 
				
			||||||
        ),
 | 
					
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					class AuthDeviceAccess implements IAuthDeviceAccess {
 | 
				
			||||||
    },
 | 
					  constructor(private tokenRepository: Repository<UserTokenEntity>) {}
 | 
				
			||||||
  };
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
  partner = {
 | 
					  async checkOwnerAccess(userId: string, deviceIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
    checkUpdateAccess: async (userId: string, partnerIds: Set<string>): Promise<Set<string>> => {
 | 
					    if (deviceIds.size === 0) {
 | 
				
			||||||
      if (partnerIds.size === 0) {
 | 
					      return new Set();
 | 
				
			||||||
        return new Set();
 | 
					    }
 | 
				
			||||||
      }
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
      return Promise.all(
 | 
					      chunks(deviceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
        chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
					        this.tokenRepository
 | 
				
			||||||
          this.partnerRepository
 | 
					          .find({
 | 
				
			||||||
            .createQueryBuilder('partner')
 | 
					            select: { id: true },
 | 
				
			||||||
            .select('partner.sharedById')
 | 
					            where: {
 | 
				
			||||||
            .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
					              userId,
 | 
				
			||||||
            .andWhere('partner.sharedWithId = :userId', { userId })
 | 
					              id: In(idChunk),
 | 
				
			||||||
            .getMany()
 | 
					            },
 | 
				
			||||||
            .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
					          })
 | 
				
			||||||
        ),
 | 
					          .then((tokens) => new Set(tokens.map((token) => token.id))),
 | 
				
			||||||
      ).then((results) => setUnion(...results));
 | 
					      ),
 | 
				
			||||||
    },
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
  };
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LibraryAccess implements ILibraryAccess {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private libraryRepository: Repository<LibraryEntity>,
 | 
				
			||||||
 | 
					    private partnerRepository: Repository<PartnerEntity>,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkOwnerAccess(userId: string, libraryIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (libraryIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(libraryIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.libraryRepository
 | 
				
			||||||
 | 
					          .find({
 | 
				
			||||||
 | 
					            select: { id: true },
 | 
				
			||||||
 | 
					            where: {
 | 
				
			||||||
 | 
					              id: In(idChunk),
 | 
				
			||||||
 | 
					              ownerId: userId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          .then((libraries) => new Set(libraries.map((library) => library.id))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (partnerIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.partnerRepository
 | 
				
			||||||
 | 
					          .createQueryBuilder('partner')
 | 
				
			||||||
 | 
					          .select('partner.sharedById')
 | 
				
			||||||
 | 
					          .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
				
			||||||
 | 
					          .andWhere('partner.sharedWithId = :userId', { userId })
 | 
				
			||||||
 | 
					          .getMany()
 | 
				
			||||||
 | 
					          .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TimelineAccess implements ITimelineAccess {
 | 
				
			||||||
 | 
					  constructor(private partnerRepository: Repository<PartnerEntity>) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkPartnerAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (partnerIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.partnerRepository
 | 
				
			||||||
 | 
					          .createQueryBuilder('partner')
 | 
				
			||||||
 | 
					          .select('partner.sharedById')
 | 
				
			||||||
 | 
					          .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
				
			||||||
 | 
					          .andWhere('partner.sharedWithId = :userId', { userId })
 | 
				
			||||||
 | 
					          .getMany()
 | 
				
			||||||
 | 
					          .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PersonAccess implements IPersonAccess {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private assetFaceRepository: Repository<AssetFaceEntity>,
 | 
				
			||||||
 | 
					    private personRepository: Repository<PersonEntity>,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkOwnerAccess(userId: string, personIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (personIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(personIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.personRepository
 | 
				
			||||||
 | 
					          .find({
 | 
				
			||||||
 | 
					            select: { id: true },
 | 
				
			||||||
 | 
					            where: {
 | 
				
			||||||
 | 
					              id: In(idChunk),
 | 
				
			||||||
 | 
					              ownerId: userId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          .then((persons) => new Set(persons.map((person) => person.id))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkFaceOwnerAccess(userId: string, assetFaceIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (assetFaceIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(assetFaceIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.assetFaceRepository
 | 
				
			||||||
 | 
					          .find({
 | 
				
			||||||
 | 
					            select: { id: true },
 | 
				
			||||||
 | 
					            where: {
 | 
				
			||||||
 | 
					              id: In(idChunk),
 | 
				
			||||||
 | 
					              asset: {
 | 
				
			||||||
 | 
					                ownerId: userId,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          .then((faces) => new Set(faces.map((face) => face.id))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PartnerAccess implements IPartnerAccess {
 | 
				
			||||||
 | 
					  constructor(private partnerRepository: Repository<PartnerEntity>) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
 | 
				
			||||||
 | 
					  async checkUpdateAccess(userId: string, partnerIds: Set<string>): Promise<Set<string>> {
 | 
				
			||||||
 | 
					    if (partnerIds.size === 0) {
 | 
				
			||||||
 | 
					      return new Set();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.all(
 | 
				
			||||||
 | 
					      chunks(partnerIds, DATABASE_PARAMETER_CHUNK_SIZE).map((idChunk) =>
 | 
				
			||||||
 | 
					        this.partnerRepository
 | 
				
			||||||
 | 
					          .createQueryBuilder('partner')
 | 
				
			||||||
 | 
					          .select('partner.sharedById')
 | 
				
			||||||
 | 
					          .where('partner.sharedById IN (:...partnerIds)', { partnerIds: idChunk })
 | 
				
			||||||
 | 
					          .andWhere('partner.sharedWithId = :userId', { userId })
 | 
				
			||||||
 | 
					          .getMany()
 | 
				
			||||||
 | 
					          .then((partners) => new Set(partners.map((partner) => partner.sharedById))),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((results) => setUnion(...results));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AccessRepository implements IAccessRepository {
 | 
				
			||||||
 | 
					  activity: IActivityAccess;
 | 
				
			||||||
 | 
					  album: IAlbumAccess;
 | 
				
			||||||
 | 
					  asset: IAssetAccess;
 | 
				
			||||||
 | 
					  authDevice: IAuthDeviceAccess;
 | 
				
			||||||
 | 
					  library: ILibraryAccess;
 | 
				
			||||||
 | 
					  person: IPersonAccess;
 | 
				
			||||||
 | 
					  partner: IPartnerAccess;
 | 
				
			||||||
 | 
					  timeline: ITimelineAccess;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    @InjectRepository(ActivityEntity) activityRepository: Repository<ActivityEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(AssetEntity) assetRepository: Repository<AssetEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(AlbumEntity) albumRepository: Repository<AlbumEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(LibraryEntity) libraryRepository: Repository<LibraryEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(PartnerEntity) partnerRepository: Repository<PartnerEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(PersonEntity) personRepository: Repository<PersonEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(AssetFaceEntity) assetFaceRepository: Repository<AssetFaceEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(SharedLinkEntity) sharedLinkRepository: Repository<SharedLinkEntity>,
 | 
				
			||||||
 | 
					    @InjectRepository(UserTokenEntity) tokenRepository: Repository<UserTokenEntity>,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.activity = new ActivityAccess(activityRepository, albumRepository);
 | 
				
			||||||
 | 
					    this.album = new AlbumAccess(albumRepository, sharedLinkRepository);
 | 
				
			||||||
 | 
					    this.asset = new AssetAccess(albumRepository, assetRepository, partnerRepository, sharedLinkRepository);
 | 
				
			||||||
 | 
					    this.authDevice = new AuthDeviceAccess(tokenRepository);
 | 
				
			||||||
 | 
					    this.library = new LibraryAccess(libraryRepository, partnerRepository);
 | 
				
			||||||
 | 
					    this.person = new PersonAccess(assetFaceRepository, personRepository);
 | 
				
			||||||
 | 
					    this.partner = new PartnerAccess(partnerRepository);
 | 
				
			||||||
 | 
					    this.timeline = new TimelineAccess(partnerRepository);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -98,8 +98,25 @@ class SqlGenerator {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const data: string[] = [`-- NOTE: This file is auto generated by ./sql-generator`];
 | 
					    const data: string[] = [`-- NOTE: This file is auto generated by ./sql-generator`];
 | 
				
			||||||
    const instance = this.app.get<Repository>(Repository);
 | 
					    const instance = this.app.get<Repository>(Repository);
 | 
				
			||||||
    const properties = Object.getOwnPropertyNames(Repository.prototype) as Array<keyof typeof Repository>;
 | 
					
 | 
				
			||||||
    for (const key of properties) {
 | 
					    // normal repositories
 | 
				
			||||||
 | 
					    data.push(...(await this.runTargets(instance, `${Repository.name}`)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // nested repositories
 | 
				
			||||||
 | 
					    if (Repository.name === AccessRepository.name) {
 | 
				
			||||||
 | 
					      for (const key of Object.keys(instance)) {
 | 
				
			||||||
 | 
					        const subInstance = (instance as any)[key];
 | 
				
			||||||
 | 
					        data.push(...(await this.runTargets(subInstance, `${Repository.name}.${key}`)));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.results[Repository.name] = data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async runTargets(instance: any, label: string) {
 | 
				
			||||||
 | 
					    const data: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const key of this.getPropertyNames(instance)) {
 | 
				
			||||||
      const target = instance[key];
 | 
					      const target = instance[key];
 | 
				
			||||||
      if (!(target instanceof Function)) {
 | 
					      if (!(target instanceof Function)) {
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
@ -116,7 +133,7 @@ class SqlGenerator {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      for (const { name, params } of queries) {
 | 
					      for (const { name, params } of queries) {
 | 
				
			||||||
        let queryLabel = `${Repository.name}.${key}`;
 | 
					        let queryLabel = `${label}.${key}`;
 | 
				
			||||||
        if (name) {
 | 
					        if (name) {
 | 
				
			||||||
          queryLabel += ` (${name})`;
 | 
					          queryLabel += ` (${name})`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -135,7 +152,7 @@ class SqlGenerator {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.results[Repository.name] = data;
 | 
					    return data;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async write() {
 | 
					  private async write() {
 | 
				
			||||||
@ -156,6 +173,10 @@ class SqlGenerator {
 | 
				
			|||||||
      await this.app.close();
 | 
					      await this.app.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private getPropertyNames(instance: any): string[] {
 | 
				
			||||||
 | 
					    return Object.getOwnPropertyNames(Object.getPrototypeOf(instance)) as any[];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new SqlGenerator({ targetDir: './src/infra/sql' })
 | 
					new SqlGenerator({ targetDir: './src/infra/sql' })
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +1,243 @@
 | 
				
			|||||||
-- NOTE: This file is auto generated by ./sql-generator
 | 
					-- NOTE: This file is auto generated by ./sql-generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.activity.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "ActivityEntity"."id" AS "ActivityEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "activity" "ActivityEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "ActivityEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "ActivityEntity"."userId" = $2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.activity.checkAlbumOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "ActivityEntity"."id" AS "ActivityEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "activity" "ActivityEntity"
 | 
				
			||||||
 | 
					  LEFT JOIN "albums" "ActivityEntity__ActivityEntity_album" ON "ActivityEntity__ActivityEntity_album"."id" = "ActivityEntity"."albumId"
 | 
				
			||||||
 | 
					  AND (
 | 
				
			||||||
 | 
					    "ActivityEntity__ActivityEntity_album"."deletedAt" IS NULL
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "ActivityEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "ActivityEntity__ActivityEntity_album"."ownerId" = $2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.activity.checkCreateAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "album"."id" AS "album_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "albums" "album"
 | 
				
			||||||
 | 
					  LEFT JOIN "albums_shared_users_users" "album_sharedUsers" ON "album_sharedUsers"."albumsId" = "album"."id"
 | 
				
			||||||
 | 
					  LEFT JOIN "users" "sharedUsers" ON "sharedUsers"."id" = "album_sharedUsers"."usersId"
 | 
				
			||||||
 | 
					  AND ("sharedUsers"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "album"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "album"."isActivityEnabled" = true
 | 
				
			||||||
 | 
					    AND (
 | 
				
			||||||
 | 
					      "album"."ownerId" = $2
 | 
				
			||||||
 | 
					      OR "sharedUsers"."id" = $2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  AND ("album"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.album.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "AlbumEntity"."id" AS "AlbumEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "albums" "AlbumEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      "AlbumEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					      AND "AlbumEntity"."ownerId" = $2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  AND ("AlbumEntity"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.album.checkSharedAlbumAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "AlbumEntity"."id" AS "AlbumEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "albums" "AlbumEntity"
 | 
				
			||||||
 | 
					  LEFT JOIN "albums_shared_users_users" "AlbumEntity_AlbumEntity__AlbumEntity_sharedUsers" ON "AlbumEntity_AlbumEntity__AlbumEntity_sharedUsers"."albumsId" = "AlbumEntity"."id"
 | 
				
			||||||
 | 
					  LEFT JOIN "users" "AlbumEntity__AlbumEntity_sharedUsers" ON "AlbumEntity__AlbumEntity_sharedUsers"."id" = "AlbumEntity_AlbumEntity__AlbumEntity_sharedUsers"."usersId"
 | 
				
			||||||
 | 
					  AND (
 | 
				
			||||||
 | 
					    "AlbumEntity__AlbumEntity_sharedUsers"."deletedAt" IS NULL
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      "AlbumEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					      AND "AlbumEntity__AlbumEntity_sharedUsers"."id" = $2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  AND ("AlbumEntity"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.album.checkSharedLinkAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "SharedLinkEntity"."albumId" AS "SharedLinkEntity_albumId",
 | 
				
			||||||
 | 
					  "SharedLinkEntity"."id" AS "SharedLinkEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "shared_links" "SharedLinkEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "SharedLinkEntity"."id" = $1
 | 
				
			||||||
 | 
					    AND "SharedLinkEntity"."albumId" IN ($2)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.asset.checkAlbumAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "asset"."id" AS "assetId",
 | 
				
			||||||
 | 
					  "asset"."livePhotoVideoId" AS "livePhotoVideoId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "albums" "album"
 | 
				
			||||||
 | 
					  INNER JOIN "albums_assets_assets" "album_asset" ON "album_asset"."albumsId" = "album"."id"
 | 
				
			||||||
 | 
					  INNER JOIN "assets" "asset" ON "asset"."id" = "album_asset"."assetsId"
 | 
				
			||||||
 | 
					  AND ("asset"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					  LEFT JOIN "albums_shared_users_users" "album_sharedUsers" ON "album_sharedUsers"."albumsId" = "album"."id"
 | 
				
			||||||
 | 
					  LEFT JOIN "users" "sharedUsers" ON "sharedUsers"."id" = "album_sharedUsers"."usersId"
 | 
				
			||||||
 | 
					  AND ("sharedUsers"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid []
 | 
				
			||||||
 | 
					    AND (
 | 
				
			||||||
 | 
					      "album"."ownerId" = $2
 | 
				
			||||||
 | 
					      OR "sharedUsers"."id" = $2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  AND ("album"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.asset.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "AssetEntity"."id" AS "AssetEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "assets" "AssetEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "AssetEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "AssetEntity"."ownerId" = $2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.asset.checkPartnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "asset"."id" AS "assetId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "partners" "partner"
 | 
				
			||||||
 | 
					  INNER JOIN "users" "sharedBy" ON "sharedBy"."id" = "partner"."sharedById"
 | 
				
			||||||
 | 
					  AND ("sharedBy"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					  INNER JOIN "assets" "asset" ON "asset"."ownerId" = "sharedBy"."id"
 | 
				
			||||||
 | 
					  AND ("asset"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  "partner"."sharedWithId" = $1
 | 
				
			||||||
 | 
					  AND "asset"."id" IN ($2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.asset.checkSharedLinkAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "assets"."id" AS "assetId",
 | 
				
			||||||
 | 
					  "assets"."livePhotoVideoId" AS "assetLivePhotoVideoId",
 | 
				
			||||||
 | 
					  "albumAssets"."id" AS "albumAssetId",
 | 
				
			||||||
 | 
					  "albumAssets"."livePhotoVideoId" AS "albumAssetLivePhotoVideoId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "shared_links" "sharedLink"
 | 
				
			||||||
 | 
					  LEFT JOIN "albums" "album" ON "album"."id" = "sharedLink"."albumId"
 | 
				
			||||||
 | 
					  AND ("album"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					  LEFT JOIN "shared_link__asset" "assets_sharedLink" ON "assets_sharedLink"."sharedLinksId" = "sharedLink"."id"
 | 
				
			||||||
 | 
					  LEFT JOIN "assets" "assets" ON "assets"."id" = "assets_sharedLink"."assetsId"
 | 
				
			||||||
 | 
					  AND ("assets"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					  LEFT JOIN "albums_assets_assets" "album_albumAssets" ON "album_albumAssets"."albumsId" = "album"."id"
 | 
				
			||||||
 | 
					  LEFT JOIN "assets" "albumAssets" ON "albumAssets"."id" = "album_albumAssets"."assetsId"
 | 
				
			||||||
 | 
					  AND ("albumAssets"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  "sharedLink"."id" = $1
 | 
				
			||||||
 | 
					  AND array[
 | 
				
			||||||
 | 
					    "assets"."id",
 | 
				
			||||||
 | 
					    "assets"."livePhotoVideoId",
 | 
				
			||||||
 | 
					    "albumAssets"."id",
 | 
				
			||||||
 | 
					    "albumAssets"."livePhotoVideoId"
 | 
				
			||||||
 | 
					  ] && array[$2]::uuid []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.authDevice.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "UserTokenEntity"."id" AS "UserTokenEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "user_token" "UserTokenEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "UserTokenEntity"."userId" = $1
 | 
				
			||||||
 | 
					    AND "UserTokenEntity"."id" IN ($2)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.library.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "LibraryEntity"."id" AS "LibraryEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "libraries" "LibraryEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      "LibraryEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					      AND "LibraryEntity"."ownerId" = $2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  AND ("LibraryEntity"."deletedAt" IS NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.library.checkPartnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "partner"."sharedById" AS "partner_sharedById",
 | 
				
			||||||
 | 
					  "partner"."sharedWithId" AS "partner_sharedWithId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "partners" "partner"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  "partner"."sharedById" IN ($1)
 | 
				
			||||||
 | 
					  AND "partner"."sharedWithId" = $2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.person.checkOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "PersonEntity"."id" AS "PersonEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "person" "PersonEntity"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "PersonEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "PersonEntity"."ownerId" = $2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.person.checkFaceOwnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "AssetFaceEntity"."id" AS "AssetFaceEntity_id"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "asset_faces" "AssetFaceEntity"
 | 
				
			||||||
 | 
					  LEFT JOIN "assets" "AssetFaceEntity__AssetFaceEntity_asset" ON "AssetFaceEntity__AssetFaceEntity_asset"."id" = "AssetFaceEntity"."assetId"
 | 
				
			||||||
 | 
					  AND (
 | 
				
			||||||
 | 
					    "AssetFaceEntity__AssetFaceEntity_asset"."deletedAt" IS NULL
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    "AssetFaceEntity"."id" IN ($1)
 | 
				
			||||||
 | 
					    AND "AssetFaceEntity__AssetFaceEntity_asset"."ownerId" = $2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.partner.checkUpdateAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "partner"."sharedById" AS "partner_sharedById",
 | 
				
			||||||
 | 
					  "partner"."sharedWithId" AS "partner_sharedWithId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "partners" "partner"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  "partner"."sharedById" IN ($1)
 | 
				
			||||||
 | 
					  AND "partner"."sharedWithId" = $2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AccessRepository.timeline.checkPartnerAccess
 | 
				
			||||||
 | 
					SELECT
 | 
				
			||||||
 | 
					  "partner"."sharedById" AS "partner_sharedById",
 | 
				
			||||||
 | 
					  "partner"."sharedWithId" AS "partner_sharedWithId"
 | 
				
			||||||
 | 
					FROM
 | 
				
			||||||
 | 
					  "partners" "partner"
 | 
				
			||||||
 | 
					WHERE
 | 
				
			||||||
 | 
					  "partner"."sharedById" IN ($1)
 | 
				
			||||||
 | 
					  AND "partner"."sharedWithId" = $2
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user