技術スタック 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.tsGET+200max-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 / MarketingAdmin / 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 appWeb 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 appMobile 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 AllAll
Supabase Auth 認証(メール+PW、OAuth、電話番号)。クライアントは SDK 経由で直接利用(例外①)、Workers は JWT 検証のみ Auth (email+password, OAuth, phone). Clients use the SDK directly (exception 1); Workers only verify JWTs AllAll
Supabase Realtime テーブル変更のライブ購読。クライアント直接接続(例外②) Live table subscriptions. Clients connect directly (exception 2) Admin, MobileAdmin, 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) InternalInternal
Cloudflare Cron Triggers 定期ジョブ。*/10 * * * *handleSponsorProximity を起動。pg_cron は使わず wrangler.toml[triggers] crons に集約 Scheduled jobs — */10 * * * * invokes handleSponsorProximity. No pg_cron; schedules live in [triggers] crons InternalInternal
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 InternalInternal
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) InternalInternal
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 toolInstagram 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. AllAll
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) AllAll
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-devdev-admin.parky.co.jp / parky-admin-prodadmin.parky.co.jp)
オーナーポータルOwner portal React 19 + Vite + TypeScript + react-hook-form + Mapbox GL JS Cloudflare Pages (parky-portal-owner-devdev-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-devdev-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-devdev.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 + MermaidStatic HTML + Mermaid Cloudflare Pages (parky-docs-devdev-docs.parky.co.jp)
Mobile Web MockMobile Web Mock 単一 HTML + Mapbox GL JSSingle HTML + Mapbox GL JS Cloudflare Pages (parky-app-mock-devdev-app-mock.parky.co.jp)

地図・位置情報Maps & geo

検索Search

AI・LLMAI & LLM

通知・外部サービスNotifications & externals

採用していない外部サービス: 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

ドキュメント・周辺ツールDocs & tooling

メモ: Note: クライアントが叩く API は Cloudflare Workers 上の /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).