技術スタック Tech stack
Parky で採用している主要技術とその役割、配置場所の一覧です。 どのプロダクトがどれを使っているかを素早く把握するためのリファレンスです。
The major technologies Parky uses, their responsibilities, and which product relies on them. A quick reference to see which client consumes what.
BFF(クライアントが叩く唯一の API 層)BFF (the only API surface clients hit)
| 技術 | Tech | 用途 | Purpose | 使用プロダクト | Used by |
|---|---|---|---|---|---|
| Cloudflare Workers | 全クライアント共通の BFF ランタイム。/v1/* を単一 API 層として提供。東京 PoP で Supabase ap-northeast-1 と至近 |
Shared BFF runtime serving /v1/* as the single API surface. Tokyo PoP colocated near Supabase ap-northeast-1 |
All (Flutter / Web / Admin / Owner / Marketing) | All (Flutter / Web / Admin / Owner / Marketing) | |
| Hono | Workers 上のルーティング・ミドルウェア。@hono/zod-openapi で OpenAPI と実装を連動 |
Routing/middleware on Workers. @hono/zod-openapi keeps implementation in sync with the spec |
— | — | |
| OpenAPI 3.1 + Zod | API 契約の Single Source of Truth(packages/api-spec/)。CI で TS / Dart クライアントを自動生成 |
Single source of truth for the API contract (packages/api-spec/). TS and Dart clients are regenerated in CI |
— | — | |
| Cloudflare Cache API | 公開 GET のエッジキャッシュ。middleware/cache.ts が GET+200 に max-age=300 を付け、それ以外は no-cache |
Edge cache for public GET. middleware/cache.ts sets max-age=300 for GET+200, no-cache otherwise |
— | — | |
Cloudflare KV (parky-cache) |
isolate 跨ぎキャッシュ。FCM OAuth access_token を 2 層キャッシュ(isolate Map + KV / TTL 55分)で共有 | Cross-isolate cache. FCM OAuth access_token uses a 2-tier cache (isolate Map + KV, TTL 55 min) | — | — | |
| Cloudflare Rate Limiting binding | ユーザー単位のレート制限(RATE_LIMIT_USER)。/v1/search/ai で 10 req / 60 秒 |
Per-user rate limiting via the RATE_LIMIT_USER binding. 10 req / 60 s for /v1/search/ai |
— | — | |
Cloudflare Analytics Engine (parky_ai_usage) |
AI 呼出のテレメトリ集計。現行は PG ai_usage_logs と dual write、段階的に AE 単独化 |
Telemetry dataset for AI calls. Dual-writes with PG ai_usage_logs today; moving to AE-only |
— | — | |
| Cloudflare Logpush / Workers Observability | 構造化ログ、req/sec、エラー率、レイテンシ分布([observability] enabled = true)。全 invocation の長期保存(R2)を担当(Sentry とは役割分担、後述) |
Structured logs, req/sec, error rate, latency ([observability] enabled = true). Owns the full-invocation long-term log archive in R2 (split of duties with Sentry — see below) |
— | — |
監視・観測性 (Sentry + Logpush)Monitoring & observability (Sentry + Logpush)
Parky の観測性は Cloudflare Workers Logs (Logpush → R2) と Sentry の役割分担で構成しています。 Logpush は全 invocation の構造化ログを R2 に長期保存し、Sentry は 5xx / 未処理例外などエラー文脈(stack trace / user_id / request_id)のリアルタイム通知と issue grouping を担います。 役割分担と運用の詳細は ops / logging、初期セットアップは ops / sentry-setup、Logpush の段階展開は ops / sentry-logpush-rollout を参照。
Observability is split between Cloudflare Workers Logs (Logpush → R2) and Sentry. Logpush owns long-term structured log archival of every invocation in R2; Sentry owns real-time alerting and issue grouping for 5xx / unhandled exceptions with full error context (stack trace / user_id / request_id). See ops / logging for the split, ops / sentry-setup for initial setup, and ops / sentry-logpush-rollout for rollout history.
| 技術 | Tech | 用途 | Purpose | 使用プロダクト | Used by |
|---|---|---|---|---|---|
Sentry (Cloudflare Workers / toucan-js) |
BFF の 5xx・未処理例外を error-handler から capture。SENTRY_DSN / SENTRY_RELEASE binding、PII 保護のため request body / cookies / IP は redact、許可ヘッダーは user-agent / x-app-version / x-request-id / accept-language のみ |
Captures BFF 5xx / unhandled exceptions through error-handler. Bound via SENTRY_DSN / SENTRY_RELEASE; request body / cookies / IP are redacted, with only user-agent / x-app-version / x-request-id / accept-language allowed |
BFF (Workers) | BFF (Workers) | |
Sentry (@sentry/react) |
Admin / Owner / Marketing Portal SPA の未処理例外・ErrorBoundary 捕捉・ルーティング Breadcrumb。VITE_SENTRY_DSN でビルド時注入 |
Captures unhandled exceptions, ErrorBoundary errors, and routing breadcrumbs in the Admin / Owner / Marketing portals. DSN injected via VITE_SENTRY_DSN at Vite build time |
Admin / Owner / Marketing | Admin / Owner / Marketing | |
Sentry (@sentry/astro) |
Web 版 (Astro) の Island 側未処理例外・ハイドレーション失敗・ナビゲーション Breadcrumb。SENTRY_DSN でビルド時注入 |
Captures Astro island unhandled exceptions, hydration errors, and navigation breadcrumbs on the public web app. DSN injected at build time via SENTRY_DSN |
Web app | Web app | |
Sentry (sentry_flutter) |
モバイルアプリの Dart 例外・Flutter framework error・isolate crash を capture。詳細は mobile / architecture | Captures Dart exceptions, Flutter framework errors, and isolate crashes in the mobile app. See mobile / architecture | Mobile app | Mobile app |
バックエンド(Workers から呼ばれるコア基盤)Backend (core, called from Workers)
| 技術 | Tech | 用途 | Purpose | 使用プロダクト | Used by |
|---|---|---|---|---|---|
| Supabase (PostgreSQL 15) | 全データの永続化。PostGIS 拡張で位置検索。Workers が Service Role で接続 | Primary datastore with PostGIS for geo queries. Workers connect with the Service Role key | All | All | |
| Supabase Auth | 認証(メール+PW、OAuth、電話番号)。クライアントは SDK 経由で直接利用(例外①)、Workers は JWT 検証のみ | Auth (email+password, OAuth, phone). Clients use the SDK directly (exception 1); Workers only verify JWTs | All | All | |
| Supabase Realtime | テーブル変更のライブ購読。クライアント直接接続(例外②) | Live table subscriptions. Clients connect directly (exception 2) | Admin, Mobile | Admin, Mobile | |
Cloudflare Hyperdrive (parky-db) |
Workers → Supabase Postgres の接続プール+グローバルキャッシュ。lib/db.ts の postgres.js が env.HYPERDRIVE.connectionString で接続(Direct Connection 経由、Supavisor は使わない) |
Connection pool + global cache for Workers → Supabase Postgres. lib/db.ts uses postgres.js via env.HYPERDRIVE.connectionString (Direct Connection; Supavisor bypassed) |
Internal | Internal | |
| Cloudflare Cron Triggers | 定期ジョブ。*/10 * * * * で handleSponsorProximity を起動。pg_cron は使わず wrangler.toml の [triggers] crons に集約 |
Scheduled jobs — */10 * * * * invokes handleSponsorProximity. No pg_cron; schedules live in [triggers] crons |
Internal | Internal | |
| Cloudflare Queues | parky-store-sync(Google Play / App Store Connect API → DB upsert)と parky-fcm-dispatch(FCM 通知 fan-out)。各 DLQ 配線・max_retries=3 |
parky-store-sync (Google Play / App Store Connect APIs → DB upsert) and parky-fcm-dispatch (FCM fan-out). DLQs wired, max_retries=3 |
Internal | Internal | |
| Cloudflare Workers AI | エッジ推論(env.AI)。Instagram tool で DETR 物体検出を使って顔/ナンバープレート候補領域を検出 |
Edge inference (env.AI). Used by the Instagram tool for DETR object detection (face / license-plate candidate regions) |
Internal | Internal | |
Cloudflare D1 (parky-instagram) |
SQLite at edge。Instagram tool 専用(Parky 本体の Supabase とは分離) | SQLite at edge for the Instagram tool (isolated from Parky's main Supabase) | Instagram tool | Instagram tool |
オブジェクトストレージObject storage
| 技術 | Tech | 用途 | Purpose | 使用プロダクト | Used by |
|---|---|---|---|---|---|
| Cloudflare R2 (S3 互換) | 駐車場画像・アバター・バッジ/テーマアセット・PDF など全バイナリの保管(bucket: parky)。Instagram tool 用に parky-instagram-assets 別 bucket も配線。 |
Holds all binary assets (bucket: parky) — parking images, avatars, badge/theme assets, PDFs. A separate parky-instagram-assets bucket is wired for the Instagram tool. |
All | All | |
R2 Custom Domain cdn.parky.co.jp |
匿名公開 GET 用のカスタムドメイン。R2_PUBLIC_BASE が未設定だと account-private endpoint(署名必須)にフォールバック |
Custom domain for anonymous public GET. If R2_PUBLIC_BASE is unset, falls back to the account-private endpoint (signature required) |
All | All | |
Workers /v1/storage/upload-url |
R2 の presigned PUT URL を発行し、assets テーブルに s3_key を含むメタデータを登録するブリッジ。JWT 検証・user_id スコープで発行。クライアントは presigned URL に直接 PUT する(バイト列は Workers を経由しない) |
Mints R2 presigned PUT URLs and registers metadata (including s3_key) in the assets table. JWT-verified and scoped to user_id. Clients PUT bytes directly — bytes skip Workers |
Admin (write), All (read) | Admin (write), All (read) | |
R2_ENDPOINT / R2_BUCKET / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_PUBLIC_BASE |
Workers に必要なシークレット。wrangler secret put で登録 |
Secrets required by Workers, registered via wrangler secret put |
— | — |
フロントエンドFrontend
| プロダクト | Product | スタック | Stack | ホスティング | Hosting |
|---|---|---|---|---|---|
| 管理者ポータル | Admin portal | React 19 + Vite + TypeScript + Lucide Icons + Mapbox GL JS | Cloudflare Pages (parky-admin-dev → dev-admin.parky.co.jp / parky-admin-prod → admin.parky.co.jp) |
||
| オーナーポータル | Owner portal | React 19 + Vite + TypeScript + react-hook-form + Mapbox GL JS | Cloudflare Pages (parky-portal-owner-dev → dev-owner.parky.co.jp / prod → owner.parky.co.jp) |
||
| マーケティングポータル | Marketing portal | React 19 + Vite + TypeScript + Mapbox GL JS + jszip / html-to-image | Cloudflare Pages (parky-portal-marketing-dev → dev-marketing.parky.co.jp / prod → marketing.parky.co.jp) |
||
| モバイルアプリ | Mobile app | Flutter (Dart, SDK ^3.8) — mobileapp/prototype/flutter/。supabase_flutter / mapbox_maps_flutter / native_geofence / geolocator / sign_in_with_apple / app_links / image_picker ほか |
App Store / Google Play(将来) | App Store / Google Play (future) | |
| Web版 (一般向け) | Web app (public) | Astro 5 + React 19 (islands) + Tailwind CSS v4 + MDX + Pagefind + Mapbox GL JS | Cloudflare Pages (parky-home-dev → dev.parky.co.jp / prod → parky.co.jp) |
||
| メディア (記事) | Media (articles) | Astro ルート (web/home/src/pages/media/) + Supabase articles テーブル駆動 |
Astro routes (web/home/src/pages/media/) backed by the Supabase articles table |
dev.parky.co.jp/media/ (web/home の同一 Pages プロジェクト) | |
| Docs(本サイト) | Docs (this site) | 静的 HTML + Mermaid | Static HTML + Mermaid | Cloudflare Pages (parky-docs-dev → dev-docs.parky.co.jp) |
|
| Mobile Web Mock | Mobile Web Mock | 単一 HTML + Mapbox GL JS | Single HTML + Mapbox GL JS | Cloudflare Pages (parky-app-mock-dev → dev-app-mock.parky.co.jp) |
地図・位置情報Maps & geo
- Mapbox GL JS — 地図表示・マーカー・経路表示。全クライアントで統一利用。Map rendering, markers, and routing — used by every client.
- PostGIS — 駐車場の半径検索、Nearest検索。Radius and nearest-neighbour queries for parking lots.
検索Search
- Pagefind (
astro-pagefind) — Web 版の/searchはビルド後のdist/をスキャンして生成される静的 Pagefind インデックスをクライアントから fetch する方式。ランタイムで Supabase に直接クエリは発行しない。The web app's/searchreads a static Pagefind index generated after build fromdist/— no direct Supabase query at runtime.
AI・LLMAI & LLM
- Claude API (Anthropic) — AI検索のメインプロバイダー。tool_use でクエリを構造化 JSON にパース。Haiku 4.5 / Sonnet 4.6 / Opus 4.6 をサポートPrimary AI search provider. Parses queries into structured JSON via tool_use. Supports Haiku 4.5 / Sonnet 4.6 / Opus 4.6
- Gemini API (Google) — フォールバック用プロバイダー。function_calling で同一スキーマに対応。Flash / Pro モデルをサポートFallback provider. Uses function_calling with the same schema. Supports Flash / Pro models
- OpenAI GPT — フォールバック用プロバイダー。function_calling で同一スキーマに対応。GPT-4o / 4.1 系をサポートFallback provider. Uses function_calling with the same schema. Supports GPT-4o / 4.1 family
- Workers
/v1/search/ai— 統一エントリポイント。プロバイダー自動選択・フォールバック・Vault からのキー復号(vault_read_secretRPC)・使用量ログを Workers 内で一括処理Unified entry point. Handles provider routing, fallback, Vault key decryption (via thevault_read_secretRPC), and usage logging — all inside Workers - Web Speech API — ブラウザネイティブの音声認識。管理画面の AI 検索モーダルで音声入力に対応(Chrome/Edge)Browser-native speech recognition. Voice input in the admin AI search modal (Chrome/Edge)
通知・外部サービスNotifications & externals
- Firebase Cloud Messaging (v1 API) — ユーザー向けプッシュ配信。
POST /v1/admin/user-notifications/{id}/sendは受信者トークンを 500 件/バッチでparky-fcm-dispatchキューに投入し、consumer(src/queue/fcm-dispatch.ts)が Web Crypto で OAuth2 JWT(RS256)を署名して FCM v1 エンドポイントを fan-out。OAuth access_token は KV(parky-cache)で isolate 跨ぎ共有。Flutter プロトタイプにはfirebase_messagingがまだ入っていないため、端末側でのトークン登録・Push 受信は未実装。End-user push delivery.POST /v1/admin/user-notifications/{id}/sendchunks receiver tokens 500/batch into theparky-fcm-dispatchqueue; the consumer (src/queue/fcm-dispatch.ts) signs the OAuth2 JWT (RS256) with Web Crypto and fans out to the FCM v1 endpoint. The OAuth access_token is shared cross-isolate via KV (parky-cache).firebase_messagingis not yet in the Flutter prototype, so device-side token registration and receive are not implemented. - Resend — トランザクションメール配信。オーナー設定リンク(72h トークン)・ニュースレター配信に使用。詳細は Owner / 発行・認証フローTransactional email. Used for owner setup links (72h tokens) and newsletter dispatch. See Owner / provisioning flow
- Google Play Developer API / App Store Connect API — Store 同期。
POST /v1/admin/store-sync-triggerはparky-store-syncキューに投入、consumer が Play Developer API(レビュー)/ App Store Connect API(レビュー・売上 DAILY SUMMARY)から取得してstore_reviews/store_sales_dailyに upsert する。Play sales(Cloud Storage 月次 CSV)は未実装Store sync.POST /v1/admin/store-sync-triggerenqueues intoparky-store-sync; the consumer pulls reviews from the Play Developer API and reviews + DAILY SUMMARY sales from App Store Connect, upserting intostore_reviews/store_sales_daily. Play sales (monthly Cloud Storage CSV) is not yet implemented - 1Password Service Account — CI/CD のシークレット配信。GitHub Actions でトークン参照。Secret delivery to CI via 1Password SA tokens in GitHub Actions.
採用していない外部サービス: Stripe(駐車場予約・決済を実装しないスコープ判断のため、プロダクトスコープ参照)、Supabase Storage(バイナリは R2)、Supabase Edge Functions(サーバーロジックは Workers に集約)。
Explicitly not adopted: Stripe (booking / payment is out of scope — see Product scope), Supabase Storage (binaries live in R2), Supabase Edge Functions (all server logic stays in Workers).
テスト基盤Testing
- Vitest — Workers / packages のユニットテスト。Unit tests for Workers and shared packages.
- Playwright (E2E) — 5 channel multi-project + manifest 駆動。tag (
@channel/@screen/@api/@smoke) とsource-map.jsonで diff → 影響テストを逆引き。Hook で同期忘れ防止。詳細: E2E テスト基盤5-channel multi-project setup driven by a YAML manifest. Tags (@channel/@screen/@api/@smoke) +source-map.jsonresolve diff → affected tests; a hook prevents drift. See E2E framework - pgTAP — RLS ポリシーの assertions。api/rls-tests.mdRLS policy assertions. api/rls-tests.md
ドキュメント・周辺ツールDocs & tooling
- Mermaid — 本ドキュメント内の図(ER/フロー/シーケンス)。All diagrams in this site (ER, flow, sequence).
- GitHub Actions — 全プロダクトのデプロイパイプライン。Deployment pipelines for every product.
/v1/* に一元化する。Supabase Edge Functions は採用せず、FCM 配信・LLM 呼び出し・ストア同期・定期ジョブまで全て Workers 内に実装する。例外は Auth / Realtime / Storage presigned PUT のみ(データ面の最適化)。バイナリは Supabase Storage ではなく R2 に格納する(コスト・エグレス最適化)。
Clients hit a single unified API surface: Cloudflare Workers /v1/*. Supabase Edge Functions are not used in Parky — FCM delivery, LLM calls, store syncs, and scheduled jobs all live inside Workers. The exceptions are Auth, Realtime, and Storage presigned PUTs — data-plane optimizations. Binary assets live in R2, not Supabase Storage (chosen for cost and egress).