feat(settings): 添加删除禁用代理功能

- 在数据库 CRUD 层添加删除所有已禁用代理的方法
- 在设置页面界面添加删除禁用项按钮
- 更新表格列数以适应新的操作列
- 在 JavaScript 中添加按钮元素引用
- 实现代理连通性测试函数用于验证功能
- 添加代理测试失败时自动禁用机制
This commit is contained in:
cnlimiter
2026-03-26 15:16:47 +08:00
parent 235c60df25
commit 904dd1f230
4 changed files with 60 additions and 1 deletions

View File

@@ -532,6 +532,11 @@ def delete_proxy(db: Session, proxy_id: int) -> bool:
db.commit() db.commit()
return True return True
def delete_disabled_proxies(db: Session) -> int:
"""删除所有已禁用代理"""
deleted = db.query(Proxy).filter(Proxy.enabled == False).delete(synchronize_session=False)
db.commit()
return deleted
def update_proxy_last_used(db: Session, proxy_id: int) -> bool: def update_proxy_last_used(db: Session, proxy_id: int) -> bool:
"""更新代理最后使用时间""" """更新代理最后使用时间"""

View File

@@ -468,6 +468,58 @@ class ProxyUpdateRequest(BaseModel):
enabled: Optional[bool] = None enabled: Optional[bool] = None
priority: Optional[int] = None priority: Optional[int] = None
def _test_proxy_connectivity(proxy_url: str) -> dict:
"""测试代理连通性并返回统一结果。"""
import time
from curl_cffi import requests as cffi_requests
test_url = "https://api.ipify.org?format=json"
start_time = time.time()
proxies_dict = {
"http": proxy_url,
"https": proxy_url
}
response = cffi_requests.get(
test_url,
proxies=proxies_dict,
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')}"
}
return {
"success": False,
"message": f"状态码: {response.status_code}"
}
def _auto_disable_proxy_on_failure(db, proxy, message: str) -> dict:
"""代理测试失败时自动禁用,并返回统一提示。"""
auto_disabled = False
if proxy.enabled:
crud.update_proxy(db, proxy.id, enabled=False)
auto_disabled = True
final_message = message
if auto_disabled:
final_message = f"{message},已自动禁用"
return {
"success": False,
"auto_disabled": auto_disabled,
"message": final_message,
}
@router.get("/proxies") @router.get("/proxies")
async def get_proxies_list(enabled: Optional[bool] = None): async def get_proxies_list(enabled: Optional[bool] = None):

View File

@@ -31,6 +31,7 @@ const elements = {
proxiesTable: document.getElementById('proxies-table'), proxiesTable: document.getElementById('proxies-table'),
addProxyBtn: document.getElementById('add-proxy-btn'), addProxyBtn: document.getElementById('add-proxy-btn'),
testAllProxiesBtn: document.getElementById('test-all-proxies-btn'), testAllProxiesBtn: document.getElementById('test-all-proxies-btn'),
deleteDisabledProxiesBtn: document.getElementById('delete-disabled-proxies-btn'),
addProxyModal: document.getElementById('add-proxy-modal'), addProxyModal: document.getElementById('add-proxy-modal'),
proxyItemForm: document.getElementById('proxy-item-form'), proxyItemForm: document.getElementById('proxy-item-form'),
closeProxyModal: document.getElementById('close-proxy-modal'), closeProxyModal: document.getElementById('close-proxy-modal'),

View File

@@ -95,6 +95,7 @@
<h3>代理列表</h3> <h3>代理列表</h3>
<div style="display: flex; gap: var(--spacing-sm);"> <div style="display: flex; gap: var(--spacing-sm);">
<button class="btn btn-secondary btn-sm" id="test-all-proxies-btn">🔌 测试全部</button> <button class="btn btn-secondary btn-sm" id="test-all-proxies-btn">🔌 测试全部</button>
<button class="btn btn-danger btn-sm" id="delete-disabled-proxies-btn" disabled>🧹 删除禁用项</button>
<button class="btn btn-primary btn-sm" id="add-proxy-btn"> 添加代理</button> <button class="btn btn-primary btn-sm" id="add-proxy-btn"> 添加代理</button>
</div> </div>
</div> </div>
@@ -115,7 +116,7 @@
</thead> </thead>
<tbody id="proxies-table"> <tbody id="proxies-table">
<tr> <tr>
<td colspan="7"> <td colspan="8">
<div class="empty-state"> <div class="empty-state">
<div class="empty-state-icon">🌐</div> <div class="empty-state-icon">🌐</div>
<div class="empty-state-title">暂无代理</div> <div class="empty-state-title">暂无代理</div>