Helt självhostat: plattformsbackend, Postgres i klustret, klientläge
Sista externa beroendet borta — hela stacken kör i eget kluster: - Ny plattformstjänst (services/plattform): egen inloggning (registrering + inloggning, lösenord bcrypt-hashade med pgcrypto, HS256-JWT), append-only händelse-API för synken (endast insert, on conflict do nothing) och publik delningsendpoint för Live Share med samma filtrering som Supabase-funktionen. Fail closed utan hemligheter; inga update/delete-operationer exponeras. - Postgres som StatefulSet med PVC och init-schema; append-only garanterat i databasen med triggers på både händelser och ärenden — historik kan inte ändras oavsett ansluten roll. CloudNativePG rekommenderas för produktion i docs. - JWT-hemligheten delas mellan plattform och AI-orkester (orkestern läser JWT_SECRET med SUPABASE_JWT_SECRET som fallback) — flödet livetestat: plattformens token accepteras av orkestern, fel hemlighet och utgångna tokens avvisas. - Klienten får självhostat läge via VITE_PLATTFORM_URL: inloggnings- panel på startsidan, synk mot plattformens API (samma konfliktfria flätning), Live Share via /api/delad och AI via klustrets orkester. Utan variabeln är Supabase-läget oförändrat. - Ingress routar /api/ai → orkestern, /api + /halsa → plattformen, / → webben; kustomize inkluderar hela stacken; CI bygger alla tre bilderna; DRIFT.md omskriven för självhostad drift. Verifierat: 28 vitest-tester gröna (inkl. append-only-triggers och API-ytan), plattformstjänsten livetestad (hälsa, 401, fail closed), JWT-paritet mellan tjänsterna testad, alla manifest YAML-validerade, produktionsbygge ok, Playwright-röktest grönt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Plattformstjänsten: självhostad auth, händelse-API och Live Share-delning.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: plattform
|
||||
namespace: guidad-felsokning
|
||||
labels: { app: plattform }
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels: { app: plattform }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: plattform }
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
containers:
|
||||
- name: plattform
|
||||
image: ghcr.io/ORGANISATION/guidad-felsokning-plattform:latest # ersätt med ert registry
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef: { name: felsokning-hemligheter, key: jwt-secret }
|
||||
- name: POSTGRES_LOSENORD
|
||||
valueFrom:
|
||||
secretKeyRef: { name: felsokning-hemligheter, key: postgres-losenord }
|
||||
- name: DATABASE_URL
|
||||
value: postgresql://plattform:$(POSTGRES_LOSENORD)@postgres:5432/felsokning
|
||||
resources:
|
||||
requests: { cpu: 100m, memory: 128Mi }
|
||||
limits: { cpu: "1", memory: 256Mi }
|
||||
readinessProbe:
|
||||
httpGet: { path: /halsa, port: 8080 }
|
||||
initialDelaySeconds: 3
|
||||
livenessProbe:
|
||||
httpGet: { path: /halsa, port: 8080 }
|
||||
periodSeconds: 15
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities: { drop: [ALL] }
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: plattform
|
||||
namespace: guidad-felsokning
|
||||
spec:
|
||||
selector: { app: plattform }
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: plattform
|
||||
namespace: guidad-felsokning
|
||||
spec:
|
||||
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: plattform }
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target: { type: Utilization, averageUtilization: 70 }
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: plattform
|
||||
namespace: guidad-felsokning
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels: { app: plattform }
|
||||
Reference in New Issue
Block a user