Skip to content

Commit

Permalink
feat(job): add purge-unused-image job
Browse files Browse the repository at this point in the history
  • Loading branch information
son-daehyeon committed Sep 15, 2024
1 parent 7125768 commit 2b2db07
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 14 deletions.
8 changes: 4 additions & 4 deletions src/common/database/mongo/mongo-model.factory.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const convertToKST = (date: Date): Date => new Date(date.getTime() + 9 * 60 * 60

export class MongoModelFactory {
static generate<T>(name: string, schema: Schema<T>): ModelDefinition {
schema = MongoModelFactory.#setSchemaOptions(schema);
schema = MongoModelFactory.setSchemaOptions(schema);

return { name, schema };
}
Expand All @@ -17,9 +17,9 @@ export class MongoModelFactory {
schema: Schema<T>,
subSchemas: { type: string; schema: Schema }[],
): ModelDefinition {
schema = MongoModelFactory.#setSchemaOptions(schema);
schema = MongoModelFactory.setSchemaOptions(schema);
subSchemas = subSchemas.map(({ type, schema }) => {
schema = MongoModelFactory.#setSchemaOptions(schema);
schema = MongoModelFactory.setSchemaOptions(schema);

return { type, schema };
});
Expand All @@ -33,7 +33,7 @@ export class MongoModelFactory {
};
}

static #setSchemaOptions(schema: Schema): Schema {
private static setSchemaOptions(schema: Schema): Schema {
schema.set('versionKey', false);
schema.set('timestamps', true);

Expand Down
10 changes: 5 additions & 5 deletions src/common/database/redis/service/redis.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class RedisService {
throw new Error('Group is not set');
}

const _key = this.#generateKey(key);
const _key = this.generateKey(key);

const exists = await this.redisClient.exists(_key);

Expand All @@ -43,7 +43,7 @@ export class RedisService {
throw new Error('Group is not set');
}

const _key = this.#generateKey(key);
const _key = this.generateKey(key);

return (await this.redisClient.get(_key)) || '';
}
Expand All @@ -53,7 +53,7 @@ export class RedisService {
throw new Error('Group is not set');
}

const _key = this.#generateKey(key);
const _key = this.generateKey(key);

let event: RedisSetEvent | RedisSetTtlEvent;

Expand All @@ -73,14 +73,14 @@ export class RedisService {
throw new Error('Group is not set');
}

const _key = this.#generateKey(key);
const _key = this.generateKey(key);

await this.redisClient.del(_key);

this.eventEmitter.emit(RedisDeleteEvent.EVENT_NAME, new RedisDeleteEvent(_key));
}

#generateKey(key: string): string {
private generateKey(key: string): string {
if (!this.group) {
throw new Error('Group is not set');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';

import { PurgeUnusedAvatarEvent } from '../../type';
import { PurgeUnusedAvatarEvent, PurgeUnusedImageEvent } from '../../type';

@Injectable()
export class SchedulerEventListener {
private readonly logger = new Logger(SchedulerEventListener.name);

@OnEvent(PurgeUnusedAvatarEvent.EVENT_NAME)
onPurgeUnusedAvatar({ keys }: PurgeUnusedAvatarEvent) {
this.logger.log(`Purge unused ${keys.length} avatars. (${keys.join(', ')})`);
this.logger.log(`Purge unused ${keys.length} avatars.`);
}

@OnEvent(PurgeUnusedImageEvent.EVENT_NAME)
onPurgeUnusedImage({ keys }: PurgeUnusedImageEvent) {
this.logger.log(`Purge unused ${keys.length} images.`);
}
}
6 changes: 6 additions & 0 deletions src/common/util/event/type/util/scheduler.event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ export class PurgeUnusedAvatarEvent {

constructor(public readonly keys: string[]) {}
}

export class PurgeUnusedImageEvent {
public static readonly EVENT_NAME = 'scheduler.purge_unused_image';

constructor(public readonly keys: string[]) {}
}
3 changes: 3 additions & 0 deletions src/domain/activity/activity.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MongooseModule } from '@nestjs/mongoose';

import { MemberModule } from '@wink/member/member.module';

import { PurgeUnusedImageJob } from '@wink/activity/common/util/scheduler';
import {
ProjectController,
ProjectAdminController,
Expand Down Expand Up @@ -77,6 +78,8 @@ const modelFactory2 = MongoModelFactory.generate<Category>(Category.name, Catego
StudyRepository,
CategoryRepository,
SocialRepository,

PurgeUnusedImageJob,
],
exports: [ProjectRepository, StudyRepository, CategoryRepository, SocialRepository],
})
Expand Down
4 changes: 2 additions & 2 deletions src/domain/activity/common/service/activity.admin.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export class ActivityAdminService {
constructor(
private readonly memberRepository: MemberRepository,

@Inject('S3_SERVICE_ACTIVITY') private readonly avatarService: S3Service,
@Inject('S3_SERVICE_ACTIVITY') private readonly activityService: S3Service,

private readonly eventEmitter: EventEmitter2,
) {}

async upload(member: Member, file: Express.Multer.File): Promise<UploadResponseDto> {
const link = await this.avatarService.upload(file);
const link = await this.activityService.upload(file);

this.eventEmitter.emit(UploadEvent.EVENT_NAME, new UploadEvent(member, file));

Expand Down
1 change: 1 addition & 0 deletions src/domain/activity/common/util/scheduler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './purge-unused-image.job';
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Inject, Injectable } from '@nestjs/common';
import { Cron, CronExpression, Timeout } from '@nestjs/schedule';
import { EventEmitter2 } from '@nestjs/event-emitter';

import { ProjectRepository, SocialRepository } from '@wink/activity/repository';

import { S3Service } from '@wink/s3';
import { PurgeUnusedImageEvent } from '@wink/event';

import * as cheerio from 'cheerio';

@Injectable()
export class PurgeUnusedImageJob {
constructor(
private readonly projectRepository: ProjectRepository,
private readonly socialRepository: SocialRepository,

@Inject('S3_SERVICE_ACTIVITY') private readonly activityService: S3Service,

private readonly eventEmitter: EventEmitter2,
) {}

@Timeout(0)
async onTimeout() {
await this.job();
}

@Cron(CronExpression.EVERY_DAY_AT_6AM)
async onCron() {
await this.job();
}

private async job() {
const usedImages = [
...(await this.socialRepository.findAll())
.flatMap((member) => member.contents)
.map((content) => content.image)
.map((image) => this.activityService.extractKeyFromUrl(image)),
...(await this.projectRepository.findAll())
.map((project) => project.content)
.flatMap((content) => this.toImagesFromHtml(content))
.map((image) => this.activityService.extractKeyFromUrl(image)),
];

const savedImages = await this.activityService.getKeys();

const unusedImages = savedImages.filter((a) => !usedImages.includes(a));

unusedImages.forEach((key) => this.activityService.delete(key));

this.eventEmitter.emit(
PurgeUnusedImageEvent.EVENT_NAME,
new PurgeUnusedImageEvent(unusedImages),
);
}

private toImagesFromHtml(html: string): string[] {
const $ = cheerio.load(html);

const images: string[] = [];
$('img').each((_, elem) => {
const src = $(elem).attr('src');

if (src) {
images.push(src);
}
});

return images;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class PurgeUnusedAvatarJob {

const unusedAvatars = savedAvatars.filter((a) => !usedAvatars.includes(a));

unusedAvatars.forEach((key) => this.avatarService.delete(key).then((_) => _));
unusedAvatars.forEach((key) => this.avatarService.delete(key));

this.eventEmitter.emit(
PurgeUnusedAvatarEvent.EVENT_NAME,
Expand Down

0 comments on commit 2b2db07

Please sign in to comment.