Files

267 lines
7.7 KiB
JavaScript

/**
* 服务器列表分类适配:优先使用 classified-v1 分页;旧协议或跨页自然排序时完整拉取。
* 分类结果和请求只在当前工厂实例内缓存,invalidate 作废在途结果对应的代次。
*/
// 这些既有通用商品在当前业务中按独立服务器展示;变更须与商品配置约定一起核对。
var e = new Set([3997, 5174, 5226, 5227, 5631, 5914, 6243]);
function t(t) {
return t.module === `idcsmart_common` && e.has(Number(t.product_id));
}
/** e 为 API,n 为目标模块集合,r 可选后端菜单 id;有菜单时由 /home/host 决定分类。 */
function n(e, n, r) {
if (r !== void 0 && (!Number.isSafeInteger(r) || r <= 0))
throw Error(`服务器导航参数无效`);
let i = (e) => e.replace(/^re(?=mf_|whmcs_)/, ``),
a = new Set(n.map(i)),
o =
r !== void 0 ||
a.has(`mf_finance`) ||
a.has(`mf_finance_dcim`) ||
a.has(`idcsmart_common`),
s = [...new Set([...n, `idcsmart_common`])].join(`,`),
c = new Map(),
l = new Map(),
u = new Map(),
d = 0,
f,
p,
m = new Map();
// 相同参数的正在进行请求共用 Promise;完成即释放,不把分页响应无限期存储。
function h(t) {
let i =
r === void 0
? {
...t,
module: s,
display_modules: n.join(`,`),
server_page: `classified-v1`,
}
: {
...t,
m: r,
},
a = JSON.stringify(i),
o = m.get(a);
if (!o) {
((o =
r === void 0
? e.get(`/host`, i)
: e.get(`/home/host`, i).then((e) => ({
...e,
server_page: `classified-v1`,
}))),
m.set(a, o));
let t = o,
n = () => {
m.get(a) === t && m.delete(a);
};
o.then(n, n);
}
return o;
}
// 通用模块需读取实例详情确认实际模块;同商品共用分类结果,缺 product_id 时按实例区分。
async function g(t, n) {
let a = t.product_id || -t.id;
if (c.has(a)) return c.get(a);
if (l.has(a)) return l.get(a);
let o = e.get(`/host/${t.id}/view`).then((e) => {
if (!e.module) throw Error(`服务器分类信息加载失败,请重试`);
let t = r === void 0 ? i(e.module) : e.module;
return (n === d && c.set(a, t), t);
});
l.set(a, o);
try {
return await o;
} finally {
l.get(a) === o && l.delete(a);
}
}
/** 完整加载列表并去重;旧协议的通用商品最多四个并发详情请求补齐分类,再按模块筛选。 */
async function _(n, o, c) {
let l = [],
u = {},
p = new Set(),
m = new Set();
for (let t = 1; ; t++) {
let r =
t === 1 && c
? c
: f === !1
? await e.get(`/host`, {
...n,
module: s,
page: t,
limit: 100,
})
: await h({
...n,
orderby: `id`,
page: t,
limit: 100,
}),
i = r.server_page === `classified-v1`;
(t === 1 &&
(u = {
select_field: r.select_field,
self_defined_field: r.self_defined_field,
}),
o === d && (f = i));
let a = Array.isArray(r.list) ? r.list : [],
g = 0;
for (let e of a)
(i && m.add(Number(e.id)),
p.has(Number(e.id)) ||
(p.add(Number(e.id)),
l.push({
...e,
}),
g++));
if (l.length >= Number(r.count ?? l.length)) break;
if (!a.length || !g) throw Error(`服务器列表分页未返回完整数据,请重试`);
}
let _ = [
...new Map(
l
.filter(
(e) => e.module === `idcsmart_common` && !m.has(Number(e.id)),
)
.map((e) => [e.product_id || -e.id, e]),
).values(),
],
v = 0,
y = new Map();
await Promise.all(
Array.from(
{
length: Math.min(4, _.length),
},
async () => {
for (; v < _.length;) {
let e = _[v++];
y.set(e.product_id || -e.id, await g(e, o));
}
},
),
);
for (let e of l)
e.module === `idcsmart_common` &&
!m.has(Number(e.id)) &&
(e.module = y.get(e.product_id || -e.id));
let b =
r === void 0
? l.filter((e) => (t(e) ? a.has(`mf_dcim`) : a.has(i(e.module || ``))))
: l;
// 跨分类全量结果按自然中文/数字顺序排序后再分页,避免每页内部排序冒充全局排序。
if (n.orderby === `dedicate_ip` || n.orderby === `name`) {
let e = n.orderby;
b.sort(
(t, r) =>
String(t[e] || ``).localeCompare(String(r[e] || ``), `zh-CN`, {
numeric: !0,
}) * (n.sort === `asc` ? 1 : -1),
);
}
return {
...u,
list: b,
};
}
/** 对外列表加载入口;探测后端能力后选择直接分页或 30 秒的完整分类缓存。 */
async function v(t) {
if (!o) return e.get(`/host`, t);
let n = Object.fromEntries(
Object.entries(t)
.filter(([e]) => ![`page`, `limit`, `module`].includes(e))
.sort(([e], [t]) => e.localeCompare(t)),
),
r = JSON.stringify(n),
i = d,
a;
if (f !== !1 && ![`name`, `dedicate_ip`].includes(String(t.orderby))) {
let e = {
...n,
page: Math.max(1, Number(t.page) || 1),
limit: Math.max(1, Number(t.limit) || 10),
},
o = JSON.stringify(e),
s,
c = !0;
if (f === !0) s = await h(e);
else {
(!p || p.version !== i) &&
(p = {
key: r,
pageKey: o,
version: i,
promise: h(e),
});
let t = p;
try {
s = await t.promise;
} catch (e) {
throw (p === t && (p = void 0), e);
}
((c = t.key === r),
s.server_page === `classified-v1` &&
t.pageKey !== o &&
(s = await h(e)),
p === t && (p = void 0));
}
if (s.server_page === `classified-v1`)
return (
i === d && (f = !0),
{
...s,
list: s.list.map((e) => ({
...e,
})),
}
);
(i === d && (f = !1),
c &&
Array.isArray(s.list) &&
Number(s.count) === s.list.length &&
(a = s));
}
// 全量结果最多缓存四组筛选条件;加载中共用 Promise,失败移除,成功后开始 30 秒 TTL。
let s = Date.now();
for (let [e, t] of u) t.expires <= s && u.delete(e);
let c = u.get(r);
if (!c) {
(u.size >= 4 && u.delete(u.keys().next().value),
(c = {
expires: 1 / 0,
promise: _(n, i, a),
}));
let e = c;
(i === d && u.set(r, e),
e.promise.then(
() => {
e.expires = Date.now() + 3e4;
},
() => {
u.get(r) === e && u.delete(r);
},
));
}
let l = await c.promise,
m = l.list,
g = Math.max(1, Number(t.limit) || 10),
v = (Math.max(1, Number(t.page) || 1) - 1) * g;
return {
...l,
list: m.slice(v, v + g).map((e) => ({
...e,
})),
count: m.length,
};
}
return Object.assign(v, {
// 常规失效保留商品分类;参数为 true 时连分类缓存也清空(例如商品/模块归属已变化)。
invalidate(e = !1) {
(d++, u.clear(), m.clear(), (p = void 0), e && (c.clear(), l.clear()));
},
});
}
export { t as n, n as t };