Improve remote endpoint detection

This commit is contained in:
lutc5
2026-04-30 12:57:47 +08:00
parent 2bcb0a6715
commit e88856e1fc
13 changed files with 628 additions and 50 deletions

View File

@@ -222,7 +222,7 @@ onUnmounted(() => {
<span class="status-dot" :class="{ running: status.running }"></span>
<div>
<strong>{{ status.running ? 'Proxy Running' : 'Proxy Stopped' }}</strong>
<small>v1.4.0</small>
<small>v1.4.1</small>
</div>
</div>
</aside>

View File

@@ -1266,6 +1266,83 @@ button:disabled {
font-size: 12px;
}
.detect-card {
display: grid;
gap: 10px;
margin-top: 14px;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(255, 255, 255, 0.54);
}
:root[data-theme="dark"] .detect-card {
background: rgba(15, 23, 42, 0.52);
}
.detect-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.detect-title strong {
color: var(--text);
font-size: 13px;
}
.detect-title button {
min-height: 28px;
padding: 0 10px;
color: var(--text);
border: 1px solid var(--line);
border-radius: 7px;
background: var(--surface-strong);
cursor: pointer;
}
.detect-card dl {
display: grid;
gap: 8px;
margin: 0;
}
.detect-card dl > div {
display: grid;
grid-template-columns: 82px minmax(0, 1fr);
gap: 10px;
align-items: start;
}
.detect-card dt {
color: var(--muted);
font-size: 12px;
font-weight: 680;
}
.detect-card dd {
min-width: 0;
margin: 0;
color: var(--text);
overflow-wrap: anywhere;
font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
font-size: 12px;
line-height: 1.45;
}
.muted-inline {
display: block;
margin-top: 4px;
color: var(--muted);
font-size: 12px;
font-weight: 650;
}
.warn-text {
color: var(--danger-text) !important;
}
.form-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));

View File

