docs: update README, AGENT, ARQUITECTURE for 555xx ports, Portainer deploy, absolute paths

This commit is contained in:
Lago
2026-04-17 18:54:30 +02:00
parent 8b0a25f9c3
commit c1c9063996
3 changed files with 97 additions and 62 deletions
+37 -22
View File
@@ -7,9 +7,9 @@ A thorough walkthrough of how the MC Cars system is wired. Complements [AGENT.md
## 1. High-level diagram
```
Browser (localhost)
Browser (<host>)
│ │
:8080 (nginx) :54321 (Kong) :3000 (Studio)
:55580 (nginx) :55521 (Kong) :55530 (Studio)
│ │ │
┌──────┴──────┐ │ │
│ web │ │ │
@@ -32,7 +32,7 @@ A thorough walkthrough of how the MC Cars system is wired. Complements [AGENT.md
│ logical) │ └───────────┘
└──────────┘ ▲
│ │
./data/db ./data/storage
/mnt/.../data/db /mnt/.../data/storage
```
Dashed lifetime services (exit 0 once done): `post-init`.
@@ -46,8 +46,8 @@ Network: single user-defined bridge `mccars`. No host networking, no ingress con
### 2.1 Public visitor reads cars
```
browser → :8080/index.html (nginx, static)
browser → :54321/rest/v1/vehicles?select=*&is_active=eq.true
browser → :55580/index.html (nginx, static)
browser → :55521/rest/v1/vehicles?select=*&is_active=eq.true
→ Kong → rest (PostgREST) → postgres
→ RLS: "vehicles_select_active" (for anon, is_active=true only)
← JSON
@@ -59,7 +59,7 @@ No auth header except the anon `apikey`. Kong strips the key and forwards with t
```
browser → supabase.from('leads').insert({...})
→ :54321/rest/v1/leads (POST, apikey=anon)
→ :55521/rest/v1/leads (POST, apikey=anon)
→ PostgREST → postgres
→ RLS "leads_anon_insert": insert allowed, select denied
← 201 (no body returned to anon)
@@ -71,7 +71,7 @@ The frontend never reads leads back. If the INSERT fails (validation, RLS), the
```
browser(admin.html) → supabase.auth.signInWithPassword
→ :54321/auth/v1/token?grant_type=password
→ :55521/auth/v1/token?grant_type=password
→ Kong → gotrue → postgres(auth.users)
← access_token (JWT, aud=authenticated), refresh_token
← user.raw_user_meta_data.must_change_password (true on bootstrap)
@@ -79,7 +79,7 @@ browser(admin.html) → supabase.auth.signInWithPassword
if must_change_password:
user blocked in "Passwort setzen" screen
supabase.auth.updateUser({password, data:{must_change_password:false}})
→ :54321/auth/v1/user (PUT with Bearer access_token)
→ :55521/auth/v1/user (PUT with Bearer access_token)
→ gotrue updates auth.users
```
@@ -106,8 +106,8 @@ Realtime publication fires:
```
admin.js → supabase.storage.from('vehicle-photos').upload(path, file)
→ :54321/storage/v1/object/vehicle-photos/<path>
→ Kong → storage-api → ./data/storage
→ :55521/storage/v1/object/vehicle-photos/<path>
→ Kong → storage-api → /mnt/.../data/storage
← public URL served via imgproxy for on-the-fly transforms
admin.js → UPDATE vehicles SET photo_url = <public url>
```
@@ -204,7 +204,7 @@ Declarative config in `supabase/kong.yml`. One consumer per role:
Plugins pipeline: `cors``key-auth``acl` → proxy. Public routes (storage public objects) are whitelisted without `key-auth`.
Host port mapping: `54321:8000` (8000 blocked by Docker Desktop's wslrelay on Windows).
Host port mapping: `55521:8000` (8000 blocked by Docker Desktop's wslrelay on Windows; `555xx` range avoids collisions on busy Docker hosts).
---
@@ -212,7 +212,8 @@ Host port mapping: `54321:8000` (8000 blocked by Docker Desktop's wslrelay on Wi
```
frontend/
├── Dockerfile (nginx:alpine, writes config.js at boot)
├── Dockerfile (legacy, not used in Portainer deploy)
├── 99-config.sh (entrypoint: writes config.js at boot)
├── nginx.conf (serves /, gzip, cache headers)
├── index.html app.js (public site, anon key, persistSession:false)
├── admin.html admin.js (admin CRM, persistSession:true)
@@ -222,30 +223,44 @@ frontend/
└── datenschutz.html
```
- `config.js` is generated at container start (`envsubst`) from `SUPABASE_URL` and `SUPABASE_ANON_KEY` only. The service_role key is never mounted into the web container.
- `config.js` is generated at container start by `99-config.sh` (bind-mounted into `/docker-entrypoint.d/`) from `$SUPABASE_URL` and `$SUPABASE_ANON_KEY` only. The service_role key is never mounted into the web container.
- The `web` service uses `nginx:1.27-alpine` directly with bind-mounted files (no `build:` step). This is Portainer-compatible: updating the frontend is `git pull` + container restart.
- `admin.js` is ES modules, imports `@supabase/supabase-js` from `esm.sh` (CDN, pinned version). One Supabase client per page.
- State lives in a single `state` object. Admin re-renders the active tab on realtime events.
- Force-password-change modal is the only state that can preempt the rest of the admin UI after login.
---
## 7. Portability & deployment
## 7. Deployment
**The contract:** everything under `c:\Coding\MC Cars GmbH\` (or whatever you rename it to) is the full stack. Copy it anywhere Docker runs, bring up the stack.
**Host root:** `/mnt/user/appdata/mc-cars`
What's state (under `./data/`):
- `./data/db/` — Postgres cluster
- `./data/storage/` — object bucket files
All bind mounts in `docker-compose.yml` use absolute paths under that root. The compose has no `build:` steps — every image is pulled from a registry, making it Portainer-compatible.
What's state (under `/mnt/user/appdata/mc-cars/data/`):
- `data/db/` — Postgres cluster
- `data/storage/` — object bucket files
What's code/config (committed):
- `docker-compose.yml`, `.env` (dev defaults), `supabase/`, `frontend/`, `AGENT.md`, `ARQUITECTURE.md`, `README.md`
- `docker-compose.yml`, `.env`, `supabase/`, `frontend/`, `AGENT.md`, `ARQUITECTURE.md`, `README.md`, `.gitattributes`
**Portainer deployment:**
1. Clone repo to `/mnt/user/appdata/mc-cars`
2. `chmod +x` the `.sh` files
3. `mkdir -p data/{db,storage}`
4. Portainer → Stacks → Add stack → paste compose + env vars → Deploy
**Nginx Proxy Manager (single public domain):**
- Proxy `/``mccars-web:80` (or `<host>:55580`)
- Custom locations `/auth/v1/`, `/rest/v1/`, `/realtime/v1/`, `/storage/v1/``mccars-kong:8000` (or `<host>:55521`)
- Do **not** expose `/pg/` or Studio publicly
- Update `.env` URLs to `https://cars.yourdomain.com`
For real deployments:
1. Generate a new `JWT_SECRET` and matching `ANON_KEY` / `SERVICE_ROLE_KEY` (see Supabase self-hosting docs).
2. Set a strong `POSTGRES_PASSWORD`, `ADMIN_PASSWORD`.
3. Put Kong behind a TLS reverse-proxy (Caddy/Traefik/nginx) and set `SITE_URL`, `API_EXTERNAL_URL`, `SUPABASE_PUBLIC_URL` to the public https URLs.
4. Wire real SMTP for password-reset mail (`SMTP_*`).
5. Back up `./data/` on a schedule.
3. Wire real SMTP for password-reset mail (`SMTP_*`).
4. Back up `/mnt/user/appdata/mc-cars/data/` on a schedule.
---