feat(18-01): write-only settings secrets, richeditor and tabs on settings forms (task 3)

- type: password on a settings form binds a lagoon.Encrypted column as a
  write-only secret (any other column fails boot naming the field): the
  GET omits it from data and reports stored, env or empty per field in
  meta.secrets; on PUT an absent key or "" keeps the value, null clears
  it, a non-string is a 422; the answer never carries a value
- cabana.SecretFallbacks (field to config key) gives the env state;
  cabana.SettingsMeta, SecretStored/SecretEnv/SecretEmpty exported
- settings forms accept richeditor: the Put sanitizes through seaglass
  like a controller save and returns meta.notices; repeater and
  dependsOn stay refused (settingsFormOnly next to controllerFormOnly)
- backend::lang secret.* phrases in en and pl
- admin SPA: PasswordField secret status line (stored with Remove stored
  value, env, empty, pending clear with Undo), SettingsFormView tabs with
  error-tab activation and the secrets/notices meta, settings payload
  that never sends an untouched secret; regenerated OpenAPI and TS
  types; rebuilt dist
- vault fixture, TestSettingsSecretCompile, Docker TestSettingsSecretSmoke
  and TestSettingsRichEditorSmoke, conform settings fixture with a
  password field; settings.md sections, forms.md notes, cabana README
This commit is contained in:
Jakub Zych
2026-10-13 11:36:12 +02:00
parent 0c8605123e
commit 1e841b4281
27 changed files with 1251 additions and 92 deletions

View File

@@ -1017,7 +1017,10 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Read admin settings */
/**
* Read admin settings
* @description The singleton's values keyed by field name. A write-only password field (a lagoon.Encrypted column) is never in data; meta.secrets reports its state instead: stored, env (empty column, the model's SecretFallbacks config key resolves) or empty. meta is omitted when the form has no password field.
*/
get: {
parameters: {
query?: never;
@@ -1068,7 +1071,10 @@ export interface paths {
};
};
};
/** Update admin settings */
/**
* Update admin settings
* @description Saves the singleton. For a password field an absent key or an empty string keeps the stored value and JSON null clears it; any other non-string value is a 422 on the field. A richeditor value is sanitized like a controller save and what was removed comes back in meta.notices. The answer carries meta.secrets with the new states and never the values.
*/
put: {
parameters: {
query?: never;
@@ -6077,11 +6083,23 @@ export interface components {
model: string;
order: number;
};
"cabana.SettingsMeta": {
/**
* @description Notices are what the seaglass sanitizer removed from richeditor
* values on this save.
*/
notices?: components["schemas"]["cabana.FieldNotice"][];
/** @description Secrets maps each password field to stored, env or empty. */
secrets?: {
[key: string]: string;
};
};
"cabana.SettingsResult": {
data: {
[key: string]: unknown;
};
exists: boolean;
meta?: components["schemas"]["cabana.SettingsMeta"];
};
"cabana.SuccessMeta": {
last_page?: number;