Files
mnote/extensions/mnote-vault/options.js
T

172 lines
5.3 KiB
JavaScript
Raw Normal View History

import { normalizeBaseUrl, vaultFetch } from "./lib/api.js";
import { loadConfig, loadPersistent } from "./lib/storage.js";
const $ = (id) => document.getElementById(id);
function setStatus(text, kind) {
const el = $("status");
el.textContent = text || "";
el.className = kind || "";
}
function setWorkspaceHint(rootUri, rootPath, source) {
const el = $("workspaceHint");
if (!el) return;
if (!rootUri) {
el.textContent = "";
return;
}
const path = rootPath || rootUri.replace(/^file:\/\//, "");
const src =
source === "default"
? "账号默认工作区"
: source === "override"
? "手动覆盖"
: "已保存";
el.innerHTML = `密码箱写入:<code>${path}</code>${src}`;
}
/**
* 必须在用户点击手势内调用:MV3 从 service worker 弹权限常被静默拒绝,
* 外网 baseUrl(含非标端口 frp)没有 host 权限时 fetch 就是 Failed to fetch。
* 同时申请 http(s)://*/*否则外站 Cookie 读不到登录态无法保存
* @param {string} baseUrl
*/
async function requestBaseUrlPermission(baseUrl) {
const origin = new URL(normalizeBaseUrl(baseUrl)).origin;
// Broad first so cookie capture works on every login site after connect.
const broad = ["http://*/*", "https://*/*", `${origin}/*`];
try {
const haveBroad = await chrome.permissions.contains({ origins: broad });
if (haveBroad) return true;
const ok = await chrome.permissions.request({ origins: broad });
if (ok) return true;
} catch {
/* fall through to narrow */
}
const origins = [`${origin}/*`];
const have = await chrome.permissions.contains({ origins });
if (have) return true;
try {
return await chrome.permissions.request({ origins });
} catch (e) {
throw new Error(
`无法申请访问权限 ${origin}${e?.message || e}(请在扩展详情里允许该站点;登录态还需「所有网站」)`
);
}
}
function formatFetchError(e, baseUrl) {
const msg = e?.message || String(e);
if (/Failed to fetch|NetworkError|Load failed/i.test(msg)) {
return (
`无法访问 ${baseUrl || "baseUrl"}${msg}。` +
`常见原因:① 未点允许扩展访问该站点 ② 外网隧道未通 ③ 证书不受信任。` +
`请重新加载扩展后再点「测试连接」,并允许弹窗中的站点权限。`
);
}
return msg;
}
async function hydrate() {
const p = await loadPersistent();
if (p.baseUrl) $("baseUrl").value = p.baseUrl;
// 默认不展示手填;仅当用户曾覆盖时回填并展开
if (p.rootUri) {
$("rootUri").value = p.rootUri;
const adv = $("advRoot");
if (adv && p.rootUri) {
// 不自动展开;连接后 hint 会显示实际路径
}
}
if (p.email) $("email").value = p.email;
const cfg = await loadConfig();
if (cfg.connected) {
setStatus(
`已连接 · ${cfg.email || cfg.userId || ""} · token 有效`,
"ok"
);
setWorkspaceHint(cfg.rootUri || p.rootUri, p.rootPath, "saved");
} else if (cfg.baseUrl) {
setStatus("已保存配置,但 token 缺失(浏览器重启后需重新登录)", "err");
setWorkspaceHint(p.rootUri, p.rootPath, "saved");
}
}
$("test").onclick = async () => {
setStatus("测试中…");
let baseUrl = "";
try {
baseUrl = normalizeBaseUrl($("baseUrl").value);
const ok = await requestBaseUrlPermission(baseUrl);
if (!ok) {
setStatus(
`已取消访问权限:需要允许扩展访问 ${new URL(baseUrl).origin}`,
"err"
);
return;
}
// whoami 不依赖 /api/health(外网入口可能未挂 health
const data = await vaultFetch(baseUrl, "/api/auth/whoami", {
method: "GET",
credentials: true,
});
setStatus(
`可达 · ${baseUrl} · whoami ok=${Boolean(data?.ok ?? true)}`,
"ok"
);
} catch (e) {
setStatus(formatFetchError(e, baseUrl || $("baseUrl").value), "err");
}
};
$("connect").onclick = async () => {
setStatus("登录中…");
setWorkspaceHint("", "");
const password = $("password").value;
let baseUrl = "";
try {
baseUrl = normalizeBaseUrl($("baseUrl").value);
// 关键:在点击手势内申请 host 权限,再交给 background 登录
const ok = await requestBaseUrlPermission(baseUrl);
if (!ok) {
setStatus(
`已取消访问权限:需要允许扩展访问 ${new URL(baseUrl).origin}(含端口)`,
"err"
);
return;
}
const res = await chrome.runtime.sendMessage({
type: "CONNECT",
baseUrl,
// 留空 → background 调 default workspace
rootUri: ($("rootUri").value || "").trim(),
email: $("email").value,
password,
});
$("password").value = "";
if (!res?.ok) {
setStatus(res?.error || "连接失败", "err");
return;
}
const r = res.result || {};
if (r.rootUri) $("rootUri").value = r.rootUri;
setStatus(
`已连接 · ${r.email || r.userId || ""} · 过期 ${r.expiresAt || "—"}`,
"ok"
);
setWorkspaceHint(r.rootUri, r.rootPath, r.workspaceSource || "default");
} catch (e) {
$("password").value = "";
setStatus(formatFetchError(e, baseUrl || $("baseUrl").value), "err");
}
};
$("disconnect").onclick = async () => {
await chrome.runtime.sendMessage({ type: "DISCONNECT" });
setStatus("已断开", "err");
setWorkspaceHint("", "");
};
hydrate();