d3ro-voice/docs/map/09-supabase-backend.md
Yun Chan eedd127ea7
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Failing after 1m13s
ci / 워크스페이스 빌드 검증 (push) Has been skipped
ci / 모바일 린트·타입·Jest (push) Failing after 1m4s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 37s
ci / .NET API 서버 테스트 (push) Successful in 27s
deploy-site / deploy (push) Failing after 20s
refactor(billing): remove Stripe; payments are Payple (web) and Google Play (mobile)
Stripe is not used. Keeping its checkout, portal and webhook paths meant a
second payment provider, a second return-URL format and dead UI.

- Delete the stripe-checkout, stripe-portal and stripe-webhook functions and
  their config; billing-catalog serves Payple prices only, and the web parser
  rejects a catalog that still mixes in Stripe prices.
- Web: drop the Stripe checkout/portal buttons, provider toggle and return
  notices; billing shows Payple only. Past rows with provider='stripe' are
  still displayed ("Stripe (종료)") with a support contact instead of a portal.
- Desktop: delete the Stripe checkout modal, payment IPC channels, preload
  namespace and their types; "Remove ads with Pro" opens the web billing page
  via license.openBilling. Support/refund copy names Payple.
- billingUrl() loses the Stripe-only success/canceled result option; the
  Deno contract is regenerated.
- Migrations and the DB's accepted provider values are untouched (history).
- Docs and the backlog record the removal (MON-04, EXT-STRIPE-01, GAP-BILL-03).

Verified: typecheck (desktop/web/admin/api-client/mobile), contract:check,
deno check all functions, deno test 80/80, desktop 1478/1480 on the Electron
runtime (2 known environment failures), web and admin builds, release
metadata and mobile boundary self-tests, eslint on changed files.
2026-09-26 20:56:18 +09:00

100 lines
6.2 KiB
Markdown

