Skip to content

Commit 6d5a6fa

Browse files
committed
Merge branch 'development'
2 parents f6f415f + cba2cf0 commit 6d5a6fa

File tree

14 files changed

+120
-51
lines changed

14 files changed

+120
-51
lines changed

apps/cron/src/common/plugins/global/cronPlugin.mts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { GenerateFeedJob } from '@jobs/GenerateFeedJob.mjs'
44
import { CalculatePostScoreJob } from '@jobs/CalculatePostScoreJob.mjs'
55
import { GenerateTrendingWritersJob } from '@jobs/GenerateTrendingWritersJob.mjs'
66
import { DeleteFeedJob } from '@jobs/DeleteFeedJob.mjs'
7-
import { FastifyPluginAsync } from 'fastify'
7+
import type { FastifyPluginAsync } from 'fastify'
88
import { container } from 'tsyringe'
99
import { ENV } from '@env'
10-
import { CheckSpamPostJob } from '@jobs/CheckSpamPostJob.mjs'
10+
import { CheckPostSpamJob } from '@jobs/CheckPostSpamJob.mjs'
1111
import { DeletePostReadJob } from '@jobs/DeletePostReadJob.mjs'
12+
import { ScorePostJob } from '@jobs/ScorePostJob.mjs'
1213

