1
0
Fork 0
mirror of https://github.com/adnanh/webhook.git synced 2026-07-22 01:40:42 +00:00
webhook/adminui/index.html
2026-04-28 13:47:51 +08:00

223 lines
9.3 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Webhook Admin</title>
<link rel="stylesheet" href="{{.BasePath}}/assets/style.css">
<script>
window.__WEBHOOK_ADMIN__ = {
basePath: {{.BasePath}}
};
</script>
<script src="{{.BasePath}}/assets/app.js" defer></script>
</head>
<body>
<div class="shell">
<section class="hero">
<span class="eyebrow">Webhook Control Plane</span>
<h1>集中管理所有 webhook 配置</h1>
<p>使用 TOTP 获取短期 JWT 会话。页面直接管理已加载的 hooks 文件,不需要额外前端构建流程。</p>
</section>
<section id="loginPanel" class="panel login">
<div class="panel-head">
<h2>管理员登录</h2>
</div>
<div class="panel-body">
<form id="loginForm" class="stack">
<div>
<label for="totpCode">TOTP 动态码</label>
<input id="totpCode" class="code-input" inputmode="numeric" autocomplete="one-time-code" maxlength="6" placeholder="123456">
</div>
<button class="primary" type="submit">登录</button>
</form>
<div class="tiny"><code>-admin-totp-secret</code> 配到你的认证器后,输入当前 6 位验证码即可登录。</div>
<div id="loginStatus" class="status status-muted">未登录。</div>
</div>
</section>
<section id="workspace" class="workspace" hidden>
<aside class="panel sidebar">
<div class="panel-head">
<h2>配置文件</h2>
<button id="refreshBtn" class="secondary" type="button">刷新</button>
</div>
<div class="panel-body stack">
<div>
<label for="fileSelect">当前文件</label>
<select id="fileSelect"></select>
</div>
<div id="fileMeta" class="meta-card">暂无可管理的 hooks 文件。</div>
<div class="toolbar-row">
<h3>Hooks</h3>
<button id="newHookBtn" class="secondary" type="button">新建</button>
</div>
<div id="hookList" class="hook-list"></div>
</div>
</aside>
<main class="panel editor">
<div class="panel-head">
<div>
<h2>结构化编辑器</h2>
<p class="panel-subtitle">按区块维护 hook 定义,避免整段 JSON 记忆负担。</p>
</div>
<button id="logoutBtn" class="secondary" type="button">退出</button>
</div>
<div class="panel-body">
<div id="readOnlyBanner" class="banner" hidden></div>
<fieldset id="editorFieldset" class="editor-fieldset">
<form id="hookForm" class="hook-form" novalidate>
<section class="section">
<div class="section-head">
<h3>Basics</h3>
<p>基本标识、执行命令和请求方法。</p>
</div>
<div class="grid grid-2">
<div>
<label for="hookId">Hook ID</label>
<input id="hookId" placeholder="deploy-webhook">
</div>
<div>
<label for="executeCommand">Execute Command</label>
<input id="executeCommand" placeholder="/usr/local/bin/deploy.sh">
</div>
<div>
<label for="commandWorkingDirectory">Working Directory</label>
<input id="commandWorkingDirectory" placeholder="/srv/app">
</div>
<div>
<label for="commandTimeout">Command Timeout</label>
<input id="commandTimeout" placeholder="30s">
</div>
<div>
<label for="httpMethods">HTTP Methods</label>
<input id="httpMethods" placeholder="POST, GET">
</div>
<div class="grid-span-2">
<label for="responseMessage">Response Message</label>
<input id="responseMessage" placeholder="Webhook triggered">
</div>
</div>
</section>
<section class="section">
<div class="section-head">
<h3>Behavior</h3>
<p>响应码、内容类型和执行行为开关。</p>
</div>
<div class="grid grid-3">
<div>
<label for="incomingPayloadContentType">Incoming Payload Content-Type</label>
<input id="incomingPayloadContentType" placeholder="application/json">
</div>
<div>
<label for="successHttpResponseCode">Success HTTP Code</label>
<input id="successHttpResponseCode" inputmode="numeric" placeholder="200">
</div>
<div>
<label for="triggerRuleMismatchHttpResponseCode">Rule Mismatch HTTP Code</label>
<input id="triggerRuleMismatchHttpResponseCode" inputmode="numeric" placeholder="403">
</div>
<div>
<label for="maxConcurrency">Max Concurrency</label>
<input id="maxConcurrency" inputmode="numeric" placeholder="0">
</div>
</div>
<div class="toggle-grid">
<label class="toggle">
<input id="captureCommandOutput" type="checkbox">
<span>Include command output in response</span>
</label>
<label class="toggle">
<input id="captureCommandOutputOnError" type="checkbox">
<span>Include command output on error</span>
</label>
<label class="toggle">
<input id="triggerSignatureSoftFailures" type="checkbox">
<span>Allow trigger signature soft failures</span>
</label>
<label class="toggle">
<input id="keepFileEnvironment" type="checkbox">
<span>Keep uploaded files as environment references</span>
</label>
</div>
</section>
<section class="section">
<div class="section-head">
<h3>Response Headers</h3>
<p>为 hook 响应追加静态响应头。</p>
</div>
<div id="responseHeadersRows" class="repeatable-list"></div>
<button id="addResponseHeaderBtn" class="ghost" type="button">添加响应头</button>
</section>
<section class="section">
<div class="section-head">
<h3>Command Inputs</h3>
<p>把请求中的值传到命令参数、环境变量或文件。</p>
</div>
<div class="subsection">
<div class="toolbar-row">
<h4>Pass Arguments To Command</h4>
<button id="addPassArgumentBtn" class="ghost" type="button">添加参数</button>
</div>
<div id="passArgumentsRows" class="repeatable-list"></div>
</div>
<div class="subsection">
<div class="toolbar-row">
<h4>Pass Environment To Command</h4>
<button id="addPassEnvironmentBtn" class="ghost" type="button">添加环境变量</button>
</div>
<div id="passEnvironmentRows" class="repeatable-list"></div>
</div>
<div class="subsection">
<div class="toolbar-row">
<h4>Pass File To Command</h4>
<button id="addPassFileBtn" class="ghost" type="button">添加文件参数</button>
</div>
<div id="passFileRows" class="repeatable-list"></div>
</div>
<div class="subsection">
<div class="toolbar-row">
<h4>Parse Parameters As JSON</h4>
<button id="addParseJSONBtn" class="ghost" type="button">添加 JSON 参数</button>
</div>
<div id="parseJSONRows" class="repeatable-list"></div>
</div>
</section>
<section class="section">
<div class="section-head">
<h3>Trigger Rule</h3>
<p>用树形表单维护 and/or/not/match 规则,不再直接手写整段对象。</p>
</div>
<div id="ruleEditor" class="rule-editor"></div>
</section>
</form>
</fieldset>
<div class="actions">
<button id="saveBtn" class="primary" type="button">保存</button>
<button id="deleteBtn" class="danger" type="button">删除</button>
</div>
<details class="preview">
<summary>JSON Preview</summary>
<pre id="jsonPreview"></pre>
</details>
<div id="workspaceStatus" class="status status-muted">已准备就绪。</div>
</div>
</main>
</section>
</div>
</body>
</html>