docs: update README, AGENT, ARQUITECTURE for 555xx ports, Portainer deploy, absolute paths
This commit is contained in:
+37
-22
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user