Files
links/static/dist/jbot.css
T
junvandCopilot a47ea316c7 feat(jbot): UI-configurable API keys + fix Dockerfile
- Add JbotApiConfig Django model (DB singleton) for storing
  Volcengine/OpenRouter credentials set via Settings UI
- New endpoint GET/POST /api/jbot/api-config/ — tokens masked as ***
  on read; only non-placeholder values are updated on write
- views.py: _get_cfg() helper reads DB first, env vars as fallback
- Settings.jsx: new 🔑 API 配置 section with show/hide token fields
  for VOLC App ID, Access Token, OpenRouter API Key, LLM Model, ASR Resource
- api.js: apiGetApiConfig() / apiSaveApiConfig() client helpers
- Dockerfile: COPY jbot/ in both builder + production stages (was missing)
- entrypoint.sh: runs migrate --noinput before gunicorn (auto-creates table)
- k8s/manifest.yaml: remove jbot-credentials secretKeyRef (no Secret needed);
  keep LLM_MODEL/VOLC_TTS_VOICE/VOLC_ASR_RESOURCE as optional env defaults

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00

2 lines
13 KiB
CSS

*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}body{background:#f1f5f9;color:#0f172a;font-family:system-ui,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}#root{height:100dvh;height:-webkit-fill-available;display:flex;flex-direction:column}:root{--bg: #F1F5F9;--surface: #FFFFFF;--border: #E2E8F0;--text: #0F172A;--text2: #475569;--text3: #94A3B8;--accent: #0EA5E9;--accent-bg: #E0F7FE;--face-bg: #080C14}.app{height:100dvh;height:-webkit-fill-available;display:flex;flex-direction:column;background:var(--bg);color:var(--text);overflow:hidden}.hdr{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding:10px 20px;background:var(--surface);border-bottom:1px solid var(--border)}.hdr h1{font-size:1.5rem;font-weight:800;letter-spacing:.1em;color:var(--text);margin:0}.accent{color:var(--accent)}.subtitle{font-size:.72rem;color:var(--text3);letter-spacing:.05em}.hdr-nav{margin-left:auto;display:flex;gap:8px}.hdr-link{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text2);font-size:.78rem;padding:4px 10px;cursor:pointer;transition:background .15s,color .15s}.hdr-link:hover{background:var(--bg);color:var(--accent)}.page-body{flex:1;min-height:0;display:flex;overflow:hidden}.left-col{flex:0 0 auto;width:296px;display:flex;flex-direction:column;overflow-y:auto;background:var(--surface);border-right:1px solid var(--border);padding:14px 14px 20px;gap:14px}.right-col{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden;background:var(--bg)}.screen-wrap{display:flex;flex-direction:column;align-items:center;gap:8px}.bezel{background:var(--face-bg);border-radius:18px;padding:12px;box-shadow:0 4px 28px #00000038,0 0 0 1px #ffffff0f}.expr-label{font-size:.85rem;color:var(--text2);height:1.4em;text-align:center}.scale-controls{display:flex;gap:6px}.scale-btn{background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text2);padding:3px 12px;font-size:.78rem;cursor:pointer;transition:all .15s}.scale-btn:hover{border-color:var(--accent);color:var(--accent)}.scale-btn.active{background:var(--accent-bg);border-color:var(--accent);color:var(--accent);font-weight:600}.panel{display:flex;flex-direction:column;gap:12px}.panel-section h3{font-size:.67rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--text3);margin:0 0 7px}.key-hint{font-weight:400;opacity:.7}.btn-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:5px}.btn-grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:4px}.btn-grid-4 .ebtn{font-size:.62rem;padding:5px 3px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ebtn{background:var(--bg);border:1px solid var(--border);border-radius:7px;color:var(--text2);padding:6px 9px;font-size:.78rem;cursor:pointer;transition:all .15s;text-align:left}.ebtn:hover{border-color:var(--c);color:var(--c);background:color-mix(in srgb,var(--c) 6%,var(--surface))}.ebtn.active{background:color-mix(in srgb,var(--c) 12%,var(--surface));border-color:var(--c);color:var(--c);font-weight:600}.toggle{display:flex;align-items:center;gap:7px;font-size:.8rem;color:var(--text2);cursor:pointer;-webkit-user-select:none;user-select:none}.toggle input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px}.switch-interval{display:flex;flex-direction:column;gap:5px}.switch-interval-label{display:flex;justify-content:space-between;font-size:.75rem;color:var(--text2)}.interval-val{color:var(--accent);font-weight:700}.switch-interval input[type=range]{width:100%;accent-color:var(--accent);cursor:pointer}.interval-ticks{display:flex;justify-content:space-between;font-size:.65rem;color:var(--text3)}.hint{font-size:.72rem;color:var(--text3);line-height:1.7}kbd{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:1px 5px;font-size:.68rem;font-family:inherit;color:var(--text2)}.hist-inline-link{background:none;border:none;color:var(--accent);font-size:.72rem;padding:0;cursor:pointer;text-decoration:underline}.chat-panel{display:flex;flex-direction:column;height:100%;padding:14px 18px;gap:10px;overflow:hidden}.chat-header{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;padding-bottom:8px;border-bottom:1px solid var(--border)}.chat-title{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--text3)}.history-link{background:none;border:none;color:var(--accent);font-size:.75rem;cursor:pointer;padding:0}.history-link:hover{text-decoration:underline}.voice-status{flex:0 0 auto;font-size:.8rem;letter-spacing:.04em;padding:6px 12px;border-radius:8px;width:fit-content}.voice-status-listening{color:#0369a1;background:#e0f2fe;animation:pulse .9s ease-in-out infinite}.voice-status-thinking{color:#92400e;background:#fef3c7;animation:pulse 1.2s ease-in-out infinite}.voice-status-speaking{color:#065f46;background:#d1fae5}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.chat-messages{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:10px;padding:4px 2px;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.chat-messages::-webkit-scrollbar{width:4px}.chat-messages::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}.chat-empty{font-size:.82rem;color:var(--text3);text-align:center;padding:40px 0;margin:auto}.bubble{display:flex;flex-direction:column;gap:3px;max-width:82%}.bubble-user{align-self:flex-end;align-items:flex-end}.bubble-bot{align-self:flex-start;align-items:flex-start}.bubble-label{font-size:.62rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--text3)}.bubble-text{border-radius:12px;padding:9px 13px;font-size:.9rem;line-height:1.55}.bubble-user .bubble-text{background:#dbeafe;color:#1e3a8a;border-bottom-right-radius:3px}.bubble-bot .bubble-text{background:var(--surface);border:1px solid var(--border);color:var(--text);border-bottom-left-radius:3px}.audio-btn{width:100%;max-width:220px;height:28px;margin-top:3px;border-radius:6px;opacity:.8}.audio-btn:hover{opacity:1}.hist-page{min-height:100vh;background:var(--bg)}.hist-toolbar{display:flex;align-items:center;justify-content:space-between;padding:12px 24px;border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--surface);z-index:10}.hist-heading{font-size:1rem;font-weight:700;color:var(--text);margin:0}.hist-back{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text2);padding:5px 12px;font-size:.78rem;cursor:pointer}.hist-back:hover{border-color:var(--accent);color:var(--accent)}.hist-clear{background:none;border:1px solid #FECACA;border-radius:6px;color:#dc2626;padding:5px 12px;font-size:.78rem;cursor:pointer}.hist-clear:hover:not(:disabled){border-color:#dc2626}.hist-clear:disabled{opacity:.3;cursor:default}.hist-empty{text-align:center;color:var(--text3);padding:64px 32px;font-size:.9rem}.hist-list{display:flex;flex-direction:column;max-width:680px;margin:0 auto;padding:16px 24px}.hist-entry{border:1px solid var(--border);border-radius:12px;padding:14px 16px;margin-bottom:12px;background:var(--surface)}.hist-meta{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.hist-time{font-size:.68rem;color:var(--text3)}.hist-del{background:none;border:none;color:var(--text3);cursor:pointer;font-size:.8rem;padding:0 4px}.hist-del:hover{color:#dc2626}.hist-row{display:flex;gap:10px;margin-bottom:8px}.hist-role{font-size:.62rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--text3);min-width:36px;padding-top:2px}.hist-row-user .hist-role{color:#2563eb}.hist-row-bot .hist-role{color:#059669}.hist-body{flex:1}.hist-text{font-size:.86rem;color:var(--text);line-height:1.5;margin:0 0 6px}.hist-audio{width:100%;max-width:260px;height:26px;opacity:.75;border-radius:6px}.hist-audio:hover{opacity:1}@media(max-width:700px){.app{height:100dvh;height:-webkit-fill-available}.hdr{padding:8px 14px}.hdr h1{font-size:1.2rem}.subtitle{display:none}.page-body{flex-direction:column}.left-col{width:100%;flex:0 0 auto;border-right:none;border-bottom:1px solid var(--border);padding:10px 12px;gap:8px;flex-direction:row;align-items:flex-start;overflow-x:hidden;overflow-y:visible}.screen-wrap{flex:0 0 auto}.bezel{padding:8px}.panel{flex:1;min-width:0;gap:8px;overflow-y:auto;max-height:272px}.btn-grid{grid-template-columns:repeat(2,1fr);gap:4px}.btn-grid-4{grid-template-columns:repeat(4,1fr);gap:3px}.ebtn{font-size:.7rem;padding:5px 6px}.btn-grid-4 .ebtn{font-size:.58rem;padding:4px 2px}.scale-controls{display:none}.right-col{flex:1;min-height:0}.chat-panel{padding:10px 12px}}.memory-panel{border:1px solid var(--border);border-radius:8px;overflow:hidden}.memory-toggle{width:100%;display:flex;align-items:center;gap:6px;padding:7px 10px;background:var(--bg);border:none;cursor:pointer;font-size:.78rem;color:var(--text2);text-align:left}.memory-toggle:hover{background:#e8eef6}.memory-badge{margin-left:auto;background:var(--accent-bg);color:var(--accent);border-radius:10px;font-size:.65rem;font-weight:700;padding:1px 6px;min-width:18px;text-align:center}.memory-arrow{font-size:.6rem;color:var(--text3)}.memory-body{padding:8px 10px;background:var(--surface);border-top:1px solid var(--border)}.memory-empty{font-size:.72rem;color:var(--text3);text-align:center;padding:6px 0;margin:0}.memory-list{list-style:none;display:flex;flex-direction:column;gap:5px;margin:0 0 8px;padding:0;max-height:160px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.memory-item{display:flex;align-items:flex-start;gap:6px;font-size:.75rem;color:var(--text2);line-height:1.4}.memory-text{flex:1}.memory-del{flex:0 0 auto;background:none;border:none;color:var(--text3);cursor:pointer;font-size:.9rem;line-height:1;padding:0 2px}.memory-del:hover{color:#dc2626}.memory-clear{width:100%;background:none;border:1px solid #FECACA;border-radius:5px;color:#dc2626;font-size:.72rem;padding:4px 0;cursor:pointer}.memory-clear:hover{background:#fef2f2}.voice-hint{margin-top:4px;display:flex;align-items:center;gap:4px;flex-wrap:wrap}.settings-page{min-height:100dvh;background:var(--bg);color:var(--text)}.settings-toolbar{display:flex;align-items:center;gap:14px;padding:12px 20px;background:var(--surface);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:10}.settings-back{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text2);font-size:.85rem;padding:5px 12px;cursor:pointer;transition:background .15s}.settings-back:hover{background:var(--bg);color:var(--accent)}.settings-heading{font-size:1.1rem;font-weight:700;margin:0;flex:1}.settings-save{background:var(--accent);border:none;border-radius:7px;color:#fff;font-size:.85rem;font-weight:600;padding:6px 16px;cursor:pointer;transition:opacity .15s}.settings-save:hover{opacity:.85}.settings-body{max-width:600px;margin:0 auto;padding:20px 18px 40px;display:flex;flex-direction:column;gap:20px}.settings-section{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:16px;display:flex;flex-direction:column;gap:12px}.settings-section-title{font-size:.85rem;font-weight:700;color:var(--text2);margin:0 0 4px;letter-spacing:.02em}.settings-label{display:flex;flex-direction:column;gap:5px;font-size:.82rem;font-weight:600;color:var(--text2)}.settings-label-mt{margin-top:6px}.settings-hint{font-size:.73rem;font-weight:400;color:var(--text3);margin-left:4px}.settings-hint-block{font-size:.75rem;color:var(--text3);margin:0}.settings-hint-block a{color:var(--accent)}.settings-input{border:1px solid var(--border);border-radius:6px;padding:7px 10px;font-size:.85rem;color:var(--text);background:var(--bg);outline:none;transition:border-color .15s;font-family:inherit}.settings-input:focus{border-color:var(--accent)}.settings-textarea{border:1px solid var(--border);border-radius:6px;padding:7px 10px;font-size:.85rem;color:var(--text);background:var(--bg);outline:none;resize:vertical;min-height:60px;transition:border-color .15s;font-family:inherit;line-height:1.5}.settings-textarea:focus{border-color:var(--accent)}.settings-radio-group{display:flex;flex-direction:column;gap:8px}.settings-radio-label{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--text2);cursor:pointer}.settings-radio-label input{accent-color:var(--accent)}.settings-voice-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:8px}.settings-voice-btn{display:flex;flex-direction:column;gap:3px;padding:10px 10px 8px;background:var(--bg);border:1.5px solid var(--border);border-radius:8px;cursor:pointer;text-align:left;transition:border-color .15s,background .15s}.settings-voice-btn:hover,.settings-voice-btn.active{border-color:var(--accent);background:var(--accent-bg)}.voice-btn-label{font-size:.88rem;font-weight:700;color:var(--text)}.voice-btn-meta{font-size:.68rem;color:var(--text3);line-height:1.3}.settings-section-danger{border-color:#fecaca}.settings-reset-btn{background:none;border:1px solid #FECACA;border-radius:6px;color:#dc2626;font-size:.82rem;padding:7px 14px;cursor:pointer;transition:background .15s}.settings-reset-btn:hover{background:#fef2f2}.settings-secret-row{display:flex;gap:.5rem;align-items:stretch}.settings-secret-row .settings-input{flex:1}.settings-show-btn{padding:.45rem .75rem;font-size:.78rem;background:#e2e8f0;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;white-space:nowrap;color:#475569}.settings-show-btn:hover{background:#cbd5e1}