feat: DSH编排基建首版(server/tests 88用例/P2插件/8020端口/PORT-NOTE/MOBILE_APP)
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<title>dsh 同步 · 安卓 App</title>
|
||||
<style>
|
||||
:root { --bg:#0f1115; --card:#171a21; --card2:#1e222b; --line:#262b36;
|
||||
--text:#e6e8ee; --muted:#8b93a7; --acc:#4c8dff; --warn:#ffb020; --ok:#31c48d; }
|
||||
* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
|
||||
body { margin:0; background:var(--bg); color:var(--text); font:15px/1.55 -apple-system,
|
||||
BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"PingFang SC",
|
||||
"Hiragino Sans GB","Microsoft YaHei",sans-serif; padding-bottom:32px; }
|
||||
header { padding:20px 18px 12px; }
|
||||
h1 { margin:0 0 4px; font-size:21px; letter-spacing:.3px; }
|
||||
header p { margin:0; color:var(--muted); font-size:13px; }
|
||||
.wrap { padding:0 14px; max-width:640px; margin:0 auto; }
|
||||
.card { background:var(--card); border:1px solid var(--line); border-radius:14px;
|
||||
padding:14px; margin:12px 0; }
|
||||
.card h2 { margin:0 0 10px; font-size:15px; font-weight:600; }
|
||||
.muted { color:var(--muted); font-size:13px; }
|
||||
.row { display:flex; gap:10px; align-items:center; }
|
||||
.grow { flex:1; min-width:0; }
|
||||
a.btn, button.btn { display:block; width:100%; text-align:center; padding:14px;
|
||||
border:0; border-radius:12px; background:var(--acc); color:#fff;
|
||||
font-size:16px; font-weight:600; text-decoration:none; cursor:pointer; }
|
||||
a.btn.ghost, button.btn.ghost { background:var(--card2); color:var(--text);
|
||||
border:1px solid var(--line); font-weight:500; font-size:14px; padding:11px; }
|
||||
a.btn[aria-disabled="true"] { background:var(--card2); color:var(--muted);
|
||||
pointer-events:none; border:1px solid var(--line); }
|
||||
.meta { display:grid; grid-template-columns:auto 1fr; gap:5px 12px;
|
||||
font-size:12.5px; margin-top:12px; }
|
||||
.meta dt { color:var(--muted); }
|
||||
.meta dd { margin:0; word-break:break-all; font-family:ui-monospace,Menlo,Consolas,monospace; }
|
||||
.qrbx { background:#fff; border-radius:14px; padding:10px; display:flex;
|
||||
justify-content:center; margin:4px 0 10px; }
|
||||
.qrbx img { width:min(210px,66vw); height:auto; }
|
||||
ol { margin:8px 0 0; padding-left:20px; font-size:13.5px; }
|
||||
ol li { margin-bottom:7px; }
|
||||
code { background:var(--card2); padding:1.5px 6px; border-radius:5px;
|
||||
font-size:12.5px; font-family:ui-monospace,Menlo,Consolas,monospace; }
|
||||
.tag { display:inline-block; padding:1px 7px; border-radius:6px; font-size:11px;
|
||||
background:var(--card2); color:var(--muted); margin-left:6px; }
|
||||
.tag.ok { color:var(--ok); }
|
||||
.tag.warn { color:var(--warn); }
|
||||
.steps { counter-reset:s; list-style:none; padding:0; margin:0; }
|
||||
.steps li { counter-increment:s; position:relative; padding-left:30px;
|
||||
margin-bottom:12px; font-size:13.5px; }
|
||||
.steps li::before { content:counter(s); position:absolute; left:0; top:0;
|
||||
width:20px; height:20px; border-radius:50%; background:var(--card2);
|
||||
color:var(--acc); font-size:11.5px; font-weight:600; display:flex;
|
||||
align-items:center; justify-content:center; }
|
||||
footer { text-align:center; color:var(--muted); font-size:12px; padding:18px 14px 0; }
|
||||
.hidden { display:none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>dsh 同步 · 安卓 App</h1>
|
||||
<p>团队设置与插件同步的移动客户端。手机浏览器版仍在 <a href="/app" style="color:var(--acc)">/app</a>。</p>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<!-- ============ download ============ -->
|
||||
<div class="card" id="dl-card">
|
||||
<h2>下载安装包 <span class="tag" id="ver-tag">…</span></h2>
|
||||
<a class="btn" id="dl" aria-disabled="true" href="/apk/dsh-sync.apk" download>准备中…</a>
|
||||
<div class="meta" id="meta"></div>
|
||||
<div class="muted" id="hint" style="margin-top:10px"></div>
|
||||
</div>
|
||||
|
||||
<!-- ============ qr ============ -->
|
||||
<div class="card" id="qr-card">
|
||||
<h2>用手机扫码直接在手机上装</h2>
|
||||
<div class="qrbx"><img id="qr" alt="下载页二维码"></div>
|
||||
<div class="muted">手机相机对准二维码 → 打开本页 → 点上面的下载按钮。
|
||||
手机上传安装包比电脑再传过去省事。</div>
|
||||
<div class="row" style="margin-top:10px">
|
||||
<button class="btn ghost grow" id="copy">复制下载地址</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ install steps ============ -->
|
||||
<div class="card">
|
||||
<h2>安装步骤</h2>
|
||||
<ol class="steps">
|
||||
<li>点上面的下载按钮,浏览器会下载 <code>dsh-sync.apk</code>。
|
||||
若提示"可能有害",选<strong>仍要下载</strong>——这是自签名包,不是商店应用。</li>
|
||||
<li>打开下载好的文件。系统会拦截一次,提示"禁止安装未知应用"。</li>
|
||||
<li>点<strong>设置</strong> → 允许当前浏览器<strong>安装未知应用</strong> → 返回 → 继续安装。
|
||||
(不同品牌路径略有差异,通常在"设置 → 应用 → 特殊权限"里。)</li>
|
||||
<li>首次打开会显示地址栏。填服务端地址,例如 <code id="host-eg">192.168.5.2:8020</code>,
|
||||
点"前往"。地址会被记住,以后直接进页面。</li>
|
||||
<li>若要从二维码一步到位:直接用 App 打开 <code>/app#invite=…</code> 深链,
|
||||
邀请码会自动填进登录页。</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- ============ what it does ============ -->
|
||||
<div class="card">
|
||||
<h2>这个 App 是什么</h2>
|
||||
<div class="muted">
|
||||
一个原生 WebView 外壳,包住服务端自带的同一个 PWA 页面。它比手机浏览器多三件事:
|
||||
</div>
|
||||
<ol class="steps" style="margin-top:10px">
|
||||
<li>记住服务端地址,省得每次输 IP。</li>
|
||||
<li>支持原生文件选择器,手机上也能发布插件 tar.gz。</li>
|
||||
<li>认领 <code>/app#invite=…</code> 深链——扫服务端二维码可直接跳进 App 并预填邀请码。</li>
|
||||
</ol>
|
||||
<div class="muted" style="margin-top:10px">
|
||||
页面逻辑与服务端同源,服务端更新后 App 打开即是新版,不需要重新装。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ build ============ -->
|
||||
<div class="card">
|
||||
<h2>服务端还没构建过?</h2>
|
||||
<div class="muted">APK 由仓库内 <code>android/</code> 工程构建,产物落在 <code>data/dist/</code>。
|
||||
在服务器上跑:</div>
|
||||
<div style="margin-top:10px"><code>bash android/build.sh</code></div>
|
||||
<div class="muted" style="margin-top:10px">
|
||||
需要 JDK 17 + Android SDK(build-tools 34、platform 34)+ Gradle 8.7。
|
||||
首次构建联网拉 AGP 依赖,之后可离线。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<footer>dsh-sync · 自签名包,仅团队内网使用</footer>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
const $ = id => document.getElementById(id);
|
||||
|
||||
function fmtSize(n) {
|
||||
if (!n) return "-";
|
||||
return n > 1048576 ? (n / 1048576).toFixed(2) + " MB" : (n / 1024).toFixed(1) + " KB";
|
||||
}
|
||||
|
||||
(async function load() {
|
||||
let info;
|
||||
try {
|
||||
info = await (await fetch("/apk/info")).json();
|
||||
} catch (e) {
|
||||
$("hint").textContent = "读取构建信息失败:" + e.message;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.available) {
|
||||
$("dl").textContent = "服务端尚未构建 APK";
|
||||
$("ver-tag").textContent = "未构建";
|
||||
$("ver-tag").className = "tag warn";
|
||||
$("hint").textContent = "在服务器上执行 " + (info.build_hint || "bash android/build.sh") + " 后刷新本页。";
|
||||
$("qr-card").classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
// Inside the app itself the install pitch is noise: say what this page is for
|
||||
// and point back at the real UI instead.
|
||||
if (/\bDshSyncApp\//.test(navigator.userAgent)) {
|
||||
const banner = document.createElement("div");
|
||||
banner.className = "card";
|
||||
banner.innerHTML = '<h2>你正在用 dsh App</h2><div class="muted">本页用于把 App 装到'
|
||||
+ '别的手机上。在 App 里请点下面的按钮回到登录/主界面。</div>'
|
||||
+ '<div style="margin-top:10px"><a class="btn" href="/app">打开 dsh 同步</a></div>';
|
||||
document.querySelector(".wrap").prepend(banner);
|
||||
}
|
||||
|
||||
$("dl").textContent = "下载 dsh-sync.apk(" + fmtSize(info.size_bytes) + ")";
|
||||
$("dl").removeAttribute("aria-disabled");
|
||||
$("ver-tag").textContent = "v" + (info.version || "?");
|
||||
$("ver-tag").className = "tag ok";
|
||||
|
||||
const rows = [
|
||||
["版本", "v" + (info.version || "?") + " (" + (info.version_code ?? "?") + ")"],
|
||||
["大小", fmtSize(info.size_bytes)],
|
||||
["包名", info.application_id || "-"],
|
||||
["系统", "Android " + (info.min_sdk ?? "?") + "+"],
|
||||
["构建于", info.built_at || "-"],
|
||||
["sha256", info.sha256 || "-"]
|
||||
];
|
||||
$("meta").innerHTML = rows.map(([k, v]) => `<dt>${k}</dt><dd>${v}</dd>`).join("");
|
||||
|
||||
try {
|
||||
const r = await fetch("/apk/qr");
|
||||
$("qr").src = URL.createObjectURL(await r.blob());
|
||||
} catch { $("qr-card").classList.add("hidden"); }
|
||||
|
||||
$("copy").onclick = async () => {
|
||||
const url = location.origin + "/apk/dsh-sync.apk";
|
||||
try { await navigator.clipboard.writeText(url); $("copy").textContent = "已复制"; }
|
||||
catch { $("copy").textContent = url; }
|
||||
setTimeout(() => $("copy").textContent = "复制下载地址", 2000);
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user