UNPKG

dnp-client

Version:

数据基础设施轻量连接器,帮助用户管理身份凭证、区域节点、业务节点,完成身份集成。

475 lines (468 loc) 20.7 kB
import { v7 as t } from "uuid"; class i { constructor(t = {}) { const {options: s = {}, callbacks: o = {}} = t; this.options = { pollingTimeout: 6e5, pollingInterval: 1e3, dnpServerUrl: "http://localhost:3521", certificate: "", requestTimeout: 6e4, apiEndpoints: { getAuthResult: "/get/authResult", getToken: "/get/token", getCert: "/get/cert", importCert: "/import/cert", createCert: "/create/cert" }, baseUrl: "undefined" != typeof window ? `${window.location.origin}/api/v1` : "", type: 1, clientType: i.detectClientType(), ...s }, this.uuidString = "", this.startTime = 0, this.isPolling = !1, this.pollingTimer = null, this.callbacks = { onLoading: t => {}, onSuccess: t => {}, onError: t => {}, onMessage: (t, i = "info") => {}, ...o }, this.login = this.login.bind(this), this.stopPolling = this.stopPolling.bind(this), this.clickAuthLogin = this.clickAuthLogin.bind(this), this.registerIdentity = this.registerIdentity.bind(this), this.importCert = this.importCert.bind(this); } static detectClientType() { return "undefined" == typeof window ? "server" : "function" == typeof window.sendToMain ? "electron" : "browser"; } static getBrowserInfo() { if ("undefined" == typeof window || !window.navigator) return "unknown"; const {userAgent: t} = window.navigator; let i = "unknown"; return t.indexOf("Chrome") > -1 && -1 === t.indexOf("Edg") ? i = "Chrome" : t.indexOf("Firefox") > -1 ? i = "Firefox" : t.indexOf("Safari") > -1 && -1 === t.indexOf("Chrome") ? i = "Safari" : t.indexOf("Edg") > -1 ? i = "Edge" : t.indexOf("Opera") > -1 || t.indexOf("OPR") > -1 ? i = "Opera" : (t.indexOf("MSIE") > -1 || t.indexOf("Trident") > -1) && (i = "IE"), i; } async login() { try { this.stopPolling(), this.callbacks.onLoading(!0), await this.clickAuthLogin(); } catch (t) { this.callbacks.onError(t), this.callbacks.onLoading(!1); } } async clickAuthLogin() { if (!this.options.nodeId) { const t = { message: "身份认证失败(JS1001),请联系您当前要登录的平台方进行处理", data: { message: "缺少必需参数:nodeId" } }; throw this.callbacks.onError(t), this.callbacks.onMessage("身份认证失败(JS1001),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), t; } if ("string" != typeof this.options.nodeId) { const t = { message: "身份认证失败(JS1002),请联系您当前要登录的平台方进行处理", data: { message: "nodeId 参数值无效,必须是字符串", data: this.options.nodeId } }; throw this.callbacks.onError(t), this.callbacks.onMessage("身份认证失败(JS1002),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), t; } this.uuidString = t().slice(0, 32), this.invokeDesktopApp(`dnp://getToken?json=${JSON.stringify({ uuid: this.uuidString, nodeId: this.options.nodeId, browser: i.getBrowserInfo() })}`, async () => { this.invokeDesktopAppLogin(); }); } invokeDesktopApp(t, i) { try { const s = document.createElement("iframe"); s.style.display = "none", s.src = t, document.body.appendChild(s), setTimeout(() => { document.body.removeChild(s); }, 1e3), i && "function" == typeof i && i(); } catch (t) { this.callbacks.onError(t), this.callbacks.onLoading(!1); } } invokeDesktopAppLogin() { this.stopPolling(), this.startTime = Date.now(), this.isPolling = !0, this.waitForResult(); } async waitForResult() { if (this.isPolling) { if (Date.now() - this.startTime >= this.options.pollingTimeout) return this.stopPolling(), this.callbacks.onMessage("获取认证结果失败", "error"), void this.callbacks.onLoading(!1); try { const t = await this.apiGetAuthResult(this.uuidString); if (t) { if ("refuse" === t) return this.stopPolling(), this.callbacks.onMessage("客户端正忙,请稍后再试", "error"), void this.callbacks.onLoading(!1); if (t.data) { if (t.data?.success) return this.stopPolling(), this.callbacks.onSuccess(), void this.callbacks.onLoading(!1); if (!t.data?.success) return this.stopPolling(), this.callbacks.onError(t.data), void this.callbacks.onLoading(!1); } return void (this.pollingTimer = setTimeout(() => { this.waitForResult(); }, this.options.pollingInterval)); } this.pollingTimer = setTimeout(() => { this.waitForResult(); }, this.options.pollingInterval); } catch (t) { if ("CORS" === t.type) return this.stopPolling(), this.callbacks.onMessage("请求失败(JS5001),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onError({ message: "请求失败(JS5001),请联系您当前要登录的平台方进行处理" }), void this.callbacks.onLoading(!1); this.pollingTimer = setTimeout(() => { this.waitForResult(); }, this.options.pollingInterval); } } } async fetchToken(t) { if (!t) { throw { message: "获取token失败(JS2001),请联系您当前要登录的平台方进行处理", data: { message: "缺少必需参数:uuid" } }; } const i = await this.apiGetToken(t); if ("refuse" === i) { throw { message: "客户端正忙,请稍后再试" }; } if (i.data?.tokenValue) return i.data.tokenValue; throw { message: i.data?.message || "获取token失败" }; } async waitForVP() { if (this.isPolling) { if (Date.now() - this.startTime >= this.options.pollingTimeout) return this.stopPolling(), this.callbacks.onMessage("获取签名失败", "error"), void this.callbacks.onLoading(!1); try { const t = await this.apiGetToken(this.options.uuidString); if (t) { if ("refuse" === t) return this.stopPolling(), this.callbacks.onMessage("客户端正忙,请稍后再试", "error"), void this.callbacks.onLoading(!1); if (t.data) { if (t.data?.tokenValue) return this.stopPolling(), this.callbacks.onSuccess(t.data.tokenValue), void this.callbacks.onLoading(!1); if (!t.data?.tokenValue) return this.stopPolling(), this.callbacks.onError(t.data), void this.callbacks.onLoading(!1); } return void (this.pollingTimer = setTimeout(() => { this.waitForVP(); }, this.options.pollingInterval)); } this.pollingTimer = setTimeout(() => { this.waitForVP(); }, this.options.pollingInterval); } catch (t) { if ("CORS" === t.type) return this.stopPolling(), this.callbacks.onMessage("请求失败(JS5001),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onError({ message: "请求失败(JS5001),请联系您当前要登录的平台方进行处理" }), void this.callbacks.onLoading(!1); this.pollingTimer = setTimeout(() => { this.waitForVP(); }, this.options.pollingInterval); } } } stopPolling() { this.isPolling = !1, this.pollingTimer && (clearTimeout(this.pollingTimer), this.pollingTimer = null); } async fetchWithErrorHandling(t, i = {}, s = "") { const o = new AbortController, e = setTimeout(() => { o.abort(); }, this.options.requestTimeout); try { const s = await fetch(t, { ...i, signal: o.signal }); if (clearTimeout(e), !s.ok) { throw { message: "请求失败(JS1004),请联系您当前要登录的平台方进行处理", data: { message: "请求失败", data: s, url: t } }; } return await s.json(); } catch (i) { clearTimeout(e); let o = s || "请求失败", n = "error"; const a = i?.message || i?.toString() || String(i), r = i?.name || ""; "AbortError" === r || "TimeoutError" === r ? (o = `请求超时,请检查网络连接或稍后重试,请求地址:${t}`, n = "error") : "TypeError" === r && (a.includes("Failed to fetch") || a.includes("NetworkError") || a.includes("Network request failed")) ? (o = `网络请求失败,可能是跨域问题或服务器未响应,请检查网络连接和服务器配置,请求地址:${t}`, n = "CORS") : a.includes("CORS") ? (o = `跨域请求被阻止,请检查服务器CORS配置或联系管理员,请求地址:${t}`, n = "CORS") : a && "[object Object]" !== a ? (o = `${a},请求地址:${t}`, n = "error") : o = `${o},请求地址:${t}`; const l = { message: o, originalError: i, status: i?.status, type: n, url: t }; throw this.callbacks.onMessage && this.callbacks.onMessage(o, n), l; } } async apiGetAuthResult(t) { const i = `${this.options.dnpServerUrl}${this.options.apiEndpoints.getAuthResult}?uuid=${t}`; return this.fetchWithErrorHandling(i, { method: "GET", headers: { "Content-Type": "application/json" } }, "身份认证失败(JS1004),联系您当前要登录的平台方进行处理"); } async apiGetToken(t) { const i = `${this.options.dnpServerUrl}${this.options.apiEndpoints.getToken}?uuid=${t}`; return this.fetchWithErrorHandling(i, { method: "GET", headers: { "Content-Type": "application/json" } }, "获取令牌失败(JS2002),请联系您当前要登录的平台方进行处理"); } async createCertificate() { try { const t = `${this.options.dnpServerUrl}${this.options.apiEndpoints.createCert}`; return await this.fetchWithErrorHandling(t, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ certificate: this.options.certificate }) }, "身份注册失败(JS3008),请联系您当前要登录的平台方进行处理"); } catch (t) { throw this.callbacks.onError(t), this.callbacks.onLoading(!1), t; } } async registerIdentity(t = {}) { try { if (this.stopPolling(), this.callbacks.onLoading(!0), !t.certType) { const t = { message: "身份注册失败(JS3001),请联系您当前要登录的平台方进行处理", data: { message: "缺少必需参数:certType" } }; throw this.callbacks.onError(t), this.callbacks.onMessage("身份注册失败(JS3001),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), t; } if (![ "1", "2", "3" ].includes(t.certType)) { const i = { message: "身份注册失败(JS3002),请联系您当前要登录的平台方进行处理", data: { message: 'certType 参数值无效,必须是 "1"、"2" 或 "3"', data: t.certType } }; throw this.callbacks.onError(i), this.callbacks.onMessage("身份注册失败(JS3002),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), i; } if (!t.subject) { const i = { message: "身份注册失败(JS3003),请联系您当前要登录的平台方进行处理", data: { message: "缺少必需参数:subject", data: t.subject } }; throw this.callbacks.onError(i), this.callbacks.onMessage("身份注册失败(JS3003),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), i; } let i; try { if (i = "string" == typeof t.subject ? JSON.parse(t.subject) : t.subject, Array.isArray(i)) { const t = {}; i.forEach(i => { i && "object" == typeof i && i.name && i.value && (t[i.name] = i.value); }), i = t; } } catch (i) { const s = { message: "身份注册失败(JS3004),请联系您当前要登录的平台方进行处理", data: { message: "subject 参数必须是有效的 JSON 字符串格式或数组格式", data: t.subject } }; throw this.callbacks.onError(s), this.callbacks.onMessage("身份注册失败(JS3004),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), s; } if (!i.O) { const i = { message: "身份注册失败(JS3005),请联系您当前要登录的平台方进行处理", data: { message: "subject 中缺少必填字段:O(企业名称)", data: t.subject } }; throw this.callbacks.onError(i), this.callbacks.onMessage("身份注册失败(JS3005),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), i; } if (!i.C) { const i = { message: "身份注册失败(JS3006),请联系您当前要登录的平台方进行处理", data: { message: "subject 中缺少必填字段:C(国家代码)", data: t.subject } }; throw this.callbacks.onError(i), this.callbacks.onMessage("身份注册失败(JS3006),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), i; } if (("2" === t.certType || "3" === t.certType) && !i.OU) { const i = { message: "身份注册失败(JS3007),请联系您当前要登录的平台方进行处理", data: { message: "subject 中缺少必填字段:OU(所属主体的标识)。连接器身份和业务节点身份必须提供 OU 字段", data: t.subject } }; throw this.callbacks.onError(i), this.callbacks.onMessage("身份注册失败(JS3007),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onLoading(!1), i; } const s = "string" == typeof t.subject ? t.subject : JSON.stringify(i); await this.invokeDesktopAppRegister(t.certType, s, t.didMethod); } catch (t) { this.callbacks.onError(t), this.callbacks.onLoading(!1); } } async invokeDesktopAppRegister(i, s, o) { this.uuidString = t().slice(0, 32); const e = { uuid: this.uuidString, certType: i, subject: s }; o && (e.didMethod = o), "electron" !== this.options.clientType ? this.invokeDesktopApp(`dnp://genKeys?json=${JSON.stringify(e)}`, () => this.invokeDesktopAppRegisterFlow()) : "function" == typeof window.sendToMain ? (window.sendToMain({ type: "register", data: e }), this.invokeDesktopAppRegisterFlow()) : (this.callbacks.onMessage("身份注册暂时不可用,请使用其他方式", "error"), this.callbacks.onLoading(!1)); } invokeDesktopAppRegisterFlow() { this.stopPolling(), this.startTime = Date.now(), this.isPolling = !0, this.waitForDid(); } async waitForDid() { if (this.isPolling) { if (Date.now() - this.startTime >= this.options.pollingTimeout) return this.stopPolling(), this.callbacks.onMessage("身份注册超时", "error"), void this.callbacks.onLoading(!1); try { const t = await this.apiGetCert("uuid", this.uuidString); if (t) { if ("refuse" === t) return this.stopPolling(), this.callbacks.onMessage("客户端正忙,请稍后再试", "error"), void this.callbacks.onLoading(!1); if (t.data) { this.stopPolling(); const i = t.data; return this.callbacks.onSuccess(i), void this.callbacks.onLoading(!1); } } Date.now() - this.startTime < this.options.pollingTimeout ? this.pollingTimer = setTimeout(() => { this.waitForDid(); }, this.options.pollingInterval) : (this.stopPolling(), this.callbacks.onMessage("身份注册超时", "error"), this.callbacks.onLoading(!1)); } catch (t) { if ("CORS" === t.type) return this.stopPolling(), this.callbacks.onMessage("请求失败(JS5001),请联系您当前要登录的平台方进行处理", "error"), this.callbacks.onError({ message: "请求失败(JS5001),请联系您当前要登录的平台方进行处理" }), void this.callbacks.onLoading(!1); Date.now() - this.startTime < this.options.pollingTimeout ? this.pollingTimer = setTimeout(() => { this.waitForDid(); }, this.options.pollingInterval) : (this.stopPolling(), this.callbacks.onMessage("身份注册超时", "error"), this.callbacks.onLoading(!1)); } } } async apiGetCert(t, i) { const s = `${this.options.dnpServerUrl}${this.options.apiEndpoints.getCert}?key=${t}&code=${i}`; return this.fetchWithErrorHandling(s, { method: "GET", headers: { "Content-Type": "application/json" } }, "身份注册失败(JS3008),请联系您当前要登录的平台方进行处理"); } async importCert(t) { try { this.stopPolling(), this.callbacks.onLoading(!0), await this.invokeDesktopAppImportVc(t); } catch (t) { this.callbacks.onError(t), this.callbacks.onLoading(!1); } } async invokeDesktopAppImportVc(t) { "electron" !== this.options.clientType ? this.invokeDesktopApp("dnp://import-vc", () => this.invokeDesktopAppImportVcFlow(t)) : "function" == typeof window.sendToMain ? (window.sendToMain({ type: "importCert", data: t }), this.invokeDesktopAppImportVcFlow(t)) : (this.callbacks.onMessage("身份凭证导入暂时不可用,请使用其他方式", "error"), this.callbacks.onLoading(!1)); } invokeDesktopAppImportVcFlow(t) { this.stopPolling(), this.startTime = Date.now(), this.isPolling = !0, this.pushVCToClient(t); } async pushVCToClient(t) { if (this.isPolling) { if (Date.now() - this.startTime >= this.options.pollingTimeout) return this.stopPolling(), this.callbacks.onMessage("VC导入超时", "error"), void this.callbacks.onLoading(!1); try { const i = await this.apiPushCert(t); if (i) { if ("refuse" === i) return this.stopPolling(), this.callbacks.onMessage("客户端正忙,请稍后再试", "error"), void this.callbacks.onLoading(!1); if (i.data) return this.stopPolling(), this.callbacks.onMessage("VC导入成功", "success"), this.callbacks.onSuccess(i), void this.callbacks.onLoading(!1); } Date.now() - this.startTime < this.options.pollingTimeout ? this.pollingTimer = setTimeout(() => { this.isPolling && this.pushVCToClient(t); }, this.options.pollingInterval) : (this.stopPolling(), this.callbacks.onMessage("VC导入超时", "error"), this.callbacks.onLoading(!1)); } catch (i) { Date.now() - this.startTime < this.options.pollingTimeout ? this.pollingTimer = setTimeout(() => { this.isPolling && this.pushVCToClient(t); }, this.options.pollingInterval) : (this.stopPolling(), this.callbacks.onMessage("VC导入失败", "error"), this.callbacks.onLoading(!1)); } } } async apiPushCert(t) { const i = `${this.options.dnpServerUrl}${this.options.apiEndpoints.importCert}`; return this.fetchWithErrorHandling(i, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(t) }, "身份凭证导入失败(JS4003),请联系您当前要登录的平台方进行处理"); } destroy() { this.stopPolling(), this.callbacks = {}; } } function createDnpLogin(t = {}) { return new i(t); } function createFetchToken(t = {}) { return new i(t); } function createDnpRegister(t = {}) { return new i(t); } function createDnpImportVc(t = {}) { return new i(t); } export { createDnpImportVc, createDnpLogin, createDnpRegister, createFetchToken, i as default };