{{- $replicas := include "convertx.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.convertx.replicas) -}}
{{- $startup := fromYaml (include "convertx.startupProbe" .Values.convertx.startupProbe) -}}
{{- $autoDelete := include "convertx.autoDeleteEveryNHours" .Values.convertx.autoDeleteEveryNHours -}}
{{- $registration := include "convertx.boolString" (dict "value" .Values.convertx.accountRegistration "default" false "path" "convertx.accountRegistration") -}}
{{- $unauthenticated := include "convertx.boolString" (dict "value" .Values.convertx.allowUnauthenticated "default" false "path" "convertx.allowUnauthenticated") -}}
{{- $httpAllowed := include "convertx.boolString" (dict "value" .Values.convertx.httpAllowed "default" false "path" "convertx.httpAllowed") -}}
ConvertX {{ .Chart.AppVersion }} — release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.

{{- if .Values.ingress.enabled }}

ACCESS
  https://{{ .Values.ingress.domain }}

  The certificate is requested by cert-manager through the "{{ .Values.ingress.clusterIssuer }}"
  cluster issuer. Until it has been issued the browser shows a warning; follow it with

    kubectl -n {{ .Release.Namespace }} get certificate tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress
{{- else }}

ACCESS — NO INGRESS
  ingress.enabled is false, so this release has no Ingress and no certificate,
  and neither ingress.clusterIssuer nor ingress.domain is needed. ConvertX has
  no base-URL setting of its own — it builds no absolute links — so nothing
  outside the Ingress reads the domain in this chart.

  Reach the release inside the cluster with

    kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-{{ .Chart.Name }}-service 8080:80

  and open http://localhost:8080. Note that the session cookie is marked
  "secure"{{ if eq $httpAllowed "true" }} — except that convertx.httpAllowed is true in this release, which
  clears that flag, so logging in over plain http works{{ else }}, so logging in over plain http will NOT work: the browser
  refuses to store the cookie. convertx.httpAllowed: true clears that flag{{ end }}.
{{- end }}
{{ if eq $replicas "0" }}
SCALED TO ZERO
  {{ if .Values.scaleDown }}scaleDown: true{{ else }}convertx.replicas: 0{{ end }} — the StatefulSet runs with replicas: 0, so the endpoint
  above does not answer until you set it back and upgrade again. PVC, Secret{{ if .Values.ingress.enabled }},
  Service and Ingress stay in place, so neither the conversions nor the
  certificate are touched.{{ else }} and
  Service stay in place, so the conversions on the PVC are untouched.{{ end }}
{{ end }}
WHOEVER OPENS THE PAGE FIRST BECOMES THE ADMINISTRATOR
  A fresh ConvertX has no users, and with no users it serves a SETUP FORM to
  every visitor — no password, no invitation, nothing to bypass. The first
  account created that way owns the instance, and convertx.accountRegistration
  does not gate it: that value decides whether accounts TWO and onwards can be
  created, not the first one.

  So on a fresh install, register yourself before the DNS name is reachable by
  anyone else. There is no second chance and no "reset admin" command; the
  repair is deleting the users row from the SQLite database on the PVC.
{{- if eq $unauthenticated "true" }}

  This release has convertx.allowUnauthenticated: true, so the point above is
  moot in the worst way — see the warning further down.
{{- end }}

THE 1PASSWORD ITEM MUST CARRY THE FIELD THE CHART READS
  secrets.jwtSecretRef points at
    {{ .Values.secrets.jwtSecretRef }}
  The 1Password operator materializes that item as the Kubernetes Secret
  {{ .Release.Name }}-{{ .Chart.Name }}-secret, with one Secret key per FIELD of the item. So the field
  name is not free-form: this chart reads exactly one field, and it must be
  spelled

    jwt-secret       a long random string — openssl rand -hex 32

  An item without it does not fail the install. The release goes through and
  the pod then hangs in CreateContainerConfigError naming the key it could not
  find. Watch the materialization with

    kubectl -n {{ .Release.Namespace }} get onepassworditem {{ .Release.Name }}-{{ .Chart.Name }}-secret
    kubectl -n {{ .Release.Namespace }} get secret {{ .Release.Name }}-{{ .Chart.Name }}-secret

  WHY THIS VALUE IS REQUIRED RATHER THAN OPTIONAL: ConvertX signs its session
  cookies with JWT_SECRET, and when the variable is unset it quietly generates
  a random one PER PROCESS. Every restart — a node drain, an image update, an
  OOM kill — would then invalidate every session, with no log line and no
  error. Rotating the field has the same effect on purpose; after a rotation:

    kubectl -n {{ .Release.Namespace }} rollout restart statefulset/{{ .Release.Name }}-{{ .Chart.Name }}-sfs

  Editing the value INSIDE the item does not roll the pod by itself (Helm
  never sees a secret's content). Pointing secrets.jwtSecretRef at a DIFFERENT
  item does — the pod template carries a checksum over the rendered
  OnePasswordItem.

TWO SWITCHES THAT OPEN THE INSTANCE UP
  convertx.accountRegistration    {{ $registration }}
  convertx.allowUnauthenticated   {{ $unauthenticated }}

  Upstream warns explicitly against enabling either on an instance reachable
  from the internet, and this chart puts a public DNS name in front by
  default. accountRegistration lets anyone with the URL create an account;
  allowUnauthenticated removes the login entirely, so anyone who reaches the
  Service can upload files and read every conversion on the volume.
{{- if or (eq $registration "true") (eq $unauthenticated "true") }}

  *** {{ if and (eq $registration "true") (eq $unauthenticated "true") }}BOTH OF THEM ARE ON{{ else }}ONE OF THEM IS ON{{ end }} IN THIS RELEASE. ***
{{- if eq $unauthenticated "true" }}
  convertx.allowUnauthenticated: true — there is no login. Anyone who can
  reach {{ if .Values.ingress.enabled }}https://{{ .Values.ingress.domain }}{{ else }}the Service{{ end }} can use it and read other people's files.
{{- end }}
{{- if eq $registration "true" }}
  convertx.accountRegistration: true — anyone with the URL can sign up.
{{- end }}
  If this instance is reachable from the internet, that is almost certainly
  not what you wanted.
{{- end }}

RETENTION IS A VALUE, AND IT IS THE ONLY THING THAT EMPTIES THE VOLUME
{{- if eq $autoDelete "0" }}
  convertx.autoDeleteEveryNHours is 0 — THE CLEANUP IS OFF. Uploads and
  converted output accumulate on the PVC for as long as the release lives and
  nothing reclaims them. Watch the volume, or set a non-zero number of hours.
{{- else }}
  convertx.autoDeleteEveryNHours is {{ $autoDelete }} — ConvertX deletes the uploads and the
  output of any job older than {{ $autoDelete }} hours. That is a hard delete, not an
  archive: a conversion nobody downloaded in time is gone. Set it to 0 to
  switch the cleanup off, and then size storage.amount for unbounded growth.
{{- end }}

STORAGE
  {{ .Release.Name }}-{{ .Chart.Name }}-pvc, {{ .Values.storage.amount }}, {{ if .Values.storage.storageClass }}storage class "{{ .Values.storage.storageClass }}"{{ else }}on the cluster's default storage class
  (storage.storageClass is unset){{ end }}, mounted at /app/data.

  Everything this release persists is on that one volume: the SQLite database
  (users, jobs, file names), every uploaded file and every converted result.
  There is no external database and no object storage, so one snapshot of the
  PVC is a complete backup — and losing it loses the accounts along with the
  files.

  Because the PVC is rendered by the chart, helm uninstall deletes it and the
  data with it, unless the storage class reclaim policy says otherwise.
{{- if gt (int $replicas) 1 }}

  convertx.replicas is {{ $replicas }}, and that does not work. The PVC is ReadWriteOnce, so
  the extra pods normally stay Pending on "Multi-Attach error for volume"; on
  a genuinely RWX storage class they would start and then corrupt the SQLite
  database two processes are now writing. Set it back to 1.
{{- end }}
{{- if .Values.ingress.enabled }}

THE INGRESS IS THE ONLY UPLOAD CEILING
  ingress.maxUploadFileSize   {{ .Values.ingress.maxUploadFileSize }}

  ConvertX enforces no size limit of its own — none, at any setting — so this
  annotation is the whole story. A file above it never reaches the
  application: the user gets the ingress controller's 413 page and nothing
  appears in the pod log, which makes it look like a broken upload button
  rather than a limit.

  Raising it has a second cost: the upload lands on the PVC above, so a
  ceiling far beyond storage.amount just moves the failure from a 413 to a
  full volume.
{{- else }}

UPLOAD CEILING — NONE FROM THIS CHART
  ingress.enabled is false, so the chart sets no body-size limit anywhere, and
  ConvertX enforces none of its own. Whatever proxy you put in front brings
  its ceiling with it — ingress-nginx defaults to 1m, small enough to look
  like a broken upload button.
{{- end }}

WHAT A GREEN POD PROVES
  All three probes are HTTP GETs against /healthcheck, which ConvertX answers
  with a static 200 and no authentication. It touches neither the database nor
  the volume, so a green pod proves the process is serving — not that a
  conversion works and not that the volume is writable.

  It is deliberately NOT "/": with the defaults of this chart "/" answers 302,
  to /setup while no account exists and to /login afterwards. A probe would
  pass on that redirect, and pass on a different one from day two onwards.

  The startup probe grants {{ $startup.failureThreshold }} × {{ $startup.periodSeconds }}s = {{ mul $startup.failureThreshold $startup.periodSeconds }}s. That budget is generous because
  ConvertX shells out to about twenty converter binaries at startup just to
  log their versions.

THE FIRST PULL IS SLOW, AND THAT IS NORMAL
  The image is roughly 1.4 GiB compressed — LibreOffice, Calibre, ImageMagick,
  ffmpeg, Inkscape and a TeX Live installation are all inside it. On a node
  that has not seen it before, expect several minutes in ContainerCreating
  before the startup probe budget above even begins. A pod that looks stuck
  there is usually still pulling:

    kubectl -n {{ .Release.Namespace }} describe pod {{ .Release.Name }}-{{ .Chart.Name }}-sfs-0

THIS CHART BYPASSES THE IMAGE ENTRYPOINT
  The image runs as root and drops privileges itself; this chart starts the
  application directly (command: bun run dist/src/index.js) as uid 1000, with
  fsGroup doing what the entrypoint's chown would have done. PUID, PGID and
  UMASK therefore configure NOTHING here — setting them through convertx.env
  has no effect at all, and the uid belongs in convertx.securityContext.pod.

  The cost to know about: if upstream moves real work into that entrypoint,
  this chart skips it silently. Re-read the upstream entrypoint.sh at every
  app-version update.
{{- if .Values.convertx.env }}

EXTRA ENVIRONMENT VARIABLES ARE ACTIVE
  convertx.env adds {{ len .Values.convertx.env }} variable(s), rendered after the ones the chart sets
  itself. Two warnings that go with that block:

  - WEBROOT moves every route of the application, /healthcheck included, so
    it breaks all three probes while the app is perfectly healthy — and this
    chart's Ingress still maps "/". Do not set it here.
  - An entry repeating a name the chart already sets is additive, not a
    replacement. At install yours wins; on an EXISTING release Kubernetes
    merges env by name and the chart's own value survives instead, while helm
    upgrade still reports success. Read it back off the StatefulSet.
{{- end }}
{{- if .Values.ingress.enabled }}
{{- with .Values.ingress.annotations }}

EXTRA INGRESS ANNOTATIONS ARE ACTIVE
{{- range $key, $value := . }}
  {{ $key }}: {{ $value }}
{{- end }}
  These are merged with the cert-manager and proxy-body-size annotations the
  chart always sets. Annotations are interpreted by the ingress controller, so
  a misspelled key is silently ignored — it does not fail the render.
{{- end }}
{{- end }}
