feat: auth capture — remote browser credential extraction
- BrowserSessionService: add create_ephemeral() for temp sessions
- New auth_capture_service.py: extract cookies, localStorage, sessionStorage from page
- New auth_capture router: POST /sessions, GET /sessions/{id}/extract, DELETE /sessions/{id}
- Frontend AuthCaptureDialog: URL input → browser view → extract → pick candidate
- Upstreams.vue: '提取' button next to Bearer Token field
- No sensitive values logged
This commit is contained in:
@@ -240,7 +240,13 @@
|
||||
</el-form-item>
|
||||
<template v-if="form.auth_type === 'bearer'">
|
||||
<el-form-item label="Bearer Token">
|
||||
<el-input v-model="form.auth_config.token" type="password" show-password placeholder="***" />
|
||||
<div class="auth-field-row">
|
||||
<el-input v-model="form.auth_config.token" type="password" show-password placeholder="***" />
|
||||
<el-button size="small" @click="openAuthCapture">
|
||||
<el-icon><Pointer /></el-icon>
|
||||
提取
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else-if="form.auth_type === 'api_key'">
|
||||
@@ -388,6 +394,12 @@
|
||||
<el-button size="small" :disabled="snapshots.length < snapshotLimit" @click="nextSnapPage">下一页</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<AuthCaptureDialog
|
||||
v-model="authCaptureVisible"
|
||||
:initial-url="form.base_url"
|
||||
@select="handleAuthCaptureSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -396,8 +408,9 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
import { Refresh, Plus, Edit, List, Delete, Warning, Clock, ArrowRight } from '@element-plus/icons-vue'
|
||||
import { Refresh, Plus, Edit, List, Delete, Warning, Clock, ArrowRight, Pointer } from '@element-plus/icons-vue'
|
||||
import { upstreamsApi, type UpstreamData } from '@/api'
|
||||
import AuthCaptureDialog from '@/components/AuthCaptureDialog.vue'
|
||||
|
||||
const list = ref<(UpstreamData & { _testing?: boolean; _checking?: boolean })[]>([])
|
||||
const tableLoading = ref(false)
|
||||
@@ -425,6 +438,22 @@ const rules = {
|
||||
base_url: [{ required: true, message: '请输入 Base URL', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
const authCaptureVisible = ref(false)
|
||||
|
||||
function openAuthCapture() {
|
||||
authCaptureVisible.value = true
|
||||
}
|
||||
|
||||
function handleAuthCaptureSelect(candidate: { type: string; value: string; cookie_name?: string; cookie_value?: string }) {
|
||||
if (candidate.type === 'bearer_token') {
|
||||
form.value.auth_config.token = candidate.value
|
||||
} else if (candidate.type === 'cookie') {
|
||||
// For cookie auth, store as a formatted cookie string
|
||||
form.value.auth_config.token = candidate.value
|
||||
}
|
||||
ElMessage.success('已填入认证信息')
|
||||
}
|
||||
|
||||
const quickPlatform = ref('sub2api')
|
||||
|
||||
function handlePlatformChange(val: string) {
|
||||
@@ -649,6 +678,18 @@ onMounted(loadList)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.auth-field-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.auth-field-row .el-input {
|
||||
flex: 1;
|
||||
}
|
||||
.auth-field-row .el-button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.upstreams-page {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user