# 09 — Backend: Supabase + Cloudflare Map
> Surfaces: `server/supabase` (Postgres + Deno Edge Functions), `server/cloudflare-worker` (edge gateway)
> Role: canonical product data, auth, RLS, storage, AI proxies, billing, notifications
---
## 1. Supabase project (`server/supabase/`)
- `config.toml` — project_id `d3ro-voice`; local ports API 55321 / DB 55322 / Studio 55323 / Inbucket 55324; DB major v17; storage 50 MiB; auth `site_url` localhost:5173, redirects include `https://d3ro.chanpaca.net` and `d3ro-voice://auth-callback`; external providers Google/GitHub/Apple; per-function `verify_jwt` settings; analytics off.
- `seed.sql`, `migrations/` (63), `functions/` (~27), `functions/_shared/`, `tests/`, `deno.json`.
> Note: root `supabase/` contains only empty scaffolding (`.branches/`, `snippets/`). The real project lives under `server/supabase/`.
### 1.1 Migrations (62) — thematic groups
| Theme | Examples |
|---|---|
| Core schema + RLS + auth triggers | initial tables, profiles, triggers |
| Storage buckets | audio, meeting documents, exports |
| Teams | team invites, membership, roles, `team_activities` feed (`20260913000033`) |
| Knowledge / RAG | `knowledge_documents`, `knowledge_chunks`, pgvector |
| Notifications / push | push tokens, durable outbox |
| Billing | Payple, subscriptions, payment provider events/operations (`'stripe'` provider values kept only for historical rows) |
| Admin | admin roles, audit log, atomic admin RPCs |
| Mobile platform | mobile platform/monetization/runtime integrity |
| Commands | atomic command reorder |
| Devices | device registration + revocation |
| Content reporting | report reasons, generation receipts |
| Meetings | meeting documents |
| STT quota | atomic quota reservations (00026) |
| Ads | ad reward receipt replay protection (00028) |
Migration numbering referenced in SSOT goes up to `00028`; CI verifies `migration-up` + shadow replay.
### 1.2 Edge Functions (~27)
| Function | Purpose |
|---|---|
| `stt-proxy` | User STT gateway: auth, atomic quota reservation/refund, provider fallback, fail-closed |
| `llm-proxy` | LLM gateway (Claude/OpenAI) |
| `realtime-token` | OpenAI Realtime ephemeral token (tier-gated, session quota) |
| `generate-meeting-document` | AI document generation (minutes/report/idea-note/mindmap) |
| `embed-chunks` / `search-knowledge` | RAG embeddings + semantic search |
| `content-report` | AI content reporting |
| `team-invite` / `team-accept` | Team invitations |
| `send-push` | Push delivery |
| `account-delete` | Account deletion cascade + provider unlink |
| `admin-users` / `admin-subscriptions` / `admin-payments` / `admin-audit-log` | Admin operations |
| `billing-catalog` | Server pricing catalog. Prices/quotas read `PLAN_PRICE_KRW`/`PLAN_QUOTA` from `functions/_shared/core-contract.generated.ts`, generated from `packages/core/src/plan-catalog.ts` by `scripts/ci/sync-core-contract.mjs` (`npm run contract:check` fails on drift; added 2026-09-26, Wave 3, 88f24d8) |
| `payple-checkout` / `payple-manage` / `payple-renew` / `payple-webhook` | Payple billing (Korea) |
| `iap-verify` | Google Play / App Store purchase verification |
| `admob-ssv` | AdMob server-side verification + reward ledger |
| `google-play-rtdn` | Play Real-time Developer Notifications |
Shared contracts in `functions/_shared/`: admin, auth, audit, cors, quota, payple, push, llm, stt, team, generation-receipt, google-play, pubsub, generative-ai-safety (+ `*.test.ts`).
### 1.3 Tests (`server/supabase/tests/`)
Integration/E2E: content-report red e2e, content-reporting (ps1/sql), mobile platform/recording/reward-race, payment provider, mobile release preflight, public mobile runtime, push claim/outbox, team push security, STT quota.
---
## 2. Cloudflare worker (`server/cloudflare-worker/`)
- `wrangler.toml` — worker `d3ro-voice-api`, compat 2024-04-01, `BACKEND_ORIGIN=http://192.168.0.39:5050`, optional custom domain route (commented).
- `src/index.ts` — CORS preflight, `/worker-health`, forwards to backend origin with `X-Forwarded-*` / `X-D3RO-Edge-Proxy` headers.
Cloudflare Tunnel `kd-nas` maps public hostnames to NAS services.
---
## 3. Data model (product tables, high level)
Canonical product data lives in Supabase Postgres with RLS:
- **Identity:** `auth.users` + `profiles`, roles/claims, identity links.
- **Content:** `history`, `meetings`, `meeting_memos`, `meeting_documents`, `memos`, `dictionary`, `commands`/instructions, `templates`.
- **Knowledge:** `knowledge_documents`, `knowledge_chunks` (pgvector).
- **Teams:** teams, members, invites, activity feed (`team_activities`, RPC-only writes, realtime-enabled).
- **Delivery:** `devices`, `push_tokens`, push outbox.
- **Monetization:** `subscriptions`, `payment_provider_events/operations`, `ad_reward_claims`, IAP receipts, generation receipts.
- **Ops:** `audit_log` + admin operation records.
- **Portability:** `portable_exports` (+ storage).
Shared TS types for these live in `packages/api-client` (SSOT).
---
## 4. Backend status summary
- Auth (email + Google/GitHub/Apple), RLS, storage, realtime: **implemented**; production Auth + Google provider entry verified GREEN; GitHub/Apple provider secrets and mobile consent callback pending (external).
- STT/LLM proxies: **implemented** and fail-closed (no synthetic transcripts); atomic quota reservations verified with 20-way concurrency.
- Billing (Payple + IAP verify + webhooks/RTDN; Stripe removed 2026-09-26): **implemented**; live provider end-to-end and Payple webhook signature verification pending.
- Ads (AdMob SSV, rewarded ledger, replay protection): **implemented** (Edge v13 ACTIVE); production AdMob serving blocked externally (review/serving limits/store link/payment profile).
- Push: Supabase owns tokens/devices/outbox/retries. Transports implemented for **FCM, Web Push (VAPID + RFC 8291), and APNs (.p8 token)**; the Cloudflare Worker cron drains the outbox every minute. Android still requires FCM at the device. Details: `docs/deployment/push-transport-without-firebase.md`.
- Content safety: generation receipts + `content-report` **implemented**.
- Data portability (`account-delete`, export/restore): **implemented**.
External gates are enumerated in `docs/v3/MOBILE_APP_COMPLETION_SSOT.md` §0/§6 and mirrored in `11-gap-backlog.md` (`EXT-*`, `BE-*`).