let _presets = (import "schemas/lib/concerns_presets.ncl").presets in { rustelo_website | default = { name | default = "website", namespace | default = "", catalog_ref | default = "rustelo_website", image | default = "", image_tag | default = "latest", port | default = 3000, domain | default = "", dns_zone | default = "", acme_email | default = "", cluster_issuer | default = "letsencrypt-prod", gateway_fip | default = "", gateway_name | default = "libre-wuji", gateway_ns | default = "kube-system", registry_secret | default = "regcred", render_mode | default = 'htmx_ssr, leptos_output_name | default = "website", data_pvc | default = { enabled = false, size = "2Gi", mount_path = "/app/data", storage_class = "longhorn-retain", }, requires | default = { storage = { size = "2Gi", persistent = true }, ports = [{ port = 3000, exposure = 'public }], credentials = ["SECRET_KEY", "DATABASE_URL"], }, provides | default = { service = "website", port = 3000, endpoints = [], }, operations | default = { install = true, update = true, delete = true, health = true, }, live_check | default = { strategy = 'k8s_pods, scope = 'cp_only, }, # ServiceConcerns — derives tls_secret, cluster_issuer, dns_zone, acme_email # from the component's own fields. let-bindings capture outer values before # entering the inner record literal to avoid Nickel's recursive self-reference. concerns | default = let _name = name in let _domain = domain in let _dns_zone = dns_zone in let _acme_email = acme_email in let _cluster_issuer = cluster_issuer in (_presets.tls_endpoint_with_acme { tls_secret = "%{_name}-tls", cluster_issuer = _cluster_issuer, hostnames = [_domain], dns_internal = [], dns_zone = _dns_zone, acme_server = "https://acme-v02.api.letsencrypt.org/directory", acme_email = _acme_email, cert_secret_ref = "", cert_provider = 'cloudflare, backup_backlog_ref = "BACKUP-WEBSITE-001", }) & { backup | force = { kind = 'pending, reason = "BackupPolicy declared at workspace level if data_pvc.enabled = true", backlog_ref = "BACKUP-WEBSITE-001", }, manifest_hooks = { init = { pre = [ # Namespace must exist before any namespaced pre-hook below # (create-cf-secret, certificate are namespaced). manifest_plan.init # also ensures it, but that phase runs AFTER these init.pre hooks — so # a brand-new namespace (e.g. ontoref) failed here while a pre-existing # one (jpl) passed. Idempotent via skip_if_exists. (ADR-051 prepare: # enter must create the namespace before namespaced enters.) { file = "namespace", action = 'apply, skip_if_exists = true }, { action = 'create-cf-secret }, { file = "clusterissuer", action = 'apply, skip_if_exists = true }, { file = "certificate", action = 'apply, skip_if_exists = true }, { file = "referencegrant", action = 'apply }, { action = 'patch-gateway-https }, ] }, update = { pre = [ { file = "certificate", action = 'apply, skip_if_exists = true }, { file = "referencegrant", action = 'apply }, { action = 'patch-gateway-https }, ] }, delete = { pre = [ { action = 'remove-gateway-https }, { file = "referencegrant", action = 'delete }, ] }, }, }, }, }