Skip to content

Commit 06e2fb0

Browse files
authored
Merge pull request #16 from DaleStudy/perf/10-reduce-subrequests
perf: AI ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ณ„๋„ Worker ํ˜ธ์ถœ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ subrequest ์˜ˆ์‚ฐ ๋…๋ฆฝํ™”
2 parents 4913987 + b49e8b1 commit 06e2fb0

File tree

11 files changed

+815
-45
lines changed

11 files changed

+815
-45
lines changed

โ€Ž.github/workflows/integration.yamlโ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@v6
1616
- uses: oven-sh/setup-bun@v2
17-
- run: bun test
17+
- run: bun test handlers/
18+
- run: bun test tests/

โ€ŽAGENTS.mdโ€Ž

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,27 @@ GitHub Organization webhook ์ˆ˜์‹ ์šฉ ์—”๋“œํฌ์ธํŠธ
244244
4. Week ์—†์Œ โ†’ ๊ฒฝ๊ณ  ๋Œ“๊ธ€ ์ž‘์„ฑ (์ค‘๋ณต ๋ฐฉ์ง€: Bot์ด ์ž‘์„ฑํ•œ ๊ฒฝ๊ณ  ๋Œ“๊ธ€์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด ์Šคํ‚ต)
245245
5. Week ์žˆ์Œ โ†’ ๊ธฐ์กด ๊ฒฝ๊ณ  ๋Œ“๊ธ€ ์‚ญ์ œ (Bot์ด ์ž‘์„ฑํ•œ Week ๊ฒฝ๊ณ  ๋Œ“๊ธ€๋งŒ)
246246

247+
### 4. AI ํ•ธ๋“ค๋Ÿฌ Worker ๋ถ„๋ฆฌ ์•„ํ‚คํ…์ฒ˜
248+
249+
PR ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์œผ๋ฉด webhook ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‘ AI ํ•ธ๋“ค๋Ÿฌ(`tagPatterns`, `postLearningStatus`)๋ฅผ **๋ณ„๋„ Worker invocation**์œผ๋กœ ๋ถ„๋ฆฌ ๋””์ŠคํŒจ์น˜ํ•œ๋‹ค. ๊ฐ invocation์€ ๋…๋ฆฝ์ ์ธ Cloudflare subrequest ์˜ˆ์‚ฐ(50)์„ ๊ฐ€์ง€๋ฏ€๋กœ ํŒŒ์ผ์ด ๋งŽ์€ PR์—์„œ๋„ ์˜ˆ์‚ฐ ์ดˆ๊ณผ๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค.
250+
251+
```
252+
GitHub webhook
253+
โ”‚
254+
โ–ผ
255+
[Invocation #1] webhook ํ•ธ๋“ค๋Ÿฌ โ† 50 subrequest ์˜ˆ์‚ฐ
256+
โ”‚ ctx.waitUntil(fetch("/internal/tag-patterns")) โ”€โ” self-fetch๋Š”
257+
โ”‚ ctx.waitUntil(fetch("/internal/learning-status")) โ”€โ”ค ์™ธ๋ถ€ ์š”์ฒญ์ด๋ผ
258+
โ”‚ โ”‚ ์ƒˆ invocation ํŠธ๋ฆฌ๊ฑฐ
259+
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ [Invocation #2] tagPatterns โ—€โ”€โ”˜ โ† ๋…๋ฆฝ 50 ์˜ˆ์‚ฐ
260+
โ”‚
261+
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ [Invocation #3] postLearningStatus โ† ๋…๋ฆฝ 50 ์˜ˆ์‚ฐ
262+
```
263+
264+
- `INTERNAL_SECRET`๊ณผ `WORKER_URL`์ด ๋ชจ๋‘ ์„ค์ •๋˜์–ด์•ผ ํ™œ์„ฑํ™”๋œ๋‹ค. ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์—†์œผ๋ฉด ๊ธฐ์กด์ฒ˜๋Ÿผ ๊ฐ™์€ invocation์—์„œ ์ˆœ์ฐจ ์‹คํ–‰(subrequest ์˜ˆ์‚ฐ ๊ณต์œ )๋˜์–ด ํŒŒ์ผ์ด ๋งŽ์€ PR์—์„œ ์˜ˆ์‚ฐ์„ ์ดˆ๊ณผํ•  ์ˆ˜ ์žˆ๋‹ค.
265+
- ๋‚ด๋ถ€ ์—”๋“œํฌ์ธํŠธ๋Š” `/internal/tag-patterns`, `/internal/learning-status`์ด๋ฉฐ `X-Internal-Secret` ํ—ค๋”๋กœ ์ธ์ฆํ•œ๋‹ค.
266+
- ์ฐธ๊ณ : `tests/subrequest-budget.test.js`๊ฐ€ 5๊ฐœ ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๊ฐ ํ•ธ๋“ค๋Ÿฌ์˜ fetch ํ˜ธ์ถœ ์ˆ˜(๊ฐ๊ฐ 22, 15ํšŒ)๋ฅผ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋กœ ๋ฐ•์•„๋‘”๋‹ค.
267+
247268
## ๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ
248269

