@umec/core 0.1.0-alpha.11 → 0.1.0-alpha.12

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.
@@ -0,0 +1,242 @@
1
+ ---
2
+ id: disclosure
3
+ version: 0.2.0
4
+ requires: { core: ">=0.1.0-alpha.11" }
5
+ touches: [src/pages/legal/disclosure.astro, src/pages/api/disclosure-request.ts, src/lib/disclosure-request.ts, src/lib/disclosure-db.ts, src/lib/disclosure-send.ts, src/pages/admin/disclosure.astro, src/pages/api/admin/disclosure-send.ts, migrations/local/0001_disclosure_requests.sql, src/pages/legal/tokushoho.astro, src/worker.ts, wrangler.jsonc, .doc/AGENTS.md, src/pages/admin/inventory.astro, src/pages/admin/orders.astro, src/pages/admin/products.astro]
6
+ risk: high # high: 法務判断を含む。弁護士確認前は本番有効化しない
7
+ ---
8
+ # 特商法開示請求(PaF-1〜PaF-3)
9
+
10
+ umec-template の特商法「全項目非表示 + 開示請求フォーム + 即時自動開示メール + 管理画面 +
11
+ 日次リマインダ」機能を、既存インスタンスへ届ける registry item。
12
+
13
+ **v0.2.0の変更**: PaF-2(管理画面・再送)・PaF-3(日次リマインダ)を追加収録。
14
+ `src/lib/disclosure-request.ts` と `src/lib/disclosure-db.ts` の内容が更新されている
15
+ (PaF-3の関数追加)ため、v0.1.0を適用済みのインスタンスに再適用する場合は
16
+ `umec add disclosure --force` が必要(未改変ファイルへの正規バージョンアップであることを
17
+ 確認してから使うこと)。
18
+
19
+ 適用先は **umec-template >= 0.1.0 由来のインスタンス**(機械可読な `requires` は CLI が解釈できる
20
+ `core` / `admin` / `items` のみのため、`@umec/core >=0.1.0-alpha.11` を記録する)。
21
+
22
+ `files/` には設計書 §10.4 の「機械的に配布してよい新規ファイル」だけを置く。
23
+ ブランド固有の既存ファイルは **`files/` に含めない**。`umec add --force` でも上書きされない。
24
+
25
+ 実体は umec-template `b0c5f69`(2026-09-17、PaF-1 dogfood)の main からコピーした。
26
+
27
+ **この item を適用しただけでは本番で有効にならない。** 手作業再適用・vars/Secret・
28
+ `umec migrate`・弁護士確認が揃うまで、特商法ページの表示も cron も開示メールも動かない。
29
+
30
+ ## 弁護士確認が済んでいない(設計書 §9.2)
31
+
32
+ コードは dogfood として umec-template に入っているが、次は未確認。**本番店舗(runrun 含む)では
33
+ 有効化しない。** go/no-go は事業者(Daisuke)が弁護士確認後に判断する。
34
+
35
+ | # | 項目 | 影響 |
36
+ |---|---|---|
37
+ | 1 | 請求手段がフォームのみ(メール・電話を出さない)が執行で「請求できる状態」と認められるか | 認められない場合はメールアドレス表示+フォーム併設へ戻す |
38
+ | 2 | 即時自動送信で「意思決定前の時間的余裕」を満たすと言えるか | 満たせないなら全項目非表示モード自体を提供しない |
39
+ | 3 | APPI 第32条の取扱事業者情報公表と全項目非表示の同時達成 | `privacy.astro` の事業者名・問い合わせ先の扱い |
40
+ | 4 | 第三者メールアドレス記載時の漏洩が「必要な範囲の提供」と言えるか | 追加緩和策の要否 |
41
+ | 5 | 将来の特商法改正(2026年検討会) | 追従方針のみ |
42
+ | 6 | 全項目非表示モードのトレードオフ承認(auto 開示による機械的収集リスク) | 提供開始の go/no-go |
43
+ | 7 | バーチャルオフィスを開示住所にする場合の Q18 要件 | セットアップ注記 |
44
+ | 8 | テンプレート既定の表示モード(全項目非表示 vs 氏名表示+住所電話開示) | 既定の方針決定 |
45
+
46
+ ## 法務リサーチの要点(設計書 §0.1)
47
+
48
+ - 氏名・住所・電話番号は、ただし書(請求により遅滞なく電磁的記録を提供する旨の表示+実際の体制)により省略可能(消費者庁通信販売広告 Q&A Q15/Q17)
49
+ - メールアドレスはウェブサイト広告の法定表示事項ではない。ただし通信販売電子メール広告を送る場合、そのメール本文に事業者メールアドレスは必須
50
+ - 「遅滞なく」=申込みの意思決定に先立って十分な時間的余裕。即時購入可能な店では **自動即時送信が本線**
51
+ - なりすまし対策は CAPTCHA・同一 IP レート制限。「購入者限定・注文番号必須・身分証」は法的に危険
52
+ - 開示内容は省略した全項目を、印刷可能なメール本文/添付で返す
53
+ - 屋号を法定の「氏名又は名称」の代わりに出してはならない。出ない電話・実在しない住所も不可
54
+
55
+ ## `umec add disclosure` がコピーするファイル(新規 + 更新)
56
+
57
+ | パス | 役割 |
58
+ |---|---|
59
+ | `src/pages/legal/disclosure.astro` | 請求フォーム(メールのみ、Turnstile) |
60
+ | `src/pages/api/disclosure-request.ts` | 受付 API の薄いアダプタ |
61
+ | `src/lib/disclosure-request.ts` | バリデーション、Turnstile、レート制限、即時開示メール、事業者通知、cron リトライ、日次リマインダ(PaF-3) |
62
+ | `src/lib/disclosure-db.ts` | インスタンス所有テーブル `disclosure_requests` の読み書き(一覧・未開示取得を含む) |
63
+ | `src/lib/disclosure-send.ts` | 管理画面からの再送・手動開示ロジック(PaF-2) |
64
+ | `src/pages/admin/disclosure.astro` | 請求一覧・再送・手動開示画面(PaF-2、`requireAdminAuth`保護) |
65
+ | `src/pages/api/admin/disclosure-send.ts` | 再送・手動開示 API(PaF-2) |
66
+ | `migrations/local/0001_disclosure_requests.sql` | 加算 DDL |
67
+
68
+ PaF-1のみ適用済み(v0.1.0)のインスタンスにv0.2.0を適用する場合、`disclosure-request.ts`と
69
+ `disclosure-db.ts`は内容が変わるため`--force`が必要(他の新規ファイルはそのまま追加される)。
70
+
71
+ ### マイグレーション番号(必ず読み直すこと)
72
+
73
+ `files/` の SQL はテンプレート正本の番号 **`0001_disclosure_requests.sql` のまま**置く。
74
+ **適用先インスタンスの `migrations/local/` に既に `0001_*.sql` がある場合は、コピー後に
75
+ 次番号へリネームしてから `umec migrate` すること。** 同名ファイルの上書き適用はしない
76
+ (`0002_orders.sql` 事故と同じ形状になる)。
77
+
78
+ 今回の自動コピーは番号を書き換えない。実際の採番し直しと D1 適用は、弁護士確認後に
79
+ 適用者が判断する。適用経路は `umec migrate` のみ。`wrangler d1 migrations apply` は使わない。
80
+
81
+ runrun の `migrations/local/` は 2026-09-17 時点で `.gitkeep` のみなので、初回適用なら
82
+ `0001_` のままで衝突しない。それでも適用前にディレクトリを目視すること。
83
+
84
+ ## 手作業で再適用するファイル(`files/` に入れない)
85
+
86
+ 設計書 §10.4: インスタンスが既にブランド固有のカスタマイズをしている可能性が高く、
87
+ 機械的な上書きは危険。差分の持ち込みではなく、下記の変更内容を手で再適用する。
88
+
89
+ ### 1. `src/pages/legal/tokushoho.astro`(設計書 §1)
90
+
91
+ 販売事業者名・運営責任者・所在地・電話番号・メールアドレスの5行を1ブロックに置き換え、
92
+ `/legal/disclosure` へリンクする。販売価格・支払方法・支払時期・引渡時期・返品の5行は
93
+ 現行どおり維持(省略不可)。屋号を法定名の代わりにしない。
94
+
95
+ 文案ブロック(全項目非表示モード):
96
+
97
+ > 販売事業者名・運営責任者名・所在地・電話番号・メールアドレスについては、ご請求があった場合に、**遅滞なく電子メールで提供**いたします。→ **開示のご請求はこちら**(`/legal/disclosure`)
98
+
99
+ umec-template の実体(CSS トークンはインスタンス側のクラス名に合わせる):
100
+
101
+ ```astro
102
+ const identityLabel = '販売事業者名・運営責任者・所在地・電話番号・メールアドレス';
103
+ ```
104
+
105
+ ```html
106
+ <dt>{identityLabel}</dt>
107
+ <dd>
108
+ 販売事業者名・運営責任者名・所在地・電話番号・メールアドレスについては、ご請求があった場合に、<strong>遅滞なく電子メールで提供</strong>いたします。
109
+ <a href="/legal/disclosure">開示のご請求はこちら</a>
110
+ </dd>
111
+ ```
112
+
113
+ ページは `prerender = true` のまま。runrun は販売条件が既に実データなので、
114
+ 条件5行は触らず、身元5行だけを差し替える。
115
+
116
+ 段階的有効化(§10.4-5): フォームと API が動くまでは tokushoho の現行表示を残し、
117
+ 動作実績の後にこの差し替えを行う。ロールバックは tokushoho を元に戻すだけで足りる。
118
+
119
+ ### 2. `src/worker.ts`(設計書 §3.3・§5.3)
120
+
121
+ `scheduled()` に `retryDisclosureEmails()`(15分cron)と `alertStaleDisclosureRequests()`
122
+ (日次cron、PaF-3)を追加する。即時送信が Resend 5xx / タイムアウト / レート制限で失敗したら
123
+ その場で1回リトライし、それでもダメなら行を `received` のまま残して cron に委譲する。
124
+ 上限(既定 3)を超えた行は無限リトライしない。日次リマインダは未開示行がある場合のみ
125
+ 事業者へ一覧メールを送る。
126
+
127
+ 15分 cron(`*/15 * * * *`)では既存の日次未発送アラート・日次リマインダを再実行しない。
128
+ runrun の `fetch()` にある `www.runrun.kids` → `runrun.kids` の 301 は残す。
129
+
130
+ ```ts
131
+ import { formatEmailFrom } from '@umec/core/config';
132
+ import config from '../umec.config';
133
+ import {
134
+ DISCLOSURE_RETRY_CRON,
135
+ retryDisclosureEmails,
136
+ alertStaleDisclosureRequests,
137
+ type DisclosureBrandContext,
138
+ type DisclosureEnv,
139
+ } from './lib/disclosure-request';
140
+
141
+ function disclosureBrandContext(): DisclosureBrandContext {
142
+ return {
143
+ brandName: config.brand.name,
144
+ brandUrl: String(config.brand.url),
145
+ emailFrom: formatEmailFrom(config.email),
146
+ emailReplyTo: config.email.replyTo,
147
+ notifyFallback: config.email.bcc,
148
+ };
149
+ }
150
+
151
+ async scheduled(controller: { cron?: string }, env: WorkerEnv) {
152
+ // 15-minute disclosure retry must not re-fire the daily unshipped alert
153
+ // or the daily undisclosed-request reminder.
154
+ if (controller?.cron !== DISCLOSURE_RETRY_CRON) {
155
+ try {
156
+ await alertUnshippedOrders(env);
157
+ } catch {
158
+ console.error('[scheduled/unshipped] alert failed');
159
+ }
160
+ try {
161
+ await alertStaleDisclosureRequests(env, disclosureBrandContext());
162
+ } catch {
163
+ console.error('[scheduled/disclosure] stale reminder failed');
164
+ }
165
+ }
166
+ await retryDisclosureEmails(env, disclosureBrandContext());
167
+ }
168
+ ```
169
+
170
+ 正本は umec-template `src/worker.ts`。適用者は既存インスタンスの `fetch()` 実装
171
+ (301リダイレクト等のブランド固有処理)を残したまま、`scheduled()` とimport文だけを
172
+ 上記の形に揃えること。
173
+
174
+ `WorkerEnv` に `DisclosureEnv` を交差させる。
175
+
176
+ ### 3. `wrangler.jsonc`(設計書 §3.3・§7)
177
+
178
+ 既存の日次 cron は消さず、15分 cron を併記する。vars に Secret を書かない。
179
+ runrun は既に `PUBLIC_STRIPE_PUBLISHABLE_KEY` 等の vars と `account_id` を持っているので、
180
+ テンプレートの wrangler を丸ごとコピーしない。
181
+
182
+ ```jsonc
183
+ "vars": {
184
+ // DISCLOSURE_PAYLOAD / DISCLOSURE_MODE / DISCLOSURE_NOTIFY_TO / DISCLOSURE_RATE_* /
185
+ // DISCLOSURE_RETRY_MAX / PUBLIC_TURNSTILE_SITE_KEY は .dev.vars またはダッシュボード vars で設定する。
186
+ // TURNSTILE_SECRET_KEY / DISCLOSURE_IP_SALT は wrangler secret put で設定する(vars に書かない)。
187
+ },
188
+ "triggers": {
189
+ "crons": ["0 0 * * *", "*/15 * * * *"]
190
+ }
191
+ ```
192
+
193
+ ### 4. `.doc/AGENTS.md`(設計書 §7)
194
+
195
+ 環境変数表へ追記する。値は git に入れない。
196
+
197
+ Secret(`wrangler secret put`):
198
+
199
+ | 変数名 | 用途 |
200
+ |---|---|
201
+ | `TURNSTILE_SECRET_KEY` | Cloudflare Turnstile のサーバ検証 |
202
+ | `DISCLOSURE_IP_SALT` | IP ハッシュ用 salt。未設定でも動くが非推奨 |
203
+
204
+ ビルド変数 / vars(`.dev.vars` またはダッシュボード。Secret ではないが git 禁止):
205
+
206
+ | 変数名 | 用途 | 未設定時 |
207
+ |---|---|---|
208
+ | `PUBLIC_TURNSTILE_SITE_KEY` | Turnstile ウィジェット。本番では必須 | 本番 503・dev スキップ |
209
+ | `DISCLOSURE_PAYLOAD` | 開示内容 JSON(`sellerName` / `operatorName` / `address` / `phone` / `email`) | 開示実行時にエラー+事業者通知 |
210
+ | `DISCLOSURE_MODE` | `auto`(既定)/ `manual`。全項目非表示では `manual` 不可 | auto |
211
+ | `DISCLOSURE_NOTIFY_TO` | 事業者通知 To | `config.email.bcc` → 無ければ通知スキップ |
212
+ | `DISCLOSURE_RATE_PER_EMAIL` / `DISCLOSURE_RATE_PER_IP` / `DISCLOSURE_RATE_GLOBAL` | 1日あたりレート | 3 / 10 / 50 |
213
+ | `DISCLOSURE_RETRY_MAX` | 開示メール自動リトライ上限 | 3 |
214
+
215
+ ```
216
+ DISCLOSURE_PAYLOAD={"sellerName":"...","operatorName":"...","address":"...","phone":"...","email":"..."}
217
+ ```
218
+
219
+ ### 5. `src/pages/admin/inventory.astro` / `orders.astro` / `products.astro`(PaF-2)
220
+
221
+ 既存ナビに1行追加するだけ(`@umec/admin`配線後もこの3ファイルの markup 自体は
222
+ インスタンス所有のまま。実質バイト一致している運用層なので機械上書きしても安全なはずだが、
223
+ 念のため手作業リストに残す):
224
+
225
+ ```html
226
+ <a href="/admin/disclosure" class="text-sm underline decoration-border underline-offset-4 hover:text-accent">開示請求</a>
227
+ ```
228
+
229
+ 既存の「CSV出力」「商品管理」「注文一覧」等のリンクの直後に追記する。3ファイルとも同じ形。
230
+
231
+ ## 適用後に残る作業(自動ではやらない)
232
+
233
+ 1. `migrations/local/` の次番号を確認し、必要なら SQL をリネームして `umec migrate`
234
+ 2. 上記5ファイル(tokushoho.astro / worker.ts / wrangler.jsonc / .doc/AGENTS.md /
235
+ admin 3ページのナビ)の手作業再適用
236
+ 3. vars / Secret をインスタンス用に設定(コード配布とは別作業)
237
+ 4. 段階的有効化(§10.4-5):
238
+ 1. `DISCLOSURE_MODE=manual` でフォームのみ(tokushoho は現行表示のまま)
239
+ 2. 受付→通知の動作確認、管理画面(PaF-2)での目視確認
240
+ 3. 問題なければ tokushoho を非表示モードへ
241
+ 4. 実績を見て `auto` に切り替え
242
+ 5. 弁護士確認とトレードオフ承認(§9.2-6)が済むまで本番有効化しない
@@ -0,0 +1 @@
1
+ This file was added by `umec add example-hello`.
@@ -0,0 +1,10 @@
1
+ ---
2
+ id: example-hello
3
+ version: 1.0.0
4
+ requires: { core: ">=0.1.0-alpha.11" }
5
+ touches: [src/example-hello.txt]
6
+ risk: low # low: UI のみ / medium: DB・API / high: 決済経路
7
+ ---
8
+ # ハローサンプル
9
+
10
+ 顧客リポジトリへサンプルテキストを1ファイル追加する、registry CLI の動作確認用 item。