@laitszkin/apollo-toolkit 3.10.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  3. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  4. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  5. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  6. package/generate-spec/SKILL.md +17 -15
  7. package/generate-spec/agents/openai.yaml +1 -1
  8. package/generate-spec/references/TEMPLATE_SPEC.md +103 -84
  9. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  10. package/init-project-html/SKILL.md +82 -126
  11. package/init-project-html/agents/openai.yaml +17 -8
  12. package/init-project-html/lib/atlas/assets/architecture.css +140 -0
  13. package/init-project-html/lib/atlas/assets/viewer.client.js +93 -0
  14. package/init-project-html/lib/atlas/cli.js +995 -0
  15. package/init-project-html/lib/atlas/layout.js +229 -0
  16. package/init-project-html/lib/atlas/render.js +485 -0
  17. package/init-project-html/lib/atlas/schema.js +310 -0
  18. package/init-project-html/lib/atlas/state.js +402 -0
  19. package/init-project-html/references/TEMPLATE_SPEC.md +123 -84
  20. package/init-project-html/sample-demo/resources/project-architecture/assets/architecture.css +139 -1058
  21. package/init-project-html/sample-demo/resources/project-architecture/assets/viewer.client.js +93 -0
  22. package/init-project-html/sample-demo/resources/project-architecture/atlas/atlas.index.yaml +34 -0
  23. package/init-project-html/sample-demo/resources/project-architecture/atlas/features/get-invite-codes.yaml +159 -0
  24. package/init-project-html/sample-demo/resources/project-architecture/atlas/features/invite-code-registration.yaml +160 -0
  25. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/index.html +67 -52
  26. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-code-generator.html +48 -163
  27. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-issuance-service.html +70 -196
  28. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/postgresql.html +64 -163
  29. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/public-api.html +68 -150
  30. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/web-get-invite-ui.html +65 -138
  31. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/index.html +61 -51
  32. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/postgresql.html +66 -159
  33. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/public-api.html +63 -143
  34. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/registration-service.html +77 -188
  35. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/web-register-ui.html +65 -138
  36. package/init-project-html/sample-demo/resources/project-architecture/index.html +232 -335
  37. package/init-project-html/scripts/architecture.js +65 -247
  38. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  39. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  40. package/package.json +6 -2
  41. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  42. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  43. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  44. package/spec-to-project-html/SKILL.md +61 -63
  45. package/spec-to-project-html/agents/openai.yaml +14 -8
  46. package/spec-to-project-html/references/TEMPLATE_SPEC.md +96 -83
  47. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
@@ -1,165 +1,66 @@
1
1
  <!DOCTYPE html>