249270
### DaleStudy Organization ์ „์šฉ
@@ -328,8 +349,16 @@ wrangler secret put OPENAI_API_KEY
328349

329350
# Webhook Secret (์„ ํƒ์‚ฌํ•ญ)
330351
wrangler secret put WEBHOOK_SECRET
352+
353+
# Internal Dispatch Secret (AI ํ•ธ๋“ค๋Ÿฌ Worker ๋ถ„๋ฆฌ์šฉ, ๊ถŒ์žฅ)
354+
# ์„ค์ •ํ•˜๋ฉด tagPatterns, learningStatus๊ฐ€ ๋ณ„๋„ Worker ํ˜ธ์ถœ๋กœ
355+
# ๋””์ŠคํŒจ์น˜๋˜์–ด ๊ฐ๊ฐ ๋…๋ฆฝ์ ์ธ subrequest ์˜ˆ์‚ฐ์„ ๊ฐ€์ง.
356+
# WORKER_URL๊ณผ ํ•จ๊ป˜ ์„ค์ •๋˜์–ด์•ผ ํ™œ์„ฑํ™”๋œ๋‹ค.
357+
wrangler secret put INTERNAL_SECRET
331358
```
332359

360+
`WORKER_URL`์€ `wrangler.jsonc`์˜ `vars`์— ์ •์˜๋˜์–ด ์žˆ์–ด ๊ธฐ๋ณธ ๋ฐฐํฌ์—๋Š” ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š” ์—†๋‹ค. ์Šคํ…Œ์ด์ง•/๋‹ค๋ฅธ ๊ณ„์ • ๋“ฑ์œผ๋กœ ๋ฐฐํฌํ•  ๋•Œ๋งŒ ๋ฎ์–ด์“ฐ๋ฉด ๋œ๋‹ค.
361+
333362
### 5. GitHub App ์„ค์น˜
334363

335364
์ €์žฅ์†Œ์— App์ด ์„ค์น˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ:
@@ -359,15 +388,79 @@ curl -X POST https://github.dalestudy.com/check-weeks \
359388
- โŒ npm ํŒจํ‚ค์ง€ ๋Œ€๋ถ€๋ถ„ ํ˜ธํ™˜ ์•ˆ ๋จ (@octokit/app ๋“ฑ)
360389
- โœ… ์ˆœ์ˆ˜ JavaScript + Web APIs๋กœ ๊ตฌํ˜„
361390

391+
## ํ…Œ์ŠคํŠธ
392+
393+
์ด ํ”„๋กœ์ ํŠธ๋Š” [Bun](https://bun.sh)์˜ ๋‚ด์žฅ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„์˜ `package.json`์ด๋‚˜ ์˜์กด์„ฑ ์„ค์น˜ ์—†์ด ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
394+
395+
### ํ…Œ์ŠคํŠธ ์‹คํ–‰
396+
397+
ํ…Œ์ŠคํŠธ๋Š” `handlers/`(ํ•ธ๋“ค๋Ÿฌ๋ณ„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ)์™€ `tests/`(ํ”„๋กœ์„ธ์Šค ๊ฒฉ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ)๋กœ ๋‚˜๋‰˜์–ด ์žˆ๋‹ค. Bun์˜ `vi.mock()`์€ ํ”„๋กœ์„ธ์Šค ์ „์—ญ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก๋˜์–ด ๊ฐ™์€ ์‹คํ–‰ ๋‚ด์—์„œ ๋‹ค๋ฅธ ํŒŒ์ผ๋กœ ๋ˆ„์ถœ๋˜๋ฏ€๋กœ, ๊ฐ™์€ ๋ชจ๋“ˆ์„ ๋ชจํ‚นํ•˜๋Š” ํ…Œ์ŠคํŠธ์™€ ์‹ค์ œ ๊ตฌํ˜„์„ ํ˜ธ์ถœํ•˜๋Š” ํ…Œ์ŠคํŠธ๋Š” **๋ณ„๋„ `bun test` ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰**ํ•ด์•ผ ํ•œ๋‹ค.
398+
399+
```bash
400+
# ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰ (๋‘ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค๋กœ)
401+
bun test handlers/ && bun test tests/
402+
403+
# ํŠน์ • ํŒŒ์ผ๋งŒ ์‹คํ–‰
404+
bun test handlers/webhooks.test.js
405+
406+
# ๊ฐ์‹œ ๋ชจ๋“œ (ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์žฌ์‹คํ–‰)
407+
bun test handlers/ --watch
408+
```
409+
410+
Bun ์„ค์น˜: https://bun.sh/docs/installation
411+
412+
### ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ž‘์„ฑ ๊ทœ์น™
413+
414+
- ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ๋Œ€์ƒ ํŒŒ์ผ๊ณผ ๊ฐ™์€ ๋””๋ ‰ํ† ๋ฆฌ์— `*.test.js` ์ด๋ฆ„์œผ๋กœ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
415+
- ์˜ˆ: `handlers/webhooks.js` โ†’ `handlers/webhooks.test.js`
416+
- `bun:test`์—์„œ ์ œ๊ณตํ•˜๋Š” API(`describe`, `it`, `expect`, `vi`)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
417+
- ์™ธ๋ถ€ ์˜์กด์„ฑ(`utils/github.js` ๋“ฑ)์€ `vi.mock()`์œผ๋กœ ๋Œ€์ฒดํ•˜๊ณ , `fetch`๋Š” `globalThis.fetch = vi.fn()...`๋กœ ์Šคํ…ํ•ฉ๋‹ˆ๋‹ค.
418+
419+
```javascript
420+
import { describe, it, expect, vi, beforeEach } from "bun:test";
421+
422+
vi.mock("../utils/github.js", () => ({
423+
generateGitHubAppToken: vi.fn().mockResolvedValue("fake-token"),
424+
}));
425+
426+
import { checkWeeks } from "./check-weeks.js";
427+
428+
describe("checkWeeks", () => {
429+
beforeEach(() => {
430+
vi.clearAllMocks();
431+
globalThis.fetch = vi.fn().mockResolvedValue({
432+
ok: true,
433+
json: () => Promise.resolve([]),
434+
});
435+
});
436+
437+
it("returns 403 for non-DaleStudy organization", async () => {
438+
const request = new Request("https://example.com/check-weeks", {
439+
method: "POST",
440+
headers: { "Content-Type": "application/json" },
441+
body: JSON.stringify({ repo_owner: "OtherOrg", repo_name: "leetcode-study" }),
442+
});
443+
444+
const response = await checkWeeks(request, {});
445+
expect(response.status).toBe(403);
446+
});
447+
});
448+
```
449+
450+
### CI ์ž๋™ ์‹คํ–‰
451+
452+
`.github/workflows/integration.yaml`์ด ๋ชจ๋“  Pull Request์™€ `main` ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ์—์„œ `bun test handlers/`์™€ `bun test tests/`๋ฅผ ๊ฐ๊ฐ ๋ณ„๋„ ์Šคํ…์œผ๋กœ ์ž๋™ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋ฉด PR ์ฒดํฌ๊ฐ€ ์‹คํŒจํ•˜๋ฏ€๋กœ, ๋จธ์ง€ ์ „์— ๋ฐ˜๋“œ์‹œ ํ†ต๊ณผ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
453+
362454
## ์ƒˆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๊ฐ€์ด๋“œ
363455

364456
์ƒˆ๋กœ์šด ์ž๋™ํ™” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”:
365457

366458
1. **์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€**: `index.js`์˜ `fetch()` ํ•จ์ˆ˜์— ์ƒˆ๋กœ์šด pathname ๋ผ์šฐํŒ… ์ถ”๊ฐ€
367459
2. **ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ์ž‘์„ฑ**: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ„๋„ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌ (์˜ˆ: `handleCheckAllPrs`)
368460
3. **GitHub App ๊ถŒํ•œ ํ™•์ธ**: ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์—†์œผ๋ฉด ์ถ”๊ฐ€
369-
4. **๋ฌธ์„œ ์—…๋ฐ์ดํŠธ**: AGENTS.md, README.md์— ์ƒˆ ๊ธฐ๋Šฅ ๋ฌธ์„œํ™”
370-
5. **ํ…Œ์ŠคํŠธ**: ๋กœ์ปฌ(`wrangler dev`)์—์„œ ๋จผ์ € ํ…Œ์ŠคํŠธ ํ›„ ๋ฐฐํฌ
461+
4. **ํ…Œ์ŠคํŠธ ์ž‘์„ฑ**: ํ•ธ๋“ค๋Ÿฌ ์˜†์— `*.test.js`๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  `bun test`๋กœ ํ†ต๊ณผ ํ™•์ธ (์œ„ "ํ…Œ์ŠคํŠธ" ์„น์…˜ ์ฐธ๊ณ )
462+
5. **๋ฌธ์„œ ์—…๋ฐ์ดํŠธ**: AGENTS.md, README.md์— ์ƒˆ ๊ธฐ๋Šฅ ๋ฌธ์„œํ™”
463+
6. **๋กœ์ปฌ ์‹คํ–‰ ํ…Œ์ŠคํŠธ**: `wrangler dev`๋กœ ์‹ค์ œ ์—”๋“œํฌ์ธํŠธ ๋™์ž‘ ํ™•์ธ ํ›„ ๋ฐฐํฌ
371464

372465
## ์ฝ”๋“œ ์ˆ˜์ • ์‹œ ์ฃผ์˜์‚ฌํ•ญ
373466

โ€ŽREADME.mdโ€Ž

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,40 @@ https://github.dalestudy.com
168168
# ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹œ์ž‘
169169
wrangler dev
170170

171-
# ๋กœ์ปฌ ํ…Œ์ŠคํŠธ (๋ณ„๋„ ํ„ฐ๋ฏธ๋„)
171+
# ๋กœ์ปฌ ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ (๋ณ„๋„ ํ„ฐ๋ฏธ๋„)
172172
curl -X POST http://localhost:8787/check-weeks \
173173
-H "Content-Type: application/json" \
174174
-d '{"repo_owner": "DaleStudy", "repo_name": "leetcode-study"}'
175175
```
176176

