feat(config): cles des sources connectees et recherche a cle editables depuis la page Configurations (#103)
This commit is contained in:
@@ -1543,6 +1543,7 @@
|
||||
<li><a href="#cfg-hidden-files" class="help-nav-link" data-i18n="config.section_hidden"></a></li>
|
||||
<li><a href="#cfg-diags" class="help-nav-link" data-i18n="settings.diagnostics"></a></li>
|
||||
<li><a href="#cfg-ai" class="help-nav-link" data-i18n="settings.ai"></a></li>
|
||||
<li><a href="#cfg-sources" class="help-nav-link" data-i18n="config.section_sources">Sources connectées</a></li>
|
||||
<li><a href="#cfg-themes" class="help-nav-link" data-i18n="settings.themes"></a></li>
|
||||
<li><a href="#cfg-profile" class="help-nav-link" data-i18n="settings.profile"></a></li>
|
||||
<li><a href="#cfg-security" class="help-nav-link" data-i18n="settings.security"></a></li>
|
||||
@@ -2260,6 +2261,133 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Connected sources & keyed web search (#103) -->
|
||||
<section
|
||||
class="config-section help-section"
|
||||
id="cfg-sources"
|
||||
>
|
||||
<h2 data-i18n="config.section_sources"
|
||||
>Sources connectées & recherche web</h2
|
||||
>
|
||||
<p
|
||||
class="config-description"
|
||||
data-i18n="config.sources_desc"
|
||||
>
|
||||
Clés utilisées par les outils de l'Assistant IA
|
||||
(recherche web à clé, Gitea, GitHub). Elles sont
|
||||
stockées localement et priment sur les variables
|
||||
d'environnement.
|
||||
</p>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-tavily-key"
|
||||
>Tavily API Key</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-tavily-key"
|
||||
class="config-input"
|
||||
placeholder="tvly-..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-brave-key"
|
||||
>Brave Search API Key</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-brave-key"
|
||||
class="config-input"
|
||||
placeholder="BSA..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-serpapi-key"
|
||||
>SerpAPI Key</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-serpapi-key"
|
||||
class="config-input"
|
||||
placeholder="..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-exa-key"
|
||||
>Exa API Key</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-exa-key"
|
||||
class="config-input"
|
||||
placeholder="..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-gitea-url"
|
||||
data-i18n="config.gitea_url"
|
||||
>URL Gitea</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="cfg-gitea-url"
|
||||
class="config-input"
|
||||
placeholder="https://git.example.net"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-gitea-token"
|
||||
>Gitea Token</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-gitea-token"
|
||||
class="config-input"
|
||||
placeholder="token personnel..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
<label
|
||||
class="config-label"
|
||||
for="cfg-github-token"
|
||||
>GitHub Token</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="cfg-github-token"
|
||||
class="config-input"
|
||||
placeholder="ghp_..."
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="config-actions-row"
|
||||
style="margin-top: 16px"
|
||||
>
|
||||
<button class="config-btn-save"
|
||||
id="cfg-save-tool-keys" data-i18n="help.shortcut_save">
|
||||
Sauvegarder
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Themes -->
|
||||
<section
|
||||
class="config-section help-section"
|
||||
|
||||
@@ -715,6 +715,7 @@ function initConfigModal() {
|
||||
await loadHiddenFilesSettings();
|
||||
loadWebhooksUI();
|
||||
loadSharesUI();
|
||||
loadToolKeys();
|
||||
safeCreateIcons();
|
||||
});
|
||||
|
||||
@@ -765,6 +766,9 @@ function initConfigModal() {
|
||||
if (saveAIKeysBtn) saveAIKeysBtn.addEventListener("click", saveAIKeys);
|
||||
const testAIKeysBtn = document.getElementById("cfg-test-ai-keys");
|
||||
if (testAIKeysBtn) testAIKeysBtn.addEventListener("click", testAIKeys);
|
||||
// Tool & connected-source keys (#103)
|
||||
const saveToolKeysBtn = document.getElementById("cfg-save-tool-keys");
|
||||
if (saveToolKeysBtn) saveToolKeysBtn.addEventListener("click", saveToolKeys);
|
||||
// Default provider/model selection
|
||||
const aiDefaultProviderSel = document.getElementById("cfg-ai-default-provider");
|
||||
if (aiDefaultProviderSel) {
|
||||
@@ -1715,6 +1719,102 @@ async function testAIKeys() {
|
||||
}
|
||||
|
||||
|
||||
// ── Tool & connected-source keys (#103) ──
|
||||
const TOOL_KEY_MAP = {
|
||||
"cfg-tavily-key": "OBSIGATE_TAVILY_API_KEY",
|
||||
"cfg-brave-key": "OBSIGATE_BRAVE_API_KEY",
|
||||
"cfg-serpapi-key": "OBSIGATE_SERPAPI_API_KEY",
|
||||
"cfg-exa-key": "OBSIGATE_EXA_API_KEY",
|
||||
"cfg-gitea-url": "OBSIGATE_GITEA_URL",
|
||||
"cfg-gitea-token": "OBSIGATE_GITEA_TOKEN",
|
||||
"cfg-github-token": "OBSIGATE_GITHUB_TOKEN",
|
||||
};
|
||||
|
||||
function _ensureToolKeyUI() {
|
||||
for (const [inputId] of Object.entries(TOOL_KEY_MAP)) {
|
||||
const input = document.getElementById(inputId);
|
||||
if (!input) continue;
|
||||
const row = input.closest(".config-row");
|
||||
if (!row || row.dataset.toolKeyEnhanced) continue;
|
||||
row.dataset.toolKeyEnhanced = "1";
|
||||
row.style.cssText += "display:flex;align-items:center;gap:8px;flex-wrap:wrap;";
|
||||
const badge = document.createElement("span");
|
||||
badge.id = inputId + "-badge";
|
||||
badge.style.cssText = "font-size:11px;padding:2px 8px;border-radius:10px;white-space:nowrap;";
|
||||
row.appendChild(badge);
|
||||
const delBtn = document.createElement("button");
|
||||
delBtn.type = "button";
|
||||
delBtn.id = inputId + "-delete";
|
||||
delBtn.className = "config-btn-secondary";
|
||||
delBtn.style.cssText = "font-size:11px;padding:4px 10px;color:var(--danger,#e74c3c);border-color:var(--danger,#e74c3c);cursor:pointer;display:none;";
|
||||
delBtn.textContent = "\u00d7 " + t("config.delete_key");
|
||||
delBtn.addEventListener("click", () => deleteToolKey(inputId));
|
||||
row.appendChild(delBtn);
|
||||
}
|
||||
}
|
||||
|
||||
function _setToolKeyBadge(inputId, hasKey) {
|
||||
const badge = document.getElementById(inputId + "-badge");
|
||||
const delBtn = document.getElementById(inputId + "-delete");
|
||||
if (badge) {
|
||||
if (hasKey) {
|
||||
badge.textContent = "\u2713 " + t("config.key_set");
|
||||
badge.style.background = "var(--success-bg, #27ae6022)";
|
||||
badge.style.color = "var(--success, #27ae60)";
|
||||
badge.style.border = "1px solid var(--success, #27ae60)";
|
||||
} else {
|
||||
badge.textContent = t("config.key_unset");
|
||||
badge.style.background = "var(--muted-bg, #ffffff10)";
|
||||
badge.style.color = "var(--text-muted, #888)";
|
||||
badge.style.border = "1px solid var(--border, #444)";
|
||||
}
|
||||
}
|
||||
if (delBtn) delBtn.style.display = hasKey ? "inline-block" : "none";
|
||||
}
|
||||
|
||||
async function loadToolKeys() {
|
||||
_ensureToolKeyUI();
|
||||
try {
|
||||
const data = await api("/api/config/tool-keys");
|
||||
for (const [inputId, name] of Object.entries(TOOL_KEY_MAP)) {
|
||||
const input = document.getElementById(inputId);
|
||||
const val = data[name] || "";
|
||||
if (input && !input.value.trim()) input.placeholder = val || input.placeholder;
|
||||
_setToolKeyBadge(inputId, !!val);
|
||||
}
|
||||
} catch(e) { /* non-admin: the section stays inert */ }
|
||||
}
|
||||
|
||||
async function saveToolKeys() {
|
||||
const keys = {};
|
||||
for (const [id, name] of Object.entries(TOOL_KEY_MAP)) {
|
||||
const input = document.getElementById(id);
|
||||
if (input && input.value.trim()) keys[name] = input.value.trim();
|
||||
}
|
||||
if (!Object.keys(keys).length) {
|
||||
showToast(t("config.no_keys"), "warning");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api("/api/config/tool-keys", { method: "POST", body: JSON.stringify(keys) });
|
||||
showToast(t("config.api_keys_saved"), "success");
|
||||
Object.keys(TOOL_KEY_MAP).forEach(id => { const el = document.getElementById(id); if (el) el.value = ""; });
|
||||
loadToolKeys();
|
||||
} catch(e) { showToast("Erreur: " + e.message, "error"); }
|
||||
}
|
||||
|
||||
async function deleteToolKey(inputId) {
|
||||
const name = TOOL_KEY_MAP[inputId];
|
||||
if (!name) return;
|
||||
if (!confirm(t("config.delete_key_confirm") + " " + name + " ?")) return;
|
||||
try {
|
||||
await api("/api/config/tool-keys/" + name, { method: "DELETE" });
|
||||
showToast(t("config.key_deleted") + " " + name, "success");
|
||||
loadToolKeys();
|
||||
} catch(e) { showToast("Erreur: " + e.message, "error"); }
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
initSidebarTabs,
|
||||
initConfigModal,
|
||||
|
||||
@@ -364,6 +364,14 @@
|
||||
"config.ai_model": "Model",
|
||||
"config.ai_openrouter_label": "OpenRouter API Key",
|
||||
"config.api_keys_saved": "API keys saved",
|
||||
"config.section_sources": "🔗 Connected sources & search",
|
||||
"config.sources_desc": "Keys used by the AI assistant tools (keyed web search: Tavily, Brave, SerpAPI, Exa; connected sources: Gitea, GitHub). They are stored server-side and take precedence over environment variables.",
|
||||
"config.gitea_url": "Gitea URL",
|
||||
"config.key_set": "Configured",
|
||||
"config.key_unset": "Not configured",
|
||||
"config.delete_key": "Delete",
|
||||
"config.delete_key_confirm": "Delete key",
|
||||
"config.key_deleted": "Key deleted:",
|
||||
"config.backups": "Backups",
|
||||
"config.backups_desc": "Manage automatic file backups.",
|
||||
"config.client_config": "Client config",
|
||||
|
||||
@@ -364,6 +364,14 @@
|
||||
"config.ai_model": "Modèle",
|
||||
"config.ai_openrouter_label": "OpenRouter API Key",
|
||||
"config.api_keys_saved": "Clés API sauvegardées",
|
||||
"config.section_sources": "🔗 Sources connectées & recherche",
|
||||
"config.sources_desc": "Clés utilisées par les outils de l'Assistant IA (recherche web à clé : Tavily, Brave, SerpAPI, Exa ; sources connectées : Gitea, GitHub). Elles sont stockées sur le serveur et priment sur les variables d'environnement.",
|
||||
"config.gitea_url": "URL Gitea",
|
||||
"config.key_set": "Configuré",
|
||||
"config.key_unset": "Non configuré",
|
||||
"config.delete_key": "Supprimer",
|
||||
"config.delete_key_confirm": "Supprimer la clé",
|
||||
"config.key_deleted": "Clé supprimée :",
|
||||
"config.backups": "Sauvegardes",
|
||||
"config.backups_desc": "Gérez les sauvegardes automatiques de vos fichiers.",
|
||||
"config.client_config": "Configuration client",
|
||||
|
||||
Reference in New Issue
Block a user