feat: frps-manager v1.0 - frp 0.70.1 multi-tenant web management system

- Multi-tenant client/proxy management with Flask+SQLite
- Frps server control (start/restart/token rotation)
- Generate frpc.ini with shared server token + admin API
- Dashboard API integration (live client/proxy status)
- RBAC: admin/tenant_admin/user roles
- Audit logging for all operations
- Systemd service files included
- Requires legacy INI format (frp 0.70 TOML auth broken)
This commit is contained in:
Your Name
2026-08-10 14:17:49 +08:00
commit 71214524f4
27 changed files with 2847 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
{% extends "base.html" %}
{% block title %}仪表盘 · frps-manager{% endblock %}
{% block content %}
<h1>仪表盘</h1>
<section class="stats">
<div class="stat-card">
<div class="stat-label">frps 版本</div>
<div class="stat-value">
{% if info.error %}
<span class="offline">离线</span>
{% else %}
{{ info.server.version }}
{% endif %}
</div>
</div>
<div class="stat-card">
<div class="stat-label">监听端口</div>
<div class="stat-value">
{% if info.error %}-{% else %}{{ info.server.bindPort }}{% endif %}
</div>
</div>
<div class="stat-card">
<div class="stat-label">在线 frpc 客户端</div>
<div class="stat-value">
{% if info.error %}-{% else %}{{ info.clients.total }}{% endif %}
</div>
</div>
<div class="stat-card">
<div class="stat-label">活跃代理</div>
<div class="stat-value">
{% if info.error %}-{% else %}{{ info.proxies.total }}{% endif %}
</div>
</div>
<div class="stat-card">
<div class="stat-label">租户 / 客户端 / 代理</div>
<div class="stat-value">{{ tenant_count }} / {{ client_count }} / {{ proxy_count }}</div>
</div>
</section>
{% if info.error %}
<div class="alert alert-error">
无法连接 frps dashboard{{ info.error }})。请确认 frps 服务已启动且 dashboard 配置正确。
</div>
{% endif %}
{% if user.role == 'admin' %}
<section class="actions">
<h2>服务控制</h2>
<form method="post" action="{{ url_for('frps_reload') }}" style="display:inline">
<button class="btn">重写 frps.ini 并重启</button>
</form>
<form method="post" action="{{ url_for('frps_rotate_token') }}" style="display:inline"
onsubmit="return confirm('旋转 server token 会让所有 frpc 立刻断连,确定?');">
<button class="btn btn-warning">旋转 server token</button>
</form>
<a class="btn" href="{{ url_for('frps_config_view') }}" target="_blank">查看 frps.ini</a>
</section>
<section>
<h2>Server Token(所有 frpc 共享)</h2>
<p class="muted small">frpc 配置 <code>token = ...</code> 字段用此值。轮换会让所有连接断掉。</p>
<code class="token-cell">{{ server_token }}</code>
</section>
{% endif %}
<section>
<h2>在线 frpc 客户端</h2>
{% if info.error or not info.clients.get("items") %}
<p class="muted">尚无客户端连接</p>
{% else %}
<table class="table">
<thead>
<tr><th>用户 (frpc [common] user)</th><th>客户端 ID</th><th>版本</th><th>NAT</th><th>连接数</th></tr>
</thead>
<tbody>
{% for c in info.clients.get("items", []) %}
<tr>
<td><code>{{ c.user or '(unnamed)' }}</code></td>
<td><code>{{ c.clientID[:12] }}…</code></td>
<td>{{ c.version }}</td>
<td>{{ c.nat }}</td>
<td>{{ c.conn_count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</section>
<section>
<h2>活跃代理(来自 frps dashboard</h2>
{% if info.error or not info.proxies.get("items") %}
<p class="muted">尚无活跃代理</p>
{% else %}
<table class="table">
<thead>
<tr><th>名称</th><th>类型</th><th>状态</th><th>流量 in</th><th>流量 out</th><th>来自 frpc</th></tr>
</thead>
<tbody>
{% for p in info.proxies.get("items", []) %}
<tr>
<td><code>{{ p.name }}</code></td>
<td><span class="tag tag-{{ p.type }}">{{ p.type }}</span></td>
<td>
{% if p.status.phase == 'online' %}<span class="badge badge-ok">在线</span>
{% else %}<span class="badge badge-off">离线</span>{% endif %}
</td>
<td>{{ p.status.todayTrafficIn|fmt_bytes }}</td>
<td>{{ p.status.todayTrafficOut|fmt_bytes }}</td>
<td><code>{{ p.clientID[:12] or '-' }}…</code></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</section>
{% endblock %}