Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jrasm91 committed Oct 2, 2024
1 parent 6c7d51d commit d9d00f7
Show file tree
Hide file tree
Showing 44 changed files with 534 additions and 1,013 deletions.
22 changes: 8 additions & 14 deletions server/src/services/activity.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Inject, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import {
ActivityCreateDto,
ActivityDto,
Expand All @@ -13,20 +13,14 @@ import {
import { AuthDto } from 'src/dtos/auth.dto';
import { ActivityEntity } from 'src/entities/activity.entity';
import { Permission } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IActivityRepository } from 'src/interfaces/activity.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';

@Injectable()
export class ActivityService {
constructor(
@Inject(IAccessRepository) private access: IAccessRepository,
@Inject(IActivityRepository) private repository: IActivityRepository,
) {}

export class ActivityService extends BaseService {
async getAll(auth: AuthDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
const activities = await this.repository.search({
const activities = await this.activityRepository.search({
userId: dto.userId,
albumId: dto.albumId,
assetId: dto.level === ReactionLevel.ALBUM ? null : dto.assetId,
Expand All @@ -38,7 +32,7 @@ export class ActivityService {

async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
return { comments: await this.repository.getStatistics(dto.assetId, dto.albumId) };
return { comments: await this.activityRepository.getStatistics(dto.assetId, dto.albumId) };
}

async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
Expand All @@ -55,7 +49,7 @@ export class ActivityService {

if (dto.type === ReactionType.LIKE) {
delete dto.comment;
[activity] = await this.repository.search({
[activity] = await this.activityRepository.search({
...common,
// `null` will search for an album like
assetId: dto.assetId ?? null,
Expand All @@ -65,7 +59,7 @@ export class ActivityService {
}

if (!activity) {
activity = await this.repository.create({
activity = await this.activityRepository.create({
...common,
isLiked: dto.type === ReactionType.LIKE,
comment: dto.comment,
Expand All @@ -77,6 +71,6 @@ export class ActivityService {

async delete(auth: AuthDto, id: string): Promise<void> {
await requireAccess(this.access, { auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
await this.repository.delete(id);
await this.activityRepository.delete(id);
}
}
21 changes: 4 additions & 17 deletions server/src/services/album.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import {
AddUsersDto,
AlbumInfoDto,
Expand All @@ -17,26 +17,13 @@ import { AlbumUserEntity } from 'src/entities/album-user.entity';
import { AlbumEntity } from 'src/entities/album.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { Permission } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IEventRepository } from 'src/interfaces/event.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
import { BaseService } from 'src/services/base.service';
import { checkAccess, requireAccess } from 'src/utils/access';
import { addAssets, removeAssets } from 'src/utils/asset.util';

@Injectable()
export class AlbumService {
constructor(
@Inject(IAccessRepository) private access: IAccessRepository,
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IEventRepository) private eventRepository: IEventRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(IAlbumUserRepository) private albumUserRepository: IAlbumUserRepository,
) {}

export class AlbumService extends BaseService {
async getStatistics(auth: AuthDto): Promise<AlbumStatisticsResponseDto> {
const [owned, shared, notShared] = await Promise.all([
this.albumRepository.getOwned(auth.user.id),
Expand Down
26 changes: 10 additions & 16 deletions server/src/services/api-key.service.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { APIKeyEntity } from 'src/entities/api-key.entity';
import { IKeyRepository } from 'src/interfaces/api-key.interface';
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
import { BaseService } from 'src/services/base.service';
import { isGranted } from 'src/utils/access';

@Injectable()
export class APIKeyService {
constructor(
@Inject(ICryptoRepository) private crypto: ICryptoRepository,
@Inject(IKeyRepository) private repository: IKeyRepository,
) {}

export class APIKeyService extends BaseService {
async create(auth: AuthDto, dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
const secret = this.crypto.newPassword(32);

if (auth.apiKey && !isGranted({ requested: dto.permissions, current: auth.apiKey.permissions })) {
throw new BadRequestException('Cannot grant permissions you do not have');
}

const entity = await this.repository.create({
const entity = await this.keyRepository.create({
key: this.crypto.hashSha256(secret),
name: dto.name || 'API Key',
userId: auth.user.id,
Expand All @@ -31,35 +25,35 @@ export class APIKeyService {
}

async update(auth: AuthDto, id: string, dto: APIKeyUpdateDto): Promise<APIKeyResponseDto> {
const exists = await this.repository.getById(auth.user.id, id);
const exists = await this.keyRepository.getById(auth.user.id, id);
if (!exists) {
throw new BadRequestException('API Key not found');
}

const key = await this.repository.update(auth.user.id, id, { name: dto.name });
const key = await this.keyRepository.update(auth.user.id, id, { name: dto.name });

return this.map(key);
}

async delete(auth: AuthDto, id: string): Promise<void> {
const exists = await this.repository.getById(auth.user.id, id);
const exists = await this.keyRepository.getById(auth.user.id, id);
if (!exists) {
throw new BadRequestException('API Key not found');
}

await this.repository.delete(auth.user.id, id);
await this.keyRepository.delete(auth.user.id, id);
}

async getById(auth: AuthDto, id: string): Promise<APIKeyResponseDto> {
const key = await this.repository.getById(auth.user.id, id);
const key = await this.keyRepository.getById(auth.user.id, id);
if (!key) {
throw new BadRequestException('API Key not found');
}
return this.map(key);
}

async getAll(auth: AuthDto): Promise<APIKeyResponseDto[]> {
const keys = await this.repository.getByUserId(auth.user.id);
const keys = await this.keyRepository.getByUserId(auth.user.id);
return keys.map((key) => this.map(key));
}

Expand Down
31 changes: 4 additions & 27 deletions server/src/services/asset-media.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
BadRequestException,
Inject,
Injectable,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { BadRequestException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { extname } from 'node:path';
import sanitize from 'sanitize-filename';
import { StorageCore } from 'src/cores/storage.core';
Expand All @@ -28,13 +22,8 @@ import {
import { AuthDto } from 'src/dtos/auth.dto';
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity';
import { AssetStatus, AssetType, CacheControl, Permission, StorageFolder } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IEventRepository } from 'src/interfaces/event.interface';
import { IJobRepository, JobName } from 'src/interfaces/job.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IStorageRepository } from 'src/interfaces/storage.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { JobName } from 'src/interfaces/job.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess, requireUploadAccess } from 'src/utils/access';
import { getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
import { ImmichFileResponse } from 'src/utils/file';
Expand All @@ -56,19 +45,7 @@ export interface UploadFile {
}

@Injectable()
export class AssetMediaService {
constructor(
@Inject(IAccessRepository) private access: IAccessRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(IEventRepository) private eventRepository: IEventRepository,
@Inject(ILoggerRepository) private logger: ILoggerRepository,
) {
this.logger.setContext(AssetMediaService.name);
}

export class AssetMediaService extends BaseService {
async getUploadAssetIdByChecksum(auth: AuthDto, checksum?: string): Promise<AssetMediaResponseDto | undefined> {
if (!checksum) {
return;
Expand Down
28 changes: 1 addition & 27 deletions server/src/services/asset.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, Inject } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import _ from 'lodash';
import { DateTime, Duration } from 'luxon';
import {
Expand All @@ -20,46 +20,20 @@ import { AuthDto } from 'src/dtos/auth.dto';
import { MemoryLaneDto } from 'src/dtos/search.dto';
import { AssetEntity } from 'src/entities/asset.entity';
import { AssetStatus, Permission } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IConfigRepository } from 'src/interfaces/config.interface';
import { IEventRepository } from 'src/interfaces/event.interface';
import {
IAssetDeleteJob,
IJobRepository,
ISidecarWriteJob,
JOBS_ASSET_PAGINATION_SIZE,
JobItem,
JobName,
JobStatus,
} from 'src/interfaces/job.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { IStackRepository } from 'src/interfaces/stack.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getAssetFiles, getMyPartnerIds, onAfterUnlink, onBeforeLink, onBeforeUnlink } from 'src/utils/asset.util';
import { usePagination } from 'src/utils/pagination';

export class AssetService extends BaseService {
constructor(
@Inject(IAccessRepository) private access: IAccessRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IConfigRepository) configRepository: IConfigRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(IEventRepository) private eventRepository: IEventRepository,
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
@Inject(IStackRepository) private stackRepository: IStackRepository,
@Inject(ILoggerRepository) logger: ILoggerRepository,
) {
super(configRepository, systemMetadataRepository, logger);
this.logger.setContext(AssetService.name);
}

async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> {
const partnerIds = await getMyPartnerIds({
userId: auth.user.id,
Expand Down
32 changes: 6 additions & 26 deletions server/src/services/audit.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { DateTime } from 'luxon';
import { resolve } from 'node:path';
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
Expand All @@ -21,44 +21,24 @@ import {
StorageFolder,
UserPathType,
} from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { IAuditRepository } from 'src/interfaces/audit.interface';
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { IPersonRepository } from 'src/interfaces/person.interface';
import { IStorageRepository } from 'src/interfaces/storage.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { BaseService } from 'src/services/base.service';
import { requireAccess } from 'src/utils/access';
import { getAssetFiles } from 'src/utils/asset.util';
import { usePagination } from 'src/utils/pagination';

@Injectable()
export class AuditService {
constructor(
@Inject(IAccessRepository) private access: IAccessRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
@Inject(IPersonRepository) private personRepository: IPersonRepository,
@Inject(IAuditRepository) private repository: IAuditRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(ILoggerRepository) private logger: ILoggerRepository,
) {
this.logger.setContext(AuditService.name);
}

export class AuditService extends BaseService {
async handleCleanup(): Promise<JobStatus> {
await this.repository.removeBefore(DateTime.now().minus(AUDIT_LOG_MAX_DURATION).toJSDate());
await this.auditRepository.removeBefore(DateTime.now().minus(AUDIT_LOG_MAX_DURATION).toJSDate());
return JobStatus.SUCCESS;
}

async getDeletes(auth: AuthDto, dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
const userId = dto.userId || auth.user.id;
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });

const audits = await this.repository.getAfter(dto.after, {
const audits = await this.auditRepository.getAfter(dto.after, {
userIds: [userId],
entityType: dto.entityType,
action: DatabaseAction.DELETE,
Expand All @@ -81,7 +61,7 @@ export class AuditService {
);
}

const checksum = await this.cryptoRepository.hashFile(filename);
const checksum = await this.crypto.hashFile(filename);
results.push({ filename, checksum: checksum.toString('base64') });
}
return results;
Expand Down
Loading

0 comments on commit d9d00f7

Please sign in to comment.