Skip to content

Commit 47e4a13

Browse files
sung17Deathgiverclaude
authored
feat(worker): add scheduled job to refresh zalo oauth tokens daily (#552)
Co-authored-by: Deathgiver <anonymous@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ecd4044 commit 47e4a13

6 files changed

Lines changed: 89 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { zaloIntegrationService } from "@chatbotx.io/business"
2+
import {
3+
calculateExpiresAt,
4+
refreshAccessToken,
5+
type ZaloAuthValue,
6+
} from "@chatbotx.io/integration-zalo"
7+
import { logger } from "../../lib/logger"
8+
9+
export async function refreshZaloTokens(): Promise<void> {
10+
const integrations = await zaloIntegrationService.findAll()
11+
logger.info(`[refreshZaloTokens] found=${integrations.length}`)
12+
13+
for (const integration of integrations) {
14+
try {
15+
const auth = integration.auth as ZaloAuthValue
16+
if (!auth.tokens.refreshToken) {
17+
logger.warn(
18+
`[refreshZaloTokens] id=${integration.id} skipped: no refreshToken`,
19+
)
20+
continue
21+
}
22+
23+
const newTokens = await refreshAccessToken(auth, auth.tokens.refreshToken)
24+
25+
await zaloIntegrationService.updateAuth(integration.id, {
26+
...auth,
27+
tokens: {
28+
...auth.tokens,
29+
accessToken: newTokens.access_token,
30+
refreshToken: newTokens.refresh_token,
31+
expiresAt: calculateExpiresAt(newTokens.expires_in),
32+
},
33+
})
34+
35+
logger.info(`[refreshZaloTokens] id=${integration.id} refreshed`)
36+
} catch (error) {
37+
logger.error(error, `[refreshZaloTokens] id=${integration.id} failed`)
38+
}
39+
}
40+
}

apps/worker/src/schedule/handlers/register-schedules.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,18 @@ export const registerSchedules = async () => {
116116
},
117117
},
118118
)
119+
120+
await scheduleQueue.upsertJobScheduler(
121+
ScheduleJobData.refreshZaloTokens,
122+
{
123+
pattern: "0 2 * * *",
124+
},
125+
{
126+
name: ScheduleJobData.refreshZaloTokens,
127+
data: {
128+
type: ScheduleJobData.refreshZaloTokens,
129+
data: {},
130+
},
131+
},
132+
)
119133
}

apps/worker/src/schedule/worker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { maintainMacPartitions } from "./handlers/maintain-mac-partitions"
1818
import { prepareBroadcast } from "./handlers/prepare-broadcast"
1919
import { processBroadcastContacts } from "./handlers/process-broadcast-contacts"
2020
import { purgeCoexistStaging } from "./handlers/purge-coexist-staging"
21+
import { refreshZaloTokens } from "./handlers/refresh-zalo-tokens"
2122
import { registerSchedules } from "./handlers/register-schedules"
2223
import { scanCoexistRuns } from "./handlers/scan-coexist-runs"
2324
import { scanSmartDelay } from "./handlers/scan-smart-delay"
@@ -90,6 +91,10 @@ async function startScheduleWorker() {
9091
await purgeCoexistStaging()
9192
return
9293

94+
case ScheduleJobData.refreshZaloTokens:
95+
await refreshZaloTokens()
96+
return
97+
9398
default:
9499
logger.warn("Unknown schedule job type")
95100
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./schema"
2+
export * from "./service"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { db, eq } from "@chatbotx.io/database/client"
2+
import { integrationZaloModel } from "@chatbotx.io/database/schema"
3+
import { BaseService } from "../base.service"
4+
5+
class ZaloIntegrationService extends BaseService {
6+
async findAll(): Promise<
7+
Array<{ id: string; auth: Record<string, unknown> }>
8+
> {
9+
return await db
10+
.select({ id: integrationZaloModel.id, auth: integrationZaloModel.auth })
11+
.from(integrationZaloModel)
12+
}
13+
14+
async updateAuth(id: string, auth: Record<string, unknown>): Promise<void> {
15+
await db
16+
.update(integrationZaloModel)
17+
.set({ auth })
18+
.where(eq(integrationZaloModel.id, id))
19+
}
20+
}
21+
22+
export const zaloIntegrationService = new ZaloIntegrationService()

packages/worker-config/src/queues/schedule/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const ScheduleJobData = {
1919
maintainMacPartitions: "maintainMacPartitions",
2020
scanCoexistRuns: "scanCoexistRuns",
2121
purgeCoexistStaging: "purgeCoexistStaging",
22+
refreshZaloTokens: "refreshZaloTokens",
2223
} as const
2324

2425
export type ScheduleJobBroadcast = {
@@ -87,6 +88,11 @@ export type ScheduleJobPurgeCoexistStaging = {
8788
data: Record<string, never>
8889
}
8990

91+
export type ScheduleJobRefreshZaloTokens = {
92+
type: typeof ScheduleJobData.refreshZaloTokens
93+
data: Record<string, never>
94+
}
95+
9096
export type ScheduleJobData =
9197
| ScheduleJobBroadcast
9298
| ScheduleJobEnqueueBroadcast
@@ -100,6 +106,7 @@ export type ScheduleJobData =
100106
| ScheduleJobMaintainMacPartitions
101107
| ScheduleJobScanCoexistRuns
102108
| ScheduleJobPurgeCoexistStaging
109+
| ScheduleJobRefreshZaloTokens
103110

104111
export const scheduleQueue =
105112
process.env.NEXT_PHASE === "phase-production-build"

0 commit comments

Comments
 (0)