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>
104 lines
5.4 KiB
Markdown
104 lines
5.4 KiB
Markdown
# Frontend cutover (Etap 9-11) — apply only after Etap 8 counts match
|
|
|
|
Two changes to `public/app-runtime.js`, both required together. Do not commit either until
|
|
`scripts/03-verify-counts.sql` shows matching counts on old vs new (14/11/53/52/460/18) and
|
|
the Etap 10 smoke test has passed against `https://api.caterium.ru` directly (before touching
|
|
the frontend).
|
|
|
|
## 1. Point the default at the new backend
|
|
|
|
```js
|
|
// public/app-runtime.js, line ~1989-1990
|
|
const DEFAULT_SUPABASE_URL = 'https://api.caterium.ru';
|
|
const DEFAULT_SUPABASE_KEY = '<NEW_ANON_KEY_FROM_.env>';
|
|
```
|
|
|
|
## 2. Auto-migrate devices that already have the OLD url cached in `localStorage`
|
|
|
|
Every Caterium device has `sunCloudV2Config` (and possibly the legacy `sunEnterpriseSettingsV1`)
|
|
in `localStorage`, which `loadConfig()` reads *before* falling back to `DEFAULT_SUPABASE_URL`.
|
|
Just changing the default does nothing for those devices — they keep talking to
|
|
`cksuehzcimitsxmeloes.supabase.co` forever unless we rewrite the stored value once. Insert this
|
|
right after the existing `DEFAULT_SUPABASE_KEY` line, and call it from inside `loadConfig()`
|
|
before the `return { ... }`:
|
|
|
|
```js
|
|
const DEFAULT_SUPABASE_URL = 'https://api.caterium.ru';
|
|
const DEFAULT_SUPABASE_KEY = '<NEW_ANON_KEY_FROM_.env>';
|
|
const LEGACY_SUPABASE_URL = 'https://cksuehzcimitsxmeloes.supabase.co';
|
|
|
|
function migrateLegacySupabaseUrl(value) {
|
|
// One-time, silent migration: any device still pointed at the retired managed
|
|
// Supabase project is switched to the self-hosted backend automatically. Users
|
|
// are never asked to clear localStorage or reconfigure anything.
|
|
if (String(value.url || '').trim().replace(/\/$/, '') === LEGACY_SUPABASE_URL) {
|
|
value.url = DEFAULT_SUPABASE_URL;
|
|
value.key = DEFAULT_SUPABASE_KEY;
|
|
}
|
|
return value;
|
|
}
|
|
```
|
|
|
|
Then in `loadConfig()`, wrap the legacy-settings merge:
|
|
|
|
```js
|
|
function loadConfig() {
|
|
let value = {};
|
|
try { value = JSON.parse(localStorage.getItem(CONFIG_KEY) || '{}') || {}; } catch (_) {}
|
|
if (!value.url || !value.key) {
|
|
try {
|
|
const legacy = JSON.parse(localStorage.getItem('sunEnterpriseSettingsV1') || '{}') || {};
|
|
const cloud = legacy.cloud || {};
|
|
value.url ||= String(cloud.supabaseUrl || '').trim();
|
|
value.key ||= String(cloud.anonKey || '').trim();
|
|
} catch (_) {}
|
|
}
|
|
value = migrateLegacySupabaseUrl(value); // <-- add this line
|
|
return {
|
|
url: String(value.url || DEFAULT_SUPABASE_URL || '').trim().replace(/\/$/, ''),
|
|
key: String(value.key || DEFAULT_SUPABASE_KEY || '').trim(),
|
|
...
|
|
```
|
|
|
|
`saveConfig()` (a few lines below) persists `config` back to `localStorage` on every config
|
|
change already, so the corrected URL/key get written back to `sunCloudV2Config` the first time
|
|
`loadConfig()` runs on each device — no explicit persistence call needed here, but if a save
|
|
is not triggered naturally during your smoke test, call `saveConfig()` once right after
|
|
`config = loadConfig();` at module init (line ~1992) to force-persist the migration immediately
|
|
rather than waiting for the next unrelated write.
|
|
|
|
## 3. Session invalidation is expected
|
|
|
|
Because the new stack uses a freshly generated `JWT_SECRET` (README decision, since the old
|
|
one can't be safely retrieved), any cached Supabase auth session (JWT) in `localStorage`
|
|
becomes invalid the moment the client points at the new URL — `supabase-js` will see a 401 on
|
|
its first call, clear the stale session automatically, and the user lands back on the Caterium
|
|
login screen. That matches the task's accepted trade-off: users log in once more with their
|
|
existing email + password (unchanged, since `auth.users.encrypted_password` is copied
|
|
byte-for-byte). No code change needed for this part — it's `supabase-js`'s default behavior.
|
|
|
|
## Checklist before running this cutover
|
|
|
|
- [ ] `scripts/03-verify-counts.sql` matches old vs new (14/11/53/52/460/18)
|
|
- [ ] `curl https://api.caterium.ru/rest/v1/`, `/auth/v1/`, `/storage/v1/`, `/functions/v1/` all respond (not connection errors)
|
|
- [ ] Realtime WebSocket connects: `wscat -c wss://api.caterium.ru/realtime/v1/websocket?apikey=<ANON_KEY>`
|
|
- [ ] `scripts/05-storage-sync.sh` finished, spot-checked photo URLs load
|
|
- [ ] `scripts/04-deploy-edge-function.sh` shows `caterium-create-employee` rejecting an
|
|
unauthenticated call with 401/400 (not 5xx)
|
|
- [ ] Manual login test against `https://api.caterium.ru` directly (temporarily point a local
|
|
dev copy of `app-runtime.js` at it) with one real existing account, confirm orders/
|
|
clients/catalog load
|
|
|
|
Only once every box above is checked: apply the two edits above, commit to `main` with a
|
|
message like `Cutover: point Caterium at self-hosted Supabase (api.caterium.ru)`, push. CI
|
|
(`Caterium QA` -> `Caterium Promote Production`) auto-promotes `main` to `production` on green,
|
|
and the existing Timeweb cron (`ops/timeweb/caterium-production-sync.sh`) deploys it to
|
|
`app.caterium.ru` within 5 minutes. Do NOT skip CI (`--no-verify` or manual production-branch
|
|
pushes) for this change — the QA workflow's Playwright e2e run is exactly the safety net you
|
|
want on a backend cutover.
|
|
|
|
After confirming `https://app.caterium.ru` works end-to-end with no `supabase.co` requests in
|
|
DevTools Network, leave the old managed Supabase project (`cksuehzcimitsxmeloes`) running
|
|
untouched as a cold backup, per the task's explicit instruction — do not pause or delete it
|
|
without separate owner confirmation.
|