caterium-app/ops/supabase-selfhosted/README.md
pavlov346346 88b7f55db4 Ops: add self-hosted Supabase stack for Timeweb Cloud migration
Full production-ready docker-compose stack (db, kong, auth, rest,
realtime, storage, imgproxy, meta, functions, studio) targeting
api.caterium.ru, plus bootstrap script for a fresh Cloud Server,
Caddy reverse-proxy config (HTTPS, WebSocket, upload limits), and
dump/restore/verify/storage-sync scripts for moving off the managed
Supabase project (cksuehzcimitsxmeloes). Does not touch public/ or
any live runtime behavior — frontend cutover is documented separately
in frontend-cutover.md and only applied after Etap 8 verification.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 16:20:45 +03:00

73 lines
5.8 KiB
Markdown

# Self-hosted Supabase for Caterium (Timeweb Cloud Server)
Target: `https://api.caterium.ru` — replaces `https://cksuehzcimitsxmeloes.supabase.co`.
This directory is deployable as-is on a fresh Ubuntu 22.04/24.04 **Timeweb Cloud Server (VPS with root)**.
It CANNOT run on the existing `timeweb-caterium` shared-hosting SSH account (`ci503744@vh470.timeweb.ru`) —
that account has no root, sudo is disabled (`no_new_privileges`), and there is no Docker daemon. Shared
hosting is fine for serving the static frontend (`public/`) but architecturally cannot host Postgres/Kong/
GoTrue/PostgREST/Realtime/Storage containers.
## What's here
| File | Purpose |
|---|---|
| `bootstrap-server.sh` | Run once (as root) on a fresh Cloud Server. Installs Docker, Docker Compose plugin, Caddy, ufw rules, clones this config, brings the stack up. |
| `docker-compose.yml` | The Supabase stack: db, kong, auth, rest, realtime, storage, meta, functions, studio, imgproxy. |
| `.env.example` | Every secret/setting the stack needs. Copy to `.env`, fill in, `chmod 600`. Never commit the real `.env`. |
| `volumes/api/kong.yml` | Kong declarative routing for `/rest/v1`, `/auth/v1`, `/storage/v1`, `/realtime/v1`, `/functions/v1`. |
| `volumes/db/init/` | One-time SQL run by the Postgres container on first boot (roles, `pg_cron`/extensions, JWT hook wiring). |
| `Caddyfile` | Reverse proxy for `api.caterium.ru`: automatic HTTPS (Let's Encrypt), WebSocket upgrade for Realtime, large upload limits for Storage, header forwarding. |
| `scripts/01-dump-old.sh` | Runs against the OLD Supabase project. Produces `roles.sql`, `schema.sql`, `data.sql` via `supabase db dump`. |
| `scripts/02-restore-new.sh` | Restores those dumps into the new self-hosted Postgres via `psql`. |
| `scripts/03-verify-counts.sql` | Row-count comparison old vs new (auth.users, orders, order_items, clients, catalog_items, storage.objects). |
| `scripts/04-deploy-edge-function.sh` | Copies `supabase/functions/caterium-create-employee` and `caterium-platform-auth-admin` into the running `functions` container's mounted volume and reloads it. |
| `scripts/05-storage-sync.sh` | Copies Storage objects (binary files) from the old project's Storage S3-compatible/API to the new bucket, preserving paths. |
## Order of operations
1. Provision the Cloud Server (see "Manual step required" below). Point DNS `api.caterium.ru` A-record at its IP.
2. `scp` this whole `ops/supabase-selfhosted/` directory to the server, or `git clone` the repo there.
3. `sudo bash bootstrap-server.sh` — installs Docker/Caddy, brings up the stack with placeholder secrets.
4. Fill in real secrets in `.env` (JWT secret, DB password, SMTP, dashboard user/pass) — generate fresh ones with
`openssl rand -base64 32` unless told to reuse the old JWT secret. `docker compose up -d` again to apply.
5. Run `scripts/01-dump-old.sh` (needs `OLD_DB_URL` — the old project's Postgres connection string with password,
from Supabase Dashboard → Project Settings → Database → Connection string → URI, "session pooler" disabled,
direct connection).
6. Run `scripts/02-restore-new.sh` against the new stack's `db` container.
7. Run `scripts/03-verify-counts.sql` old vs new and compare against the expected counts (14 / 11 / 53 / 52 / 460 / 18).
8. Run `scripts/05-storage-sync.sh` to copy the 18 Storage objects and their metadata/policies.
9. Run `scripts/04-deploy-edge-function.sh` to bring `caterium-create-employee` online, verify with a test call.
10. Only after 5-9 pass: flip `DEFAULT_SUPABASE_URL` in `public/app-runtime.js`, ship the `localStorage` migration
shim (see `ops/supabase-selfhosted/frontend-cutover.md`), commit to `main`, let CI promote to `production`.
## Manual step required right now (nothing beyond this is blocked)
I do not have a Timeweb Cloud API token or root SSH credentials to any Timeweb **Cloud Server** (VPS) — only to
the existing shared-hosting account used for the static frontend, which cannot run Docker. To unblock steps 1-10:
**Either**
- **(A)** In the Timeweb Cloud panel (https://timeweb.cloud/), create a Cloud Server: Ubuntu 22.04, minimum
2 vCPU / 4 GB RAM / 50 GB NVMe (recommended: 4 vCPU / 8 GB for headroom), public IPv4, and add my SSH public
key as an authorized key OR give me the root password so I can add my own key. Then tell me the server's IP.
- **(B)** Generate a Timeweb Cloud API token (panel → API → "Токены доступа" → create token with Cloud Servers
write scope) and give it to me; I will provision the server via the Timeweb API myself
(`POST https://api.timeweb.cloud/api/v1/servers`).
Whichever you pick, also add a DNS A-record: `api.caterium.ru` → the new server's IP (in whatever DNS provider
currently hosts `caterium.ru`'s zone — outside Cloudflare per your instruction not to put Cloudflare in the
backend path).
**Separately**, to actually migrate the data (step 5), I need the OLD Supabase project's direct Postgres
connection string with the real database password (Supabase Dashboard → Project Settings → Database →
Connection string → URI, non-pooled). I cannot retrieve this myself — it's not in any file on this machine and
I won't ask you to paste a password into a browser form on my behalf. Paste the connection string here in chat,
or set it as `OLD_DB_URL` in the shell before I run `scripts/01-dump-old.sh` for you. If you'd rather I don't
see the raw password in chat, run `scripts/01-dump-old.sh` yourself with `OLD_DB_URL` exported and send me the
three resulting `.sql` files (or their location on this machine) and I'll take it from there.
I will use a **freshly generated JWT secret** for the new stack (per your instruction that this is acceptable) —
existing sessions will be invalidated and all 14 users will need to log in once more with their existing
email+password (which migrate unchanged, since `auth.users.encrypted_password` is copied byte-for-byte from
the dump).