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:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user