task-11: complete QA + hardening with resilience fixes

- Created comprehensive QA checklist covering edge cases (missing EXIF, timezones, codecs, corrupt files)
- Added ErrorBoundary component wrapped around TimelineTree and MediaPanel
- Created global error.tsx page for unhandled errors
- Improved failed asset UX with red borders, warning icons, and inline error display
- Added loading skeletons to TimelineTree and MediaPanel
- Added retry button for failed media loads
- Created DEPLOYMENT_VALIDATION.md with validation commands and checklist
- Applied k8s recommendations:
  - Changed node affinity to required for compute nodes (Pi 5)
  - Enabled Tailscale LoadBalancer service for MinIO S3 (reliable Range requests)
  - Enabled cleanup CronJob for staging files
This commit is contained in:
OpenCode Test
2025-12-24 12:45:22 -08:00
parent 232b4f2488
commit 4e2ab7cdd8
13 changed files with 1444 additions and 131 deletions

View File

@@ -140,6 +140,19 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- end -}}
{{- define "tline.registryServer" -}}
{{- if .Values.registrySecret.server -}}
{{- .Values.registrySecret.server -}}
{{- else -}}
{{- /* Derive registry host from image repository (first path segment). */ -}}
{{- $repo := .Values.images.worker.repository | default .Values.images.web.repository | default "" -}}
{{- if not $repo -}}
{{- fail "registrySecret.server is required when no images.*.repository is set" -}}
{{- end -}}
{{- (first (regexSplit "/" $repo -1)) -}}
{{- end -}}
{{- end -}}
{{- define "tline.imagePullSecrets" -}}
{{- $secrets := .Values.imagePullSecrets | default (list) -}}
{{- if .Values.registrySecret.create -}}

View File

@@ -29,8 +29,8 @@ metadata:
labels:
{{ include "tline.labels" . | indent 4 }}
type: kubernetes.io/dockerconfigjson
{{ $server := required "registrySecret.server is required" .Values.registrySecret.server -}}
{{ $user := .Values.registrySecret.username | default "" -}}
{{ $server := include "tline.registryServer" . -}}
{{ $user := required "registrySecret.username is required" .Values.registrySecret.username -}}
{{ $pass := required "registrySecret.password is required" .Values.registrySecret.password -}}
{{ $email := .Values.registrySecret.email | default "" -}}
{{ $auth := printf "%s:%s" $user $pass | b64enc -}}

View File

@@ -17,11 +17,10 @@ scheduling:
compute:
affinity:
nodeAffinity:
# Prefer compute nodes when they exist, but don't require them.
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
# Require compute nodes (Pi 5). Pi 3 has capacity=low:NoExecute taint.
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-class
operator: In
values:
@@ -88,9 +87,10 @@ imagePullSecrets: []
registrySecret:
create: false
name: "" # defaults to <release>-<chart>-registry
server: "" # e.g. registry.lan:5000
username: ""
password: ""
# Registry host. If empty, derived from images.*.repository (first path segment).
server: "" # e.g. gitea-gitea-http.taildb3494.ts.net
username: "" # required when create=true
password: "" # required when create=true
email: ""
web:
@@ -169,7 +169,7 @@ minio:
# This can be more reliable for streaming / Range requests depending on
# Tailscale operator + cluster behavior.
tailscaleServiceS3:
enabled: false
enabled: true
hostnameLabel: minio
tags: []
extraAnnotations: {}
@@ -240,7 +240,7 @@ jobs:
cronjobs:
cleanupStaging:
enabled: false
enabled: true
schedule: "0 4 * * *"
# Remove objects under `staging/` older than this many days.
# This CronJob must never touch `originals/`.