defineEnv — zod 検証つき env リゾルバ
3リポのどこにも無かった欠落機能である。DATABASE_URL / CRON_SECRET / FOURS_* の欠落を boot 時に fail-fast で検出し、本番で undefined のまま静かに走らせない。
// src/env.ts
import { z } from "zod";
import { defineEnv } from "@event/core";
export const env = defineEnv({
FOURS_EVENT_SLUG: z.string().min(1),
ADMIN_PASSWORD: z.string().min(8),
SESSION_SECRET: z.string().min(16),
DATABASE_URL: z.string().url().optional(),
});
検証失敗は EnvValidationError を throw し、キーごとの理由を列挙する。同じ shape から envExample(shape, comments) で .env.example の内容を生成できる(optional キーには # optional 注記が付く)。生成物は commit して運用する。
4S 接続(@event/fours-sdk — createFoursConfig が解決)
| 変数 | 用途 | 既定 / 備考 |
|---|
FOURS_API_BASE | event API base | https://api.4s.link。legacy FOURS_API / FOUR_S_API fallback あり |
FOURS_EVENT_SLUG | 対象イベント slug | fetchEvent の既定 slug |
FOURS_PUBLIC_READ | 1/true で public-read mode | 認証なし読み取り |
FOURS_API_KEY | write 系 Bearer。設定で authenticated mode | legacy FOUR_S_API_KEY / FOUR_S_SERVICE_TOKEN fallback |
USE_MOCK_DATA | 1 で mock-locked(4S に一切出ない) | mode 判定の最優先 |
FOURS_WEBHOOK_SECRET | inbound webhook HMAC-SHA256 鍵 | verifyWebhookSignature |
FOURS_V2_API_BASE / NEXT_PUBLIC_4S_V2_API_BASE | auth 系(login / users/me / handoff)base | 未指定なら apiBase と同一ホスト |
NEXT_PUBLIC_4S_V2_WEB_BASE | 4S Web base(プロフィール URL 組み立て) | https://4s.link |
NEXT_PUBLIC_4S_LOGIN_URL | ログインレス handoff の起点 URL | https://4s.link/ja/handoff/launch |
セッション / 認証
| 変数 | 用途 | 備考 |
|---|
SESSION_SECRET | jose HS256 cookie セッションの署名鍵 | 本番未設定は createSessionCodec が throw(fail-closed)。dev は警告つき固定鍵 |
ADMIN_PASSWORD | 共有パスワード admin の期待値 | 未設定は fail-closed(ログイン不可)。realm ごとに passwordEnv で別変数も指定可 |
4S service / admin API(opt-in)
| 変数 | 用途 |
|---|
FOURS_REFRESH_TOKEN | Cognito refresh token(優先。Google ログインのアカウント可) |
FOURS_LOGIN_EMAIL / FOURS_LOGIN_PASSWORD | POST /auth/login fallback |
FOURS_EVENT_ID | admin API(/manage/events/{id}/entries 等)の対象イベント ID |
コンテンツストア(@event/core createContentStore — 本番のみ)
| 変数 | 用途 | 既定 |
|---|
GITHUB_TOKEN | Octokit commit 用 PAT | 必須(本番書き込み) |
GITHUB_REPO_OWNER / GITHUB_REPO_NAME | commit 先リポジトリ | 必須 |
GITHUB_BRANCH | commit 先ブランチ | main |
ストレージ / KV / cron
| 変数 | 用途 | 備考 |
|---|
BLOB_READ_WRITE_TOKEN | Vercel Blob(@event/asset-kit createAssetStore) | 未設定時 put は throw |
KV_REST_API_URL / KV_REST_API_TOKEN | Upstash Redis REST(Vercel KV) | 未設定なら dev in-memory shim に自動フォールバック |
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN | 同上の別名 | kv() が両方を解決 |
CRON_SECRET | Authorization: Bearer 厳格一致(withCronAuth) | 未設定は 503 fail-closed。比較は trim 硬化 |
機能モジュール別
| 変数 | 機能 | 用途 | 備考 |
|---|
LIVE_HOST_SECRET | surveys | live poll の host key 導出秘密 | 未設定は ADMIN_PASSWORD に fallback(どちらも無ければ throw) |
DATABASE_URL | db / booking | Neon pooled 接続 | schema 操作(MCP addField)は不要。行データ操作のみ必須 |
DATABASE_URL_UNPOOLED | db | migration 適用用の直接接続 | apply-migrations.mjs が優先使用 |
MCP_BEARER_TOKEN | mcp | HTTP(URL 経由)MCP の Bearer 認証 | HTTP を使うなら必須。stdio は不要 |
BOOKING_OPEN | booking | 予約/見積の受付 kill-switch | 0 で停止。既定 open |
RESEND_API_KEY | mailer / leads | Resend 送信キー | 未設定は preview-only / no-op(fail-safe) |
MAIL_FROM / MAIL_REPLY_TO | mailer | 差出人 / 返信先 | 例 "Event <onboarding@resend.dev>" |
MAIL_BRAND_NAME / MAIL_BRAND_COLOR | mailer | メール HTML shell のブランド | 任意 |
SLACK_WEBHOOK_URL | mailer / leads | 運用 Slack 通知 / リード着信通知 | 未設定は無言 no-op |
LEAD_NOTIFY_TO | leads | リード通知メールの宛先 | RESEND_API_KEY と両方揃うと有効 |
NEXT_PUBLIC_SITE_URL | share-card | OG 画像の絶対 URL 解決 | Vercel は VERCEL_URL で自動解決。ローカル共有プレビュー時のみ |
ビルド制御
| 変数 | 用途 |
|---|
NEXT_DIST_DIR | distDir の明示指定(createNextConfig) |
BUILD_LOCAL | 1 で distDir を .next-local に分離(dev 中の yarn build 事故防止) |
既知のトラップ
- 前後空白 / 末尾スラッシュ: env に紛れ込んだ空白は Authorization ヘッダを壊して 401 になる。
createFoursConfig は全値を trim() し、base URL は normalizeBase() で末尾スラッシュも除去する。CRON_SECRET の比較も trim 済み。ただし SDK を通らない自前コードは自衛が必要 *2 付きホスト(api-staging2.4s.link / api-develop2.4s.link)は staging / develop 専用。本番は api.4s.link / 4s.link- legacy
FOUR_S_ 系は fallback として読まれるが、新規イベントでは FOURS_ に統一する