1314
const cronPlugin: FastifyPluginAsync = async (fastfiy) => {
1415
const calculatePostScoreJob = container.resolve(CalculatePostScoreJob)
@@ -18,8 +19,9 @@ const cronPlugin: FastifyPluginAsync = async (fastfiy) => {
1819
const statsDailyJob = container.resolve(StatsDaily)
1920
const statsWeeklyJob = container.resolve(StatsWeekly)
2021
const statsMonthlyJob = container.resolve(StatsMonthly)
21-
const checkSpamPostJob = container.resolve(CheckSpamPostJob)
22+
const checkPostSpamJob = container.resolve(CheckPostSpamJob)
2223
const deleteReadPostJob = container.resolve(DeletePostReadJob)
24+
const scorePostJob = container.resolve(ScorePostJob)
2325

2426
// 덜 실행하면서, 실행되는 순서로 정렬
2527
// crontime은 UTC 기준으로 작성되기 때문에 KST에서 9시간을 빼줘야함
@@ -51,10 +53,15 @@ const cronPlugin: FastifyPluginAsync = async (fastfiy) => {
5153
jobService: calculatePostScoreJob,
5254
param: 0.5,
5355
},
56+
{
57+
name: 'score post in every 1 minutes',
58+
cronTime: '*/1 * * * *', // every 1 minutes
59+
jobService: scorePostJob,
60+
},
5461
{
5562
name: 'check post spam in every 2 minutes',
5663
cronTime: '*/2 * * * *', // every 2 minutes
57-
jobService: checkSpamPostJob,
64+
jobService: checkPostSpamJob,
5865
},
5966
{
6067
name: 'delete post read in every 2 minutes',
@@ -151,8 +158,9 @@ type JobService =
151158
| StatsDaily
152159
| StatsWeekly
153160
| StatsMonthly
154-
| CheckSpamPostJob
161+
| CheckPostSpamJob
155162
| DeletePostReadJob
163+
| ScorePostJob
156164

157165
type BaseJobService = {
158166
name: string

apps/cron/src/jobs/CheckSpamPostJob.mts renamed to apps/cron/src/jobs/CheckPostSpamJob.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { injectable, singleton } from 'tsyringe'
22
import { Job, JobProgress } from './JobProgress.mjs'
33
import { PostService } from '@services/PostService/index.mjs'
4-
import { CheckPostSpamArgs, RedisService } from '@lib/redis/RedisService.mjs'
4+
import { type CheckPostSpamQueueData, RedisService } from '@lib/redis/RedisService.mjs'
55
import { DiscordService } from '@lib/discord/DiscordService.mjs'
66

77
@injectable()
88
@singleton()
9-
export class CheckSpamPostJob extends JobProgress implements Job {
9+
export class CheckPostSpamJob extends JobProgress implements Job {
1010
constructor(
1111
private readonly redis: RedisService,
1212
private readonly discord: DiscordService,
@@ -23,7 +23,7 @@ export class CheckSpamPostJob extends JobProgress implements Job {
2323
while (true) {
2424
const item = await this.redis.lindex(spamQueueName, 0)
2525
if (!item) break
26-
const data: CheckPostSpamArgs = JSON.parse(item)
26+
const data: CheckPostSpamQueueData = JSON.parse(item)
2727
try {
2828
await this.postService.checkSpam(data)
2929
} catch (error) {

apps/cron/src/jobs/GenerateFeedJob.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export class GenerateFeedJob extends JobProgress implements Job {
2222
const { fk_following_id, fk_post_id } = data
2323
try {
2424
await this.feedService.createFeed({ followingId: fk_following_id, postId: fk_post_id })
25-
await this.redis.lpop(feedQueueName)
26-
handledQueueCount++
2725
} catch (error) {
2826
console.log('Error occurred while creating feed', error)
2927
console.log('data', data)
30-
continue
28+
} finally {
29+
await this.redis.lpop(feedQueueName)
30+
handledQueueCount++
3131
}
3232
}
3333
console.log(`Created Feed Count: ${handledQueueCount}`)

apps/cron/src/jobs/ScorePostJob.mts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { injectable, singleton } from 'tsyringe'
2+
import { Job, JobProgress } from './JobProgress.mjs'
3+
import { RedisService } from '@lib/redis/RedisService.mjs'
4+
import { PostService } from '@services/PostService/index.mjs'
5+
import { ScorePostQueueData } from '@packages/database/velog-redis'
6+
import { DiscordService } from '@lib/discord/DiscordService.mjs'
7+
8+
@singleton()
9+
@injectable()
10+
export class ScorePostJob extends JobProgress implements Job {
11+
constructor(
12+
private readonly postService: PostService,
13+
private readonly redis: RedisService,
14+
private readonly discord: DiscordService,
15+
) {
16+
super()
17+
}
18+
public async runner(): Promise<void> {
19+
console.log('ScorePostJob start...')
20+
console.time('ScorePostJob')
21+
22+
const scorePostQueueName = this.redis.queueName.scorePost
23+
let handledQueueCount = 0
24+
25+
while (true) {
26+
const item = await this.redis.lindex(scorePostQueueName, 0)
27+
if (!item) break
28+
const data: ScorePostQueueData = JSON.parse(item)
29+
try {
30+
await this.postService.scoreCalculator(data.post_id)
31+
} catch (error) {
32+
console.log('ScorePostJob error', error)
33+
const message = { message: 'ScorePostJob error', payload: item, error: error }
34+
this.discord.sendMessage('error', JSON.stringify(message))
35+
} finally {
36+
await this.redis.lpop(scorePostQueueName)
37+
handledQueueCount++
38+
}
39+
}
40+
41+
console.log(`handled Score Post Queue count: ${handledQueueCount}`)
42+
console.timeEnd('ScorePostJob')
43+
}
44+
}

apps/cron/src/lib/redis/RedisService.mts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { injectable, singleton } from 'tsyringe'
22
import { ENV } from '@env'
33
import { RedisService as Redis } from '@packages/database/velog-redis'
4-
export type { ChangeEmailArgs } from '@packages/database/velog-redis'
5-
export type { CheckPostSpamArgs } from '@packages/database/velog-redis'
6-
export type { CreateFeedArgs } from '@packages/database/velog-redis'
4+
export type {
5+
ChangeEmailArgs,
6+
CheckPostSpamQueueData,
7+
CreateFeedQueueData,
8+
} from '@packages/database/velog-redis'
79

810
@injectable()
911
@singleton()

apps/cron/src/routes/posts/v1/index.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ const v1: FastifyPluginCallback = (fastify, opts, done) => {
1919
},
2020
)
2121

22+
// dev 환경에서만 사용 가능
2223
fastify.patch('/score', async (_, reply) => {
2324
const processedPostsCount = await postController.calculateRecentPostScore()
2425
reply.status(HttpStatus.OK).send({ processedPostsCount })
2526
})
2627

28+
// dev 환경에서만 사용 가능
2729
fastify.post('/test/spam-filter', async (_, reply) => {
2830
await postController.spamFilterTestRunner()
2931
reply.status(HttpStatus.OK).send(HttpStatusMessage.Ok)

apps/cron/src/services/PostService/index.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import { injectable, singleton } from 'tsyringe'
44
import geoip from 'geoip-country'
55
import { subMonths } from 'date-fns'
66
import { DiscordService } from '@lib/discord/DiscordService.mjs'
7-
import { CheckPostSpamArgs } from '@lib/redis/RedisService.mjs'
7+
import type { CheckPostSpamQueueData } from '@lib/redis/RedisService.mjs'
88

99
interface Service {
1010
findById(postId: string): Promise<Post | null>
1111
scoreCalculator(postId: string): Promise<void>
12-
checkSpam(args: CheckPostSpamArgs): Promise<void>
12+
checkSpam(data: CheckPostSpamQueueData): Promise<void>
1313
}
1414

1515
@singleton()
@@ -68,7 +68,7 @@ export class PostService implements Service {
6868
})
6969
}
7070

71-
public async checkSpam({ post_id, user_id, ip }: CheckPostSpamArgs): Promise<void> {
71+
public async checkSpam({ post_id, user_id, ip }: CheckPostSpamQueueData): Promise<void> {
7272
const post = await this.db.post.findUnique({
7373
where: {
7474
id: post_id,

apps/server/src/lib/redis/RedisService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { injectable, singleton } from 'tsyringe'
33
import { RedisService as Redis } from '@packages/database/velog-redis'
44
export type {
55
ChangeEmailArgs,
6-
CheckPostSpamArgs,
7-
CreateFeedArgs,
6+
CheckPostSpamQueueData,
7+
CreateFeedQueueData,
88
} from '@packages/database/velog-redis'
99

1010
@injectable()

apps/server/src/services/FeedService/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ interface Service {
1515
@injectable()
1616
@singleton()
1717
export class FeedService implements Service {
18-
constructor(
19-
private readonly db: DbService,
20-
private readonly utils: UtilsService,
21-
) {}
18+
constructor(private readonly db: DbService, private readonly utils: UtilsService) {}
2219
async getFeedPosts(input: FeedPostsInput, singedUserId?: string): Promise<Post[]> {
2320
if (!singedUserId) {
2421
return []

apps/server/src/services/PostApiService/index.mts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import { SeriesService } from '@services/SeriesService/index.mjs'
1717
import { SearchService } from '@services/SearchService/index.js'
1818
import { ExternalIntegrationService } from '@services/ExternalIntegrationService/index.js'
1919
import { PostService } from '@services/PostService/index.js'
20-
import { CreateFeedArgs, RedisService, CheckPostSpamArgs } from '@lib/redis/RedisService.js'
20+
import {
21+
RedisService,
22+
type CreateFeedQueueData,
23+
type CheckPostSpamQueueData,
24+
} from '@lib/redis/RedisService.js'
2125
import { GraphcdnService } from '@lib/graphcdn/GraphcdnService.js'
2226
import { ImageService } from '@services/ImageService/index.js'
2327
import { UserService } from '@services/UserService/index.js'
@@ -201,10 +205,20 @@ export class PostApiService implements Service {
201205
const isTusted = await this.userService.checkTrust(signedUserId)
202206
if (isPublish && !isTusted) {
203207
const isVerified = await this.verifyTurnstile(token)
204-
checks.push({
205-
type: 'turnstile',
206-
value: !isVerified,
207-
})
208+
if (!isVerified) {
209+
await this.alertIsSpam({
210+
action: type,
211+
userId: user.id,
212+
title: input.title!,
213+
country,
214+
ip,
215+
type: 'turnstile',
216+
})
217+
}
218+
// checks.push({
219+
// type: 'turnstile',
220+
// value: !isVerified,
221+
// })
208222
}
209223

210224
const isSpam = checks.map(({ value }) => value).some((check) => check)
@@ -315,24 +329,24 @@ export class PostApiService implements Service {
315329
// create feed
316330
setTimeout(() => {
317331
if (!post) return
318-
const queueData: CreateFeedArgs = {
332+
const queueData: CreateFeedQueueData = {
319333
fk_following_id: signedUserId,
320334
fk_post_id: post.id,
321335
}
322-
this.redis.createFeedQueue(queueData)
336+
this.redis.addToCreateFeedQueue(queueData)
323337
}, 0)
324338

325339
// check spam
326340
setTimeout(() => {
327341
if (!post) return
328342
if (isSpam) return
329343
if (isTusted) return
330-
const queueData: CheckPostSpamArgs = {
344+
const queueData: CheckPostSpamQueueData = {
331345
post_id: post.id,
332346
user_id: signedUserId,
333347
ip,
334348
}
335-
this.redis.addToSpamCheckQueue(queueData)
349+
this.redis.addToCheckPostSpamQueue(queueData)
336350
}, 0)
337351
}
338352

apps/server/src/services/PostService/index.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -405,17 +405,7 @@ export class PostService implements Service {
405405
}
406406
}
407407
public async updatePostScore(postId: string) {
408-
try {
409-
await axios.patch(
410-
`${ENV.cronHost}/api/posts/v1/score/${postId}`,
411-
{},
412-
{
413-
headers: {
414-
'Cron-Api-Key': ENV.cronApiKey,
415-
},
416-
},
417-
)
418-
} catch (_) {}
408+
await this.redis.addToScorePostQueue({ post_id: postId })
419409
}
420410
public shortDescription(post: Post): string {
421411
if (post.short_description) return post.short_description

infrastructure/Pulumi.production.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
config:
22
aws:region: ap-northeast-2
33
velog:DOCKER_ENV: production
4-
velog:target: server
4+
velog:target: cron

infrastructure/Pulumi.stage.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
config:
22
aws:region: ap-northeast-2
33
velog:DOCKER_ENV: stage
4-
velog:target: server
4+
velog:target: server,cron

packages/database/src/velog-redis/index.mts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ interface Service {
44
connection(): Promise<void>
55
get generateKey(): GenerateRedisKey
66
get queueName(): Record<QueueName, string>
7-
createFeedQueue(data: CreateFeedArgs): Promise<number>
7+
addToCreateFeedQueue(data: CreateFeedQueueData): Promise<number>
8+
addToCheckPostSpamQueue(data: CheckPostSpamQueueData): Promise<number>
9+
addToScorePostQueue(data: ScorePostQueueData): Promise<number>
810
}
911

1012
type RedisOptions = {
@@ -53,18 +55,24 @@ export class RedisService extends Redis.default implements Service {
5355
return {
5456
createFeed: 'queue:feed',
5557
checkPostSpam: 'queue:checkPostSpam',
58+
scorePost: 'queue:scorePost',
5659
}
5760
}
5861

59-
public async createFeedQueue(data: CreateFeedArgs): Promise<number> {
62+
public async addToCreateFeedQueue(data: CreateFeedQueueData): Promise<number> {
6063
const queueName = this.queueName.createFeed
6164
return await this.lpush(queueName, JSON.stringify(data))
6265
}
6366

64-
public async addToSpamCheckQueue(data: CheckPostSpamArgs): Promise<number> {
67+
public async addToCheckPostSpamQueue(data: CheckPostSpamQueueData): Promise<number> {
6568
const queueName = this.queueName.checkPostSpam
6669
return await this.lpush(queueName, JSON.stringify(data))
6770
}
71+
72+
public async addToScorePostQueue(data: ScorePostQueueData): Promise<number> {
73+
const queueName = this.queueName.scorePost
74+
return await this.lpush(queueName, JSON.stringify(data))
75+
}
6876
}
6977

7078
type GenerateRedisKey = {
@@ -81,20 +89,24 @@ type GenerateRedisKey = {
8189
deployBook: (bookId: string) => string
8290
}
8391

84-
type QueueName = 'createFeed' | 'checkPostSpam'
92+
type QueueName = 'createFeed' | 'checkPostSpam' | 'scorePost'
8593

8694
export type ChangeEmailArgs = {
8795
email: string
8896
userId: string
8997
}
9098

91-
export type CreateFeedArgs = {
99+
export type CreateFeedQueueData = {
92100
fk_following_id: string
93101
fk_post_id: string
94102
}
95103

96-
export type CheckPostSpamArgs = {
104+
export type CheckPostSpamQueueData = {
97105
post_id: string
98106
user_id: string
99107
ip: string
100108
}
109+
110+
export type ScorePostQueueData = {
111+
post_id: string
112+
}

0 commit comments

Comments
 (0)