Files
alva/infra/k8s/plattform.yaml
T
Claude 3a0be7261e Märkesspecifika kopplingar — kunden lägger in sina egna credentials
Verkstaden har redan sina avtal: Volvo-verkstaden har VIDA, VAG-verkstaden
har erWin, den fria verkstaden har en fordonsdataleverantör. Kopplingarna
konfigureras därför av kunden själv under Inställningar, med sina egna
uppgifter — vi tillhandahåller ramen, inte kontot.

Uppgifterna når aldrig webbläsaren. De krypteras med AES-256-GCM
(INTEGRATION_NYCKEL) innan de skrivs till tabellen integrationer, och
API:t returnerar hemliga fält maskerade. Alla uppslag görs av servern.
Saknas krypteringsnyckeln sparas ingenting alls — 503 och en förklaring i
gränssnittet i stället för klartext i databasen. Endast
systemadministratören hanterar uppgifterna; kopplingarna är
organisationsknutna som all annan ärendedata.

Leverantörer är data, inte kod: URL-mall, autentiseringstyp
(bearer/header/basic/query) och svarsmappning beskrivs i
services/plattform/integrationer.json, utbytbar via ConfigMap
(INTEGRATIONER_FIL). Nya märken läggs till utan att appen byggs om.

Varje uppslag skriver senast_testad och senaste_status på kopplingen, så
ett utgånget abonnemang syns i inställningarna i stället för att ge tysta
tomma svar.

Två latenta krascher hittade av klicktestet och åtgärdade: TextFalt och
UNDANTAGSORSAKER användes utan import. vite build typkontrollerar inte,
så de passerade bygget — därav nya npm-skriptet typkontroll, nu del av
verifieringen.

Verifierat: 80 vitest-tester, typkontroll, eslint, OpenAPI-validering,
integrationstest mot riktig Postgres (rollstyrning, kryptering i vila,
maskering, organisationsisolering, fail closed, borttagning) och
klickgenomgång mot en körande plattform.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-03 12:50:44 +00:00

87 lines
2.6 KiB
YAML

# 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 }
# Krypteringsnyckel för kundernas märkesspecifika credentials
# (32 byte hex eller base64). Saknas den sparas inga uppgifter
# alls — tjänsten failar closed i stället för att lagra i klartext.
- name: INTEGRATION_NYCKEL
valueFrom:
secretKeyRef: { name: felsokning-hemligheter, key: integration-nyckel }
- 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 }