- Create Kong declarative configuration for routing and authentication. - Implement initialization script to set up the database. - Add SQL migration for initializing roles, schemas, and seeding vehicle data. - Create leads and customers tables with appropriate policies and functions for CRM. - Seed admin user and configure storage bucket with RLS policies.
5.7 KiB
MC Cars – Dockerized Supabase CRM
Self-hosted Supabase stack + bilingual (DE/EN) public website + lead-management admin panel. Everything lives under this folder. Copying the folder to another machine and running docker compose up -d reproduces the stack bit-for-bit — all runtime state is under ./data/ bind mounts, no named volumes.
What's inside
| Service | Image | Purpose |
|---|---|---|
db |
postgres:15-alpine |
Postgres with wal_level=logical |
auth |
supabase/gotrue:v2.158.1 |
Email+password auth (signup disabled) |
rest |
postgrest/postgrest:v12.2.0 |
Auto-generated REST API |
storage |
supabase/storage-api:v1.11.13 |
S3-like bucket API (backed by ./data/storage) |
imgproxy |
darthsim/imgproxy:v3.8.0 |
On-the-fly image transforms |
realtime |
supabase/realtime:v2.30.23 |
Live postgres_changes subscriptions |
meta |
supabase/postgres-meta:v0.84.2 |
Schema introspection for Studio |
post-init |
postgres:15-alpine |
Idempotent bootstrap: seed admin + migrations |
kong |
kong:2.8.1 |
Single API gateway at :54321 |
studio |
supabase/studio |
Supabase dashboard (:3000) |
web |
local nginx:alpine build |
Public site + admin panel (:8080) |
Requirements
- Docker Desktop / Docker Engine with Compose v2
- Free ports:
3000,5432,8080,54321,54443
Run
cd 'c:\Coding\MC Cars GmbH'
docker compose up -d
First boot pulls ~1.5 GB of images and runs migrations (01-init.sql, post-boot.sql, 02-leads.sql). Give it 30–60 s to settle.
Stop / reset
docker compose down # stop, keep data
docker compose down -v # stop + delete named volumes (there are none anymore)
Remove-Item -Recurse -Force .\data # FULL wipe (needed to re-run first-boot migrations)
URLs
| Purpose | URL |
|---|---|
| Public website | http://localhost:8080 |
| Admin panel | http://localhost:8080/admin.html |
| Supabase Studio | http://localhost:3000 |
| API gateway (Kong) | http://localhost:54321 |
| Postgres | localhost:5432 |
Admin access is deliberately not linked from the public site. Bookmark it.
Credentials (dev defaults – rotate before any deployment)
| Account | Value |
|---|---|
| Admin bootstrap user | admin@mccars.local / mc-cars-admin |
| Postgres superuser | postgres / see POSTGRES_PASSWORD |
The admin is seeded with must_change_password = true in raw_user_meta_data. On first login the UI forces a rotation and refuses to reuse the bootstrap password. The real working password never equals .env.
Data model
public.vehicles— fleet, public-readable whereis_active.public.leads— booking form submissions.anonmayINSERTonly;authenticatedhas full CRUD. Status:new | qualified | disqualified.public.customers— created only by qualifying a lead. Hard FKlead_idpreserves the audit link to the originating lead.- RPCs:
qualify_lead(uuid, text),disqualify_lead(uuid, text),reopen_lead(uuid)— transactional,SECURITY INVOKER,authenticatedonly. - Realtime:
supabase_realtimepublication broadcasts inserts/updates on leads, customers, vehicles.
Portability
Runtime state under ./data/:
data/
├── db/ # Postgres cluster (bind mount)
└── storage/ # vehicle-photos bucket content
Everything else (config, migrations, frontend) is in the repo. Zip the folder, scp it, docker compose up -d — you have the same stack.
Project layout
MC Cars/
├── docker-compose.yml # full stack, bind-mount portability
├── .env # dev secrets / tunables
├── data/ # runtime state (git-ignored)
├── supabase/
│ ├── kong.yml # gateway routes (/auth, /rest, /realtime, /storage, /pg)
│ └── migrations/
│ ├── 00-run-init.sh # creates supabase service roles
│ ├── 01-init.sql # vehicles + bucket + seed cars
│ ├── post-boot.sql # admin user (must_change_password) + bucket row
│ └── 02-leads.sql # leads, customers, RPCs, realtime publication
├── frontend/
│ ├── Dockerfile # nginx + runtime anon-key injection
│ ├── nginx.conf
│ ├── index.html # public DE/EN site, booking form -> leads
│ ├── admin.html # auth-gated CRM
│ ├── app.js
│ ├── admin.js # realtime + qualify/disqualify + password change
│ ├── config.js # anon-only runtime config
│ ├── i18n.js
│ ├── styles.css
│ ├── impressum.html
│ └── datenschutz.html
├── AGENT.md # findings, conventions, traps
└── ARQUITECTURE.md # architecture deep-dive
See AGENT.md and ARQUITECTURE.md for everything deeper.