/** * 服务器列表分类适配:优先使用后端 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 };