hifun-tools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init/TenantManager.d.ts +23 -0
- package/dist/init/TenantManager.js +94 -0
- package/dist/init/getResource.d.ts +5 -0
- package/dist/init/getResource.js +44 -0
- package/dist/init/index.d.ts +29 -0
- package/dist/init/index.js +140 -0
- package/dist/init/utils.d.ts +1 -0
- package/dist/init/utils.js +62 -0
- package/dist/request/index.d.ts +2 -0
- package/dist/request/index.js +101 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/init/index.d.ts +6 -0
- package/dist/src/init/index.js +7 -0
- package/dist/src/request/index.d.ts +1 -0
- package/dist/src/request/index.js +6 -0
- package/dist/src/tenant/index.d.ts +5 -0
- package/dist/src/tenant/index.js +7 -0
- package/dist/tenant/index.d.ts +5 -0
- package/dist/tenant/index.js +5 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.js +75 -0
- package/package.json +21 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type TenantConfig = {
|
|
2
|
+
PUBLIC_TITLE: string;
|
|
3
|
+
SITE_TITLE: string;
|
|
4
|
+
SITE_URL: string;
|
|
5
|
+
USER_TENANT: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class TenantManagerClass {
|
|
8
|
+
private tenantConfig;
|
|
9
|
+
private initialized;
|
|
10
|
+
private static instance;
|
|
11
|
+
tenant: string | null;
|
|
12
|
+
constructor();
|
|
13
|
+
/** 获取配置 */
|
|
14
|
+
getConfig(): TenantConfig | null;
|
|
15
|
+
/** 获取租户名 */
|
|
16
|
+
getTenant(): string;
|
|
17
|
+
getImgPath(imgName: string): string;
|
|
18
|
+
/** 严格同步获取租户信息 */
|
|
19
|
+
private getTenantInfoStrictSync;
|
|
20
|
+
/** 严格初始化(必须在应用启动时调用) */
|
|
21
|
+
initialize(): void;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getResource } from "./getResource";
|
|
2
|
+
export class TenantManagerClass {
|
|
3
|
+
tenantConfig = null;
|
|
4
|
+
initialized = false;
|
|
5
|
+
static instance;
|
|
6
|
+
tenant = null;
|
|
7
|
+
constructor() {
|
|
8
|
+
if (TenantManagerClass.instance) {
|
|
9
|
+
return TenantManagerClass.instance;
|
|
10
|
+
}
|
|
11
|
+
TenantManagerClass.instance = this;
|
|
12
|
+
}
|
|
13
|
+
/** 获取配置 */
|
|
14
|
+
getConfig() {
|
|
15
|
+
return this.tenantConfig;
|
|
16
|
+
}
|
|
17
|
+
/** 获取租户名 */
|
|
18
|
+
getTenant() {
|
|
19
|
+
if (!this.initialized || !this.tenant) {
|
|
20
|
+
throw new Error("租户尚未初始化或初始化失败");
|
|
21
|
+
}
|
|
22
|
+
return this.tenant;
|
|
23
|
+
}
|
|
24
|
+
getImgPath(imgName) {
|
|
25
|
+
if (!this.initialized || !this.tenant) {
|
|
26
|
+
throw new Error("租户尚未初始化或初始化失败");
|
|
27
|
+
}
|
|
28
|
+
return getResource(`${this.tenant}/image/${imgName}`);
|
|
29
|
+
}
|
|
30
|
+
/** 严格同步获取租户信息 */
|
|
31
|
+
getTenantInfoStrictSync() {
|
|
32
|
+
const xhr = new XMLHttpRequest();
|
|
33
|
+
xhr.open("GET", "/lineTenants.txt?t=" + Date.now(), false);
|
|
34
|
+
xhr.send(null);
|
|
35
|
+
if (xhr.status === 200) {
|
|
36
|
+
try {
|
|
37
|
+
const rawData = atob(xhr.responseText);
|
|
38
|
+
const data = JSON.parse(rawData);
|
|
39
|
+
const host = location.host;
|
|
40
|
+
const matchedEntry = Object.entries(data).find(([key]) => {
|
|
41
|
+
// key 是明文主机地址,直接比较即可
|
|
42
|
+
// 兼容 www 前缀的情况
|
|
43
|
+
// 如果当前 host 有 www 前缀,则也匹配无 www 前缀的配置
|
|
44
|
+
// 如果当前 host 无 www 前缀,则也匹配有 www 前缀的配置
|
|
45
|
+
return (key === host ||
|
|
46
|
+
(host.startsWith("www.") && key === host.substring(4)) ||
|
|
47
|
+
(!host.startsWith("www.") && key === "www." + host));
|
|
48
|
+
});
|
|
49
|
+
if (matchedEntry) {
|
|
50
|
+
const matched = matchedEntry[1];
|
|
51
|
+
if (data && matched) {
|
|
52
|
+
console.info(`🏠 匹配租户: ${matched}`);
|
|
53
|
+
return { tenant: matched };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error("解析 lineTenants.txt 失败:", error);
|
|
59
|
+
if (location.host.includes("iggame") ||
|
|
60
|
+
location.host.includes("localhost")) {
|
|
61
|
+
console.info(`🏠 iggame匹配到默认租户`);
|
|
62
|
+
return {
|
|
63
|
+
tenant: "iggame",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
throw new Error("lineTenants.txt 格式错误");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (location.host.includes("iggame") ||
|
|
70
|
+
location.host.includes("localhost")) {
|
|
71
|
+
console.info(`🏠 iggame匹配到默认租户`);
|
|
72
|
+
return {
|
|
73
|
+
tenant: "iggame",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
throw new Error("无法获取有效的租户信息");
|
|
77
|
+
}
|
|
78
|
+
/** 严格初始化(必须在应用启动时调用) */
|
|
79
|
+
initialize() {
|
|
80
|
+
if (this.initialized)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
const tenantInfo = this.getTenantInfoStrictSync();
|
|
84
|
+
this.tenant = tenantInfo.tenant;
|
|
85
|
+
this.tenantConfig = getResource(`${tenantInfo.tenant}/config.json`);
|
|
86
|
+
this.initialized = true;
|
|
87
|
+
console.info(`🏢 严格加载租户成功: ${this.tenant}`, this.getImgPath("logo-default.png"));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.error("严格租户加载失败:", error);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用资源加载函数(支持 Vite 与 Webpack)
|
|
3
|
+
* @param path - 相对路径,如 "tenant/config.json" 或 "tenant/logo.png"
|
|
4
|
+
*/
|
|
5
|
+
export function getResource(path) {
|
|
6
|
+
const isVite = typeof import.meta !== "undefined" && !!import.meta.env;
|
|
7
|
+
const isWebpack = typeof __webpack_require__ === "function" ||
|
|
8
|
+
(typeof require !== "undefined" &&
|
|
9
|
+
typeof require.context === "function");
|
|
10
|
+
if (isVite) {
|
|
11
|
+
// ---------------- VITE ----------------
|
|
12
|
+
const modules = import.meta.glob([
|
|
13
|
+
"/config/**/*.json",
|
|
14
|
+
"/config/**/*.png",
|
|
15
|
+
"/config/**/*.jpg",
|
|
16
|
+
"/config/**/*.jpeg",
|
|
17
|
+
"/config/**/*.svg",
|
|
18
|
+
"/config/**/*.webp",
|
|
19
|
+
], {
|
|
20
|
+
eager: true,
|
|
21
|
+
import: "default",
|
|
22
|
+
});
|
|
23
|
+
const match = Object.entries(modules).find(([key]) => key.endsWith(path));
|
|
24
|
+
if (!match) {
|
|
25
|
+
console.warn(`[getResource] 未找到文件: ${path}`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const [, mod] = match;
|
|
29
|
+
return mod;
|
|
30
|
+
}
|
|
31
|
+
if (isWebpack) {
|
|
32
|
+
// ---------------- WEBPACK ----------------
|
|
33
|
+
const ctx = require.context("/config", true, /\.(json|png|jpg|jpeg|svg|webp)$/);
|
|
34
|
+
const files = ctx.keys();
|
|
35
|
+
const match = files.find((key) => key.endsWith(path));
|
|
36
|
+
if (!match) {
|
|
37
|
+
console.warn(`[getResource] 未找到文件: ${path}`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return ctx(match);
|
|
41
|
+
}
|
|
42
|
+
console.error("[getResource] 不支持的构建环境(需要 Vite 或 Webpack)");
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface InitConfig {
|
|
2
|
+
fileType: ("lineAddress" | "lineTenants")[];
|
|
3
|
+
}
|
|
4
|
+
type TenantConfig = {
|
|
5
|
+
PUBLIC_TITLE: string;
|
|
6
|
+
SITE_TITLE: string;
|
|
7
|
+
SITE_URL: string;
|
|
8
|
+
USER_TENANT: string;
|
|
9
|
+
};
|
|
10
|
+
declare class InitCls {
|
|
11
|
+
private domainBaseUrl;
|
|
12
|
+
private tenantConfig;
|
|
13
|
+
private initialized;
|
|
14
|
+
private tenant;
|
|
15
|
+
getBaseUrl(): string;
|
|
16
|
+
/** 获取配置 */
|
|
17
|
+
getTenantConfig(): TenantConfig | null;
|
|
18
|
+
/** 获取租户名 */
|
|
19
|
+
getTenant(): string;
|
|
20
|
+
getImgPath(imgName: string): string;
|
|
21
|
+
/** 严格同步获取租户信息 */
|
|
22
|
+
private getTenantInfoStrictSync;
|
|
23
|
+
/** 严格初始化(必须在应用启动时调用) */
|
|
24
|
+
private initialize;
|
|
25
|
+
InitConfig({ fileType }: InitConfig): void;
|
|
26
|
+
private LoadGatewayConfig;
|
|
27
|
+
}
|
|
28
|
+
declare const HF: InitCls;
|
|
29
|
+
export { HF };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { getResource } from "./getResource";
|
|
2
|
+
import { getOptimalDecodedString } from "./utils";
|
|
3
|
+
class InitCls {
|
|
4
|
+
domainBaseUrl = "";
|
|
5
|
+
tenantConfig = null;
|
|
6
|
+
initialized = false;
|
|
7
|
+
tenant = null;
|
|
8
|
+
getBaseUrl() {
|
|
9
|
+
if (!this.initialized || !this.domainBaseUrl) {
|
|
10
|
+
throw new Error("域名尚未初始化或初始化失败");
|
|
11
|
+
}
|
|
12
|
+
return this.domainBaseUrl;
|
|
13
|
+
}
|
|
14
|
+
/** 获取配置 */
|
|
15
|
+
getTenantConfig() {
|
|
16
|
+
if (!this.initialized || !this.tenant) {
|
|
17
|
+
throw new Error("租户尚未初始化或初始化失败");
|
|
18
|
+
}
|
|
19
|
+
return this.tenantConfig;
|
|
20
|
+
}
|
|
21
|
+
/** 获取租户名 */
|
|
22
|
+
getTenant() {
|
|
23
|
+
if (!this.initialized || !this.tenant) {
|
|
24
|
+
throw new Error("租户尚未初始化或初始化失败");
|
|
25
|
+
}
|
|
26
|
+
return this.tenant;
|
|
27
|
+
}
|
|
28
|
+
getImgPath(imgName) {
|
|
29
|
+
if (!this.initialized || !this.tenant) {
|
|
30
|
+
throw new Error("租户尚未初始化或初始化失败");
|
|
31
|
+
}
|
|
32
|
+
return getResource(`${this.tenant}/image/${imgName}`);
|
|
33
|
+
}
|
|
34
|
+
/** 严格同步获取租户信息 */
|
|
35
|
+
getTenantInfoStrictSync() {
|
|
36
|
+
const xhr = new XMLHttpRequest();
|
|
37
|
+
xhr.open("GET", "/lineTenants.txt?t=" + Date.now(), false);
|
|
38
|
+
xhr.send(null);
|
|
39
|
+
if (xhr.status === 200) {
|
|
40
|
+
try {
|
|
41
|
+
const rawData = atob(xhr.responseText);
|
|
42
|
+
const data = JSON.parse(rawData);
|
|
43
|
+
const host = location.host;
|
|
44
|
+
const matchedEntry = Object.entries(data).find(([key]) => {
|
|
45
|
+
// key 是明文主机地址,直接比较即可
|
|
46
|
+
// 兼容 www 前缀的情况
|
|
47
|
+
// 如果当前 host 有 www 前缀,则也匹配无 www 前缀的配置
|
|
48
|
+
// 如果当前 host 无 www 前缀,则也匹配有 www 前缀的配置
|
|
49
|
+
return (key === host ||
|
|
50
|
+
(host.startsWith("www.") && key === host.substring(4)) ||
|
|
51
|
+
(!host.startsWith("www.") && key === "www." + host));
|
|
52
|
+
});
|
|
53
|
+
if (matchedEntry) {
|
|
54
|
+
const matched = matchedEntry[1];
|
|
55
|
+
if (data && matched) {
|
|
56
|
+
console.info(`🏠 匹配租户: ${matched}`);
|
|
57
|
+
return { tenant: matched };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error("解析 lineTenants.txt 失败:", error);
|
|
63
|
+
if (location.host.includes("iggame") ||
|
|
64
|
+
location.host.includes("localhost")) {
|
|
65
|
+
console.info(`🏠 iggame匹配到默认租户`);
|
|
66
|
+
return {
|
|
67
|
+
tenant: "iggame",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
throw new Error("lineTenants.txt 格式错误");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (location.host.includes("iggame") ||
|
|
74
|
+
location.host.includes("localhost")) {
|
|
75
|
+
console.info(`🏠 iggame匹配到默认租户`);
|
|
76
|
+
return {
|
|
77
|
+
tenant: "iggame",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
throw new Error("无法获取有效的租户信息");
|
|
81
|
+
}
|
|
82
|
+
/** 严格初始化(必须在应用启动时调用) */
|
|
83
|
+
initialize() {
|
|
84
|
+
if (this.initialized)
|
|
85
|
+
return;
|
|
86
|
+
try {
|
|
87
|
+
const tenantInfo = this.getTenantInfoStrictSync();
|
|
88
|
+
this.tenant = tenantInfo.tenant;
|
|
89
|
+
this.tenantConfig = getResource(`${tenantInfo.tenant}/config.json`);
|
|
90
|
+
this.initialized = true;
|
|
91
|
+
console.info(`🏢 严格加载租户成功: ${this.tenant}`, this.getImgPath("logo-default.png"));
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error("严格租户加载失败:", error);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
InitConfig({ fileType }) {
|
|
99
|
+
console.log("加载类型:", fileType);
|
|
100
|
+
if (fileType.includes("lineAddress")) {
|
|
101
|
+
console.log("初始化 lineAddress...");
|
|
102
|
+
this.LoadGatewayConfig();
|
|
103
|
+
}
|
|
104
|
+
if (fileType.includes("lineTenants")) {
|
|
105
|
+
console.log("初始化 lineTenants...");
|
|
106
|
+
this.initialize();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async LoadGatewayConfig() {
|
|
110
|
+
if (this.domainBaseUrl)
|
|
111
|
+
return this.domainBaseUrl;
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(`/lineAddress.txt?t=${Date.now()}`);
|
|
114
|
+
const configText = await response.text();
|
|
115
|
+
const baseUrl = JSON.parse(atob(configText));
|
|
116
|
+
console.log("baseUrlbaseUrl", baseUrl);
|
|
117
|
+
if (Array.isArray(baseUrl)) {
|
|
118
|
+
try {
|
|
119
|
+
this.domainBaseUrl = await getOptimalDecodedString(baseUrl);
|
|
120
|
+
console.log("✅ 成功加载生产环境配置(测速选择):", this.domainBaseUrl);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.domainBaseUrl = baseUrl[0];
|
|
124
|
+
console.warn("⚠️ 测速失败,使用第一个配置:", this.domainBaseUrl, error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.domainBaseUrl = baseUrl;
|
|
129
|
+
console.log("✅ 成功加载生产环境配置:", this.domainBaseUrl);
|
|
130
|
+
}
|
|
131
|
+
return this.domainBaseUrl;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.warn("⚠️ 加载 lineAddress.txt 失败:", error);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const HF = new InitCls();
|
|
140
|
+
export { HF };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getOptimalDecodedString(encodedArray: string[]): Promise<string>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function isIPv4(str) {
|
|
2
|
+
if (str.startsWith("http://") || str.startsWith("https://")) {
|
|
3
|
+
try {
|
|
4
|
+
const url = new URL(str);
|
|
5
|
+
str = url.hostname;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (str === "localhost")
|
|
12
|
+
return false;
|
|
13
|
+
const ipv4Regex = /^(25[0-5]|2[0-4]\d|1?\d{1,2})(\.(25[0-5]|2[0-4]\d|1?\d{1,2})){3}$/;
|
|
14
|
+
return ipv4Regex.test(str);
|
|
15
|
+
}
|
|
16
|
+
export async function getOptimalDecodedString(encodedArray) {
|
|
17
|
+
if (!encodedArray?.length)
|
|
18
|
+
throw new Error("输入数组为空或无效");
|
|
19
|
+
const hostname = window.location.hostname;
|
|
20
|
+
const isIp = isIPv4(hostname);
|
|
21
|
+
// ✅ 修正:Base64 解码后再过滤
|
|
22
|
+
const decodedList = encodedArray.filter(Boolean);
|
|
23
|
+
const ipList = decodedList.filter((v) => isIPv4(v));
|
|
24
|
+
const domainList = decodedList.filter((v) => !isIPv4(v));
|
|
25
|
+
const getOptimal = (arr) => new Promise((resolve) => {
|
|
26
|
+
if (!arr.length) {
|
|
27
|
+
resolve("");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const currentProtocol = window.location.protocol;
|
|
31
|
+
const filteredArr = arr.filter((url) => url.startsWith("http") ? url.startsWith(currentProtocol) : true);
|
|
32
|
+
const targetArr = filteredArr.length ? filteredArr : arr;
|
|
33
|
+
if (targetArr.length === 1)
|
|
34
|
+
return resolve(targetArr[0]);
|
|
35
|
+
let hasResolved = false;
|
|
36
|
+
targetArr.forEach((url) => {
|
|
37
|
+
const testUrl = (url.startsWith("http")
|
|
38
|
+
? `${url}/health`
|
|
39
|
+
: `${currentProtocol}//${url}/health`) + `?t=${Date.now()}`;
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timeoutId = setTimeout(() => {
|
|
42
|
+
controller.abort();
|
|
43
|
+
}, 3000);
|
|
44
|
+
fetch(testUrl, { method: "GET", signal: controller.signal })
|
|
45
|
+
.then(() => {
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
if (!hasResolved) {
|
|
48
|
+
hasResolved = true;
|
|
49
|
+
resolve(url);
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
.catch(() => clearTimeout(timeoutId));
|
|
53
|
+
});
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
if (!hasResolved) {
|
|
56
|
+
hasResolved = true;
|
|
57
|
+
resolve(targetArr[0]);
|
|
58
|
+
}
|
|
59
|
+
}, 3500);
|
|
60
|
+
});
|
|
61
|
+
return isIp ? await getOptimal(ipList) : await getOptimal(domainList);
|
|
62
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import md5 from "md5";
|
|
2
|
+
import { Local, getParams, isMobile } from "../utils/index";
|
|
3
|
+
import { HF } from "../init";
|
|
4
|
+
/** 生成随机 UDID */
|
|
5
|
+
function generateUdid() {
|
|
6
|
+
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
7
|
+
return Array.from({ length: 32 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join("");
|
|
8
|
+
}
|
|
9
|
+
/** 获取或生成 Finger(UDID) */
|
|
10
|
+
async function getFinger() {
|
|
11
|
+
const udidFromUrl = getParams("udid");
|
|
12
|
+
const localFinger = Local("finger");
|
|
13
|
+
if (udidFromUrl) {
|
|
14
|
+
// 若 URL 参数 udid 与本地不一致,清除旧的密钥缓存
|
|
15
|
+
if (localFinger && localFinger !== udidFromUrl) {
|
|
16
|
+
localStorage.removeItem("SkeyData");
|
|
17
|
+
}
|
|
18
|
+
Local("finger", udidFromUrl);
|
|
19
|
+
Local("isAppUdid", "1");
|
|
20
|
+
return udidFromUrl;
|
|
21
|
+
}
|
|
22
|
+
if (localFinger)
|
|
23
|
+
return localFinger;
|
|
24
|
+
const newId = generateUdid();
|
|
25
|
+
Local("finger", newId);
|
|
26
|
+
return newId;
|
|
27
|
+
}
|
|
28
|
+
/** 通用 HTTP 请求封装 */
|
|
29
|
+
export async function HttpServer(endpoint, params = {}, method = "GET") {
|
|
30
|
+
const udid = await getFinger();
|
|
31
|
+
const baseUrl = HF.getBaseUrl();
|
|
32
|
+
const fullUrl = new URL(endpoint, baseUrl).toString();
|
|
33
|
+
const timestamp = Date.now();
|
|
34
|
+
const lang = getParams("lang") || "YN";
|
|
35
|
+
// 基础请求体
|
|
36
|
+
const data = {
|
|
37
|
+
...params,
|
|
38
|
+
language: lang,
|
|
39
|
+
timestamp,
|
|
40
|
+
sign: md5(udid + "jgyh,kasd" + timestamp),
|
|
41
|
+
paySign: md5(udid.substring(0, 6) + "8qiezi" + timestamp),
|
|
42
|
+
currentUserAppVersion: "2.0.5",
|
|
43
|
+
channel: "",
|
|
44
|
+
version: "1.0.0",
|
|
45
|
+
os: isMobile() ? "5" : "6",
|
|
46
|
+
platForm: isMobile() ? "h5" : "pc",
|
|
47
|
+
deviceType: "1",
|
|
48
|
+
udid,
|
|
49
|
+
};
|
|
50
|
+
// Header
|
|
51
|
+
const headers = {
|
|
52
|
+
"X-UDID": udid,
|
|
53
|
+
"X-Timestamp": String(timestamp),
|
|
54
|
+
"X-Language": lang,
|
|
55
|
+
"X-Sign": data.sign,
|
|
56
|
+
"Accept-Language": lang,
|
|
57
|
+
tenantSys: HF.getTenant(),
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
os: data.os,
|
|
60
|
+
};
|
|
61
|
+
// 附加 URL 参数(如 puid、id、token)
|
|
62
|
+
const allParams = getParams();
|
|
63
|
+
Object.entries(allParams).forEach(([k, v]) => {
|
|
64
|
+
if (v)
|
|
65
|
+
headers[k] = encodeURIComponent(String(v));
|
|
66
|
+
});
|
|
67
|
+
const puid = getParams("puid") || getParams("id");
|
|
68
|
+
if (puid)
|
|
69
|
+
headers.puid = puid;
|
|
70
|
+
const token = Local("token") || getParams("token");
|
|
71
|
+
if (token) {
|
|
72
|
+
headers.Authorization = `HSBox ${token}`;
|
|
73
|
+
data.token = token;
|
|
74
|
+
}
|
|
75
|
+
// 拼接请求
|
|
76
|
+
try {
|
|
77
|
+
const url = method === "GET"
|
|
78
|
+
? `${fullUrl}${Object.keys(params).length
|
|
79
|
+
? "?" +
|
|
80
|
+
new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString()
|
|
81
|
+
: ""}`
|
|
82
|
+
: fullUrl;
|
|
83
|
+
const res = await fetch(url, {
|
|
84
|
+
method,
|
|
85
|
+
headers,
|
|
86
|
+
...(method !== "GET" && { body: JSON.stringify(data) }),
|
|
87
|
+
});
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
const json = JSON.parse(text);
|
|
90
|
+
if (json.code === 0 || json.code === 200) {
|
|
91
|
+
return json.data;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
throw new Error(json.msg || "Request failed");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
console.error("[HttpServer Error]", err);
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function request(url: string, options?: RequestInit): Promise<any>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function Local(name: string, value?: any, time?: number): any;
|
|
2
|
+
/**
|
|
3
|
+
* 获取链接参数
|
|
4
|
+
* @param name
|
|
5
|
+
* @returns 传参获取单个值,或者获取全部object
|
|
6
|
+
*/
|
|
7
|
+
export declare function getParams(name?: string): any;
|
|
8
|
+
export declare function isMobile(): boolean;
|
|
9
|
+
export declare function isIOS(): boolean;
|
|
10
|
+
export declare function isAndroid(): boolean;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export function Local(name, value, time) {
|
|
2
|
+
const date = Number(new Date().getTime() / 1000);
|
|
3
|
+
if (value === null) {
|
|
4
|
+
localStorage.removeItem(name);
|
|
5
|
+
}
|
|
6
|
+
else if (value !== undefined) {
|
|
7
|
+
if (time) {
|
|
8
|
+
localStorage.setItem(name, JSON.stringify({ time: date + time, value }));
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
localStorage.setItem(name, JSON.stringify({ value }));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
const v = localStorage.getItem(name);
|
|
16
|
+
if (v) {
|
|
17
|
+
if (JSON.parse(v).time) {
|
|
18
|
+
if (JSON.parse(v).time - date > 0) {
|
|
19
|
+
return JSON.parse(v).value;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
localStorage.removeItem(name);
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return JSON.parse(v).value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 获取链接参数
|
|
37
|
+
* @param name
|
|
38
|
+
* @returns 传参获取单个值,或者获取全部object
|
|
39
|
+
*/
|
|
40
|
+
export function getParams(name) {
|
|
41
|
+
const pa = location.search.replace("?", "").split("&");
|
|
42
|
+
const arr = pa.reduce((sum, item) => {
|
|
43
|
+
const sj = item.split("=");
|
|
44
|
+
sum[sj[0]] = sj[1];
|
|
45
|
+
return sum;
|
|
46
|
+
}, {});
|
|
47
|
+
if (name == "puid")
|
|
48
|
+
return arr.id || arr.puid;
|
|
49
|
+
return name ? arr[name] : arr;
|
|
50
|
+
}
|
|
51
|
+
export function isMobile() {
|
|
52
|
+
const ua = window.navigator.userAgent;
|
|
53
|
+
if (ua.match(/(Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone|micromessenger)/)) {
|
|
54
|
+
if (/iPhone|iPad|iPod/.test(ua)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
else if (/Android/.test(ua)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 判断是ios
|
|
69
|
+
export function isIOS() {
|
|
70
|
+
return /(iPhone|iPad|iPod)/i.test(navigator.userAgent);
|
|
71
|
+
}
|
|
72
|
+
// 判断是android
|
|
73
|
+
export function isAndroid() {
|
|
74
|
+
return /(Android)/i.test(navigator.userAgent);
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hifun-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"publish": "npm run build && npm publish --access public"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"md5": "^2.3.0",
|
|
19
|
+
"typescript": "^5.6.3"
|
|
20
|
+
}
|
|
21
|
+
}
|