2
- <html lang="zh-Hant">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>子模組 · postgresql — 獲取邀請碼(iss 視角)</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;600&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap"
11
- rel="stylesheet"
12
- />
13
- <link rel="stylesheet" href="../../assets/architecture.css" />
14
- </head>
15
- <body>
16
- <main class="atlas-page atlas-page--feature">
17
- <nav class="breadcrumb" aria-label="階層導航">
18
- <a href="../../index.html">宏觀架構</a> · <a href="./index.html">獲取邀請碼</a> · <span>postgresql</span>
19
- </nav>
20
-
21
- <header class="atlas-header">
22
- <p class="atlas-kicker">Submodule · postgresql(iss 視角)</p>
23
- <h1 class="atlas-title">postgresql — 發放側操作</h1>
24
- <p class="atlas-meta">
25
- 自身職責:對 <code>invite_codes</code> 表執行<strong>計數</strong>與<strong>插入</strong>。寫入的列會在「邀請碼註冊」功能裡被另一個視角讀取(跨功能資料流見<a href="../../index.html">宏觀架構圖</a>)。本頁只列自身 SQL 函式的 I/O。
26
- </p>
27
- </header>
28
-
29
- <section class="sub-io">
30
- <h2>SQL 函式 I/O</h2>
31
- <table>
32
- <thead><tr><th>函式</th><th>簽名/SQL</th><th>副作用</th><th>用途</th></tr></thead>
33
- <tbody>
34
- <tr>
35
- <td><code>CountInvitesSince</code></td>
36
- <td class="sub-io__signature">
37
- <strong>in:</strong> <code>tx</code>, <code>userID</code>, <code>since</code><br>
38
- <strong>out:</strong> <code>int64</code><br>
39
- SQL: <code>SELECT count(*) FROM invite_codes WHERE owner_user_id=$1 AND created_at &gt; $2</code>
40
- </td>
41
- <td><span class="sub-io__side sub-io__side--io">read</span></td>
42
- <td>滑動視窗計數,給呼叫端做額度判斷。</td>
43
- </tr>
44
- <tr>
45
- <td><code>InsertInviteCode</code></td>
46
- <td class="sub-io__signature">
47
- <strong>in:</strong> <code>tx</code>, <code>InviteCodeRecord</code><br>
48
- <strong>out:</strong> <code>InviteID</code> | <code>UniqueViolation</code><br>
49
- SQL: <code>INSERT INTO invite_codes (code, owner_user_id, allowed_registration, ...) VALUES (...) RETURNING id</code>
50
- </td>
51
- <td><span class="sub-io__side sub-io__side--write">write</span> 新列(待 commit)</td>
52
- <td><code>invite_codes.code UNIQUE</code>;衝突時呼叫端負責換碼重試。</td>
53
- </tr>
54
- <tr>
55
- <td><code>GetUserStatus</code></td>
56
- <td class="sub-io__signature">
57
- <strong>in:</strong> <code>tx</code>, <code>userID</code><br>
58
- <strong>out:</strong> <code>UserStatus</code> | <code>NotFound</code><br>
59
- SQL: <code>SELECT status, banned_at FROM users WHERE id=$1</code>
60
- </td>
61
- <td><span class="sub-io__side sub-io__side--io">read</span></td>
62
- <td>給呼叫端做帳號狀態檢查。</td>
63
- </tr>
64
- </tbody>
65
- </table>
66
- </section>
67
-
68
- <section class="sub-vars">
69
- <h2>變數與業務用途</h2>
70
- <p class="sub-vars__intro">本表分兩段:上半為 SQL 函式參數/回傳,下半為 <code>invite_codes</code> / <code>users</code> 表上的關鍵列欄位(被本子模組寫入或讀取)。</p>
71
- <table>
72
- <thead><tr><th>變數</th><th>型別</th><th>作用域</th><th>業務用途</th></tr></thead>
73
- <tbody>
74
- <tr>
75
- <td class="sub-vars__name">tx</td>
76
- <td class="sub-vars__type">*sql.Tx</td>
77
- <td><span class="sub-vars__scope sub-vars__scope--tx">tx</span></td>
78
- <td>上層管理的隔離單元;本子模組所有 SQL 都掛在這個 <code>tx</code> 上以保證原子寫入。</td>
79
- </tr>
80
- <tr>
81
- <td class="sub-vars__name">userID</td>
82
- <td class="sub-vars__type">UUID</td>
83
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
84
- <td>SQL 參數;標記新邀請碼擁有者,也是計數視窗鍵;錯誤帶入會把額度算到別人頭上。</td>
85
- </tr>
86
- <tr>
87
- <td class="sub-vars__name">since</td>
88
- <td class="sub-vars__type">timestamp</td>
89
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
90
- <td>滑動視窗起點;決定 <code>count(*)</code> 的時段;起點漂移會讓額度執行不準。</td>
91
- </tr>
92
- <tr>
93
- <td class="sub-vars__name">InviteCodeRecord</td>
94
- <td class="sub-vars__type">{code, owner_user_id, allowed_registration, ...}</td>
95
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
96
- <td>待插入的邀請碼資料;其欄位直接落在 <code>invite_codes</code> 列上。</td>
97
- </tr>
98
- <tr>
99
- <td class="sub-vars__name">UserStatus</td>
100
- <td class="sub-vars__type">{status, banned_at}</td>
101
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
102
- <td><code>GetUserStatus</code> 的回傳;給呼叫端判定是否准予發放。</td>
103
- </tr>
104
- <tr>
105
- <td class="sub-vars__name">invite_codes.code</td>
106
- <td class="sub-vars__type">text UNIQUE</td>
107
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
108
- <td>列上欄位;UNIQUE 約束在本子模組是「衝突信號源」,呼叫端據此換碼重試。</td>
109
- </tr>
110
- <tr>
111
- <td class="sub-vars__name">invite_codes.owner_user_id</td>
112
- <td class="sub-vars__type">FK → users.id</td>
113
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
114
- <td>列上欄位;建立「誰擁有這張邀請」的關聯;額度查詢與後續審計都依此欄。</td>
115
- </tr>
116
- <tr>
117
- <td class="sub-vars__name">invite_codes.allowed_registration</td>
118
- <td class="sub-vars__type">boolean</td>
119
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
120
- <td>列上欄位;發放時寫 <code>true</code>;「邀請碼註冊」功能在判定時讀此欄,<code>false</code> 即拒絕該邀請可用。</td>
121
- </tr>
122
- <tr>
123
- <td class="sub-vars__name">invite_codes.consumed_at</td>
124
- <td class="sub-vars__type">timestamp NULL</td>
125
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
126
- <td>列上欄位;本子模組寫入時為 <code>NULL</code>;之後由「邀請碼註冊」標記消費,是橋接兩個功能模組的關鍵欄位。</td>
127
- </tr>
128
- <tr>
129
- <td class="sub-vars__name">invite_codes.created_at</td>
130
- <td class="sub-vars__type">timestamp</td>
131
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
132
- <td>列上欄位;額度的滑動視窗計數憑此欄位,業務上要與 <code>now</code> 同步以免漂移。</td>
133
- </tr>
134
- </tbody>
135
- </table>
136
- </section>
137
-
138
- <section class="sub-dataflow">
139
- <h2>內部資料流(單一交易視角)</h2>
140
- <ol>
141
- <li>呼叫端開 <code>tx</code> → 本子模組以該 <code>tx</code> 執行 SQL;交易控制不在本子模組內。</li>
142
- <li><code>CountInvitesSince</code> 與 <code>GetUserStatus</code> 為純讀;不變動列。</li>
143
- <li><code>InsertInviteCode</code>:寫入後回傳 <code>InviteID</code> 或 <code>UniqueViolation</code>;<strong>無</strong> partial row。</li>
144
- <li>所有寫入皆在 <code>tx</code> 內;本子模組對外只報告每個 SQL 結果。</li>
145
- </ol>
146
- </section>
147
-
148
- <section class="sub-errors">
149
- <h2>本子模組會回傳的錯誤</h2>
150
- <table>
151
- <thead><tr><th>錯誤</th><th>觸發 SQL</th></tr></thead>
152
- <tbody>
153
- <tr><td><code>UniqueViolation</code></td><td><code>InsertInviteCode</code>(<code>code</code> 已存在)</td></tr>
154
- <tr><td><code>NotFound</code></td><td><code>GetUserStatus</code></td></tr>
155
- <tr><td><code>ConnTransient</code></td><td>任意</td></tr>
156
- </tbody>
157
- </table>
158
- </section>
159
-
160
- <footer class="atlas-meta" style="margin-top: 2rem">
161
- <p><a href="../../index.html">← 宏觀架構</a> · <a href="./index.html">← 功能總覽</a></p>
162
- </footer>
163
- </main>
164
- </body>
2
+ <html lang="en" data-atlas-page="submodule">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Get invite codes · postgresql</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="../../assets/architecture.css">
8
+ </head>
9
+ <body>
10
+ <header class="submodule-header">
11
+ <nav class="submodule-breadcrumb"><a href="../../index.html">← Atlas</a> · <a href="index.html">← Get invite codes</a></nav>
12
+ <h1>postgresql <small class="submodule-kind submodule-kind--db">DB</small></h1>
13
+ <p class="submodule-role">Owns the `invite_codes` table (producer side of the cross-feature data row).</p>
14
+ </header>
15
+ <main class="submodule-main">
16
+ <section class="sub-io" aria-label="Function I/O">
17
+ <h2>Function I/O</h2>
18
+ <table class="sub-table">
19
+ <thead><tr><th scope="col">Name</th><th scope="col">In</th><th scope="col">Out</th><th scope="col">Side</th><th scope="col">Purpose</th></tr></thead>
20
+ <tbody>
21
+ <tr><td>INSERT_invite_codes</td><td>row</td><td>rowid</td><td>write</td><td>Persists the issued invite row.</td></tr>
22
+ </tbody>
23
+ </table>
24
+ </section>
25
+ <section class="sub-vars" aria-label="Variables">
26
+ <h2>Variables</h2>
27
+ <table class="sub-table">
28
+ <thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Scope</th><th scope="col">Purpose</th></tr></thead>
29
+ <tbody>
30
+ <tr><td>invite_codes.code</td><td>text</td><td>persist</td><td>Unique business key consumed by the registration flow.</td></tr>
31
+ <tr><td>invite_codes.consumed_at</td><td>timestamptz</td><td>persist</td><td>Stays null until the registration flow consumes the code.</td></tr>
32
+ </tbody>
33
+ </table>
34
+ </section>
35
+ <section class="sub-dataflow" aria-label="Internal data flow">
36
+ <h2>Internal data flow</h2>
37
+ <svg class="sub-dataflow__svg" viewBox="0 0 400 264" role="img" aria-label="Internal dataflow">
38
+ <defs><marker id="sub-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker></defs>
39
+ <g class="sub-dataflow__step">
40
+ <rect x="20" y="20" width="360" height="56" rx="8" ry="8" />
41
+ <text x="200" y="44" text-anchor="middle">Accept INSERT from invite-issuance-service.</text>
42
+ </g>
43
+ <line class="sub-dataflow__arrow" x1="200" y1="76" x2="200" y2="104" marker-end="url(#sub-arrow)" />
44
+ <g class="sub-dataflow__step">
45
+ <rect x="20" y="104" width="360" height="56" rx="8" ry="8" />
46
+ <text x="200" y="128" text-anchor="middle">Enforce unique constraint on code.</text>
47
+ </g>
48
+ <line class="sub-dataflow__arrow" x1="200" y1="160" x2="200" y2="188" marker-end="url(#sub-arrow)" />
49
+ <g class="sub-dataflow__step">
50
+ <rect x="20" y="188" width="360" height="56" rx="8" ry="8" />
51
+ <text x="200" y="212" text-anchor="middle">Return new rowid.</text>
52
+ </g>
53
+ </svg>
54
+ </section>
55
+ <section class="sub-errors" aria-label="Errors">
56
+ <h2>Errors</h2>
57
+ <table class="sub-table">
58
+ <thead><tr><th scope="col">Name</th><th scope="col">When</th><th scope="col">Means</th></tr></thead>
59
+ <tbody>
60
+ <tr><td>ErrUniqueViolation</td><td>Two issuance attempts pick the same code.</td><td>Bubble up so issuance can retry.</td></tr>
61
+ </tbody>
62
+ </table>
63
+ </section>
64
+ </main>
65
+ </body>
165
66
  </html>
