Files
socks-manager/templates/instances.html
T
Your Name 0bd9bcac12 fix: 修复暗色主题UI对比度、端口占用死循环及多项Web表单bug
后端修复:
- engine/instances.py: Socks5Server.start() 端口占用时死循环, 增加超时和异常透传
- engine/instances.py: import time 移至顶层
- engine/instances.py: sync_instances/start_instance 容忍单实例启动失败
- run.py: 启动时 sync_instances 失败不退出进程
- web/routes.py: instances start/restart/edit 路由捕获 OSError, flash 友好提示
- api/v1.py: instances start/restart/update API 返回 JSON 错误而非 500 崩溃
- web/routes.py: toggle_user/ban_user 支持 hidden 字段语义, 缺字段时切换状态
- web/routes.py: inject_nav 增加独立 nav.dashboard 和 page_name, 修复双 active bug
- auth.py: login 路由添加 logging import, 修 NameError

前端重构:
- templates/base.html: 全局对比度提升(--text #f1f5f9, --accent #a5b4fc), 标题色显式声明, alert/modal/progress/table 完整暗色样式
- templates/base.html: main-content padding-top 修复 navbar 遮挡内容
- templates/base.html: 侧边栏 active 高亮 (白字 + elevated bg + 左边框)
- templates/base.html: 顶栏动态显示当前页面名
- templates/*.html: 统一 page-header 类, 所有图标按钮添加 title tooltip
- templates/users.html: toggle/ban 表单添加 hidden enabled/ban 字段
- templates/connections.html: fmtBytes 改为 Jinja macro, 时间格式化
- templates/dashboard.html: 时间格式化为 YYYY-MM-DD HH:MM:SS, chart canvas 高度调整
- templates/logs.html: 时间格式化为可读格式
- templates/stats.html: 提升 chart 颜色对比度
- templates/system.html: 使用 time.localtime() 显示本地时间
2026-07-16 23:01:39 +08:00

239 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}代理实例 · SOCKS Manager{% endblock %}
{% block content %}
<div class="page-header">
<h5><i class="bi bi-server"></i> 代理实例</h5>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addInstanceModal">
<i class="bi bi-plus-lg"></i> 新建实例</button>
</div>
<!-- 实例列表 -->
<div class="card">
<div class="card-body p-0">
<table class="table table-dark align-middle">
<thead>
<tr><th>名称</th><th>监听地址</th><th>认证</th><th>限速</th><th>并发</th>
<th>状态</th><th>活跃连接</th><th>操作</th></tr>
</thead>
<tbody>
{% for inst in instances %}
<tr>
<td>
<strong>{{ inst.name }}</strong>
{% if inst.notes %}<br><small class="text-muted">{{ inst.notes }}</small>{% endif %}
</td>
<td><code>{{ inst.host }}:{{ inst.port }}</code></td>
<td>
<span class="badge badge-{{ 'info' if inst.auth_method=='userpass' else 'secondary' }}">
{{ '账号密码' if inst.auth_method=='userpass' else '无认证' }}</span>
</td>
<td>
<span class="text-muted small">↓{{ inst.bandwidth_down or '不限' }} ↑{{ inst.bandwidth_up or '不限' }} Mbps</span>
</td>
<td>{{ inst.max_concurrent }}</td>
<td>
<span class="status-dot {{ 'dot-online' if inst.running else 'dot-offline' }}"></span>
{{ '运行中' if inst.running else '已停止' }}
</td>
<td>
<span class="badge badge-{{ 'success' if inst.active_connections>0 else 'secondary' }}">
{{ inst.active_connections }}</span>
</td>
<td>
<div class="btn-group btn-group-sm">
{% if not inst.running %}
<form method="post" action="/instances/{{ inst.id }}/start" style="display:inline">
<button class="btn btn-outline-success" title="启动"><i class="bi bi-play-fill"></i></button>
</form>
{% else %}
<form method="post" action="/instances/{{ inst.id }}/stop" style="display:inline">
<button class="btn btn-outline-warning" title="停止"><i class="bi bi-pause-fill"></i></button>
</form>
<form method="post" action="/instances/{{ inst.id }}/restart" style="display:inline">
<button class="btn btn-outline-primary" title="重启"><i class="bi bi-arrow-clockwise"></i></button>
</form>
{% endif %}
<button class="btn btn-outline-secondary" title="编辑" data-bs-toggle="modal"
data-bs-target="#editInstanceModal"
data-name="{{ inst.name }}"
data-host="{{ inst.host }}"
data-port="{{ inst.port }}"
data-timeout="{{ inst.timeout }}"
data-auth="{{ inst.auth_method }}"
data-bw-down="{{ inst.bandwidth_down }}"
data-bw-up="{{ inst.bandwidth_up }}"
data-concurrent="{{ inst.max_concurrent }}"
data-notes="{{ inst.notes }}"
data-id="{{ inst.id }}">
<i class="bi bi-pencil"></i>
</button>
<form method="post" action="/instances/{{ inst.id }}/delete" style="display:inline"
onsubmit="return confirmDelete('确认删除实例 {{ inst.name }}')">
<button class="btn btn-outline-danger" title="删除"><i class="bi bi-trash"></i></button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not instances %}
<div class="text-center py-5 text-muted">
<i class="bi bi-server text-muted" style="font-size:2rem;display:block;margin-bottom:.5rem"></i>
暂无实例,点击上方"新建实例"开始
</div>
{% endif %}
</div>
</div>
<!-- 添加实例模态框 -->
<div class="modal fade" id="addInstanceModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">新建代理实例</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="post" action="/instances/add">
<div class="modal-body">
<div class="mb-3"><label class="form-label">实例名称</label>
<input name="name" class="form-control" required placeholder="例: main-proxy">
</div>
<div class="row g-2 mb-3">
<div class="col-7">
<label class="form-label">监听地址</label>
<input name="listen_host" class="form-control" value="0.0.0.0" placeholder="0.0.0.0 或 ::">
</div>
<div class="col-5">
<label class="form-label">监听端口</label>
<input name="listen_port" type="number" class="form-control" value="1080" min="1" max="65535">
</div>
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label">超时 (秒)</label>
<input name="timeout" type="number" class="form-control" value="30">
</div>
<div class="col-6">
<label class="form-label">认证方式</label>
<select name="auth_method" class="form-select">
<option value="none">无认证</option>
<option value="userpass">账号密码</option>
</select>
</div>
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label">下行限速 (Mbps, 0=不限)</label>
<input name="bandwidth_down" type="number" class="form-control" value="0" step="0.1">
</div>
<div class="col-6">
<label class="form-label">上行限速 (Mbps, 0=不限)</label>
<input name="bandwidth_up" type="number" class="form-control" value="0" step="0.1">
</div>
</div>
<div class="mb-3">
<label class="form-label">最大并发连接数</label>
<input name="max_concurrent" type="number" class="form-control" value="10">
</div>
<div class="mb-3">
<label class="form-label">备注</label>
<textarea name="notes" class="form-control" rows="2"></textarea>
</div>
<div class="form-check">
<input type="checkbox" name="start" class="form-check-input" id="startCheck" checked>
<label class="form-check-label" for="startCheck">创建后立即启动</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary btn-sm">创建</button>
</div>
</form>
</div>
</div>
</div>
<!-- 编辑实例模态框 -->
<div class="modal fade" id="editInstanceModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">编辑实例</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="editInstanceForm" method="post">
<div class="modal-body">
<input type="hidden" name="id" id="editId">
<div class="row g-2 mb-3">
<div class="col-7">
<label class="form-label">监听地址</label>
<input name="listen_host" class="form-control" id="editHost">
</div>
<div class="col-5">
<label class="form-label">监听端口</label>
<input name="listen_port" type="number" class="form-control" id="editPort">
</div>
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label">超时 (秒)</label>
<input name="timeout" type="number" class="form-control" id="editTimeout">
</div>
<div class="col-6">
<label class="form-label">认证方式</label>
<select name="auth_method" class="form-select" id="editAuth">
<option value="none">无认证</option>
<option value="userpass">账号密码</option>
</select>
</div>
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label">下行限速 (Mbps)</label>
<input name="bandwidth_down" type="number" class="form-control" id="editBwDown" step="0.1">
</div>
<div class="col-6">
<label class="form-label">上行限速 (Mbps)</label>
<input name="bandwidth_up" type="number" class="form-control" id="editBwUp" step="0.1">
</div>
</div>
<div class="mb-3">
<label class="form-label">最大并发连接数</label>
<input name="max_concurrent" type="number" class="form-control" id="editConcurrent">
</div>
<div class="mb-3">
<label class="form-label">备注</label>
<textarea name="notes" class="form-control" rows="2" id="editNotes"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary btn-sm">保存</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_script %}
<script>
document.getElementById('editInstanceModal').addEventListener('show.bs.modal', function(e) {
const btn = e.relatedTarget;
const form = document.getElementById('editInstanceForm');
form.action = '/instances/' + btn.dataset.id + '/edit';
document.getElementById('editHost').value = btn.dataset.host;
document.getElementById('editPort').value = btn.dataset.port;
document.getElementById('editTimeout').value = btn.dataset.timeout;
document.getElementById('editAuth').value = btn.dataset.auth;
document.getElementById('editBwDown').value = btn.dataset.bwDown;
document.getElementById('editBwUp').value = btn.dataset.bwUp;
document.getElementById('editConcurrent').value = btn.dataset.concurrent;
document.getElementById('editNotes').value = btn.dataset.notes || '';
});
</script>
{% endblock %}