177-
### ํ”„๋กœ๋•์…˜ ํ…Œ์ŠคํŠธ
177+
### ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰
178+
179+
์ด ํ”„๋กœ์ ํŠธ๋Š” **[Bun](https://bun.sh)์˜ ๋‚ด์žฅ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ**๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. `package.json`์ด๋‚˜ `node_modules`๊ฐ€ ์—†๋Š” ์ด์œ ๋Š” Bun์ด ๋Ÿฐํƒ€์ž„ยทํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆยท๋ชจํ‚น API(`vi.mock`, `vi.fn`)๋ฅผ ๋ชจ๋‘ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์–ด์„œ ๋ณ„๋„ ์„ค์น˜ ์—†์ด ๋ฐ”๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
180+
181+
```bash
182+
# Bun ์„ค์น˜ (์ตœ์ดˆ 1ํšŒ) โ€” https://bun.sh/docs/installation
183+
curl -fsSL https://bun.sh/install | bash
184+
185+
# ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰ (๋‘ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค๋กœ)
186+
bun test handlers/ && bun test tests/
187+
188+
# ํŠน์ • ํŒŒ์ผ๋งŒ ์‹คํ–‰
189+
bun test handlers/webhooks.test.js
190+
191+
# ๊ฐ์‹œ ๋ชจ๋“œ (ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์žฌ์‹คํ–‰)
192+
bun test handlers/ --watch
193+
```
194+
195+
ํ…Œ์ŠคํŠธ๋Š” ๋‘ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋‚˜๋‰˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค:
196+
197+
- `handlers/*.test.js`: ๋Œ€์ƒ ํŒŒ์ผ ์˜†์— ๋‘๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
198+
- `tests/*.test.js`: Bun `vi.mock()`์˜ ์ „์—ญ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋ˆ„์ถœ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ (์˜ˆ: `subrequest-budget.test.js`)
199+
200+
์ž์„ธํ•œ ์ž‘์„ฑ ๊ทœ์น™๊ณผ ์˜ˆ์ œ๋Š” `AGENTS.md`์˜ "ํ…Œ์ŠคํŠธ" ์„น์…˜์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.
201+
202+
๋ชจ๋“  Pull Request์™€ `main` ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ์—์„œ `.github/workflows/integration.yaml`์ด ๋‘ ๋””๋ ‰ํ† ๋ฆฌ์˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
203+
204+
### ํ”„๋กœ๋•์…˜ ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
178205

179206
```bash
180207
curl -X POST https://github.dalestudy.com/check-weeks \

โ€Žhandlers/check-weeks.test.jsโ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function makeRequest(body) {
2424

2525
const env = {};
2626

27-
describe("check-weeks repo filtering", () => {
27+
describe("check-weeks ์ €์žฅ์†Œ ํ•„ํ„ฐ๋ง", () => {
2828
beforeEach(() => {
2929
vi.clearAllMocks();
3030
globalThis.fetch = vi.fn().mockResolvedValue({
@@ -33,7 +33,7 @@ describe("check-weeks repo filtering", () => {
3333
});
3434
});
3535

36-
it("returns 403 for non-DaleStudy organization", async () => {
36+
it("DaleStudy ๊ฐ€ ์•„๋‹Œ organization ์€ 403 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค", async () => {
3737
const request = makeRequest({
3838
repo_owner: "OtherOrg",
3939
repo_name: "leetcode-study",
@@ -46,7 +46,7 @@ describe("check-weeks repo filtering", () => {
4646
expect(body.error).toContain("Unauthorized organization");
4747
});
4848

49-
it("returns 403 for non-leetcode-study repo_name", async () => {
49+
it("leetcode-study ๊ฐ€ ์•„๋‹Œ repo_name ์€ 403 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค", async () => {
5050
const request = makeRequest({
5151
repo_owner: "DaleStudy",
5252
repo_name: "daleui",
@@ -61,7 +61,7 @@ describe("check-weeks repo filtering", () => {
6161
expect(generateGitHubAppToken).not.toHaveBeenCalled();
6262
});
6363

64-
it("processes leetcode-study repo_name successfully", async () => {
64+
it("leetcode-study repo_name ์€ ์ •์ƒ ์ฒ˜๋ฆฌํ•œ๋‹ค", async () => {
6565
const request = makeRequest({
6666
repo_owner: "DaleStudy",
6767
repo_name: "leetcode-study",
@@ -74,7 +74,7 @@ describe("check-weeks repo filtering", () => {
7474
expect(body.success).toBe(true);
7575
});
7676

77-
it("returns 400 when repo_name is missing", async () => {
77+
it("repo_name ์ด ์—†์œผ๋ฉด 400 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค", async () => {
7878
const request = makeRequest({
7979
repo_owner: "DaleStudy",
8080
});
@@ -86,7 +86,7 @@ describe("check-weeks repo filtering", () => {
8686
expect(body.error).toContain("repo_name");
8787
});
8888

89-
it("defaults repo_owner to DaleStudy when omitted", async () => {
89+
it("repo_owner ์ƒ๋žต ์‹œ DaleStudy ๋กœ ๊ธฐ๋ณธ ์„ค์ •๋œ๋‹ค", async () => {
9090
const request = makeRequest({
9191
repo_name: "leetcode-study",
9292
});
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* ๋‚ด๋ถ€ ๋””์ŠคํŒจ์น˜ ํ•ธ๋“ค๋Ÿฌ
3+
*
4+
* self-fetch๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋˜๋Š” ๋‚ด๋ถ€ ์—”๋“œํฌ์ธํŠธ.
5+
* ๊ฐ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ณ„๋„ Worker ํ˜ธ์ถœ(invocation)์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ
6+
* ๋…๋ฆฝ์ ์ธ subrequest ์˜ˆ์‚ฐ(50)์„ ๊ฐ–๋Š”๋‹ค.
7+
*/
8+
9+
import { generateGitHubAppToken } from "../utils/github.js";
10+
import { errorResponse, corsResponse } from "../utils/cors.js";
11+
import { tagPatterns } from "./tag-patterns.js";
12+
import { postLearningStatus } from "./learning-status.js";
13+
14+
const INTERNAL_HEADER = "X-Internal-Secret";
15+
16+
/**
17+
* ๋‚ด๋ถ€ ์š”์ฒญ ์ธ์ฆ ๊ฒ€์ฆ
18+
*/
19+
function verifyInternalRequest(request, env) {
20+
if (!env.INTERNAL_SECRET) {
21+
console.error("[internal-dispatch] INTERNAL_SECRET not configured");
22+
return false;
23+
}
24+
return request.headers.get(INTERNAL_HEADER) === env.INTERNAL_SECRET;
25+
}
26+
27+
/**
28+
* ๋‚ด๋ถ€ ๋””์ŠคํŒจ์น˜ ์—”๋“œํฌ์ธํŠธ ๋ผ์šฐํ„ฐ
29+
*
30+
* @param {Request} request
31+
* @param {object} env
32+
* @param {string} pathname
33+
*/
34+
export async function handleInternalDispatch(request, env, pathname) {
35+
if (!verifyInternalRequest(request, env)) {
36+
return errorResponse("Unauthorized", 401);
37+
}
38+
39+
const payload = await request.json();
40+
41+
try {
42+
const appToken = await generateGitHubAppToken(env);
43+
44+
switch (pathname) {
45+
case "/internal/tag-patterns":
46+
return await handleTagPatterns(payload, appToken, env);
47+
48+
case "/internal/learning-status":
49+
return await handleLearningStatus(payload, appToken, env);
50+
51+
default:
52+
return errorResponse("Not found", 404);
53+
}
54+
} catch (error) {
55+
console.error(`[internal-dispatch] ${pathname} failed:`, error);
56+
return errorResponse(`Internal handler error: ${error.message}`, 500);
57+
}
58+
}
59+
60+
async function handleTagPatterns(payload, appToken, env) {
61+
const { repoOwner, repoName, prNumber, headSha, prData } = payload;
62+
const result = await tagPatterns(
63+
repoOwner,
64+
repoName,
65+
prNumber,
66+
headSha,
67+
prData,
68+
appToken,
69+
env.OPENAI_API_KEY
70+
);
71+
return corsResponse({ handler: "tag-patterns", result });
72+
}
73+
74+
async function handleLearningStatus(payload, appToken, env) {
75+
const { repoOwner, repoName, prNumber, username } = payload;
76+
const result = await postLearningStatus(
77+
repoOwner,
78+
repoName,
79+
prNumber,
80+
username,
81+
appToken,
82+
env.OPENAI_API_KEY
83+
);
84+
return corsResponse({ handler: "learning-status", result });
85+
}

0 commit comments

Comments
ย (0)