All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 21s
- 后端: Auth(登录/登出/me + session 中间件)、Users、Tokens 路由与 Service, scrypt 哈希+timingSafeEqual 校验、requireAdmin 保护、admin 用户自动初始化 - 前端: login.html(登录页, 无账号密码提示)、admin.html(用户+Token 管理, 令牌明文显隐/复制/吊销/重置密码)、标题栏用户信息+系统管理入口 - 持久化: jsonAdapter 支持 users/api-tokens,运行时数据忽略入库 - 文档: 需求.md/原型设计.md 同步
354 lines
16 KiB
HTML
354 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Registry - 微服务配置管理中心</title>
|
||
<link rel="stylesheet" href="/css/style.css">
|
||
</head>
|
||
<body>
|
||
<!-- 标题栏:仅展示应用名与当前项目 -->
|
||
<header class="titlebar">
|
||
<span class="app-title">📋 Registry 配置管理中心</span>
|
||
<span class="current-project" id="current-project-name">未选择项目</span>
|
||
<div class="titlebar-right">
|
||
<span class="titlebar-user">
|
||
👤 <span id="user-name"></span>
|
||
<span class="role-badge" id="user-role"></span>
|
||
</span>
|
||
<a class="titlebar-link" id="admin-entry" href="/admin.html" style="display:none">⚙ 系统管理</a>
|
||
<a class="titlebar-link" href="/projects.html">项目列表</a>
|
||
<a class="titlebar-link" id="logout-link" href="#">退出登录</a>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 工具栏:分组处理(项目 / 模式 / 配置 / 数据) -->
|
||
<div class="toolbar">
|
||
<div class="tool-group" title="项目操作">
|
||
<span class="group-label">项目</span>
|
||
<select id="project-selector" class="toolbar-select">
|
||
<option value="">-- 选择项目 --</option>
|
||
</select>
|
||
<button id="btn-new-project" class="toolbar-btn" title="新建项目">📁 新建</button>
|
||
<button id="btn-delete-project" class="toolbar-btn" title="删除项目">🗑 删除</button>
|
||
</div>
|
||
|
||
<div class="tool-group" title="工作模式">
|
||
<span class="group-label">模式</span>
|
||
<div class="mode-switcher">
|
||
<button class="mode-btn" data-mode="plan" title="规划模式:定义配置结构">📐 规划</button>
|
||
<button class="mode-btn active" data-mode="edit" title="编辑模式:修改配置值">✏️ 编辑</button>
|
||
<button class="mode-btn" data-mode="readonly" title="只读模式:仅查看">👁️ 只读</button>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="tool-group" title="数据操作">
|
||
<span class="group-label">数据</span>
|
||
<button id="btn-export-config" class="toolbar-btn" title="导出配置">📥 导出</button>
|
||
<button id="btn-import-config" class="toolbar-btn" title="导入配置">📤 导入</button>
|
||
<input type="file" id="import-file-input" accept=".json" style="display:none">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 维度栏:展示已启用维度的筛选器与映射配置入口 -->
|
||
<div class="dimension-bar" id="dimension-bar">
|
||
|
||
<div class="dim-item" id="dim-idc" style="display:none">
|
||
<span class="dim-name">机房</span>
|
||
<select class="dim-select" id="dim-idc-select"></select>
|
||
<span class="dim-mapping" id="dim-idc-mapping"></span>
|
||
</div>
|
||
<div class="dim-item" id="dim-env" style="display:none">
|
||
<span class="dim-name">环境</span>
|
||
<select class="dim-select" id="dim-env-select"></select>
|
||
<span class="dim-mapping" id="dim-env-mapping"></span>
|
||
</div>
|
||
<div class="dim-item" id="dim-group" style="display:none">
|
||
<span class="dim-name">分组</span>
|
||
<select class="dim-select" id="dim-group-select"></select>
|
||
<span class="dim-mapping" id="dim-group-mapping"></span>
|
||
</div>
|
||
<button id="btn-dim-mapping" class="toolbar-btn" style="display:none" title="配置维度映射方式">⚙ 映射配置</button>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="main-container">
|
||
<!-- 左侧树形导航 -->
|
||
<aside class="tree-panel" id="tree-panel">
|
||
<div class="tree-header">
|
||
<span>配置树</span>
|
||
<div class="tree-header-actions">
|
||
<button id="btn-add-config" class="icon-btn" title="新增配置项">➕</button>
|
||
<button id="btn-collapse-all" class="icon-btn" title="全部折叠">⊟</button>
|
||
</div>
|
||
</div>
|
||
<div class="tree-content" id="tree-content">
|
||
<div class="tree-empty">请先选择一个项目</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 分隔条 -->
|
||
<div class="resizer" id="resizer"></div>
|
||
|
||
<!-- 右侧详情面板 -->
|
||
<main class="detail-panel" id="detail-panel">
|
||
<!-- 配置导航地址栏:显示当前路径,可拷贝、可粘贴回车跳转 -->
|
||
<div class="address-bar">
|
||
<span class="address-icon">📍</span>
|
||
<input type="text" id="address-input" class="address-input" placeholder="配置路径,回车跳转(类似注册表地址栏)">
|
||
<button id="btn-address-go" class="addr-btn" title="跳转到路径">转到</button>
|
||
<button id="btn-address-copy" class="addr-btn" title="拷贝路径">拷贝</button>
|
||
</div>
|
||
<div class="detail-table-wrapper">
|
||
<table class="config-table" id="config-table">
|
||
<thead>
|
||
<tr>
|
||
<th class="col-key">键名</th>
|
||
<th class="col-value">值</th>
|
||
<th class="col-type">类型</th>
|
||
<th class="col-status">状态</th>
|
||
<th class="col-desc">说明</th>
|
||
<th class="col-actions">操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="config-tbody">
|
||
<tr class="empty-row">
|
||
<td colspan="6">请在左侧选择节点查看配置</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- 状态栏 -->
|
||
<footer class="statusbar">
|
||
<span id="status-text">就绪</span>
|
||
<span id="status-mode">当前模式:编辑</span>
|
||
</footer>
|
||
|
||
<!-- 模态框:新建项目 -->
|
||
<div class="modal-overlay" id="modal-project" style="display:none">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 id="modal-project-title">新建项目</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-project')">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>项目名称 <span class="required">*</span></label>
|
||
<input type="text" id="project-name" placeholder="例如:用户服务">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>项目描述</label>
|
||
<textarea id="project-description" rows="2" placeholder="项目配置说明"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>维度配置 <span class="hint">(是否启用创建后不可修改;映射方式后续可调整)</span></label>
|
||
<div class="dim-config-row">
|
||
<label class="checkbox-label"><input type="checkbox" id="project-enable-idc" checked> 机房</label>
|
||
<select id="project-idc-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
<div class="dim-config-row">
|
||
<label class="checkbox-label"><input type="checkbox" id="project-enable-env" checked> 环境</label>
|
||
<select id="project-env-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
<div class="dim-config-row">
|
||
<label class="checkbox-label"><input type="checkbox" id="project-enable-group" checked> 分组</label>
|
||
<select id="project-group-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('modal-project')">取消</button>
|
||
<button class="btn btn-primary" id="btn-save-project">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模态框:维度映射配置 -->
|
||
<div class="modal-overlay" id="modal-mapping" style="display:none">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>维度映射配置</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-mapping')">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="hint" style="margin-bottom:12px">映射为路径:该维度作为配置存储路径前缀;仅作标签:该维度仅用于筛选,不进入路径。</p>
|
||
<div class="dim-config-row" id="map-idc-row">
|
||
<span class="map-name">机房</span>
|
||
<select id="map-idc-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
<div class="dim-config-row" id="map-env-row">
|
||
<span class="map-name">环境</span>
|
||
<select id="map-env-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
<div class="dim-config-row" id="map-group-row">
|
||
<span class="map-name">分组</span>
|
||
<select id="map-group-mapping" class="mapping-select">
|
||
<option value="path">映射为路径</option>
|
||
<option value="label">仅作标签</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('modal-mapping')">取消</button>
|
||
<button class="btn btn-primary" id="btn-save-mapping">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 抽屉:新增/编辑/查看配置项(右侧滑出,占屏幕 68%) -->
|
||
<div class="modal-overlay drawer-overlay" id="modal-config" style="display:none">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 id="modal-config-title">新增配置项</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-config')">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>键名 <span class="required">*</span> <span class="hint">(可用 / 嵌套,如 db/pool/max)</span></label>
|
||
<input type="text" id="config-key" placeholder="例如:db/host 或 db.host">
|
||
</div>
|
||
<div id="config-dimension-fields">
|
||
<div class="form-row">
|
||
<div class="form-group form-half" id="dim-idc-group">
|
||
<label>机房</label>
|
||
<input type="text" id="config-idc" placeholder="例如:华北">
|
||
</div>
|
||
<div class="form-group form-half" id="dim-env-group">
|
||
<label>环境</label>
|
||
<input type="text" id="config-environment" placeholder="例如:dev">
|
||
</div>
|
||
</div>
|
||
<div class="form-group" id="dim-group-group">
|
||
<label>分组</label>
|
||
<input type="text" id="config-group" placeholder="例如:用户模块">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>值</label>
|
||
<input type="text" id="config-value" placeholder="配置值">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>默认值 <span class="hint">(规划模式,编辑模式下值为空时展示回退)</span></label>
|
||
<input type="text" id="config-default-value" placeholder="配置值为空时展示/回退的默认值">
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group form-half">
|
||
<label>类型</label>
|
||
<select id="config-type">
|
||
<option value="string">string</option>
|
||
<option value="number">number</option>
|
||
<option value="boolean">boolean</option>
|
||
<option value="json">json</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group form-half">
|
||
<label>值范围 <span class="hint">(规划模式)</span></label>
|
||
<input type="text" id="config-value-range" placeholder="例如:1-100、枚举: A,B 或 /正则/">
|
||
</div>
|
||
</div>
|
||
<div class="form-group" id="config-range-preset-group" style="display:none">
|
||
<label>常见格式 <span class="hint">(string 类型:值范围按正则校验,选择后自动填入)</span></label>
|
||
<select id="config-range-preset">
|
||
<option value="">— 选择常见格式 —</option>
|
||
<option value="phone">📱 手机号(中国大陆 11 位)</option>
|
||
<option value="email">✉️ 邮箱</option>
|
||
<option value="idcard">🪪 身份证(18 位)</option>
|
||
<option value="ipv4">🌐 IPv4 地址</option>
|
||
<option value="url">🔗 URL 链接</option>
|
||
<option value="port">🔌 端口号(1-65535)</option>
|
||
<option value="alphanumeric">🔤 字母数字</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group form-half">
|
||
<label>是否必须 <span class="hint">(规划模式)</span></label>
|
||
<label class="checkbox-label"><input type="checkbox" id="config-required"> 编辑模式下必须填写值</label>
|
||
</div>
|
||
<div class="form-group form-half">
|
||
<label>是否存在 <span class="hint">(编辑模式)</span></label>
|
||
<label class="checkbox-label"><input type="checkbox" id="config-exists" checked> 该配置项当前存在/生效</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>说明 <span class="required">*</span></label>
|
||
<textarea id="config-description" rows="2" placeholder="配置项用途说明"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>用途 <span class="hint">(规划模式)</span></label>
|
||
<textarea id="config-purpose" rows="2" placeholder="详细用途描述"></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('modal-config')">取消</button>
|
||
<button class="btn btn-primary" id="btn-save-config">保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模态框:确认删除 -->
|
||
<div class="modal-overlay" id="modal-confirm" style="display:none">
|
||
<div class="modal modal-sm">
|
||
<div class="modal-header">
|
||
<h3>确认操作</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-confirm')">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p id="confirm-message"></p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('modal-confirm')">取消</button>
|
||
<button class="btn btn-danger" id="btn-confirm-ok">确认</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/js/api.js"></script>
|
||
<script src="/js/tree.js"></script>
|
||
<script>
|
||
// 认证初始化:检查登录状态,填充标题栏用户信息
|
||
(async function initAuth() {
|
||
try {
|
||
var user = await API.getMe();
|
||
document.getElementById("user-name").textContent = user.displayName || user.username;
|
||
var roleEl = document.getElementById("user-role");
|
||
if (user.role === "admin") {
|
||
roleEl.textContent = "管理员";
|
||
roleEl.className = "role-badge admin";
|
||
document.getElementById("admin-entry").style.display = "";
|
||
}
|
||
} catch (e) {
|
||
window.location.href = "/login.html";
|
||
return;
|
||
}
|
||
})();
|
||
document.getElementById("logout-link").addEventListener("click", async function (e) {
|
||
e.preventDefault();
|
||
try { await API.logout(); } catch (e) {}
|
||
localStorage.removeItem("registry_token");
|
||
localStorage.removeItem("registry_user");
|
||
window.location.href = "/login.html";
|
||
});
|
||
</script>
|
||
<script src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|