Files
remoteconn-gitea/apps/miniprogram/utils/syncAuth.js
2026-03-21 18:57:10 +08:00

175 lines
5.5 KiB
JavaScript
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.

/* global wx, console, require, module */
const { getOpsConfig } = require("./opsConfig");
const SYNC_AUTH_STORAGE_KEY = "remoteconn.sync.auth.v1";
function logSyncAuth(level, message, extra) {
if (level === "info") return;
const payload = extra && typeof extra === "object" ? extra : {};
const writer = level === "warn" ? console.warn : level === "error" ? console.error : console.info;
writer("[sync.auth]", message, payload);
}
function readAuthCache() {
try {
const raw = wx.getStorageSync(SYNC_AUTH_STORAGE_KEY);
return raw && typeof raw === "object" ? raw : {};
} catch {
return {};
}
}
function writeAuthCache(value) {
try {
wx.setStorageSync(SYNC_AUTH_STORAGE_KEY, value && typeof value === "object" ? value : {});
} catch {
// ignore
}
}
function resolveSyncBaseUrl() {
const ops = getOpsConfig();
const raw = String(ops.gatewayUrl || "").trim();
if (!raw) {
logSyncAuth("warn", "未配置 gatewayUrl无法发起同步请求");
return "";
}
let normalized = raw;
if (normalized.startsWith("ws://")) normalized = `http://${normalized.slice(5)}`;
if (normalized.startsWith("wss://")) normalized = `https://${normalized.slice(6)}`;
if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
normalized = `https://${normalized}`;
}
normalized = normalized.replace(/[?#].*$/, "").replace(/\/+$/, "");
const matched = normalized.match(/^(https?):\/\/([^/]+)(?:\/.*)?$/i);
if (!matched) {
logSyncAuth("warn", "gatewayUrl 非法,无法推导同步基地址", { raw });
return "";
}
const baseUrl = `${matched[1].toLowerCase()}://${matched[2]}`;
logSyncAuth("info", "同步基地址推导成功", { baseUrl });
return baseUrl;
}
function requestJson(url, options) {
logSyncAuth("info", "发起同步请求", {
method: (options && options.method) || "GET",
url
});
return new Promise((resolve, reject) => {
wx.request({
url,
method: (options && options.method) || "GET",
data: options && options.data,
header: (options && options.header) || {},
...(Number.isFinite(Number(options && options.timeoutMs)) && Number(options && options.timeoutMs) >= 1000
? { timeout: Math.round(Number(options && options.timeoutMs)) }
: {}),
success(res) {
const ok = res && res.statusCode >= 200 && res.statusCode < 300;
if (!ok) {
const message =
res && res.data && typeof res.data === "object" && res.data.message
? String(res.data.message)
: `sync request failed: ${res.statusCode}`;
logSyncAuth("warn", "同步请求返回非 2xx", {
method: (options && options.method) || "GET",
url,
statusCode: res && res.statusCode,
message
});
reject(new Error(message));
return;
}
logSyncAuth("info", "同步请求成功", {
method: (options && options.method) || "GET",
url,
statusCode: res.statusCode
});
resolve(res.data || {});
},
fail(error) {
logSyncAuth("error", "同步请求失败", {
method: (options && options.method) || "GET",
url,
error: error && error.errMsg ? error.errMsg : String(error || "")
});
reject(error);
}
});
});
}
async function loginAndFetchToken() {
const baseUrl = resolveSyncBaseUrl();
const ops = getOpsConfig();
if (!baseUrl || !ops.gatewayToken) {
logSyncAuth("warn", "同步鉴权配置不完整", {
hasBaseUrl: Boolean(baseUrl),
hasGatewayToken: Boolean(ops.gatewayToken)
});
throw new Error("sync config incomplete");
}
logSyncAuth("info", "准备执行 wx.login 获取同步令牌", { baseUrl });
const loginResult = await new Promise((resolve, reject) => {
wx.login({
success(res) {
if (!res.code) {
logSyncAuth("warn", "wx.login 成功但未返回 code");
reject(new Error("wx.login missing code"));
return;
}
logSyncAuth("info", "wx.login 成功,已拿到 code");
resolve(res);
},
fail(error) {
logSyncAuth("error", "wx.login 失败", {
error: error && error.errMsg ? error.errMsg : String(error || "")
});
reject(error);
}
});
});
const payload = await requestJson(`${baseUrl}/api/miniprogram/auth/login`, {
method: "POST",
data: { code: loginResult.code },
header: {
"content-type": "application/json",
"x-gateway-token": ops.gatewayToken
}
});
if (!payload || payload.ok !== true || !payload.token) {
throw new Error((payload && payload.message) || "sync login failed");
}
const session = {
token: String(payload.token || ""),
expiresAt: String(payload.expiresAt || "")
};
writeAuthCache(session);
logSyncAuth("info", "同步令牌获取成功", {
expiresAt: session.expiresAt
});
return session;
}
async function ensureSyncAuthToken() {
const cached = readAuthCache();
const expiresAt = +new Date(cached.expiresAt || 0);
if (cached.token && Number.isFinite(expiresAt) && expiresAt - Date.now() > 60 * 1000) {
logSyncAuth("info", "复用本地缓存的同步令牌", {
expiresAt: cached.expiresAt
});
return String(cached.token);
}
logSyncAuth("info", "本地无可用同步令牌,准备重新登录");
const session = await loginAndFetchToken();
return session.token;
}
module.exports = {
ensureSyncAuthToken,
resolveSyncBaseUrl,
requestJson
};