Files
idsync/internal/webui/dist/index.html
T
caowangrenbo 947b1a1475 chore: 初始化 idsync 项目
飞书通讯录 ↔ LDAP 同步工具的首个提交。

- internal/feishu: 飞书通讯录客户端、快照与重试
- internal/ldaplib: LDAP 读写与批量操作
- internal/mapping: 飞书部门/用户到 LDAP 条目的映射
- internal/syncer: 同步计划生成、执行与调度
- internal/store: SQLite 台账
- internal/server + internal/webui: 管理后台与内嵌前端产物
- internal/password: SSHA 口令处理
- config.example.yml / Dockerfile / Makefile / web 前端源码

真实凭据(config.yml)、运行时数据(data/、backup/)与构建产物(bin/)不入库。
2026-09-29 00:10:59 +08:00

230 lines
9.6 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.
<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>idsync · 飞书通讯录同步</title>
<link rel="stylesheet" href="/static/app.css" />
</head>
<body class="bg-base-200 min-h-screen">
<!-- 未登录:登录卡片 -->
<div id="loginView" class="hidden items-center justify-center min-h-screen p-4">
<div class="card w-full max-w-sm bg-base-100 shadow-lg">
<div class="card-body">
<div class="flex items-center gap-2 mb-2">
<span class="inline-block size-3 rounded-full bg-primary"></span>
<h1 class="card-title text-lg m-0">idsync</h1>
</div>
<p class="text-sm text-base-content/60 mb-4">飞书通讯录 → OpenLDAP 同步</p>
<div id="loginError" class="alert alert-error text-sm mb-3 hidden"></div>
<label class="label" for="username"><span class="label-text">用户名</span></label>
<input id="username" type="text" class="input w-full mb-3" autocomplete="username" />
<label class="label" for="password"><span class="label-text">密码</span></label>
<input id="password" type="password" class="input w-full mb-5" autocomplete="current-password" />
<button id="loginBtn" class="btn btn-primary w-full">登录</button>
</div>
</div>
</div>
<!-- 已登录:主界面 -->
<div id="appView" class="hidden">
<header class="navbar bg-base-100 shadow-sm px-4 sticky top-0 z-20">
<div class="flex-1 flex items-center gap-2">
<span class="inline-block size-3 rounded-full bg-primary"></span>
<span class="text-lg font-semibold">idsync</span>
<span class="hidden sm:inline text-sm text-base-content/50">飞书通讯录 → OpenLDAP</span>
</div>
<div class="flex-none flex items-center gap-2">
<button id="themeToggle" class="btn btn-sm btn-ghost" title="切换明暗主题">主题</button>
<button id="logoutBtn" class="btn btn-sm btn-outline">退出</button>
</div>
</header>
<main class="max-w-7xl mx-auto p-4">
<!-- 页签 -->
<div class="tabs tabs-border mb-4" role="tablist">
<button class="tab tab-active" data-view="overview" role="tab">概览</button>
<button class="tab" data-view="users" role="tab">用户</button>
<button class="tab" data-view="groups" role="tab">部门分组</button>
<button class="tab" data-view="plan" role="tab">变更预览</button>
<button class="tab" data-view="runs" role="tab">同步历史</button>
</div>
<div id="globalError" class="alert alert-error text-sm mb-4 hidden"></div>
<!-- 概览 -->
<section id="view-overview" class="view">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<span class="text-sm text-base-content/60">在职账号</span>
<span id="statActive" class="text-3xl font-semibold">–</span>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<span class="text-sm text-base-content/60">离职账号</span>
<span id="statInactive" class="text-3xl font-semibold">–</span>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<span class="text-sm text-base-content/60">部门分组</span>
<span id="statGroups" class="text-3xl font-semibold">–</span>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-base">服务状态</h2>
<div id="connStatus" class="space-y-2 text-sm"></div>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-base">上次同步</h2>
<div id="lastRunInfo" class="text-sm text-base-content/70">暂无记录</div>
</div>
</div>
</div>
</section>
<!-- 用户 -->
<section id="view-users" class="view hidden">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="flex flex-wrap items-center justify-between gap-3 mb-3">
<h2 class="card-title text-base">受管用户</h2>
<div class="flex items-center gap-2">
<input id="userFilter" type="search" class="input input-sm w-56" placeholder="按 uid / 姓名 / 邮箱过滤" />
<button id="reloadUsers" class="btn btn-sm btn-outline">刷新</button>
</div>
</div>
<div class="idsync-scroll">
<table class="table table-sm">
<thead>
<tr>
<th>uid</th><th>姓名</th><th>企业邮箱</th><th>状态</th>
<th>归属部门数</th><th>最后同步</th><th>一致性</th><th>DN</th>
</tr>
</thead>
<tbody id="userRows"></tbody>
</table>
</div>
<p id="userEmpty" class="hidden text-sm text-base-content/50 py-4 text-center">暂无受管用户,请先执行一次同步。</p>
</div>
</div>
</section>
<!-- 部门分组 -->
<section id="view-groups" class="view hidden">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h2 class="card-title text-base mb-3">受管部门分组</h2>
<div class="idsync-scroll">
<table class="table table-sm">
<thead>
<tr><th>部门</th><th>飞书部门 ID</th><th>父部门</th><th>DN</th></tr>
</thead>
<tbody id="groupRows"></tbody>
</table>
</div>
<p id="groupEmpty" class="hidden text-sm text-base-content/50 py-4 text-center">暂无受管分组。</p>
</div>
</div>
</section>
<!-- 变更预览 -->
<section id="view-plan" class="view hidden">
<div class="card bg-base-100 shadow-sm mb-4">
<div class="card-body">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 class="card-title text-base">变更预览(dry-run)</h2>
<p class="text-sm text-base-content/60 mt-1">
预览与实际执行使用同一份计划代码,因此这里显示的就是将要发生的变更。
</p>
</div>
<div class="flex gap-2">
<button id="previewBtn" class="btn btn-sm btn-outline">重新计算</button>
<button id="applyBtn" class="btn btn-sm btn-primary">应用变更</button>
</div>
</div>
</div>
</div>
<div id="planWarnings" class="hidden mb-4"></div>
<div id="planSummary" class="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-3 mb-4"></div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<h3 class="card-title text-base mb-2">操作明细</h3>
<div class="idsync-scroll">
<table class="table table-sm">
<thead>
<tr><th class="w-40">类型</th><th class="w-56">对象</th><th>说明</th></tr>
</thead>
<tbody id="planRows"></tbody>
</table>
</div>
<p id="planEmpty" class="hidden text-sm text-base-content/50 py-4 text-center">
没有需要变更的内容,两侧状态已一致。
</p>
</div>
</div>
</section>
<!-- 同步历史 -->
<section id="view-runs" class="view hidden">
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="flex items-center justify-between mb-3">
<h2 class="card-title text-base">同步历史</h2>
<button id="reloadRuns" class="btn btn-sm btn-outline">刷新</button>
</div>
<div class="idsync-scroll">
<table class="table table-sm">
<thead>
<tr>
<th>开始时间</th><th>模式</th><th>触发</th><th>结果</th>
<th>新建</th><th>更新</th><th>迁移</th><th>加组</th><th>移组</th><th>失败</th>
</tr>
</thead>
<tbody id="runRows"></tbody>
</table>
</div>
<p id="runEmpty" class="hidden text-sm text-base-content/50 py-4 text-center">暂无同步记录。</p>
</div>
</div>
</section>
</main>
</div>
<!-- 应用变更的确认对话框 -->
<dialog id="applyModal" class="modal">
<div class="modal-box">
<h3 class="text-lg font-semibold mb-2">确认应用变更</h3>
<p class="text-sm text-base-content/70 mb-4">
将对 OpenLDAP 执行下列变更。用户密码不会被修改,Authelia 重置的密码不受影响。
</p>
<div id="applyModalBody" class="text-sm mb-4"></div>
<div class="modal-action">
<button id="applyCancel" class="btn btn-ghost">取消</button>
<button id="applyConfirm" class="btn btn-primary">确认执行</button>
</div>
</div>
</dialog>
<script src="/static/flyonui.js"></script>
<script src="/static/app.js"></script>
</body>
</html>