feat: platform backbone and field capture, first part (checkpoint)

Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks:
- run roles api/worker/job/all with shared startup checks (Task 1)
- schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2)
- audit, notifications and domain events written in the same transaction (Task 3)
- dispatcher with SKIP LOCKED claims, retries and stale release (Task 4)
- worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5)
- worker process and entry points (Task 6)
- throttler counters in Postgres (Task 19)
- separate keys per purpose with key ring (Task 20)
- public origin page without exact location (Task 22)
- shop keys split into read and write (Task 23)
- Kustomize base, components, overlay encry, CI validation (Task 26)

Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks:
- brood frames, read endpoints, apiary overview and offline pack
- network basics, offline queue v3, navigation sheets, form building blocks
- new inspection with last-time hints and weather, varroa per day,
  treatment prefill, apiary page with round
- inspection photo (Task 10) is implemented, review pending

Not rolled out. Remaining tasks follow in later commits.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-13 17:57:54 +02:00
1 parent 10cbe9bf2d
commit c01a38835a
340 files changed
+25695 -1808

No files matched your search

+132
View File
@@ -0,0 +1,132 @@
# PostgreSQL wie im Cluster (uebernommen 2026-10-13 per kubectl get, nur
# lesend). Selektor und Volumes eines StatefulSets sind unveraenderlich, und
# das Repo darf die Live-Haertung nie zuruecknehmen: Aenderungen hier immer
# erst gegen `kubectl -n apicore get sts apicore-postgres -o yaml` pruefen.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: apicore-postgres
labels:
app.kubernetes.io/name: apicore-postgres
spec:
serviceName: apicore-postgres
replicas: 1
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
app.kubernetes.io/name: apicore-postgres
template:
metadata:
labels:
app.kubernetes.io/name: apicore-postgres
encry.de/tier: database
spec:
automountServiceAccountToken: false
enableServiceLinks: false
terminationGracePeriodSeconds: 120
securityContext:
seccompProfile:
type: RuntimeDefault
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: media-gpu
operator: NotIn
values: ["true"]
- weight: 50
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: [rkem01]
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
encry.de/tier: database
volumes:
# Statisches Volume (data-pvc.yaml), keine volumeClaimTemplates.
- name: data
persistentVolumeClaim:
claimName: apicore-postgres-data
# Root-Dateisystem ist nur lesbar: Socket, /tmp und Shared Memory
# liegen in begrenzten emptyDirs.
- name: postgres-runtime
emptyDir:
medium: Memory
sizeLimit: 16Mi
- name: tmp
emptyDir:
sizeLimit: 64Mi
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 256Mi
containers:
- name: postgres
image: postgres:18.6-alpine
imagePullPolicy: IfNotPresent
ports:
- name: postgres
containerPort: 5432
envFrom:
- secretRef:
name: apicore-postgres-secret
env:
# Unterordner statt Mount-Root (Layout von Postgres 18): initdb
# verlangt ein leeres Verzeichnis, der Mount-Root ist es nicht immer.
- name: PGDATA
value: /var/lib/postgresql/18/docker
securityContext:
# uid/gid 70 = postgres im Alpine-Image, passend zum SMB-Mount
# (uid=70,gid=70) des Volumes.
runAsUser: 70
runAsGroup: 70
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: data
mountPath: /var/lib/postgresql
- name: postgres-runtime
mountPath: /var/run/postgresql
- name: tmp
mountPath: /tmp
- name: dshm
mountPath: /dev/shm
# Bis zu 10 Minuten Start (Recovery nach Absturz), danach liveness
# mit viel Geduld: ein Neustart mitten in einer Recovery schadet mehr.
startupProbe:
exec:
command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"']
failureThreshold: 60
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"']
failureThreshold: 10
periodSeconds: 20
timeoutSeconds: 10
readinessProbe:
exec:
command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"']
failureThreshold: 3
periodSeconds: 10
timeoutSeconds: 5
resources:
requests:
cpu: 30m
memory: 128Mi
limits:
cpu: "1"
memory: 1Gi