mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-05-06 20:02:51 +08:00
feat(config): 采用列表模式
This commit is contained in:
@@ -484,14 +484,31 @@ def update_proxy_last_used(db: Session, proxy_id: int) -> bool:
|
||||
|
||||
|
||||
def get_random_proxy(db: Session) -> Optional[Proxy]:
|
||||
"""随机获取一个启用的代理"""
|
||||
"""随机获取一个启用的代理,优先返回 is_default=True 的代理"""
|
||||
import random
|
||||
# 优先返回默认代理
|
||||
default_proxy = db.query(Proxy).filter(Proxy.enabled == True, Proxy.is_default == True).first()
|
||||
if default_proxy:
|
||||
return default_proxy
|
||||
proxies = get_enabled_proxies(db)
|
||||
if not proxies:
|
||||
return None
|
||||
return random.choice(proxies)
|
||||
|
||||
|
||||
def set_proxy_default(db: Session, proxy_id: int) -> Optional[Proxy]:
|
||||
"""将指定代理设为默认,同时清除其他代理的默认标记"""
|
||||
# 清除所有默认标记
|
||||
db.query(Proxy).filter(Proxy.is_default == True).update({"is_default": False})
|
||||
# 设置新的默认代理
|
||||
proxy = db.query(Proxy).filter(Proxy.id == proxy_id).first()
|
||||
if proxy:
|
||||
proxy.is_default = True
|
||||
db.commit()
|
||||
db.refresh(proxy)
|
||||
return proxy
|
||||
|
||||
|
||||
def get_proxies_count(db: Session, enabled: Optional[bool] = None) -> int:
|
||||
"""获取代理数量"""
|
||||
query = db.query(func.count(Proxy.id))
|
||||
|
||||
@@ -156,6 +156,7 @@ class Proxy(Base):
|
||||
username = Column(String(100))
|
||||
password = Column(String(255))
|
||||
enabled = Column(Boolean, default=True)
|
||||
is_default = Column(Boolean, default=False) # 是否为默认代理
|
||||
priority = Column(Integer, default=0) # 优先级(保留字段)
|
||||
last_used = Column(DateTime) # 最后使用时间
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
@@ -171,6 +172,7 @@ class Proxy(Base):
|
||||
'port': self.port,
|
||||
'username': self.username,
|
||||
'enabled': self.enabled,
|
||||
'is_default': self.is_default or False,
|
||||
'priority': self.priority,
|
||||
'last_used': self.last_used.isoformat() if self.last_used else None,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
|
||||
@@ -110,6 +110,7 @@ class DatabaseSessionManager:
|
||||
("accounts", "subscription_type", "VARCHAR(20)"),
|
||||
("accounts", "subscription_at", "DATETIME"),
|
||||
("accounts", "cookies", "TEXT"),
|
||||
("proxies", "is_default", "BOOLEAN DEFAULT 0"),
|
||||
]
|
||||
|
||||
# 确保新表存在(create_tables 已处理,此处兜底)
|
||||
|
||||
@@ -347,18 +347,22 @@ def _run_sync_registration_task(task_uuid: str, email_service_type: str, proxy:
|
||||
saved_account = db.query(AccountModel).filter_by(email=result.email).first()
|
||||
if saved_account and saved_account.access_token:
|
||||
token_data = generate_token_json(saved_account)
|
||||
# 解析指定 CPA 服务
|
||||
# 解析指定 CPA 服务,未指定则取第一个启用的服务
|
||||
_cpa_api_url = None
|
||||
_cpa_api_token = None
|
||||
_svc = None
|
||||
if cpa_service_id:
|
||||
try:
|
||||
_svc = crud.get_cpa_service_by_id(db, cpa_service_id)
|
||||
if _svc:
|
||||
_cpa_api_url = _svc.api_url
|
||||
_cpa_api_token = _svc.api_token
|
||||
log_callback(f"[CPA] 使用服务: {_svc.name}")
|
||||
except Exception:
|
||||
pass
|
||||
if _svc is None:
|
||||
svcs = crud.get_cpa_services(db, enabled=True)
|
||||
_svc = svcs[0] if svcs else None
|
||||
if _svc:
|
||||
_cpa_api_url = _svc.api_url
|
||||
_cpa_api_token = _svc.api_token
|
||||
log_callback(f"[CPA] 使用服务: {_svc.name}")
|
||||
cpa_success, cpa_msg = upload_to_cpa(token_data, api_url=_cpa_api_url, api_token=_cpa_api_token)
|
||||
if cpa_success:
|
||||
saved_account.cpa_uploaded = True
|
||||
|
||||
@@ -111,101 +111,6 @@ async def get_all_settings():
|
||||
}
|
||||
|
||||
|
||||
@router.get("/proxy")
|
||||
async def get_proxy_settings():
|
||||
"""获取代理设置"""
|
||||
settings = get_settings()
|
||||
|
||||
return {
|
||||
"enabled": settings.proxy_enabled,
|
||||
"type": settings.proxy_type,
|
||||
"host": settings.proxy_host,
|
||||
"port": settings.proxy_port,
|
||||
"username": settings.proxy_username,
|
||||
"has_password": bool(settings.proxy_password),
|
||||
"proxy_url": settings.proxy_url,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/proxy")
|
||||
async def update_proxy_settings(request: ProxySettings):
|
||||
"""更新代理设置"""
|
||||
update_dict = {
|
||||
"proxy_enabled": request.enabled,
|
||||
"proxy_type": request.type,
|
||||
"proxy_host": request.host,
|
||||
"proxy_port": request.port,
|
||||
"proxy_username": request.username,
|
||||
}
|
||||
|
||||
if request.password:
|
||||
update_dict["proxy_password"] = request.password
|
||||
|
||||
update_settings(**update_dict)
|
||||
|
||||
return {"success": True, "message": "代理设置已更新"}
|
||||
|
||||
|
||||
@router.post("/proxy/test")
|
||||
async def test_proxy_settings(request: ProxySettings):
|
||||
"""测试代理连接"""
|
||||
import time
|
||||
from curl_cffi import requests as cffi_requests
|
||||
|
||||
# 构建代理 URL
|
||||
if request.type == "http":
|
||||
scheme = "http"
|
||||
elif request.type == "socks5":
|
||||
scheme = "socks5"
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="不支持的代理类型")
|
||||
|
||||
auth = ""
|
||||
if request.username and request.password:
|
||||
auth = f"{request.username}:{request.password}@"
|
||||
|
||||
proxy_url = f"{scheme}://{auth}{request.host}:{request.port}"
|
||||
|
||||
# 测试连接
|
||||
test_url = "https://api.ipify.org?format=json"
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
proxies = {
|
||||
"http": proxy_url,
|
||||
"https": proxy_url
|
||||
}
|
||||
|
||||
response = cffi_requests.get(
|
||||
test_url,
|
||||
proxies=proxies,
|
||||
timeout=3,
|
||||
impersonate="chrome110"
|
||||
)
|
||||
|
||||
elapsed_time = time.time() - start_time
|
||||
|
||||
if response.status_code == 200:
|
||||
ip_info = response.json()
|
||||
return {
|
||||
"success": True,
|
||||
"ip": ip_info.get("ip", ""),
|
||||
"response_time": round(elapsed_time * 1000), # 毫秒
|
||||
"message": f"代理连接成功,出口 IP: {ip_info.get('ip', 'unknown')}"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"代理返回错误状态码: {response.status_code}"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"代理连接失败: {str(e)}"
|
||||
}
|
||||
|
||||
|
||||
@router.get("/proxy/dynamic")
|
||||
async def get_dynamic_proxy_settings():
|
||||
"""获取动态代理设置"""
|
||||
@@ -639,6 +544,16 @@ async def delete_proxy_item(proxy_id: int):
|
||||
return {"success": True, "message": "代理已删除"}
|
||||
|
||||
|
||||
@router.post("/proxies/{proxy_id}/set-default")
|
||||
async def set_proxy_default(proxy_id: int):
|
||||
"""将指定代理设为默认"""
|
||||
with get_db() as db:
|
||||
proxy = crud.set_proxy_default(db, proxy_id)
|
||||
if not proxy:
|
||||
raise HTTPException(status_code=404, detail="代理不存在")
|
||||
return {"success": True, "proxy": proxy.to_dict()}
|
||||
|
||||
|
||||
@router.post("/proxies/{proxy_id}/test")
|
||||
async def test_proxy_item(proxy_id: int):
|
||||
"""测试单个代理"""
|
||||
@@ -774,77 +689,6 @@ async def disable_proxy(proxy_id: int):
|
||||
return {"success": True, "message": "代理已禁用"}
|
||||
|
||||
|
||||
# ============== CPA 设置 ==============
|
||||
|
||||
class CPASettings(BaseModel):
|
||||
"""CPA 设置"""
|
||||
enabled: bool = False
|
||||
api_url: str = ""
|
||||
api_token: str = ""
|
||||
|
||||
|
||||
class CPATestRequest(BaseModel):
|
||||
"""CPA 测试请求"""
|
||||
api_url: str
|
||||
api_token: str
|
||||
|
||||
|
||||
@router.get("/cpa")
|
||||
async def get_cpa_settings():
|
||||
"""获取 CPA 设置"""
|
||||
settings = get_settings()
|
||||
|
||||
return {
|
||||
"enabled": settings.cpa_enabled,
|
||||
"api_url": settings.cpa_api_url,
|
||||
"has_token": bool(settings.cpa_api_token and settings.cpa_api_token.get_secret_value()),
|
||||
}
|
||||
|
||||
|
||||
@router.post("/cpa")
|
||||
async def update_cpa_settings(request: CPASettings):
|
||||
"""更新 CPA 设置"""
|
||||
update_dict = {
|
||||
"cpa_enabled": request.enabled,
|
||||
"cpa_api_url": request.api_url,
|
||||
}
|
||||
|
||||
# 只有提供了 token 才更新
|
||||
if request.api_token:
|
||||
update_dict["cpa_api_token"] = request.api_token
|
||||
|
||||
update_settings(**update_dict)
|
||||
|
||||
return {"success": True, "message": "CPA 设置已更新"}
|
||||
|
||||
|
||||
@router.post("/cpa/test")
|
||||
async def test_cpa_connection(request: CPATestRequest):
|
||||
"""测试 CPA 连接"""
|
||||
from ...core.cpa_upload import test_cpa_connection as do_test
|
||||
|
||||
settings = get_settings()
|
||||
proxy = settings.proxy_url
|
||||
|
||||
# 如果传入 'use_saved_token',使用已保存的 token
|
||||
api_token = request.api_token
|
||||
if api_token == 'use_saved_token' or not api_token:
|
||||
if settings.cpa_api_token:
|
||||
api_token = settings.cpa_api_token.get_secret_value()
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "未配置 API Token"
|
||||
}
|
||||
|
||||
success, message = do_test(request.api_url, api_token, proxy)
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"message": message
|
||||
}
|
||||
|
||||
|
||||
# ============== Outlook 设置 ==============
|
||||
|
||||
class OutlookSettings(BaseModel):
|
||||
|
||||
@@ -102,19 +102,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 检查 CPA 是否启用,未启用则禁用复选框;同时加载 CPA 服务列表
|
||||
async function checkCpaEnabled() {
|
||||
if (!elements.autoUploadCpa) return;
|
||||
// 加载 CPA 服务列表,列表为空则禁用复选框
|
||||
await loadCpaServiceOptions();
|
||||
try {
|
||||
const data = await api.get('/settings/cpa');
|
||||
if (!data.enabled) {
|
||||
const services = await api.get('/cpa-services?enabled=true');
|
||||
if (!services || services.length === 0) {
|
||||
elements.autoUploadCpa.disabled = true;
|
||||
elements.autoUploadCpa.title = '请先在设置中启用 CPA 上传';
|
||||
elements.autoUploadCpa.title = '请先在设置中添加 CPA 服务';
|
||||
const label = elements.autoUploadCpa.closest('label');
|
||||
if (label) label.style.opacity = '0.5';
|
||||
}
|
||||
} catch (e) {
|
||||
elements.autoUploadCpa.disabled = true;
|
||||
}
|
||||
// 加载 CPA 服务列表
|
||||
await loadCpaServiceOptions();
|
||||
// 复选框联动显示/隐藏服务选择器
|
||||
if (elements.autoUploadCpa) {
|
||||
elements.autoUploadCpa.addEventListener('change', () => {
|
||||
@@ -130,8 +130,7 @@ async function loadCpaServiceOptions() {
|
||||
if (!elements.cpaServiceSelect) return;
|
||||
try {
|
||||
const services = await api.get('/cpa-services?enabled=true');
|
||||
// 保留「使用全局配置」选项
|
||||
const defaultOpt = '<option value="">使用全局配置</option>';
|
||||
const defaultOpt = '<option value="">自动选择(第一个启用的服务)</option>';
|
||||
const opts = services.map(s =>
|
||||
`<option value="${s.id}">${s.name.replace(/</g,'<')}</option>`
|
||||
).join('');
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
const elements = {
|
||||
tabs: document.querySelectorAll('.tab-btn'),
|
||||
tabContents: document.querySelectorAll('.tab-content'),
|
||||
proxyForm: document.getElementById('proxy-form'),
|
||||
registrationForm: document.getElementById('registration-settings-form'),
|
||||
testProxyBtn: document.getElementById('test-proxy-btn'),
|
||||
backupBtn: document.getElementById('backup-btn'),
|
||||
cleanupBtn: document.getElementById('cleanup-btn'),
|
||||
addEmailServiceBtn: document.getElementById('add-email-service-btn'),
|
||||
@@ -41,9 +39,6 @@ const elements = {
|
||||
// 动态代理设置
|
||||
dynamicProxyForm: document.getElementById('dynamic-proxy-form'),
|
||||
testDynamicProxyBtn: document.getElementById('test-dynamic-proxy-btn'),
|
||||
// CPA 设置
|
||||
cpaForm: document.getElementById('cpa-form'),
|
||||
testCpaBtn: document.getElementById('test-cpa-btn'),
|
||||
// CPA 服务管理
|
||||
addCpaServiceBtn: document.getElementById('add-cpa-service-btn'),
|
||||
cpaServicesTable: document.getElementById('cpa-services-table'),
|
||||
@@ -95,16 +90,6 @@ function initTabs() {
|
||||
|
||||
// 事件监听
|
||||
function initEventListeners() {
|
||||
// 代理表单
|
||||
if (elements.proxyForm) {
|
||||
elements.proxyForm.addEventListener('submit', handleSaveProxy);
|
||||
}
|
||||
|
||||
// 测试代理
|
||||
if (elements.testProxyBtn) {
|
||||
elements.testProxyBtn.addEventListener('click', handleTestProxy);
|
||||
}
|
||||
|
||||
// 注册配置表单
|
||||
if (elements.registrationForm) {
|
||||
elements.registrationForm.addEventListener('submit', handleSaveRegistration);
|
||||
@@ -228,15 +213,6 @@ function initEventListeners() {
|
||||
elements.testDynamicProxyBtn.addEventListener('click', handleTestDynamicProxy);
|
||||
}
|
||||
|
||||
// CPA 设置
|
||||
if (elements.cpaForm) {
|
||||
elements.cpaForm.addEventListener('submit', handleSaveCpa);
|
||||
}
|
||||
|
||||
if (elements.testCpaBtn) {
|
||||
elements.testCpaBtn.addEventListener('click', handleTestCpa);
|
||||
}
|
||||
|
||||
// 验证码设置
|
||||
if (elements.emailCodeForm) {
|
||||
elements.emailCodeForm.addEventListener('submit', handleSaveEmailCode);
|
||||
@@ -286,13 +262,6 @@ async function loadSettings() {
|
||||
try {
|
||||
const data = await api.get('/settings');
|
||||
|
||||
// 代理设置
|
||||
document.getElementById('proxy-enabled').checked = data.proxy?.enabled || false;
|
||||
document.getElementById('proxy-type').value = data.proxy?.type || 'http';
|
||||
document.getElementById('proxy-host').value = data.proxy?.host || '127.0.0.1';
|
||||
document.getElementById('proxy-port').value = data.proxy?.port || 7890;
|
||||
document.getElementById('proxy-username').value = data.proxy?.username || '';
|
||||
|
||||
// 动态代理设置
|
||||
document.getElementById('dynamic-proxy-enabled').checked = data.proxy?.dynamic_enabled || false;
|
||||
document.getElementById('dynamic-proxy-api-url').value = data.proxy?.dynamic_api_url || '';
|
||||
@@ -312,8 +281,6 @@ async function loadSettings() {
|
||||
document.getElementById('email-code-poll-interval').value = data.email_code.poll_interval || 3;
|
||||
}
|
||||
|
||||
// 加载 CPA 设置
|
||||
loadCpaSettings();
|
||||
// 加载 Outlook 设置
|
||||
loadOutlookSettings();
|
||||
// 加载 Team Manager 设置
|
||||
@@ -445,57 +412,6 @@ async function loadDatabaseInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
// 保存代理设置
|
||||
async function handleSaveProxy(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
enabled: document.getElementById('proxy-enabled').checked,
|
||||
type: document.getElementById('proxy-type').value,
|
||||
host: document.getElementById('proxy-host').value,
|
||||
port: parseInt(document.getElementById('proxy-port').value),
|
||||
username: document.getElementById('proxy-username').value || null,
|
||||
password: document.getElementById('proxy-password').value || null,
|
||||
};
|
||||
|
||||
try {
|
||||
await api.post('/settings/proxy', data);
|
||||
toast.success('代理设置已保存');
|
||||
} catch (error) {
|
||||
toast.error('保存失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 测试代理
|
||||
async function handleTestProxy() {
|
||||
elements.testProxyBtn.disabled = true;
|
||||
elements.testProxyBtn.innerHTML = '<span class="loading-spinner"></span> 测试中...';
|
||||
|
||||
try {
|
||||
const data = {
|
||||
enabled: document.getElementById('proxy-enabled').checked,
|
||||
type: document.getElementById('proxy-type').value,
|
||||
host: document.getElementById('proxy-host').value,
|
||||
port: parseInt(document.getElementById('proxy-port').value),
|
||||
username: document.getElementById('proxy-username').value || null,
|
||||
password: document.getElementById('proxy-password').value || null,
|
||||
};
|
||||
|
||||
const result = await api.post('/settings/proxy/test', data);
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
} else {
|
||||
toast.error(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('测试失败: ' + error.message);
|
||||
} finally {
|
||||
elements.testProxyBtn.disabled = false;
|
||||
elements.testProxyBtn.textContent = '🔌 测试连接';
|
||||
}
|
||||
}
|
||||
|
||||
// 保存注册配置
|
||||
async function handleSaveRegistration(e) {
|
||||
e.preventDefault();
|
||||
@@ -838,6 +754,12 @@ function renderProxies(proxies) {
|
||||
<td>${escapeHtml(proxy.name)}</td>
|
||||
<td><span class="badge">${proxy.type.toUpperCase()}</span></td>
|
||||
<td><code>${escapeHtml(proxy.host)}:${proxy.port}</code></td>
|
||||
<td>
|
||||
${proxy.is_default
|
||||
? '<span class="status-badge active">默认</span>'
|
||||
: `<button class="btn btn-ghost btn-sm" onclick="handleSetProxyDefault(${proxy.id})" title="设为默认">设默认</button>`
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<span class="status-badge ${proxy.enabled ? 'active' : 'disabled'}">
|
||||
${proxy.enabled ? '已启用' : '已禁用'}
|
||||
@@ -864,6 +786,17 @@ function renderProxies(proxies) {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 设为默认代理
|
||||
async function handleSetProxyDefault(id) {
|
||||
try {
|
||||
await api.post(`/settings/proxies/${id}/set-default`);
|
||||
toast.success('已设为默认代理');
|
||||
loadProxies();
|
||||
} catch (error) {
|
||||
toast.error('操作失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开代理模态框
|
||||
function openProxyModal(proxy = null) {
|
||||
elements.proxyModalTitle.textContent = proxy ? '编辑代理' : '添加代理';
|
||||
@@ -987,45 +920,6 @@ async function handleTestAllProxies() {
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// CPA 设置管理
|
||||
// ============================================================================
|
||||
|
||||
// 加载 CPA 设置
|
||||
async function loadCpaSettings() {
|
||||
try {
|
||||
const data = await api.get('/settings/cpa');
|
||||
|
||||
document.getElementById('cpa-enabled').checked = data.enabled || false;
|
||||
document.getElementById('cpa-api-url').value = data.api_url || '';
|
||||
// 不填充 token,只显示是否有值
|
||||
document.getElementById('cpa-api-token').value = '';
|
||||
document.getElementById('cpa-api-token').placeholder = data.has_token ? '已配置,留空保持不变' : '请输入 API Token';
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载 CPA 设置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存 CPA 设置
|
||||
async function handleSaveCpa(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
enabled: document.getElementById('cpa-enabled').checked,
|
||||
api_url: document.getElementById('cpa-api-url').value,
|
||||
api_token: document.getElementById('cpa-api-token').value || ''
|
||||
};
|
||||
|
||||
try {
|
||||
await api.post('/settings/cpa', data);
|
||||
toast.success('CPA 设置已保存');
|
||||
loadCpaSettings();
|
||||
} catch (error) {
|
||||
toast.error('保存失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Outlook 设置管理
|
||||
// ============================================================================
|
||||
@@ -1055,47 +949,6 @@ async function handleSaveOutlookSettings(e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 CPA 连接
|
||||
async function handleTestCpa() {
|
||||
const apiUrl = document.getElementById('cpa-api-url').value;
|
||||
const apiToken = document.getElementById('cpa-api-token').value;
|
||||
|
||||
if (!apiUrl) {
|
||||
toast.warning('请输入 API URL');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果 token 为空,尝试使用已保存的 token 进行测试
|
||||
if (!apiToken) {
|
||||
const cpaSettings = await api.get('/settings/cpa');
|
||||
if (!cpaSettings.has_token) {
|
||||
toast.warning('请输入 API Token');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
elements.testCpaBtn.disabled = true;
|
||||
elements.testCpaBtn.innerHTML = '<span class="loading-spinner"></span> 测试中...';
|
||||
|
||||
try {
|
||||
const result = await api.post('/settings/cpa/test', {
|
||||
api_url: apiUrl,
|
||||
api_token: apiToken || 'use_saved_token'
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
} else {
|
||||
toast.error(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('测试失败: ' + error.message);
|
||||
} finally {
|
||||
elements.testCpaBtn.disabled = false;
|
||||
elements.testCpaBtn.textContent = '🔌 测试连接';
|
||||
}
|
||||
}
|
||||
|
||||
// ============== 动态代理设置 ==============
|
||||
|
||||
async function handleSaveDynamicProxy(e) {
|
||||
|
||||
@@ -48,66 +48,11 @@
|
||||
|
||||
<!-- 代理设置 -->
|
||||
<div class="tab-content active" id="proxy-tab">
|
||||
<!-- 默认代理配置 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>默认代理配置</h3>
|
||||
<span class="hint">当代理列表为空时使用此配置</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="proxy-form">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="proxy-enabled" name="enabled">
|
||||
启用代理
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="proxy-type">代理类型</label>
|
||||
<select id="proxy-type" name="type">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="socks5">SOCKS5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="proxy-host">主机地址</label>
|
||||
<input type="text" id="proxy-host" name="host" value="127.0.0.1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="proxy-port">端口</label>
|
||||
<input type="number" id="proxy-port" name="port" value="7890">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="proxy-username">用户名 (可选)</label>
|
||||
<input type="text" id="proxy-username" name="username" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="proxy-password">密码 (可选)</label>
|
||||
<input type="password" id="proxy-password" name="password" autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">💾 保存设置</button>
|
||||
<button type="button" class="btn btn-secondary" id="test-proxy-btn">🔌 测试连接</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 动态代理配置 -->
|
||||
<div class="card" style="margin-top: var(--spacing-lg);">
|
||||
<div class="card-header">
|
||||
<h3>动态代理配置</h3>
|
||||
<span class="hint">通过 API 每次获取新代理 IP,优先级高于静态代理和代理列表</span>
|
||||
<span class="hint">通过 API 每次获取新代理 IP,优先级高于代理列表</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="dynamic-proxy-form">
|
||||
@@ -163,9 +108,10 @@
|
||||
<th>名称</th>
|
||||
<th>类型</th>
|
||||
<th>地址</th>
|
||||
<th style="width: 60px;">默认</th>
|
||||
<th style="width: 80px;">状态</th>
|
||||
<th style="width: 120px;">最后使用</th>
|
||||
<th style="width: 150px;">操作</th>
|
||||
<th style="width: 180px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="proxies-table">
|
||||
@@ -258,43 +204,8 @@
|
||||
|
||||
<!-- CPA 上传设置 -->
|
||||
<div class="tab-content" id="cpa-tab">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>CPA 上传配置</h3>
|
||||
<span class="hint">配置 CliProxyApi 上传功能</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="cpa-form">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="cpa-enabled" name="enabled">
|
||||
启用 CPA 上传
|
||||
</label>
|
||||
<p class="hint">启用后可在账号管理页面上传账号到 CPA 管理平台</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cpa-api-url">API URL</label>
|
||||
<input type="text" id="cpa-api-url" name="api_url" placeholder="例如: https://cpa.example.com">
|
||||
<p class="hint">CPA 管理平台的 API 地址</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cpa-api-token">API Token</label>
|
||||
<input type="password" id="cpa-api-token" name="api_token" placeholder="留空则保持原值" autocomplete="new-password">
|
||||
<p class="hint">CPA 管理平台的认证 Token</p>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">💾 保存设置</button>
|
||||
<button type="button" class="btn btn-secondary" id="test-cpa-btn">🔌 测试连接</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CPA 服务管理 -->
|
||||
<div class="card" style="margin-top: 16px;">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>CPA 服务管理</h3>
|
||||
<button class="btn btn-primary btn-sm" id="add-cpa-service-btn">+ 添加服务</button>
|
||||
|
||||
Reference in New Issue
Block a user