857 lines
24 KiB
JavaScript
857 lines
24 KiB
JavaScript
import CryptoJS from "crypto-js";
|
||
import { defineStore } from "pinia";
|
||
import { navigateTo, useRuntimeConfig } from "#app";
|
||
import { computed, ref } from "vue";
|
||
var ce = [
|
||
[/产品类型错误.{0,30}安全组[^\n]*/g, `该商品未接入安全组`],
|
||
[/商品未关联.{0,20}模块接口/g, `该商品暂不可用`],
|
||
[/未关联.{0,12}模块接口/g, `该商品暂不可用`],
|
||
[/尚未接入.{0,12}模块/g, `该商品暂不可用`],
|
||
];
|
||
var le = [
|
||
[/快快云-弹性云|弹性云/g, ``],
|
||
[/快快云|快快/g, ``],
|
||
[/魔方云|魔方财务|智简魔方|智简/g, ``],
|
||
[/美得云|美德云|meidecloud/gi, ``],
|
||
[/dwvps[a-z0-9_]*/gi, ``],
|
||
[/miloce[a-z0-9_]*/gi, ``],
|
||
[/jinghanyun[a-z0-9_]*/gi, ``],
|
||
[/kkidc[a-z0-9_]*/gi, ``],
|
||
[/kky[a-z0-9_]*/gi, ``],
|
||
[/idcsmart[a-z0-9_]*/gi, ``],
|
||
[/mf[_-]?cloud[a-z0-9_]*/gi, ``],
|
||
[/mf[_-]?dcim[a-z0-9_]*/gi, ``],
|
||
[/common[_-]?cloud[a-z0-9_]*/gi, ``],
|
||
[/pve[_-]?cloud[a-z0-9_]*/gi, ``],
|
||
];
|
||
var ue =
|
||
/模块接口|自定义配置|模块未关联|模块标识|上游|第三方云|[a-z]{2,}[_-][a-z0-9_]+|\bDCIM\b|whmcs|reserver|https?:\/\//i;
|
||
function W(e) {
|
||
let t = typeof e == `string` ? e : ``;
|
||
if (!t) return ``;
|
||
let n = t;
|
||
for (let [e, t] of ce) n = n.replace(e, t);
|
||
for (let [e, t] of le) n = n.replace(e, t);
|
||
return (
|
||
(n = n
|
||
.replace(/\s{2,}/g, ` `)
|
||
.replace(/[,,、]{2,}/g, `,`)
|
||
.replace(/^[\s,,、::;;-]+|[\s,,、::;;-]+$/g, ``)
|
||
.trim()),
|
||
n.length < 2 || ue.test(n) ? `操作失败,请稍后重试或联系客服` : n
|
||
);
|
||
}
|
||
function de(e) {
|
||
let t = typeof e == `string` ? e : ``;
|
||
return /^\s*上游熔断解除[::]/u.test(t)
|
||
? `服务连接已恢复正常`
|
||
: /^\s*上游熔断[::]/u.test(t)
|
||
? `服务连接暂时异常,请稍后重试`
|
||
: t;
|
||
}
|
||
var fe = 32;
|
||
var pe = 200;
|
||
var me = 6e4;
|
||
var G = new Map();
|
||
var he = Symbol(`uninitialized`);
|
||
var ge = 0;
|
||
function _e() {
|
||
(ge++, G.clear());
|
||
}
|
||
function ve() {
|
||
return ge;
|
||
}
|
||
function ye(e, t, n = ``) {
|
||
return JSON.stringify([e, n, t], (e, t) =>
|
||
t && typeof t == `object` && !Array.isArray(t)
|
||
? Object.fromEntries(
|
||
Object.keys(t)
|
||
.sort()
|
||
.map((e) => [e, t[e]]),
|
||
)
|
||
: t,
|
||
);
|
||
}
|
||
function be(e) {
|
||
return JSON.parse(JSON.stringify(e));
|
||
}
|
||
function xe(e, t, n = Date.now()) {
|
||
he !== e && (_e(), (he = e));
|
||
for (let [e, t] of G) t.expires <= n && G.delete(e);
|
||
let r = G.get(t);
|
||
if (r) return (G.delete(t), G.set(t, r), be(r.data));
|
||
}
|
||
function Se(e, t, n, r, i = Date.now()) {
|
||
if (e === he && r === ge) {
|
||
if (!Array.isArray(n.list) || n.list.length > pe) {
|
||
G.delete(t);
|
||
return;
|
||
}
|
||
try {
|
||
let e = be(n);
|
||
(G.delete(t),
|
||
G.size >= fe && G.delete(G.keys().next().value),
|
||
G.set(t, {
|
||
data: e,
|
||
expires: i + me,
|
||
}));
|
||
} catch {
|
||
G.delete(t);
|
||
}
|
||
}
|
||
}
|
||
var Ce = 2147483647;
|
||
function K(e, t) {
|
||
let n =
|
||
typeof e == `number`
|
||
? e
|
||
: typeof e == `string` && /^\d+$/.test(e)
|
||
? Number(e)
|
||
: NaN;
|
||
if (!Number.isSafeInteger(n) || n < t || n > Ce)
|
||
throw Error(`购买信息无效,请重新确认后重试`);
|
||
return n;
|
||
}
|
||
function we(e) {
|
||
if (!e || typeof e != `object` || Array.isArray(e))
|
||
throw Error(`购买信息无效,请重新确认后重试`);
|
||
return e;
|
||
}
|
||
function Te(e) {
|
||
let t = we(e);
|
||
return {
|
||
product_id: K(t.product_id, 1),
|
||
qty: K(t.qty, 1),
|
||
};
|
||
}
|
||
function Ee(e, t, n) {
|
||
if (e === `POST` && t === `/product/settle`)
|
||
return {
|
||
mode: `single`,
|
||
...Te(n),
|
||
};
|
||
if (e === `POST` && t === `/cart`) {
|
||
let e = we(n);
|
||
if (e.products !== void 0) {
|
||
if (
|
||
!Array.isArray(e.products) ||
|
||
!e.products.length ||
|
||
e.products.length > 1e3
|
||
)
|
||
throw Error(`购买信息无效,请重新确认后重试`);
|
||
return {
|
||
mode: `cart_add`,
|
||
products: e.products.map(Te),
|
||
};
|
||
}
|
||
return {
|
||
mode: `cart_add`,
|
||
...Te(e),
|
||
};
|
||
}
|
||
if (e === `POST` && t === `/cart/settle`) {
|
||
let e = we(n);
|
||
if (!Array.isArray(e.positions) || !e.positions.length)
|
||
throw Error(`请选择要结算的商品`);
|
||
return {
|
||
mode: `cart_settle`,
|
||
positions: [...new Set(e.positions.map((e) => K(e, 0)))],
|
||
};
|
||
}
|
||
let r = e === `PUT` ? /^\/cart\/(\d+)(\/qty)?$/.exec(t) : null;
|
||
if (r) {
|
||
let e = we(n);
|
||
return {
|
||
mode: `cart_update`,
|
||
position: K(r[1], 0),
|
||
qty: K(e.qty, 1),
|
||
...(r[2]
|
||
? {}
|
||
: {
|
||
product_id: K(e.product_id, 1),
|
||
}),
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
function De(e) {
|
||
if (!e || typeof e != `object` || Array.isArray(e)) return !1;
|
||
let t = e;
|
||
return (
|
||
typeof t.allowed == `boolean` &&
|
||
typeof t.message == `string` &&
|
||
(t.allowed || !!t.message.trim())
|
||
);
|
||
}
|
||
class ApiError extends Error {
|
||
status;
|
||
data;
|
||
constructor(status, message, data) {
|
||
super(message);
|
||
this.status = status;
|
||
this.data = data;
|
||
this.name = "ApiError";
|
||
}
|
||
}
|
||
|
||
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
||
const FILE_TRANSFER_TIMEOUT_MS = 120_000;
|
||
|
||
function requestTimeoutError() {
|
||
return new ApiError(408, "请求超时,请稍后重试");
|
||
}
|
||
|
||
async function withRequestDeadline(
|
||
operation,
|
||
timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
|
||
controller = new AbortController(),
|
||
) {
|
||
const duration =
|
||
Number.isFinite(timeoutMs) && timeoutMs > 0 && timeoutMs <= 2_147_483_647
|
||
? timeoutMs
|
||
: DEFAULT_REQUEST_TIMEOUT_MS;
|
||
let timer;
|
||
const deadline = new Promise((_, reject) => {
|
||
timer = setTimeout(() => {
|
||
controller.abort();
|
||
reject(requestTimeoutError());
|
||
}, duration);
|
||
});
|
||
try {
|
||
// The deadline also covers response-body decoding and any purchase preflight.
|
||
return await Promise.race([
|
||
Promise.resolve().then(() => operation(controller.signal)),
|
||
deadline,
|
||
]);
|
||
} finally {
|
||
clearTimeout(timer);
|
||
}
|
||
}
|
||
|
||
function assertRequestActive(signal) {
|
||
if (signal.aborted) throw requestTimeoutError();
|
||
}
|
||
|
||
function normalizeRequestError(error) {
|
||
return error instanceof ApiError
|
||
? error
|
||
: new ApiError(0, "网络异常,请检查网络后重试");
|
||
}
|
||
|
||
function useApi() {
|
||
const config = useRuntimeConfig();
|
||
const auth = useAuth();
|
||
|
||
function clearUnauthorizedSession(requestToken) {
|
||
if (
|
||
!requestToken ||
|
||
localStorage.getItem("jwt") !== requestToken ||
|
||
auth.token !== requestToken
|
||
)
|
||
return;
|
||
|
||
auth.clear();
|
||
if (window.location.pathname === "/login") return;
|
||
const redirect = window.location.pathname + window.location.search;
|
||
try {
|
||
void Promise.resolve(
|
||
navigateTo({
|
||
path: "/login",
|
||
query: redirect === "/" ? undefined : { redirect },
|
||
}),
|
||
).catch(() => {});
|
||
} catch {}
|
||
}
|
||
|
||
function responseObject(value) {
|
||
return value && typeof value === "object" && !Array.isArray(value)
|
||
? value
|
||
: {};
|
||
}
|
||
|
||
function responseError(response, payload, requestToken, fallbackMessage) {
|
||
const envelope = responseObject(payload);
|
||
const status =
|
||
response.status === 401
|
||
? 401
|
||
: typeof envelope.status === "number" && envelope.status !== 200
|
||
? envelope.status
|
||
: response.status;
|
||
if (status === 401) clearUnauthorizedSession(requestToken);
|
||
return new ApiError(
|
||
status,
|
||
W(envelope.msg || envelope.message) || fallbackMessage,
|
||
envelope.data,
|
||
);
|
||
}
|
||
|
||
async function decodeResponse(
|
||
response,
|
||
requestToken,
|
||
fallbackMessage,
|
||
signal,
|
||
allowStatuslessDataArray = false,
|
||
) {
|
||
const payload = await response.json().catch(() => null);
|
||
assertRequestActive(signal);
|
||
const envelope = responseObject(payload);
|
||
if (response.ok && envelope.status === 200) return payload;
|
||
if (
|
||
response.ok &&
|
||
allowStatuslessDataArray &&
|
||
!("status" in envelope) &&
|
||
Array.isArray(envelope.data)
|
||
) {
|
||
return { status: 200, msg: "", data: envelope.data };
|
||
}
|
||
throw responseError(response, payload, requestToken, fallbackMessage);
|
||
}
|
||
|
||
function apiUrl(path, query, keepEmpty = false) {
|
||
let url = `${String(config.public.apiBase ?? "")}/console/v1${path}`;
|
||
const search = new URLSearchParams();
|
||
for (const [key, value] of Object.entries(query ?? {})) {
|
||
if (value == null || (value === "" && !keepEmpty)) continue;
|
||
if (Array.isArray(value)) {
|
||
for (const item of value) {
|
||
if (item != null && item !== "")
|
||
search.append(`${key}[]`, String(item));
|
||
}
|
||
} else {
|
||
search.append(key, String(value));
|
||
}
|
||
}
|
||
if (search.size) url += `?${search}`;
|
||
return url;
|
||
}
|
||
|
||
async function request(method, path, options = {}) {
|
||
// Capture identity before the first await, including when logout clears it immediately.
|
||
const requestToken = auth.token;
|
||
const headers = { Accept: "application/json" };
|
||
if (requestToken) headers.Authorization = `Bearer ${requestToken}`;
|
||
if (options.body !== undefined)
|
||
headers["Content-Type"] = "application/json";
|
||
try {
|
||
const serializedBody =
|
||
options.body === undefined ? undefined : JSON.stringify(options.body);
|
||
return await withRequestDeadline(async (signal) => {
|
||
if (requestToken) {
|
||
let purchase;
|
||
try {
|
||
purchase = Ee(method, path, options.body);
|
||
} catch (error) {
|
||
throw new ApiError(
|
||
422,
|
||
error instanceof Error
|
||
? error.message
|
||
: "购买信息无效,请重新确认后重试",
|
||
);
|
||
}
|
||
if (purchase) {
|
||
const authToken = auth.token;
|
||
const permission = await request("POST", "/purchase_limit/check", {
|
||
body: purchase,
|
||
timeoutMs: options.timeoutMs ?? 15_000,
|
||
});
|
||
assertRequestActive(signal);
|
||
if (
|
||
localStorage.getItem("jwt") !== requestToken ||
|
||
auth.token !== authToken
|
||
) {
|
||
throw new ApiError(409, "登录状态已变化,请重新确认后重试");
|
||
}
|
||
if (JSON.stringify(options.body) !== serializedBody) {
|
||
throw new ApiError(409, "购买信息已变化,请重新确认后重试");
|
||
}
|
||
if (!De(permission))
|
||
throw new ApiError(502, "限购验证暂不可用,请稍后重试");
|
||
if (!permission.allowed)
|
||
throw new ApiError(422, permission.message);
|
||
}
|
||
}
|
||
assertRequestActive(signal);
|
||
const response = await fetch(
|
||
apiUrl(path, options.query, options.keepEmpty),
|
||
{
|
||
method,
|
||
headers,
|
||
body: serializedBody,
|
||
signal,
|
||
},
|
||
);
|
||
assertRequestActive(signal);
|
||
const envelope = await decodeResponse(
|
||
response,
|
||
requestToken,
|
||
response.ok
|
||
? "响应格式异常,请稍后重试"
|
||
: `请求失败(HTTP ${response.status})`,
|
||
signal,
|
||
options.allowStatuslessDataArray,
|
||
);
|
||
assertRequestActive(signal);
|
||
if (
|
||
method !== "GET" &&
|
||
path !== "/purchase_limit/check" &&
|
||
!/^\/product\/\d+\/config_option$/.test(path)
|
||
) {
|
||
_e();
|
||
}
|
||
return options.raw ? envelope : envelope.data;
|
||
}, options.timeoutMs);
|
||
} catch (error) {
|
||
throw normalizeRequestError(error);
|
||
}
|
||
}
|
||
|
||
async function upload(file) {
|
||
const body = new FormData();
|
||
body.append("file", file);
|
||
const headers = { Accept: "application/json" };
|
||
const requestToken = auth.token;
|
||
if (requestToken) headers.Authorization = `Bearer ${requestToken}`;
|
||
try {
|
||
return await withRequestDeadline(async (signal) => {
|
||
const response = await fetch(apiUrl("/upload"), {
|
||
method: "POST",
|
||
headers,
|
||
body,
|
||
signal,
|
||
});
|
||
assertRequestActive(signal);
|
||
const envelope = await decodeResponse(
|
||
response,
|
||
requestToken,
|
||
"上传失败",
|
||
signal,
|
||
);
|
||
assertRequestActive(signal);
|
||
return envelope.data;
|
||
}, FILE_TRANSFER_TIMEOUT_MS);
|
||
} catch (error) {
|
||
throw normalizeRequestError(error);
|
||
}
|
||
}
|
||
|
||
async function download(path, data, filename = "export.xlsx", options = {}) {
|
||
const method = options.method ?? "GET";
|
||
const headers = { Accept: "*/*" };
|
||
const requestToken = auth.token;
|
||
if (requestToken) headers.Authorization = `Bearer ${requestToken}`;
|
||
if (method === "POST") headers["Content-Type"] = "application/json";
|
||
try {
|
||
await withRequestDeadline(async (signal) => {
|
||
const response = await fetch(
|
||
apiUrl(path, method === "GET" ? data : undefined),
|
||
{
|
||
method,
|
||
headers,
|
||
...(method === "POST" ? { body: JSON.stringify(data ?? {}) } : {}),
|
||
signal,
|
||
},
|
||
);
|
||
assertRequestActive(signal);
|
||
const isJson = /\bapplication\/(?:[\w.-]+\+)?json\b/i.test(
|
||
response.headers.get("content-type") || "",
|
||
);
|
||
if (!response.ok || isJson) {
|
||
const payload = await response
|
||
.clone()
|
||
.json()
|
||
.catch(() => null);
|
||
assertRequestActive(signal);
|
||
const envelope = responseObject(payload);
|
||
if (
|
||
!response.ok ||
|
||
(typeof envelope.status === "number" && envelope.status !== 200)
|
||
) {
|
||
throw responseError(
|
||
response,
|
||
payload,
|
||
requestToken,
|
||
`下载失败(HTTP ${response.status})`,
|
||
);
|
||
}
|
||
}
|
||
const disposition = response.headers.get("content-disposition") || "";
|
||
const match = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(disposition);
|
||
if (match?.[1]) {
|
||
try {
|
||
filename = decodeURIComponent(match[1]);
|
||
} catch {
|
||
filename = match[1];
|
||
}
|
||
}
|
||
const blob = await response.blob();
|
||
assertRequestActive(signal);
|
||
const objectUrl = URL.createObjectURL(blob);
|
||
const link = document.createElement("a");
|
||
try {
|
||
link.href = objectUrl;
|
||
link.download = filename;
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
} finally {
|
||
link.remove();
|
||
URL.revokeObjectURL(objectUrl);
|
||
}
|
||
}, options.timeoutMs ?? FILE_TRANSFER_TIMEOUT_MS);
|
||
} catch (error) {
|
||
throw normalizeRequestError(error);
|
||
}
|
||
}
|
||
|
||
return {
|
||
identity: () => auth.token,
|
||
request,
|
||
upload,
|
||
download,
|
||
get: (path, query, keepEmpty = false, options = {}) =>
|
||
request("GET", path, {
|
||
...options,
|
||
query,
|
||
keepEmpty,
|
||
}),
|
||
getRaw: (path, query, options = {}) =>
|
||
request("GET", path, {
|
||
...options,
|
||
query,
|
||
raw: true,
|
||
}),
|
||
post: (path, body, raw = false, options = {}) =>
|
||
request("POST", path, {
|
||
...options,
|
||
body,
|
||
raw,
|
||
}),
|
||
put: (path, body, query) => request("PUT", path, { body, query }),
|
||
del: (path, query, body) => request("DELETE", path, { query, body }),
|
||
};
|
||
}
|
||
var ot = CryptoJS.enc.Utf8.parse(`idcsmart.finance`);
|
||
var st = CryptoJS.enc.Utf8.parse(`9311019310287172`);
|
||
function ct(e) {
|
||
return CryptoJS.AES.encrypt(e, ot, {
|
||
mode: CryptoJS.mode.CBC,
|
||
padding: CryptoJS.pad.Pkcs7,
|
||
iv: st,
|
||
}).toString();
|
||
}
|
||
function lt(e, t = `登录失败,请稍后重试`) {
|
||
let n = e && typeof e == `object` ? e : {},
|
||
r = typeof n.message == `string` ? n.message.trim() : ``,
|
||
i = n.data && typeof n.data == `object` ? n.data : {};
|
||
return i.need_security_verify || i.ip_exception_verify
|
||
? `本次登录需要额外的安全验证,请联系客服协助完成验证。`
|
||
: n.status === 429
|
||
? `登录尝试过于频繁,请稍后再试`
|
||
: n.status === 408 || /超时|timeout/i.test(r)
|
||
? `登录请求超时,请检查网络后重试`
|
||
: n.status === 0 ||
|
||
/网络异常|Failed to fetch|NetworkError|Load failed/i.test(r)
|
||
? `网络连接失败,请检查网络后重试`
|
||
: (n.status ?? 0) >= 500 ||
|
||
/页面错误|响应格式异常|Internal Server Error|Bad Gateway|Service Unavailable/i.test(
|
||
r,
|
||
)
|
||
? `登录服务暂时异常,请稍后重试;若持续失败,请联系客服。`
|
||
: !r ||
|
||
/<[^>]+>|SQLSTATE|\b(?:TypeError|ReferenceError|Exception)\b/.test(
|
||
r,
|
||
)
|
||
? t
|
||
: r;
|
||
}
|
||
function ut(e) {
|
||
return (
|
||
typeof e == `string` && e.length > 0 && e.length <= 8192 && !/\s/.test(e)
|
||
);
|
||
}
|
||
function dt(e) {
|
||
if (!e || typeof e != `object`) return null;
|
||
let t = e,
|
||
n = [t, t.data, t.error, t.response];
|
||
for (let e of n) {
|
||
if (!e || typeof e != `object`) continue;
|
||
let t = e,
|
||
n = t.data && typeof t.data == `object` ? t.data : t;
|
||
if (
|
||
n.need_security_verify === !0 ||
|
||
n.need_security_verify === 1 ||
|
||
n.need_security_verify === `1` ||
|
||
(Array.isArray(n.available_methods) && n.available_methods.length > 0)
|
||
)
|
||
return n;
|
||
}
|
||
return null;
|
||
}
|
||
function ft(e) {
|
||
let t = dt(e);
|
||
if (t)
|
||
throw Object.assign(Error(`安全验证尚未完成,请重试`), {
|
||
data: t,
|
||
});
|
||
}
|
||
var pt = {
|
||
operate_password: `操作密码`,
|
||
email_code: `邮箱验证码`,
|
||
phone_code: `手机验证码`,
|
||
certification: `实名认证`,
|
||
};
|
||
function mt(e) {
|
||
return (
|
||
Array.isArray(e.available_methods) ? e.available_methods : []
|
||
).flatMap((t) => {
|
||
let n =
|
||
typeof t == `string`
|
||
? {
|
||
value: t,
|
||
}
|
||
: t && typeof t == `object`
|
||
? t
|
||
: {},
|
||
r = String(n.value ?? ``);
|
||
return pt[r]
|
||
? [
|
||
{
|
||
value: r,
|
||
label: pt[r],
|
||
tip: String(n.tip ?? ``),
|
||
account: String(n.account ?? ``),
|
||
phone_code: String(n.phone_code ?? ``),
|
||
security_verify_token: String(
|
||
n.security_verify_token ?? e.security_verify_token ?? ``,
|
||
),
|
||
},
|
||
]
|
||
: [];
|
||
});
|
||
}
|
||
const useAuth = defineStore("auth", () => {
|
||
const token = ref(localStorage.getItem("jwt"));
|
||
const overview = ref(null);
|
||
const loading = ref(false);
|
||
const legacyLoginPending = ref(false);
|
||
const isLoggedIn = computed(() => !!token.value);
|
||
let overviewPromise = null;
|
||
let sessionVersion = 0;
|
||
let legacySession = null;
|
||
|
||
function loginWithJwt(jwt) {
|
||
if (!ut(jwt))
|
||
throw Error("登录响应无效,请重新登录;若持续失败,请联系客服。");
|
||
resetSessionState();
|
||
token.value = jwt;
|
||
localStorage.setItem("jwt", jwt);
|
||
}
|
||
|
||
function prepareLegacyLogin(jwt = localStorage.getItem("jwt")) {
|
||
clear();
|
||
legacySession = {
|
||
jwt: ut(jwt) ? jwt : null,
|
||
version: sessionVersion,
|
||
controller: null,
|
||
promise: null,
|
||
};
|
||
legacyLoginPending.value = true;
|
||
}
|
||
|
||
function cancelLegacyLogin() {
|
||
legacySession?.controller?.abort();
|
||
legacySession = null;
|
||
legacyLoginPending.value = false;
|
||
}
|
||
|
||
function verifyLegacyLogin() {
|
||
const session = legacySession;
|
||
if (!session?.jwt) {
|
||
return Promise.reject(
|
||
Error("缺少有效登录凭证,请返回原平台重新进入,或使用账号登录。"),
|
||
);
|
||
}
|
||
if (session.promise) return session.promise;
|
||
const apiBase = String(useRuntimeConfig().public.apiBase ?? "");
|
||
const controller = new AbortController();
|
||
session.controller = controller;
|
||
const isCurrentSession = () =>
|
||
legacySession === session && sessionVersion === session.version;
|
||
const promise = withRequestDeadline(
|
||
async (signal) => {
|
||
let response;
|
||
try {
|
||
response = await fetch(`${apiBase}/console/v1/index`, {
|
||
headers: {
|
||
Accept: "application/json",
|
||
Authorization: `Bearer ${session.jwt}`,
|
||
},
|
||
credentials: "omit",
|
||
cache: "no-store",
|
||
referrerPolicy: "no-referrer",
|
||
signal,
|
||
});
|
||
} catch {
|
||
throw Error("网络连接失败或验证超时,请检查网络后重试。");
|
||
}
|
||
const envelope = await response.json().catch(() => null);
|
||
assertRequestActive(signal);
|
||
if (!isCurrentSession()) throw Error("登录状态已变化,请重新进入。");
|
||
if (response.status === 401 || envelope?.status === 401) {
|
||
throw Error("登录凭证已失效,请返回原平台重新进入,或使用账号登录。");
|
||
}
|
||
const account = envelope?.data?.account;
|
||
if (
|
||
!response.ok ||
|
||
envelope?.status !== 200 ||
|
||
!account ||
|
||
typeof account !== "object" ||
|
||
!Number.isSafeInteger(account.id) ||
|
||
account.id <= 0
|
||
)
|
||
throw Error("暂时无法验证登录身份,请稍后重试。");
|
||
loginWithJwt(session.jwt);
|
||
overview.value = account;
|
||
},
|
||
15_000,
|
||
controller,
|
||
)
|
||
.catch((error) => {
|
||
if (error instanceof ApiError && error.status === 408) {
|
||
throw Error("网络连接失败或验证超时,请检查网络后重试。");
|
||
}
|
||
throw error;
|
||
})
|
||
.finally(() => {
|
||
if (isCurrentSession()) {
|
||
session.controller = null;
|
||
session.promise = null;
|
||
}
|
||
});
|
||
session.promise = promise;
|
||
return promise;
|
||
}
|
||
|
||
async function login(
|
||
account,
|
||
password,
|
||
captcha = "",
|
||
captchaToken = "",
|
||
type = "password",
|
||
code = "",
|
||
phoneCode = "86",
|
||
verification,
|
||
) {
|
||
if (type === "code" && account.includes("@"))
|
||
throw Error("验证码登录仅支持手机号");
|
||
const api = useApi();
|
||
const version = sessionVersion;
|
||
loading.value = true;
|
||
try {
|
||
const result = await api.post(
|
||
"/login",
|
||
{
|
||
type,
|
||
account,
|
||
...(account.includes("@") ? {} : { phone_code: phoneCode }),
|
||
code,
|
||
password: type === "password" ? ct(password) : "",
|
||
remember_password: "0",
|
||
captcha,
|
||
token: captchaToken,
|
||
...(verification || {}),
|
||
},
|
||
false,
|
||
{ timeoutMs: 15_000 },
|
||
);
|
||
if (version !== sessionVersion) throw Error("登录状态已变化,请重试");
|
||
ft(result);
|
||
if (!ut(result?.jwt))
|
||
throw Error("登录响应无效,请重新登录;若持续失败,请联系客服。");
|
||
loginWithJwt(result.jwt);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
function loadOverview() {
|
||
if (!overviewPromise) {
|
||
const version = sessionVersion;
|
||
const promise = useApi()
|
||
.get("/index", undefined, false, { timeoutMs: 10_000 })
|
||
.then((result) => {
|
||
if (version !== sessionVersion) throw Error("登录状态已变化,请重试");
|
||
overview.value = result.account;
|
||
return result.account;
|
||
})
|
||
.finally(() => {
|
||
if (overviewPromise === promise) overviewPromise = null;
|
||
});
|
||
overviewPromise = promise;
|
||
}
|
||
return overviewPromise;
|
||
}
|
||
|
||
async function fetchOverview() {
|
||
return overview.value ?? loadOverview();
|
||
}
|
||
|
||
async function refreshOverview() {
|
||
return loadOverview();
|
||
}
|
||
|
||
function logout() {
|
||
// request() captures the original Bearer synchronously; remote revocation never owns local logout.
|
||
const revocation = useApi().post("/logout");
|
||
clear();
|
||
void revocation.catch(() => {});
|
||
}
|
||
|
||
function resetSessionState() {
|
||
_e();
|
||
cancelLegacyLogin();
|
||
sessionVersion += 1;
|
||
overview.value = null;
|
||
overviewPromise = null;
|
||
}
|
||
|
||
function clear() {
|
||
resetSessionState();
|
||
token.value = null;
|
||
localStorage.removeItem("jwt");
|
||
}
|
||
|
||
return {
|
||
token,
|
||
overview,
|
||
loading,
|
||
isLoggedIn,
|
||
login,
|
||
loginWithJwt,
|
||
fetchOverview,
|
||
refreshOverview,
|
||
logout,
|
||
clear,
|
||
legacyLoginPending,
|
||
prepareLegacyLogin,
|
||
verifyLegacyLogin,
|
||
cancelLegacyLogin,
|
||
};
|
||
});
|
||
export {
|
||
lt as loginErrorMessage,
|
||
ApiError,
|
||
ye as apiCacheKey,
|
||
ve as apiCacheVersion,
|
||
W as sanitizeMessage,
|
||
de as operationMessage,
|
||
mt as securityMethods,
|
||
useApi,
|
||
Se as writeApiCache,
|
||
ft as assertSecurityComplete,
|
||
ut as isValidJwt,
|
||
xe as readApiCache,
|
||
dt as securityChallenge,
|
||
useAuth,
|
||
_e as clearApiCache,
|
||
};
|