@@ -1,10 +1,11 @@
<script setup>
import { computed, onMounted, ref } from 'vue'
import { GetConfig, UpdateConfig } from '../../wailsjs/go/main/App.js'
import { GetConfig, GetDetectionInfo, UpdateConfig } from '../../wailsjs/go/main/App.js'
const emit = defineEmits(['log', 'status-refresh'])
const config = ref({})
const detection = ref(null)
const saving = ref(false)
const openSelect = ref('')
@@ -47,20 +48,31 @@ function toggleSelect(field) {
function chooseOption(field, value) {
config.value[field] = value
openSelect.value = ''
refreshDetection()
}
onMounted(async () => {
try {
config.value = await GetConfig()
await refreshDetection()
} catch (e) {
emit('log', 'error', '配置加载失败:' + (e.message || String(e)))
}
})
async function refreshDetection() {
try {
detection.value = await GetDetectionInfo()
} catch (e) {
emit('log', 'warn', '探测信息加载失败:' + (e.message || String(e)))
}
}
async function save() {
saving.value = true
try {
await UpdateConfig(config.value)
await refreshDetection()
emit('log', 'info', '配置已保存,代理已按需重启')
emit('status-refresh')
} catch (e) {
@@ -169,6 +181,50 @@ async function save() {
<strong>自动探测失败时</strong>
<span>IPC 模式先确认 VS Code / Lingma 插件已启动并登录远端 API 模式会优先读取认证文件留空时只读 <code>~/.lingma/cache/user</code></span>
</div>
<div v-if="detection" class="detect-card">
<div class="detect-title">
<strong>当前解析结果</strong>
<button type="button" @click="refreshDetection">刷新</button>
</div>
<dl>
<div>
<dt>监听地址</dt>
<dd>{{ detection.listenUrl || '未启动' }}</dd>
</div>
<div>
<dt>当前后端</dt>
<dd>{{ detection.backendLabel || detection.backend }}</dd>
</div>
<div>
<dt>IPC 地址</dt>
<dd v-if="detection.ipcSuccess">{{ detection.ipcTransport }} · {{ detection.ipcEndpoint }}</dd>
<dd v-else class="warn-text">{{ detection.ipcError || '未探测到' }}</dd>
</div>
<div>
<dt>远端域名</dt>
<dd>
{{ detection.remoteBaseUrl }}
<span v-if="detection.remoteBaseUrlSource" class="muted-inline">来自 {{ detection.remoteBaseUrlSource }}</span>
</dd>
</div>
<div>
<dt>登录态来源</dt>
<dd v-if="detection.remoteCredentialSuccess">{{ detection.remoteCredentialSource }}</dd>
<dd v-else class="warn-text">{{ detection.remoteCredentialError || '未探测到' }}</dd>
</div>
<div v-if="detection.remoteCredentialSuccess">
<dt>账号 / 机器</dt>
<dd>{{ detection.remoteUserId || '未知用户' }} · {{ detection.remoteMachineId || '未知机器' }}</dd>
</div>
<div v-if="detection.remoteCredentialSuccess">
<dt>登录态有效期</dt>
<dd :class="{ 'warn-text': detection.remoteTokenExpired }">
{{ detection.remoteTokenExpireAt || '未提供' }}
<span v-if="detection.remoteTokenExpired">已过期</span>
</dd>
</div>
</dl>
</div>
</div>
<div class="glass-panel">

View File

@@ -9,6 +9,8 @@ export function ClearRequests():Promise<void>;
export function GetConfig():Promise<service.Config>;
export function GetDetectionInfo():Promise<main.DetectionInfo>;
export function GetModels():Promise<Array<main.ModelInfo>>;
export function GetRequests():Promise<Array<main.RequestRecord>>;

View File

@@ -14,6 +14,10 @@ export function GetConfig() {
return window['go']['main']['App']['GetConfig']();
}
export function GetDetectionInfo() {
return window['go']['main']['App']['GetDetectionInfo']();
}
export function GetModels() {
return window['go']['main']['App']['GetModels']();
}

View File

@@ -1,5 +1,47 @@
export namespace main {
export class DetectionInfo {
listenUrl: string;
backend: string;
backendLabel: string;
ipcSuccess: boolean;
ipcTransport?: string;
ipcEndpoint?: string;
ipcError?: string;
remoteBaseUrl: string;
remoteBaseUrlSource?: string;
remoteCredentialSuccess: boolean;
remoteCredentialSource?: string;
remoteUserId?: string;
remoteMachineId?: string;
remoteTokenExpireAt?: string;
remoteTokenExpired: boolean;
remoteCredentialError?: string;
static createFrom(source: any = {}) {
return new DetectionInfo(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.listenUrl = source["listenUrl"];
this.backend = source["backend"];
this.backendLabel = source["backendLabel"];
this.ipcSuccess = source["ipcSuccess"];
this.ipcTransport = source["ipcTransport"];
this.ipcEndpoint = source["ipcEndpoint"];
this.ipcError = source["ipcError"];
this.remoteBaseUrl = source["remoteBaseUrl"];
this.remoteBaseUrlSource = source["remoteBaseUrlSource"];
this.remoteCredentialSuccess = source["remoteCredentialSuccess"];
this.remoteCredentialSource = source["remoteCredentialSource"];
this.remoteUserId = source["remoteUserId"];
this.remoteMachineId = source["remoteMachineId"];
this.remoteTokenExpireAt = source["remoteTokenExpireAt"];
this.remoteTokenExpired = source["remoteTokenExpired"];
this.remoteCredentialError = source["remoteCredentialError"];
}
}
export class ModelInfo {
id: string;
name: string;
@@ -17,6 +59,7 @@ export namespace main {
export class ProxyStatus {
running: boolean;
addr: string;
backend: string;
models: number;
model?: string;
startedAt?: string;
@@ -29,6 +72,7 @@ export namespace main {
if ('string' === typeof source) source = JSON.parse(source);
this.running = source["running"];
this.addr = source["addr"];
this.backend = source["backend"];
this.models = source["models"];
this.model = source["model"];
this.startedAt = source["startedAt"];