267 lines
7.7 KiB
JavaScript
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 };
|