Skip to content

ご招待フォーム — 運用 / デプロイ

apps/invite-app (Worker invite-web) の開発・デプロイ・環境変数・メール送信設定。 本アプリは公開フォームのみ (認証なし)。招待リンク・文面の設定や履歴閲覧は別アプリ tied-workspace-admin が担う。

バインディング / 環境変数

apps/invite-app/wrangler.toml で定義する。型は worker-configuration.d.ts

種別名前用途
D1INVITE_DB (tied-workspace)invite_settings (読取) / invitations (書込)
send_emailINVITE_EMAIL招待リンクのメール配信 (Cloudflare Email Routing)
varINVITE_EMAIL_FROM送信元アドレス (例: no-reply@tied-workspace.com)
varINVITE_EMAIL_FROM_NAME送信元表示名
varTURNSTILE_SITE_KEYTurnstile (ボット対策) のサイトキー (公開値)
secretTURNSTILE_SECRET_KEYTurnstile のシークレットキー (siteverify 用)

認証は持たないが、公開フォームのボット/スパム対策として Cloudflare Turnstile の シークレットキーのみ secret として設定する。

bash
cd apps/invite-app
bunx wrangler secret put TURNSTILE_SECRET_KEY

ローカル開発では apps/invite-app/.dev.vars に置く (コミットしない)。テスト用なら Cloudflare の常に成功するテストキー TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA が使える。

ローカル開発

bash
# 依存インストール (リポジトリルート)
bun install

# D1 のローカル状態にスキーマを適用 (Miniflare)
bunx wrangler d1 migrations apply tied-workspace --local \
  --persist-to apps/invite-app/.wrangler/state -c packages/d1/wrangler.toml

# 招待設定を投入 (admin アプリ未整備の間は手動で 1 行入れる)
bunx wrangler d1 execute tied-workspace --local \
  --persist-to apps/invite-app/.wrangler/state -c packages/d1/wrangler.toml \
  --command "INSERT INTO invite_settings (id, invite_url, email_subject, email_body, updated_at) VALUES ('default', 'https://join.slack.com/t/...', 'TiedWorkspace へのご招待', '{{name}} 様\n\n{{inviteUrl}}', '2026-06-14T00:00:00Z')"

# 開発サーバ
bun run --filter @tied-workspace/invite-app dev

invite_settings に招待リンクが入るとフォームが受付可能になる (未設定の間はフォーム送信を無効化して受付停止メッセージを出す)。本番では tied-workspace-admin から設定する。

デプロイ

bash
bun run --filter @tied-workspace/invite-app deploy   # react-router build && wrangler deploy

CI は .github/workflows/deploy-invite-app.yml。PR で型チェック + ビルド、main への push で 本番デプロイする。D1 スキーマの適用はアプリと独立した deploy-d1.ymlpackages/d1/** の変更で行う (スキーマ変更を伴う場合は migration 側を先にマージ・適用する)。

公開ドメイン invite.tied-workspace.com は Cloudflare 側でカスタムドメインの紐付けが必要。

ボット対策 (Cloudflare Turnstile)

公開フォームは未認証でメール配信を起動できるため、送信時に Cloudflare Turnstile の トークンを検証してボット/スパムを弾く。

  • フロント: フォームに Turnstile widget (class="cf-turnstile") を描画する (スクリプトは app/routes/index.tsx が読み込む)。サイトキーは var TURNSTILE_SITE_KEY
  • サーバー: action が送信時に cf-turnstile-response トークンを https://challenges.cloudflare.com/turnstile/v0/siteverify で検証する (app/lib/turnstile.server.ts)。失敗したら同意記録もメール配信もしない。

本番では Cloudflare ダッシュボードで Turnstile ウィジェットを作成し、 発行されたサイトキーwrangler.tomlTURNSTILE_SITE_KEY に、シークレットキーを secret TURNSTILE_SECRET_KEY に設定する。リポジトリの既定値は常に成功するテストキーのため、 本番では必ず差し替えること (差し替え忘れるとボット対策が無効になる)。

メール送信 (重要な制約)

配信は Cloudflare Email Routing の send_email バインディング (cloudflare:email) を使う (app/lib/email.server.ts)。

send_email の宛先制約

send_email バインディングは、宛先 (To) が Email Routing で「検証済みの宛先アドレス」で ある場合にのみ送信できる。任意の招待者メールアドレスへそのままは届かない。 本番で外部の招待者へ確実に届けるには、次のいずれかが必要:

  1. 送信ドメイン (tied-workspace.com 等) を Cloudflare Email Routing に登録し、 DNS (MX / SPF / DKIM / DMARC) を整える。検証済み宛先のみに配信する運用にするか、
  2. 任意宛先へのトランザクションメールに対応した外部プロバイダ (Resend 等) へ切り替える。 その場合も EmailSender ポートを差し替えるだけでよく、ユースケースや UI は変更不要。

送信に失敗した同意は invitations.status = 'failed' (および email_error) として記録に残る。 tied-workspace-admin の履歴から失敗を把握できる (再送は候補者の再送信、または将来の再送 UI で対応)。

利用規約の改定

規約本文は apps/invite-app/app/lib/terms.ts が正本。改定したら本文を更新し、 必ず TERMS_VERSION を上げる。以後の同意記録には新しいバージョンが保存され、 どの版に同意したかを invitations.terms_version で追跡できる。