@@ -1,152 +1,70 @@
1
1
  <!DOCTYPE html>
2
- <html lang="zh-Hant">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>子模組 · public-api — 獲取邀請碼</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;600&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap"
11
- rel="stylesheet"
12
- />
13
- <link rel="stylesheet" href="../../assets/architecture.css" />
14
- </head>
15
- <body>
16
- <main class="atlas-page atlas-page--feature">
17
- <nav class="breadcrumb" aria-label="階層導航">
18
- <a href="../../index.html">宏觀架構</a> · <a href="./index.html">獲取邀請碼</a> · <span>public-api</span>
19
- </nav>
20
-
21
- <header class="atlas-header">
22
- <p class="atlas-kicker">Submodule · public-api</p>
23
- <h1 class="atlas-title">public-api(iss)</h1>
24
- <p class="atlas-meta">自身職責:HTTP 邊界:認證解析、輸入綁定、把網域結果映射為狀態碼。</p>
25
- </header>
26
-
27
- <section class="sub-io">
28
- <h2>函式 I/O</h2>
29
- <table>
30
- <thead><tr><th>函式</th><th>簽名</th><th>副作用</th><th>用途</th></tr></thead>
31
- <tbody>
32
- <tr>
33
- <td><code>AuthMiddleware</code></td>
34
- <td class="sub-io__signature">
35
- <strong>in:</strong> <code>http.Request</code><br>
36
- <strong>out:</strong> <code>Principal</code> | <code>401</code>
37
- </td>
38
- <td><span class="sub-io__side sub-io__side--io">io</span> 解 cookie / JWT</td>
39
- <td>驗證並注入 <code>Principal.UserID</code>。</td>
40
- </tr>
41
- <tr>
42
- <td><code>CreateInviteCodeHandler</code></td>
43
- <td class="sub-io__signature">
44
- <strong>in:</strong> <code>http.Request</code>, <code>Principal</code><br>
45
- <strong>out:</strong> <code>http.Response</code>
46
- </td>
47
- <td><span class="sub-io__side sub-io__side--io">io</span> 寫 body</td>
48
- <td>路由綁定的對外入口。</td>
49
- </tr>
50
- <tr>
51
- <td><code>mapDomainError</code></td>
52
- <td class="sub-io__signature">
53
- <strong>in:</strong> <code>error</code><br>
54
- <strong>out:</strong> <code>(httpStatus, ErrorBody)</code>
55
- </td>
56
- <td><span class="sub-io__side sub-io__side--pure">pure</span></td>
57
- <td>quota → 429(含 <code>Retry-After</code>)等對映。</td>
58
- </tr>
59
- <tr>
60
- <td><code>writeCreated</code></td>
61
- <td class="sub-io__signature">
62
- <strong>in:</strong> <code>NewCode</code>, <code>http.ResponseWriter</code><br>
63
- <strong>out:</strong> —
64
- </td>
65
- <td><span class="sub-io__side sub-io__side--write">io</span> 寫 201 body</td>
66
- <td>序列化新碼 + metadata。</td>
67
- </tr>
68
- </tbody>
69
- </table>
70
- </section>
71
-
72
- <section class="sub-vars">
73
- <h2>變數與業務用途</h2>
74
- <p class="sub-vars__intro">HTTP 邊界子模組持有的識別子;本子模組的責任是「正確識別調用者」與「正確翻譯網域結果為協議語意」。</p>
75
- <table>
76
- <thead><tr><th>變數</th><th>型別</th><th>作用域</th><th>業務用途</th></tr></thead>
77
- <tbody>
78
- <tr>
79
- <td class="sub-vars__name">http.Request</td>
80
- <td class="sub-vars__type">request</td>
81
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
82
- <td>邊界輸入;只在 handler 期間有效。</td>
83
- </tr>
84
- <tr>
85
- <td class="sub-vars__name">Principal.UserID</td>
86
- <td class="sub-vars__type">UUID</td>
87
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
88
- <td>由 middleware 注入;業務上代表「為誰生成邀請碼」;<strong>禁止</strong>從 body 取以避免使用者冒名為他人發碼。</td>
89
- </tr>
90
- <tr>
91
- <td class="sub-vars__name">httpStatus</td>
92
- <td class="sub-vars__type">int</td>
93
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
94
- <td><code>mapDomainError</code> 的輸出之一;<code>ErrQuotaExceeded</code> 必須對映成 <code>429</code> 才能搭配 <code>Retry-After</code> 讓 UI 知道是節流。</td>
95
- </tr>
96
- <tr>
97
- <td class="sub-vars__name">ErrorBody</td>
98
- <td class="sub-vars__type">{code, message}</td>
99
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
100
- <td>結構化錯誤體;<code>code</code> 給 UI 對映本地化文字(例如「今日額度用盡」)。</td>
101
- </tr>
102
- <tr>
103
- <td class="sub-vars__name">Retry-After</td>
104
- <td class="sub-vars__type">int seconds</td>
105
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
106
- <td>節流提示 header;UI 透過它倒數禁用按鈕,避免使用者反覆 429。</td>
107
- </tr>
108
- <tr>
109
- <td class="sub-vars__name">NewCode</td>
110
- <td class="sub-vars__type">string</td>
111
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
112
- <td>網域回傳的新碼;序列化為 201 body 給使用者複製分享。</td>
113
- </tr>
114
- <tr>
115
- <td class="sub-vars__name">http.ResponseWriter</td>
116
- <td class="sub-vars__type">writer</td>
117
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
118
- <td>對外副作用通道;保證一次回應只寫一次。</td>
119
- </tr>
120
- </tbody>
121
- </table>
122
- </section>
123
-
124
- <section class="sub-dataflow">
125
- <h2>內部資料流</h2>
126
- <ol>
127
- <li><code>Request</code> → <code>AuthMiddleware</code> → 注入 <code>Principal{UserID}</code>,失敗即 <code>401</code>。</li>
128
- <li><code>Principal.UserID</code> 作為網域層輸入;本子模組不持有 token 明文。</li>
129
- <li>從網域層取得 <code>NewCode | error</code>。</li>
130
- <li><code>NewCode</code> → <code>writeCreated</code>;<code>error</code> → <code>mapDomainError</code>。</li>
131
- </ol>
132
- </section>
133
-
134
- <section class="sub-errors">
135
- <h2>HTTP 對映</h2>
136
- <table>
137
- <thead><tr><th>條件</th><th>HTTP</th></tr></thead>
138
- <tbody>
139
- <tr><td>無 session</td><td><code>401</code></td></tr>
140
- <tr><td>網域 <code>ErrQuotaExceeded</code></td><td><code>429</code> + <code>Retry-After</code></td></tr>
141
- <tr><td>網域 <code>ErrForbidden</code></td><td><code>403</code></td></tr>
142
- <tr><td>網域 <code>ErrTransient</code></td><td><code>503</code></td></tr>
143
- </tbody>
144
- </table>
145
- </section>
146
-
147
- <footer class="atlas-meta" style="margin-top: 2rem">
148
- <p><a href="../../index.html">← 宏觀架構</a> · <a href="./index.html">← 功能總覽</a></p>
149
- </footer>
150
- </main>
151
- </body>
2
+ <html lang="en" data-atlas-page="submodule">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Get invite codes · public-api</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="../../assets/architecture.css">
8
+ </head>
9
+ <body>
10
+ <header class="submodule-header">
11
+ <nav class="submodule-breadcrumb"><a href="../../index.html">← Atlas</a> · <a href="index.html">← Get invite codes</a></nav>
12
+ <h1>public-api <small class="submodule-kind submodule-kind--api">API</small></h1>
13
+ <p class="submodule-role">HTTP boundary for `/api/invites` POST requests.</p>
14
+ </header>
15
+ <main class="submodule-main">
16
+ <section class="sub-io" aria-label="Function I/O">
17
+ <h2>Function I/O</h2>
18
+ <table class="sub-table">
19
+ <thead><tr><th scope="col">Name</th><th scope="col">In</th><th scope="col">Out</th><th scope="col">Side</th><th scope="col">Purpose</th></tr></thead>
20
+ <tbody>
21
+ <tr><td>postInvites</td><td>HttpRequest</td><td>HttpResponse</td><td>network</td><td>Validates the bearer token then delegates to invite-issuance-service.</td></tr>
22
+ </tbody>
23
+ </table>
24
+ </section>
25
+ <section class="sub-vars" aria-label="Variables">
26
+ <h2>Variables</h2>
27
+ <table class="sub-table">
28
+ <thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Scope</th><th scope="col">Purpose</th></tr></thead>
29
+ <tbody>
30
+ <tr><td>token</td><td>string</td><td>call</td><td>Bearer token used to resolve the requesting member.</td></tr>
31
+ </tbody>
32
+ </table>
33
+ </section>
34
+ <section class="sub-dataflow" aria-label="Internal data flow">
35
+ <h2>Internal data flow</h2>
36
+ <svg class="sub-dataflow__svg" viewBox="0 0 400 348" role="img" aria-label="Internal dataflow">
37
+ <defs><marker id="sub-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker></defs>
38
+ <g class="sub-dataflow__step">
39
+ <rect x="20" y="20" width="360" height="56" rx="8" ry="8" />
40
+ <text x="200" y="44" text-anchor="middle">Validate Authorization header.</text>
41
+ </g>
42
+ <line class="sub-dataflow__arrow" x1="200" y1="76" x2="200" y2="104" marker-end="url(#sub-arrow)" />
43
+ <g class="sub-dataflow__step">
44
+ <rect x="20" y="104" width="360" height="56" rx="8" ry="8" />
45
+ <text x="200" y="128" text-anchor="middle">Resolve userId from token.</text>
46
+ </g>
47
+ <line class="sub-dataflow__arrow" x1="200" y1="160" x2="200" y2="188" marker-end="url(#sub-arrow)" />
48
+ <g class="sub-dataflow__step">
49
+ <rect x="20" y="188" width="360" height="56" rx="8" ry="8" />
50
+ <text x="200" y="212" text-anchor="middle">Call invite-issuance-service.Issue.</text>
51
+ </g>
52
+ <line class="sub-dataflow__arrow" x1="200" y1="244" x2="200" y2="272" marker-end="url(#sub-arrow)" />
53
+ <g class="sub-dataflow__step">
54
+ <rect x="20" y="272" width="360" height="56" rx="8" ry="8" />
55
+ <text x="200" y="296" text-anchor="middle">Serialize response.</text>
56
+ </g>
57
+ </svg>
58
+ </section>
59
+ <section class="sub-errors" aria-label="Errors">
60
+ <h2>Errors</h2>
61
+ <table class="sub-table">
62
+ <thead><tr><th scope="col">Name</th><th scope="col">When</th><th scope="col">Means</th></tr></thead>
63
+ <tbody>
64
+ <tr><td>ErrUnauthorized</td><td>Token missing or invalid.</td><td>401 response.</td></tr>
65
+ </tbody>
66
+ </table>
67
+ </section>
68
+ </main>
69
+ </body>
152
70
  </html>