mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 00:48:00 +00:00
feat(seed-warn): dynamic seed key detection in UI modal; feat(https): TLS config (tls_cert_file/tls_key_file) with dynamic base_url; docs: rotate admin key reminder
This commit is contained in:
@ -468,6 +468,7 @@ const STR = {
|
||||
toastDelOk:'已删除', confirmDelSrc:'确定删除源 %s 吗?', confirmDelAdp:'确定删除适配器 %s 吗?',
|
||||
u:'用户', a:'助手', at:'助手[思考]', aEmpty:'(空回复)', aErr:'请求失败: %s', cErr:'错误: %s',
|
||||
chatMeta:'模型=%s · 耗时 %s ms · %d 字符', connPinned:'# 固定到模型: %s · %s', connAuto:'# 模型名 AUTO',
|
||||
seedWarnTitle:'请更换初始管理员密钥', seedWarnText:'当前登录的是配置文件中的初始密钥,明文写入 config.yaml、存在泄露风险。请在下方创建新的管理员密钥,用新密钥登录后删除此初始密钥。', seedWarnGo:'去更换密钥', seedWarnLater:'稍后', seedWarnDismiss:'本次不再提示',
|
||||
sortTitle:'拖拽积木配置模型优先级', sortHint:'每行 = 一个优先级档位,行从上到下优先级递减;同一行的模型并排,视为同优先级。按住积木右侧 ⠿ 把手拖动:拖到行内 = 放入该档位或调整同档顺序,拖到行与行之间的缝隙 = 提升或降低到新档位。生图模型不参与排序。', sortDragGrip:'拖拽前须按住把手',
|
||||
sortSave:'保存排序', sortReset:'重置', sortAdd:'添加档位', sortSaved:'排序已保存并热重载', sortNoChange:'无变更', sortHintSave:'点击保存排序后生效',
|
||||
sortSource:'源', sortPrio:'优先级 %s', sortEmpty:'该源暂无模型',
|
||||
@ -521,6 +522,7 @@ kMeTitle:'My key', kMeRole:'Role', kMeModels:'Models I can use', kMeHint:'Keys c
|
||||
toastDelOk:'Deleted', confirmDelSrc:'Delete source %s?', confirmDelAdp:'Delete adapter %s?',
|
||||
u:'You:', a:'Assistant:', at:'Assistant [thinking]', aEmpty:'empty', aErr:'Request failed: %s',
|
||||
cErr:'Error: %s', chatMeta:'Model=%s · %s ms · %d chars', connPinned:'# Pinned to model: %s · %s', connAuto:'# Model "AUTO" follows the Priority page AUTO chain',
|
||||
seedWarnTitle:'Replace the initial admin key', seedWarnText:'You are logged in with the seed key from config.yaml. It is plaintext in the config file and a security risk. Create a new admin key below, log in with it, then delete this seed key.', seedWarnGo:'Change my key', seedWarnLater:'Later', seedWarnDismiss:'Don\'t ask again',
|
||||
sortTitle:'Drag blocks to set model priority', sortHint:'Each row = one priority tier, rows go high→low; models on the same row sit side by side and share that priority. Grab the ⠿ handle on the right of a block to drag: drop into a row = join that tier or reorder within it, drop into the gap between rows = move up/down a tier. Image models stay out.', sortDragGrip:'grab the handle to drag',
|
||||
sortSave:'Save order', sortReset:'Reset', sortAdd:'Add slot', sortSaved:'Order saved & hot-reloaded', sortNoChange:'No changes', sortHintSave:'Click Save for it to take effect',
|
||||
sortSource:'source', sortPrio:'priority %s', sortEmpty:'no models in this source',
|
||||
@ -563,12 +565,13 @@ document.getElementById('btn-lang').onclick = () => {
|
||||
};
|
||||
document.getElementById('btn-logout').onclick = () => { location.href = '/api/logout'; };
|
||||
document.querySelectorAll('nav button').forEach(b => {
|
||||
b.onclick = () => {
|
||||
document.querySelectorAll('nav button').forEach(x => x.classList.toggle('active', x === b));
|
||||
['status','chat','keys','sort','sources','adapters'].forEach(tn => $('#tab-' + tn).classList.toggle('hidden', tn !== b.dataset.tab));
|
||||
lastTab = b.dataset.tab; refresh(lastTab);
|
||||
};
|
||||
b.onclick = () => goTab(b.dataset.tab);
|
||||
});
|
||||
function goTab(name) {
|
||||
document.querySelectorAll('nav button').forEach(x => x.classList.toggle('active', x.dataset.tab === name));
|
||||
['status','chat','keys','sort','sources','adapters'].forEach(tn => $('#tab-' + tn).classList.toggle('hidden', tn !== name));
|
||||
lastTab = name; refresh(lastTab);
|
||||
}
|
||||
applyI18n();
|
||||
|
||||
/* ---------- helpers ---------- */
|
||||
@ -1736,6 +1739,20 @@ async function renderKeys() {
|
||||
window._me = me.key;
|
||||
return me.key.role === 'admin' ? renderKeysAdmin(me.key) : renderKeysUser(me.key);
|
||||
}
|
||||
function maybeWarnSeed(k) {
|
||||
if (!k || !k.seed) return;
|
||||
if (localStorage.getItem('llms-proxy.seedWarn') === '1') return;
|
||||
const wrap = document.createElement('div'); wrap.id = 'modal-wrap';
|
||||
wrap.style.cssText = 'position:fixed;inset:0;background:rgba(15,22,44,.45);display:flex;align-items:flex-start;justify-content:center;overflow:auto;padding:48px 20px;z-index:50';
|
||||
wrap.innerHTML = `<div class="card" style="width:460px;max-width:100%">
|
||||
<h2>⚠ ${esc(t('seedWarnTitle'))}</h2>
|
||||
<p class="muted" style="line-height:1.6">${esc(t('seedWarnText'))}</p>
|
||||
<p><button onclick="this.closest('#modal-wrap').remove();goTab('keys')">${esc(t('seedWarnGo'))}</button>
|
||||
<button class="ghost" onclick="this.closest('#modal-wrap').remove()">${esc(t('seedWarnLater'))}</button>
|
||||
<button class="ghost" onclick="localStorage.setItem('llms-proxy.seedWarn','1');this.closest('#modal-wrap').remove()">${esc(t('seedWarnDismiss'))}</button></p>
|
||||
</div>`;
|
||||
document.body.appendChild(wrap);
|
||||
}
|
||||
async function renderKeysUser(me) {
|
||||
$('#tab-keys').innerHTML = `
|
||||
<div class="card"><h2>${t('kMeTitle')}</h2>
|
||||
@ -2067,6 +2084,7 @@ function refresh(tab) {
|
||||
try {
|
||||
const me = await api('/api/keys/me');
|
||||
window._me = me.key;
|
||||
maybeWarnSeed(me.key);
|
||||
if (me.key.role !== 'admin') {
|
||||
['sort', 'sources', 'adapters'].forEach(tn => {
|
||||
const b = document.querySelector(`nav button[data-tab="${tn}"]`);
|
||||
|
||||
Reference in New Issue
Block a user