mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-10 17:42:49 +08:00
feat: gateway auth supports token/password mode switching; fix ClawApp description
This commit is contained in:
@@ -31,7 +31,7 @@ export async function render() {
|
||||
</div>
|
||||
<div id="clawapp-card" class="config-section">
|
||||
<div class="config-section-title">ClawApp 移动客户端</div>
|
||||
<div class="form-hint" style="margin-bottom:var(--space-md)">基于 LobeChat 的 AI 对话客户端,通过 Gateway 连接模型服务。支持本地和外网访问。</div>
|
||||
<div class="form-hint" style="margin-bottom:var(--space-md)">H5 移动聊天客户端,通过代理服务端连接 Gateway。支持本地和外网访问。</div>
|
||||
<div id="clawapp-content"><div class="stat-card loading-placeholder" style="height:64px"></div></div>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -136,14 +136,47 @@ function renderConfig(page, state) {
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
|
||||
安全认证
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">访问密钥</label>
|
||||
<div class="form-group" style="margin-bottom:var(--space-md)">
|
||||
<label class="form-label">认证方式</label>
|
||||
<div class="gw-option-cards">
|
||||
<label class="gw-option-card ${gw.auth?.mode === 'password' ? '' : 'selected'}" data-auth="token">
|
||||
<input type="radio" name="gw-auth-mode" value="token" ${gw.auth?.mode === 'password' ? '' : 'checked'} hidden>
|
||||
<div class="gw-option-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 11-7.778 7.778 5.5 5.5 0 017.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
|
||||
</div>
|
||||
<div class="gw-option-text">
|
||||
<div class="gw-option-title">Token 密钥</div>
|
||||
<div class="gw-option-desc">标准认证方式,适合本地和局域网使用</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="gw-option-card ${gw.auth?.mode === 'password' ? 'selected' : ''}" data-auth="password">
|
||||
<input type="radio" name="gw-auth-mode" value="password" ${gw.auth?.mode === 'password' ? 'checked' : ''} hidden>
|
||||
<div class="gw-option-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
|
||||
</div>
|
||||
<div class="gw-option-text">
|
||||
<div class="gw-option-title">密码认证</div>
|
||||
<div class="gw-option-desc">Tailscale Funnel 等外网暴露场景必须使用此模式</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="gw-auth-token-group" style="${gw.auth?.mode === 'password' ? 'display:none' : ''}">
|
||||
<label class="form-label">访问密钥(Token)</label>
|
||||
<div style="display:flex;gap:8px">
|
||||
<input class="form-input" id="gw-token" type="password" value="${gw.auth?.token || gw.authToken || ''}" placeholder="不设置则任何人都能调用" style="flex:1">
|
||||
<button class="btn btn-sm btn-secondary" id="btn-toggle-token">显示</button>
|
||||
</div>
|
||||
<div class="form-hint">设置后,应用调用时需要带上这个密钥才能通过。如果选了「局域网共享」,强烈建议设置</div>
|
||||
</div>
|
||||
<div class="form-group" id="gw-auth-password-group" style="${gw.auth?.mode === 'password' ? '' : 'display:none'}">
|
||||
<label class="form-label">密码</label>
|
||||
<div style="display:flex;gap:8px">
|
||||
<input class="form-input" id="gw-password" type="password" value="${gw.auth?.password || ''}" placeholder="设置 Gateway 访问密码" style="flex:1">
|
||||
<button class="btn btn-sm btn-secondary" id="btn-toggle-password">显示</button>
|
||||
</div>
|
||||
<div class="form-hint">通过 Tailscale Funnel 暴露 Gateway 时,必须使用密码认证模式</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gw-advanced-toggle" id="gw-advanced-toggle">
|
||||
@@ -170,17 +203,22 @@ function renderConfig(page, state) {
|
||||
|
||||
function bindConfigEvents(el) {
|
||||
// 密码显示/隐藏
|
||||
el.querySelector('#btn-toggle-token').onclick = () => {
|
||||
const input = el.querySelector('#gw-token')
|
||||
const btn = el.querySelector('#btn-toggle-token')
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text'
|
||||
btn.textContent = '隐藏'
|
||||
} else {
|
||||
input.type = 'password'
|
||||
btn.textContent = '显示'
|
||||
function bindToggle(btnId, inputId) {
|
||||
const btn = el.querySelector('#' + btnId)
|
||||
if (!btn) return
|
||||
btn.onclick = () => {
|
||||
const input = el.querySelector('#' + inputId)
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text'
|
||||
btn.textContent = '隐藏'
|
||||
} else {
|
||||
input.type = 'password'
|
||||
btn.textContent = '显示'
|
||||
}
|
||||
}
|
||||
}
|
||||
bindToggle('btn-toggle-token', 'gw-token')
|
||||
bindToggle('btn-toggle-password', 'gw-password')
|
||||
|
||||
// 选项卡片点击高亮
|
||||
el.querySelectorAll('.gw-option-cards').forEach(group => {
|
||||
@@ -192,6 +230,17 @@ function bindConfigEvents(el) {
|
||||
})
|
||||
})
|
||||
|
||||
// 认证模式切换:显示/隐藏对应输入框
|
||||
el.querySelectorAll('input[name="gw-auth-mode"]').forEach(radio => {
|
||||
radio.addEventListener('change', () => {
|
||||
const mode = radio.value
|
||||
const tokenGroup = el.querySelector('#gw-auth-token-group')
|
||||
const passwordGroup = el.querySelector('#gw-auth-password-group')
|
||||
if (tokenGroup) tokenGroup.style.display = mode === 'token' ? '' : 'none'
|
||||
if (passwordGroup) passwordGroup.style.display = mode === 'password' ? '' : 'none'
|
||||
})
|
||||
})
|
||||
|
||||
// 高级选项折叠
|
||||
el.querySelector('#gw-advanced-toggle').onclick = () => {
|
||||
const panel = el.querySelector('#gw-advanced-panel')
|
||||
@@ -208,13 +257,20 @@ async function saveConfig(page, state) {
|
||||
const bind = bindRadio?.value || 'loopback'
|
||||
const modeRadio = page.querySelector('input[name="gw-mode"]:checked')
|
||||
const mode = modeRadio?.value || 'local'
|
||||
const authModeRadio = page.querySelector('input[name="gw-auth-mode"]:checked')
|
||||
const authMode = authModeRadio?.value || 'token'
|
||||
const authToken = page.querySelector('#gw-token')?.value || ''
|
||||
const authPassword = page.querySelector('#gw-password')?.value || ''
|
||||
const tailscaleAddr = page.querySelector('#gw-tailscale')?.value || ''
|
||||
|
||||
const auth = authMode === 'password'
|
||||
? { mode: 'password', password: authPassword }
|
||||
: authToken ? { mode: 'token', token: authToken } : {}
|
||||
|
||||
state.config.gateway = {
|
||||
...state.config.gateway,
|
||||
port, bind, mode,
|
||||
auth: { token: authToken },
|
||||
auth,
|
||||
tailscale: tailscaleAddr.trim() ? { address: tailscaleAddr.trim() } : undefined,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user