feat(secrets): scoped shared secrets rule-management UI (Phase 2)

Completes scoped shared secrets end-to-end: /shared-secrets list/new/edit
routes (mirroring metric-alert-rules) with an env-key name, a WRITE-ONLY
value (password input; never pre-filled — the API returns only has_value;
omitted on PATCH to keep the stored secret, provided to rotate; cleared
after save), an encrypted toggle (flipping it requires re-entering the
value, matching the server's 400 guard), a global|app scope with an
App-grouping picker (listApps), description, and enabled. 409 conflicts
surface a friendly message. New "System" nav entry (IconKey) + api.ts
client + full sharedsecrets.* i18n (en/ru parity).

Reviewed: typescript APPROVE (0 CRITICAL/HIGH).
This commit is contained in:
2026-05-29 16:11:46 +03:00
parent fa6d5bd3ba
commit 15e5b186cd
7 changed files with 1990 additions and 1 deletions
+91
View File
@@ -18,6 +18,7 @@
"eventTriggers": "Event Triggers",
"logScanRules": "Log Rules",
"metricAlertRules": "Metric Alerts",
"sharedSecrets": "Shared Secrets",
"triggers": "Triggers",
"proxies": "Proxies",
"events": "Events",
@@ -987,6 +988,96 @@
"disabled": "disabled"
}
},
"sharedsecrets": {
"eyebrow": "System",
"title": "Shared secrets",
"titleNew": "Forge a new secret",
"titleSingular": "Shared secret",
"lede": "Named environment keys the reconciler injects into matching workloads. Scope a secret globally to reach every app, or to a single app to keep it contained. Values are write-only — they are stored once and never returned. {enabled} of {total} enabled.",
"ledeNew": "Give the secret an env key and a value. Scope it globally to apply to every app, or to a single app to keep it contained. The value is write-only — once saved it is never shown again.",
"stat": {
"total": "TOTAL",
"global": "GLOBAL",
"app": "APP",
"enabled": "ENABLED"
},
"toolbar": {
"newButton": "New secret",
"backToList": "Back to secrets"
},
"filter": {
"all": "ALL",
"global": "GLOBAL",
"app": "APP"
},
"empty": {
"heading": "No shared secrets yet",
"body": "Start with a global secret like DATABASE_URL, then narrow per-app by scoping a secret to a single app.",
"cta": "Create the first secret"
},
"list": {
"name": "Name",
"scope": "Scope",
"value": "Value",
"encrypted": "Encrypted",
"status": "Status",
"open": "Open",
"valueSet": "set",
"valueNone": "—",
"encOn": "encrypted",
"encOff": "plain"
},
"detail": {
"config": "Configuration",
"configSub": "scope {scope}",
"dangerZone": "Danger zone",
"dangerZoneSub": "Deleting a shared secret removes it immediately and stops it from being injected.",
"deleteButton": "Delete secret",
"deleteTitle": "Delete shared secret?",
"deleteMessage": "Secret \"{name}\" will be removed immediately and will stop being injected."
},
"form": {
"name": "Name",
"namePlaceholder": "e.g. DATABASE_URL",
"nameHint": "The environment variable key injected into matching workloads.",
"value": "Value",
"valuePlaceholder": "Enter the secret value",
"valuePlaceholderEdit": "Leave blank to keep the stored value",
"valueHintNew": "The secret value. Stored write-only — once saved it is never shown again.",
"valueHintEditSet": "A value is set. Leave blank to keep it, or enter a new value to rotate it.",
"valueHintEditUnset": "No value is stored yet. Enter one to set it.",
"encrypted": "Encrypted",
"encryptedHint": "Encrypted secrets are stored sealed at rest and decrypted only when injected. Disable only for non-sensitive config values.",
"encryptedFlipWarning": "Changing the encrypted setting requires re-entering the value. Enter a value above to save.",
"scope": "Scope",
"scopeGlobalOption": "Global — every app",
"scopeAppOption": "App — a single app",
"scopeAppEmpty": "No app selected",
"scopePick": "Pick app…",
"scopePickTitle": "Pick an app",
"scopeClear": "Clear app",
"scopeSelected": "App",
"scopeUnknown": "Unknown app",
"scopeHint": "Global secrets apply to every app. App-scoped secrets apply only to that app's workloads.",
"description": "Description",
"descriptionPlaceholder": "Optional note about what this secret is for",
"enabled": "Enabled",
"enabledHint": "Disabled secrets stay in the table but are never injected.",
"required": "REQUIRED",
"optional": "OPTIONAL",
"submit": "Forge secret",
"submitting": "Forging…",
"conflict": "A shared secret with this scope and name already exists."
},
"scope": {
"global": "Global",
"app": "App · {name}"
},
"status": {
"enabled": "enabled",
"disabled": "disabled"
}
},
"logscan": {
"title": "Log scan rules",
"titleNew": "Forge a new rule",