mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-06-19 07:03:42 +08:00
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>
57 lines
1 KiB
SCSS
Vendored
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);
|
|
}
|
|
}
|