discourse/app/assets/stylesheets/common/form-kit/_control-password.scss
Sam 79e3d8b003
FEATURE: add centralized AI secrets management (#37592)
Introduce an `AiSecret` model to allow admins to manage
API keys and secrets in a single place, shared across
LLMs and embedding definitions.

Previously each LLM and embedding stored its own api_key
directly. This change introduces a secrets vault so that
a single secret can be referenced by multiple models,
reducing duplication and making key rotation easier.

Key changes:
- New `ai_secrets` table, model, serializer, and CRUD
  controller with in-use protection on delete
- LlmModel and EmbeddingDefinition now accept an optional
  `ai_secret_id` foreign key as an alternative to inline
  `api_key`; validation ensures one or the other is set
- Provider params of type `:secret` (e.g. Bedrock
  `access_key_id`) resolve through AiSecret at runtime
- Admin UI: new Secrets nav tab with list/edit views,
  inline AiSecretSelector dropdown + quick-create modal
  on LLM and embedding editor forms
- Post-migration deduplicates existing api_key values
  into the new secrets table and back-fills foreign keys
- Fabricator and specs for model, controller, and
  usage-tracking logic

---------

Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
Co-authored-by: Keegan George <kgeorge13@gmail.com>
2026-02-11 10:09:52 +11:00

57 lines
1 KiB
SCSS
Vendored

@use "lib/viewport";
.form-kit__control-password {
width: 100%;
height: 100%;
padding: 0 0 0 0.5em !important;
margin: 0 !important;
border: 0 !important;
min-width: 0;
font-family: monospace;
@include viewport.until(sm) {
width: 100% !important;
}
&:hover,
&:focus {
border: 0 !important;
outline: 0 !important;
}
}
.form-kit__control-password-wrapper {
display: flex;
max-width: 100%;
width: 100%;
height: 2em;
background: var(--secondary);
border: 1px solid var(--primary-low-mid);
border-radius: var(--d-input-border-radius);
box-sizing: border-box;
@include viewport.until(sm) {
width: 100% !important;
height: 2.25em;
}
.form-kit__control-password-toggle {
height: 100%;
}
&.is-focused {
border-color: var(--tertiary);
outline: 2px solid var(--tertiary);
outline-offset: -2px;
}
&:hover:not(:disabled) {
.discourse-no-touch & {
border-color: var(--tertiary);
}
}
&.has-errors {
border-color: var(--danger);
}
}