first commit

This commit is contained in:
dekun
2026-05-12 15:25:03 +08:00
commit 895e1bed0f
15 changed files with 1452 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
{% extends "base.html" %}
{% block title %}{{ title }} · 本地导航{% endblock %}
{% block body %}
<header class="topbar">
<h1>{{ title }}</h1>
<nav>
<a href="{{ url_for('admin_groups') }}">返回列表</a>
<a href="{{ url_for('index') }}">导航首页</a>
</nav>
</header>
<div class="page-wrap">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-wrap">
{% for cat, msg in messages %}
<div class="flash {{ cat }}">{{ msg }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form method="post" novalidate>
{{ form.hidden_tag() }}
<div class="form-row">
{{ form.name.label }}
{{ form.name() }}
{% if form.name.errors %}
<div class="errors">{{ form.name.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.sort_order.label }}
{{ form.sort_order() }}
{% if form.sort_order.errors %}
<div class="errors">{{ form.sort_order.errors[0] }}</div>
{% endif %}
</div>
<div class="toolbar" style="margin-top: 1.25rem">
{{ form.submit(class="btn btn-primary", style="width: auto") }}
<a class="btn btn-secondary" href="{{ url_for('admin_groups') }}">取消</a>
</div>
</form>
</div>
{% endblock %}
+69
View File
@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block title %}分组管理 · 本地导航{% endblock %}
{% block body %}
<header class="topbar">
<h1>分组管理</h1>
<nav>
<a href="{{ url_for('index') }}">返回导航</a>
<a href="{{ url_for('admin_services') }}">服务管理</a>
<a href="{{ url_for('logout') }}">退出</a>
</nav>
</header>
<div class="page-wrap">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-wrap">
{% for cat, msg in messages %}
<div class="flash {{ cat }}">{{ msg }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="toolbar">
<h1 style="margin: 0; flex: 1">自定义分组</h1>
<a class="btn btn-primary" href="{{ url_for('admin_group_new') }}" style="width: auto">新建分组</a>
</div>
<div class="table-wrap">
<table class="data">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>排序</th>
<th style="width: 200px">操作</th>
</tr>
</thead>
<tbody>
{% for g in groups %}
<tr>
<td>{{ g.id }}</td>
<td>{{ g.name }}</td>
<td>{{ g.sort_order }}</td>
<td>
<a href="{{ url_for('admin_group_edit', gid=g.id) }}">编辑</a>
·
<a href="{{ url_for('admin_service_new', group_id=g.id) }}">在此分组添加服务</a>
·
<form
class="inline-form"
method="post"
action="{{ url_for('admin_group_delete', gid=g.id) }}"
onsubmit="return confirm('确定删除该分组?其下所有服务也会被删除。');"
>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<button type="submit" class="btn btn-danger">删除</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="hint">暂无分组,点击「新建分组」</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
+72
View File
@@ -0,0 +1,72 @@
{% extends "base.html" %}
{% block title %}{{ title }} · 本地导航{% endblock %}
{% block body %}
<header class="topbar">
<h1>{{ title }}</h1>
<nav>
<a href="{{ url_for('admin_services') }}">返回列表</a>
<a href="{{ url_for('index') }}">导航首页</a>
</nav>
</header>
<div class="page-wrap">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-wrap">
{% for cat, msg in messages %}
<div class="flash {{ cat }}">{{ msg }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form method="post" novalidate>
{{ form.hidden_tag() }}
<div class="form-row">
{{ form.name.label }}
{{ form.name() }}
{% if form.name.errors %}
<div class="errors">{{ form.name.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.host.label }}
{{ form.host(placeholder="例如 192.168.1.10 或 主机名") }}
{% if form.host.errors %}
<div class="errors">{{ form.host.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.port.label }}
{{ form.port() }}
{% if form.port.errors %}
<div class="errors">{{ form.port.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.path.label }}
{{ form.path(placeholder="/ 或 /path") }}
{% if form.path.errors %}
<div class="errors">{{ form.path.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.group_id.label }}
{{ form.group_id() }}
{% if form.group_id.errors %}
<div class="errors">{{ form.group_id.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.sort_order.label }}
{{ form.sort_order() }}
{% if form.sort_order.errors %}
<div class="errors">{{ form.sort_order.errors[0] }}</div>
{% endif %}
</div>
<div class="toolbar" style="margin-top: 1.25rem">
{{ form.submit(class="btn btn-primary", style="width: auto") }}
<a class="btn btn-secondary" href="{{ url_for('admin_services') }}">取消</a>
</div>
</form>
</div>
{% endblock %}
+80
View File
@@ -0,0 +1,80 @@
{% extends "base.html" %}
{% block title %}服务管理 · 本地导航{% endblock %}
{% block body %}
<header class="topbar">
<h1>服务管理</h1>
<nav>
<a href="{{ url_for('index') }}">返回导航</a>
<a href="{{ url_for('admin_groups') }}">分组管理</a>
<a href="{{ url_for('logout') }}">退出</a>
</nav>
</header>
<div class="page-wrap">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-wrap">
{% for cat, msg in messages %}
<div class="flash {{ cat }}">{{ msg }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="toolbar">
<h1 style="margin: 0; flex: 1">内网服务</h1>
<a class="btn btn-primary" href="{{ url_for('admin_service_new') }}" style="width: auto">新建服务</a>
</div>
<form class="toolbar" method="get" action="{{ url_for('admin_services') }}">
<label for="group_id" class="hint" style="margin: 0">按分组筛选</label>
<select name="group_id" id="group_id" onchange="this.form.submit()">
<option value="">全部分组</option>
{% for g in groups %}
<option value="{{ g.id }}" {% if filter_group_id == g.id %}selected{% endif %}>{{ g.name }}</option>
{% endfor %}
</select>
</form>
<div class="table-wrap">
<table class="data">
<thead>
<tr>
<th>名称</th>
<th>地址</th>
<th>分组</th>
<th>排序</th>
<th style="width: 140px">操作</th>
</tr>
</thead>
<tbody>
{% for s in services %}
<tr>
<td>{{ s.name }}</td>
<td><code style="font-size: 0.82rem">{{ s.build_url() }}</code></td>
<td>{{ s.group.name if s.group else '—' }}</td>
<td>{{ s.sort_order }}</td>
<td>
<a href="{{ url_for('admin_service_edit', sid=s.id) }}">编辑</a>
·
<form
class="inline-form"
method="post"
action="{{ url_for('admin_service_delete', sid=s.id) }}"
onsubmit="return confirm('确定删除该服务?');"
>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<button type="submit" class="btn btn-danger">删除</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="5" class="hint">暂无服务,点击「新建服务」</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p class="hint">说明:目标站点若设置禁止被嵌入(如部分面板),iframe 会空白或报错,属对方安全策略,与本站无关。</p>
</div>
{% endblock %}
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{% block title %}本地导航{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
+79
View File
@@ -0,0 +1,79 @@
{% extends "base.html" %}
{% block title %}导航 · 本地导航{% endblock %}
{% block body %}
<div class="app-shell">
<header class="topbar">
<h1>本地导航</h1>
<nav>
<span class="user">{{ current_user.username }}</span>
<a href="{{ url_for('admin_groups') }}">分组管理</a>
<a href="{{ url_for('admin_services') }}">服务管理</a>
<a href="{{ url_for('logout') }}">退出</a>
</nav>
</header>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-wrap" style="padding-top: 0.5rem">
{% for cat, msg in messages %}
<div class="flash {{ cat }}">{{ msg }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="layout-main">
<aside class="sidebar" id="sidebar">
{% for group, services in grouped %}
<div class="sidebar-section">
<h2>{{ group.name }}</h2>
{% for svc in services %}
<a
href="#"
class="nav-link"
role="button"
data-url="{{ svc.build_url() }}"
data-name="{{ svc.name | e }}"
>{{ svc.name }}</a
>
{% else %}
<div class="hint" style="padding: 0 1rem">该分组下暂无服务</div>
{% endfor %}
</div>
{% else %}
<div class="hint" style="padding: 1rem">
暂无分组与服务,请到「分组管理」「服务管理」添加。
</div>
{% endfor %}
</aside>
<div class="frame-wrap">
<div class="frame-placeholder" id="placeholder">在左侧点击服务,在此区域以内嵌方式打开(不跳转、不开新标签)</div>
<iframe id="svc-frame" title="内嵌服务" hidden></iframe>
</div>
</div>
</div>
<script>
(function () {
var frame = document.getElementById("svc-frame");
var placeholder = document.getElementById("placeholder");
var links = document.querySelectorAll(".nav-link[data-url]");
function setActive(el) {
links.forEach(function (a) {
a.classList.remove("active");
});
if (el) el.classList.add("active");
}
links.forEach(function (a) {
a.addEventListener("click", function (e) {
e.preventDefault();
var url = a.getAttribute("data-url");
if (!url) return;
placeholder.hidden = true;
frame.hidden = false;
frame.src = url;
setActive(a);
});
});
})();
</script>
{% endblock %}
+27
View File
@@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %}登录 · 本地导航{% endblock %}
{% block body %}
<div class="login-page">
<div class="login-card">
<h1>本地导航站</h1>
<form method="post" novalidate>
{{ form.hidden_tag() }}
<div class="form-row">
{{ form.username.label }}
{{ form.username(class="") }}
{% if form.username.errors %}
<div class="errors">{{ form.username.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.password.label }}
{{ form.password(class="") }}
{% if form.password.errors %}
<div class="errors">{{ form.password.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">{{ form.submit(class="btn btn-primary") }}</div>
</form>
</div>
</div>
{% endblock %}