diff --git a/.claude/deploy-memory.md b/.claude/deploy-memory.md index a3f55bf..f40325d 100644 --- a/.claude/deploy-memory.md +++ b/.claude/deploy-memory.md @@ -30,18 +30,43 @@ Push git, le CI build + rollout auto (filter sur `landing/**`, `Dockerfile`, ## 2. App SaaS (`app.rubis.arthurbarre.fr`) -### Infra -- Deployment : `rubis-app` · Container : `app` · NodePort : `30110` +**Architecture en 2 services** : nginx en frontal (web) + API Node interne. + +``` +Traefik :443 → app.rubis.arthurbarre.fr:30110 → rubis-web (nginx) + ├─ / → SPA static (try_files) + └─ /api/* → rubis-api (ClusterIP :3333) +``` + +### rubis-web — frontend (NodePort 30110, exposé) +- Image : `git.arthurbarre.fr/ordinarthur/rubis-web` +- Container : `web` (nginx-alpine + SPA Vite dist) +- Sert /assets/* (cache 1y immutable), / (SPA fallback try_files) +- Reverse-proxy /api/* → rubis-api.rubis.svc.cluster.local:3333 +- Manifest : `k3s/app/web.yml` +- Workflow CI : `.gitea/workflows/deploy-web.yml` +- Source : `apps/web`, `packages/shared` + `apps/web/nginx.conf` + +### rubis-api — backend (ClusterIP, interne uniquement) +- Image : `git.arthurbarre.fr/ordinarthur/rubis-api` +- Container : `api` (Node 22 + AdonisJS V7) +- Workers BullMQ dans le même process (cf. `start/queue.ts`) - Init container `migrate` : `node ace.js migration:run --force` depuis `/app/apps/api/build` (idempotent) -- Sidecar Redis : Deployment `rubis-redis` (ClusterIP, PVC 1Gi local-path, - backend BullMQ + cache) -- Image : `git.arthurbarre.fr/ordinarthur/rubis-app` -- Domaine : https://app.rubis.arthurbarre.fr -- Manifests : `k3s/app/{deployment.yml,service.yml,redis.yml}` +- Probes K3s sur `/api/v1/health` +- Manifest : `k3s/app/api.yml` (Deployment + Service ClusterIP + ConfigMap) +- Workflow CI : `.gitea/workflows/deploy-api.yml` +- Source : `apps/api`, `packages/shared` + +### rubis-redis — backend BullMQ + cache (ClusterIP) +- Image : `redis:7.4-alpine` +- PVC 1Gi local-path, AOF on, maxmemory 256mb allkeys-lru +- Manifest : `k3s/app/redis.yml` +- Re-déployé par le workflow API (path filter inclut `redis.yml`) + +### Infra commune +- Domaine : https://app.rubis.arthurbarre.fr (DNS A 5413305619) - Route Traefik : `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis-app.yml.j2` -- Workflow CI : `.gitea/workflows/deploy-app.yml` -- Source : `apps/api`, `apps/web`, `packages/shared` (monorepo pnpm) ### Dépendances externes (déjà déployées) - **Postgres** : 10.10.10.3:5432, base `rubis_prod`, user `rubis` @@ -66,25 +91,31 @@ kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \ ``` ### Mise à jour -Push git, le CI build l'image (`Dockerfile.app`, multi-stage), apply les -manifests `k3s/app/`, set image, rollout auto. Filter sur `apps/**`, -`packages/**`, `Dockerfile.app`, `k3s/app/**`. +Push git → un (ou les deux) workflow(s) CI se déclenchent selon les paths +modifiés. Build+rollout indépendants. -### Particularités du Dockerfile.app -- `node ace build` plante en CI avec `ERR_UNKNOWN_FILE_EXTENSION` car +| Path modifié | Workflow déclenché | +|---|---| +| `apps/web/**`, `Dockerfile.web`, `k3s/app/web.yml` | deploy-web.yml | +| `apps/api/**`, `Dockerfile.api`, `k3s/app/api.yml`, `k3s/app/redis.yml` | deploy-api.yml | +| `packages/shared/**`, `pnpm-lock.yaml`, `tsconfig.base.json`, … | les deux | + +### Particularités du Dockerfile.api +- `node ace build` plante avec `ERR_UNKNOWN_FILE_EXTENSION` car `@poppinss/ts-exec` ne s'enregistre pas à temps avant l'import de - `bin/console.ts`. **Solution** : on appelle ace via `tsx` (esbuild), - qui gère nativement les `.ts` → `pnpm exec tsx ace.js build --ignore-ts-errors` + `bin/console.ts`. **Solution** : `pnpm exec tsx ace.js build + --ignore-ts-errors` (tsx = esbuild, gère .ts nativement). - `--ignore-ts-errors` car `tests/bootstrap.ts` référence un type généré - dans `.adonisjs/client/registry/schema.d.ts` qui arrive trop tard. Le - typecheck strict est exécuté côté CI séparément (`pnpm typecheck`). -- Vite build appelé directement (`pnpm exec vite build`) au lieu de - `pnpm build` qui fait `tsc -b && vite build` — le `tsc -b` plante sans - cache `.tsbuildinfo` à cause de @tanstack/router-core. -- SPA dist (`apps/web/dist`) copié dans `apps/api/build/public/` pour - être servi par le static middleware AdonisJS. Une route wildcard - (`start/routes.ts`) sert `index.html` pour les chemins non-API → SPA - routing TanStack Router. + tardivement (`.adonisjs/client/registry/schema.d.ts`). Le typecheck + strict est exécuté en CI séparée (`pnpm typecheck`). + +### Particularités du Dockerfile.web +- Vite build appelé directement (`pnpm exec vite build`) au lieu du + script `tsc -b && vite build` qui plante sans cache `.tsbuildinfo` à + cause de @tanstack/router-core. +- nginx.conf inclut le upstream `rubis-api.rubis.svc.cluster.local:3333`. + Si on renomme le service API, c'est ici qu'il faut mettre à jour + (en plus du manifest). --- @@ -92,16 +123,28 @@ manifests `k3s/app/`, set image, rollout auto. Filter sur `apps/**`, ```bash TAG=$(git rev-parse --short HEAD) -# Landing +# Landing (rubis.arthurbarre.fr) docker build --platform linux/amd64 -t git.arthurbarre.fr/ordinarthur/rubis:$TAG . docker push git.arthurbarre.fr/ordinarthur/rubis:$TAG kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \ -n rubis set image deploy/rubis rubis=git.arthurbarre.fr/ordinarthur/rubis:$TAG -# App -docker build --platform linux/amd64 -f Dockerfile.app -t git.arthurbarre.fr/ordinarthur/rubis-app:$TAG . -docker push git.arthurbarre.fr/ordinarthur/rubis-app:$TAG + +# App — web (frontend) +docker build --platform linux/amd64 -f Dockerfile.web \ + -t git.arthurbarre.fr/ordinarthur/rubis-web:$TAG . +docker push git.arthurbarre.fr/ordinarthur/rubis-web:$TAG kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \ - -n rubis set image deploy/rubis-app app=git.arthurbarre.fr/ordinarthur/rubis-app:$TAG + -n rubis set image deploy/rubis-web web=git.arthurbarre.fr/ordinarthur/rubis-web:$TAG + +# App — api (backend) +docker build --platform linux/amd64 -f Dockerfile.api \ + -t git.arthurbarre.fr/ordinarthur/rubis-api:$TAG . +docker push git.arthurbarre.fr/ordinarthur/rubis-api:$TAG +kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \ + -n rubis set image deploy/rubis-api api=git.arthurbarre.fr/ordinarthur/rubis-api:$TAG \ + && kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \ + -n rubis patch deploy/rubis-api --type=json \ + -p="[{\"op\":\"replace\",\"path\":\"/spec/template/spec/initContainers/0/image\",\"value\":\"git.arthurbarre.fr/ordinarthur/rubis-api:$TAG\"}]" ``` ⚠️ Cross-compile ARM Mac → linux/amd64 plante sur `@swc/core` au build de @@ -126,8 +169,9 @@ challenge LE). --- ## Déjà fait — NE PAS refaire -- Dockerfile (landing) + Dockerfile.app (app) -- Manifests `k3s/` (landing) + `k3s/app/` (app + Redis) +- Dockerfile (landing) + Dockerfile.web + Dockerfile.api (app split) +- nginx.conf (apps/web/nginx.conf) avec upstream rubis-api +- Manifests `k3s/` (landing) + `k3s/app/{api,web,redis}.yml` - Routes Traefik `rubis.yml.j2` + `rubis-app.yml.j2` - DNS OVH : A records `rubis` (id 5413044152) + `app.rubis` (id 5413305619) - Repo Gitea + secrets CI (`KUBECONFIG`, `REGISTRY_PASSWORD`) @@ -135,5 +179,6 @@ challenge LE). - Postgres : base `rubis_prod` + user `rubis` (10.10.10.3) - MinIO : bucket `rubis-prod-invoices` - Secret K3s `rubis-app-secrets` (APP_KEY, DB pwd, MinIO, Resend, Mistral) +- ConfigMap `rubis-api-config` (env non-sensibles) Les prochains `/deploy` font uniquement build + rollout via push git.