mirror of
https://github.com/immich-app/immich.git
synced 2024-12-26 10:50:29 +02:00
refactor(server): partner core (#2678)
* refactor(server): partner core * refactor(server): partner access check
This commit is contained in:
parent
d1db479727
commit
6ce35d47f5
@ -9,9 +9,9 @@ import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-use
|
|||||||
import { DownloadService } from '../../modules/download/download.service';
|
import { DownloadService } from '../../modules/download/download.service';
|
||||||
import { AlbumRepository, IAlbumRepository } from '../album/album-repository';
|
import { AlbumRepository, IAlbumRepository } from '../album/album-repository';
|
||||||
import {
|
import {
|
||||||
|
IAccessRepository,
|
||||||
ICryptoRepository,
|
ICryptoRepository,
|
||||||
IJobRepository,
|
IJobRepository,
|
||||||
IPartnerRepository,
|
|
||||||
ISharedLinkRepository,
|
ISharedLinkRepository,
|
||||||
IStorageRepository,
|
IStorageRepository,
|
||||||
JobName,
|
JobName,
|
||||||
@ -20,6 +20,7 @@ import {
|
|||||||
assetEntityStub,
|
assetEntityStub,
|
||||||
authStub,
|
authStub,
|
||||||
fileStub,
|
fileStub,
|
||||||
|
newAccessRepositoryMock,
|
||||||
newCryptoRepositoryMock,
|
newCryptoRepositoryMock,
|
||||||
newJobRepositoryMock,
|
newJobRepositoryMock,
|
||||||
newSharedLinkRepositoryMock,
|
newSharedLinkRepositoryMock,
|
||||||
@ -131,10 +132,10 @@ const _getArchivedAssetsCountByUserId = (): AssetCountByUserIdResponseDto => {
|
|||||||
describe('AssetService', () => {
|
describe('AssetService', () => {
|
||||||
let sut: AssetService;
|
let sut: AssetService;
|
||||||
let a: Repository<AssetEntity>; // TO BE DELETED AFTER FINISHED REFACTORING
|
let a: Repository<AssetEntity>; // TO BE DELETED AFTER FINISHED REFACTORING
|
||||||
|
let accessMock: jest.Mocked<IAccessRepository>;
|
||||||
let assetRepositoryMock: jest.Mocked<IAssetRepository>;
|
let assetRepositoryMock: jest.Mocked<IAssetRepository>;
|
||||||
let albumRepositoryMock: jest.Mocked<IAlbumRepository>;
|
let albumRepositoryMock: jest.Mocked<IAlbumRepository>;
|
||||||
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
|
let downloadServiceMock: jest.Mocked<Partial<DownloadService>>;
|
||||||
let partnerRepositoryMock: jest.Mocked<IPartnerRepository>;
|
|
||||||
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
|
let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>;
|
||||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||||
let jobMock: jest.Mocked<IJobRepository>;
|
let jobMock: jest.Mocked<IJobRepository>;
|
||||||
@ -173,12 +174,14 @@ describe('AssetService', () => {
|
|||||||
downloadArchive: jest.fn(),
|
downloadArchive: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
accessMock = newAccessRepositoryMock();
|
||||||
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
|
sharedLinkRepositoryMock = newSharedLinkRepositoryMock();
|
||||||
jobMock = newJobRepositoryMock();
|
jobMock = newJobRepositoryMock();
|
||||||
cryptoMock = newCryptoRepositoryMock();
|
cryptoMock = newCryptoRepositoryMock();
|
||||||
storageMock = newStorageRepositoryMock();
|
storageMock = newStorageRepositoryMock();
|
||||||
|
|
||||||
sut = new AssetService(
|
sut = new AssetService(
|
||||||
|
accessMock,
|
||||||
assetRepositoryMock,
|
assetRepositoryMock,
|
||||||
albumRepositoryMock,
|
albumRepositoryMock,
|
||||||
a,
|
a,
|
||||||
@ -187,7 +190,6 @@ describe('AssetService', () => {
|
|||||||
jobMock,
|
jobMock,
|
||||||
cryptoMock,
|
cryptoMock,
|
||||||
storageMock,
|
storageMock,
|
||||||
partnerRepositoryMock,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
when(assetRepositoryMock.get)
|
when(assetRepositoryMock.get)
|
||||||
|
@ -25,12 +25,12 @@ import { CuratedObjectsResponseDto } from './response-dto/curated-objects-respon
|
|||||||
import {
|
import {
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
getLivePhotoMotionFilename,
|
getLivePhotoMotionFilename,
|
||||||
|
IAccessRepository,
|
||||||
ImmichReadStream,
|
ImmichReadStream,
|
||||||
IStorageRepository,
|
IStorageRepository,
|
||||||
JobName,
|
JobName,
|
||||||
mapAsset,
|
mapAsset,
|
||||||
mapAssetWithoutExif,
|
mapAssetWithoutExif,
|
||||||
PartnerCore,
|
|
||||||
} from '@app/domain';
|
} from '@app/domain';
|
||||||
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||||
import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto';
|
import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto';
|
||||||
@ -55,7 +55,6 @@ import { DownloadService } from '../../modules/download/download.service';
|
|||||||
import { DownloadDto } from './dto/download-library.dto';
|
import { DownloadDto } from './dto/download-library.dto';
|
||||||
import { IAlbumRepository } from '../album/album-repository';
|
import { IAlbumRepository } from '../album/album-repository';
|
||||||
import { SharedLinkCore } from '@app/domain';
|
import { SharedLinkCore } from '@app/domain';
|
||||||
import { IPartnerRepository } from '@app/domain';
|
|
||||||
import { ISharedLinkRepository } from '@app/domain';
|
import { ISharedLinkRepository } from '@app/domain';
|
||||||
import { DownloadFilesDto } from './dto/download-files.dto';
|
import { DownloadFilesDto } from './dto/download-files.dto';
|
||||||
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||||
@ -82,9 +81,9 @@ export class AssetService {
|
|||||||
readonly logger = new Logger(AssetService.name);
|
readonly logger = new Logger(AssetService.name);
|
||||||
private shareCore: SharedLinkCore;
|
private shareCore: SharedLinkCore;
|
||||||
private assetCore: AssetCore;
|
private assetCore: AssetCore;
|
||||||
private partnerCore: PartnerCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private _assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private _assetRepository: IAssetRepository,
|
||||||
@Inject(IAlbumRepository) private _albumRepository: IAlbumRepository,
|
@Inject(IAlbumRepository) private _albumRepository: IAlbumRepository,
|
||||||
@InjectRepository(AssetEntity)
|
@InjectRepository(AssetEntity)
|
||||||
@ -94,11 +93,9 @@ export class AssetService {
|
|||||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||||
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
||||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
|
||||||
) {
|
) {
|
||||||
this.assetCore = new AssetCore(_assetRepository, jobRepository);
|
this.assetCore = new AssetCore(_assetRepository, jobRepository);
|
||||||
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
||||||
this.partnerCore = new PartnerCore(partnerRepository);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async uploadFile(
|
public async uploadFile(
|
||||||
@ -581,7 +578,7 @@ export class AssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Check if any partner owns the asset
|
// Step 3: Check if any partner owns the asset
|
||||||
const canAccess = await this.partnerCore.hasAssetAccess(assetId, authUser.id);
|
const canAccess = await this.accessRepository.hasPartnerAssetAccess(authUser.id, assetId);
|
||||||
if (canAccess) {
|
if (canAccess) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -601,7 +598,8 @@ export class AssetService {
|
|||||||
|
|
||||||
private async checkUserAccess(authUser: AuthUserDto, userId: string) {
|
private async checkUserAccess(authUser: AuthUserDto, userId: string) {
|
||||||
// Check if userId shares assets with authUser
|
// Check if userId shares assets with authUser
|
||||||
if (!(await this.partnerCore.get({ sharedById: userId, sharedWithId: authUser.id }))) {
|
const canAccess = await this.accessRepository.hasPartnerAccess(authUser.id, userId);
|
||||||
|
if (!canAccess) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
server/libs/domain/src/access/access.repository.ts
Normal file
6
server/libs/domain/src/access/access.repository.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const IAccessRepository = 'IAccessRepository';
|
||||||
|
|
||||||
|
export interface IAccessRepository {
|
||||||
|
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
|
||||||
|
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean>;
|
||||||
|
}
|
1
server/libs/domain/src/access/index.ts
Normal file
1
server/libs/domain/src/access/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './access.repository';
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './access';
|
||||||
export * from './album';
|
export * from './album';
|
||||||
export * from './api-key';
|
export * from './api-key';
|
||||||
export * from './asset';
|
export * from './asset';
|
||||||
@ -13,10 +14,10 @@ export * from './job';
|
|||||||
export * from './media';
|
export * from './media';
|
||||||
export * from './metadata';
|
export * from './metadata';
|
||||||
export * from './oauth';
|
export * from './oauth';
|
||||||
|
export * from './partner';
|
||||||
export * from './person';
|
export * from './person';
|
||||||
export * from './search';
|
export * from './search';
|
||||||
export * from './server-info';
|
export * from './server-info';
|
||||||
export * from './partner';
|
|
||||||
export * from './shared-link';
|
export * from './shared-link';
|
||||||
export * from './smart-info';
|
export * from './smart-info';
|
||||||
export * from './storage';
|
export * from './storage';
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './partner.core';
|
|
||||||
export * from './partner.repository';
|
export * from './partner.repository';
|
||||||
export * from './partner.service';
|
export * from './partner.service';
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { PartnerEntity } from '@app/infra/entities';
|
|
||||||
import { IPartnerRepository, PartnerIds } from './partner.repository';
|
|
||||||
|
|
||||||
export enum PartnerDirection {
|
|
||||||
SharedBy = 'shared-by',
|
|
||||||
SharedWith = 'shared-with',
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PartnerCore {
|
|
||||||
constructor(private repository: IPartnerRepository) {}
|
|
||||||
|
|
||||||
async getAll(userId: string, direction: PartnerDirection): Promise<PartnerEntity[]> {
|
|
||||||
const partners = await this.repository.getAll(userId);
|
|
||||||
const key = direction === PartnerDirection.SharedBy ? 'sharedById' : 'sharedWithId';
|
|
||||||
return partners.filter((partner) => partner[key] === userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(ids: PartnerIds): Promise<PartnerEntity | null> {
|
|
||||||
return this.repository.get(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
async create(ids: PartnerIds): Promise<PartnerEntity> {
|
|
||||||
return this.repository.create(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(ids: PartnerIds): Promise<void> {
|
|
||||||
await this.repository.remove(ids as PartnerEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasAssetAccess(assetId: string, userId: string): Promise<boolean> {
|
|
||||||
return this.repository.hasAssetAccess(assetId, userId);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,11 @@ export interface PartnerIds {
|
|||||||
sharedWithId: string;
|
sharedWithId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PartnerDirection {
|
||||||
|
SharedBy = 'shared-by',
|
||||||
|
SharedWith = 'shared-with',
|
||||||
|
}
|
||||||
|
|
||||||
export const IPartnerRepository = 'IPartnerRepository';
|
export const IPartnerRepository = 'IPartnerRepository';
|
||||||
|
|
||||||
export interface IPartnerRepository {
|
export interface IPartnerRepository {
|
||||||
@ -12,5 +17,4 @@ export interface IPartnerRepository {
|
|||||||
get(partner: PartnerIds): Promise<PartnerEntity | null>;
|
get(partner: PartnerIds): Promise<PartnerEntity | null>;
|
||||||
create(partner: PartnerIds): Promise<PartnerEntity>;
|
create(partner: PartnerIds): Promise<PartnerEntity>;
|
||||||
remove(entity: PartnerEntity): Promise<void>;
|
remove(entity: PartnerEntity): Promise<void>;
|
||||||
hasAssetAccess(assetId: string, userId: string): Promise<boolean>;
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { authStub, newPartnerRepositoryMock, partnerStub } from '../../test';
|
import { authStub, newPartnerRepositoryMock, partnerStub } from '../../test';
|
||||||
import { PartnerDirection } from './partner.core';
|
import { IPartnerRepository, PartnerDirection } from './partner.repository';
|
||||||
import { IPartnerRepository } from './partner.repository';
|
|
||||||
import { PartnerService } from './partner.service';
|
import { PartnerService } from './partner.service';
|
||||||
|
|
||||||
const responseDto = {
|
const responseDto = {
|
||||||
|
@ -1,41 +1,38 @@
|
|||||||
import { PartnerEntity } from '@app/infra/entities';
|
import { PartnerEntity } from '@app/infra/entities';
|
||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { AuthUserDto } from '../auth';
|
import { AuthUserDto } from '../auth';
|
||||||
import { IPartnerRepository, PartnerCore, PartnerDirection, PartnerIds } from '../partner';
|
import { IPartnerRepository, PartnerDirection, PartnerIds } from '../partner';
|
||||||
import { mapUser, UserResponseDto } from '../user';
|
import { mapUser, UserResponseDto } from '../user';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PartnerService {
|
export class PartnerService {
|
||||||
private partnerCore: PartnerCore;
|
constructor(@Inject(IPartnerRepository) private repository: IPartnerRepository) {}
|
||||||
|
|
||||||
constructor(@Inject(IPartnerRepository) partnerRepository: IPartnerRepository) {
|
|
||||||
this.partnerCore = new PartnerCore(partnerRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async create(authUser: AuthUserDto, sharedWithId: string): Promise<UserResponseDto> {
|
async create(authUser: AuthUserDto, sharedWithId: string): Promise<UserResponseDto> {
|
||||||
const partnerId: PartnerIds = { sharedById: authUser.id, sharedWithId };
|
const partnerId: PartnerIds = { sharedById: authUser.id, sharedWithId };
|
||||||
const exists = await this.partnerCore.get(partnerId);
|
const exists = await this.repository.get(partnerId);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
throw new BadRequestException(`Partner already exists`);
|
throw new BadRequestException(`Partner already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const partner = await this.partnerCore.create(partnerId);
|
const partner = await this.repository.create(partnerId);
|
||||||
return this.map(partner, PartnerDirection.SharedBy);
|
return this.map(partner, PartnerDirection.SharedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(authUser: AuthUserDto, sharedWithId: string): Promise<void> {
|
async remove(authUser: AuthUserDto, sharedWithId: string): Promise<void> {
|
||||||
const partnerId: PartnerIds = { sharedById: authUser.id, sharedWithId };
|
const partnerId: PartnerIds = { sharedById: authUser.id, sharedWithId };
|
||||||
const partner = await this.partnerCore.get(partnerId);
|
const partner = await this.repository.get(partnerId);
|
||||||
if (!partner) {
|
if (!partner) {
|
||||||
throw new BadRequestException('Partner not found');
|
throw new BadRequestException('Partner not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.partnerCore.remove(partner);
|
await this.repository.remove(partner);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(authUser: AuthUserDto, direction: PartnerDirection): Promise<UserResponseDto[]> {
|
async getAll(authUser: AuthUserDto, direction: PartnerDirection): Promise<UserResponseDto[]> {
|
||||||
const partners = await this.partnerCore.getAll(authUser.id, direction);
|
const partners = await this.repository.getAll(authUser.id);
|
||||||
return partners.map((partner) => this.map(partner, direction));
|
const key = direction === PartnerDirection.SharedBy ? 'sharedById' : 'sharedWithId';
|
||||||
|
return partners.filter((partner) => partner[key] === authUser.id).map((partner) => this.map(partner, direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
private map(partner: PartnerEntity, direction: PartnerDirection): UserResponseDto {
|
private map(partner: PartnerEntity, direction: PartnerDirection): UserResponseDto {
|
||||||
|
8
server/libs/domain/test/access.repository.mock.ts
Normal file
8
server/libs/domain/test/access.repository.mock.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { IAccessRepository } from '../src';
|
||||||
|
|
||||||
|
export const newAccessRepositoryMock = (): jest.Mocked<IAccessRepository> => {
|
||||||
|
return {
|
||||||
|
hasPartnerAccess: jest.fn(),
|
||||||
|
hasPartnerAssetAccess: jest.fn(),
|
||||||
|
};
|
||||||
|
};
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './access.repository.mock';
|
||||||
export * from './album.repository.mock';
|
export * from './album.repository.mock';
|
||||||
export * from './api-key.repository.mock';
|
export * from './api-key.repository.mock';
|
||||||
export * from './asset.repository.mock';
|
export * from './asset.repository.mock';
|
||||||
|
@ -6,6 +6,5 @@ export const newPartnerRepositoryMock = (): jest.Mocked<IPartnerRepository> => {
|
|||||||
remove: jest.fn(),
|
remove: jest.fn(),
|
||||||
getAll: jest.fn(),
|
getAll: jest.fn(),
|
||||||
get: jest.fn(),
|
get: jest.fn(),
|
||||||
hasAssetAccess: jest.fn(),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
IAccessRepository,
|
||||||
IAlbumRepository,
|
IAlbumRepository,
|
||||||
IAssetRepository,
|
IAssetRepository,
|
||||||
ICommunicationRepository,
|
ICommunicationRepository,
|
||||||
@ -30,6 +31,7 @@ import { databaseConfig } from './database.config';
|
|||||||
import { databaseEntities } from './entities';
|
import { databaseEntities } from './entities';
|
||||||
import { bullConfig, bullQueues } from './infra.config';
|
import { bullConfig, bullQueues } from './infra.config';
|
||||||
import {
|
import {
|
||||||
|
AccessRepository,
|
||||||
AlbumRepository,
|
AlbumRepository,
|
||||||
APIKeyRepository,
|
APIKeyRepository,
|
||||||
AssetRepository,
|
AssetRepository,
|
||||||
@ -53,6 +55,7 @@ import {
|
|||||||
} from './repositories';
|
} from './repositories';
|
||||||
|
|
||||||
const providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
|
{ provide: IAccessRepository, useClass: AccessRepository },
|
||||||
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
||||||
{ provide: IAssetRepository, useClass: AssetRepository },
|
{ provide: IAssetRepository, useClass: AssetRepository },
|
||||||
{ provide: ICommunicationRepository, useClass: CommunicationRepository },
|
{ provide: ICommunicationRepository, useClass: CommunicationRepository },
|
||||||
|
38
server/libs/infra/src/repositories/access.repository.ts
Normal file
38
server/libs/infra/src/repositories/access.repository.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { IAccessRepository } from '@app/domain';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { PartnerEntity } from '../entities';
|
||||||
|
|
||||||
|
export class AccessRepository implements IAccessRepository {
|
||||||
|
constructor(@InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>) {}
|
||||||
|
|
||||||
|
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean> {
|
||||||
|
return this.partnerRepository.exist({
|
||||||
|
where: {
|
||||||
|
sharedWithId: userId,
|
||||||
|
sharedById: partnerId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPartnerAssetAccess(userId: string, assetId: string): Promise<boolean> {
|
||||||
|
return this.partnerRepository.exist({
|
||||||
|
where: {
|
||||||
|
sharedWith: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
sharedBy: {
|
||||||
|
assets: {
|
||||||
|
id: assetId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relations: {
|
||||||
|
sharedWith: true,
|
||||||
|
sharedBy: {
|
||||||
|
assets: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './access.repository';
|
||||||
export * from './album.repository';
|
export * from './album.repository';
|
||||||
export * from './api-key.repository';
|
export * from './api-key.repository';
|
||||||
export * from './asset.repository';
|
export * from './asset.repository';
|
||||||
|
@ -24,27 +24,4 @@ export class PartnerRepository implements IPartnerRepository {
|
|||||||
async remove(entity: PartnerEntity): Promise<void> {
|
async remove(entity: PartnerEntity): Promise<void> {
|
||||||
await this.repository.remove(entity);
|
await this.repository.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasAssetAccess(assetId: string, userId: string): Promise<boolean> {
|
|
||||||
const count = await this.repository.count({
|
|
||||||
where: {
|
|
||||||
sharedWith: {
|
|
||||||
id: userId,
|
|
||||||
},
|
|
||||||
sharedBy: {
|
|
||||||
assets: {
|
|
||||||
id: assetId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
relations: {
|
|
||||||
sharedWith: true,
|
|
||||||
sharedBy: {
|
|
||||||
assets: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return count == 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user