@alibarbar/common 1.1.1 → 1.1.3
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/README.md +10 -0
- package/dist/crypto.cjs +184 -248
- package/dist/crypto.d.mts +50 -103
- package/dist/crypto.d.ts +50 -103
- package/dist/crypto.js +170 -234
- package/dist/{index-DchqyDBQ.d.mts → index-CqWtZgzO.d.mts} +1 -1
- package/dist/{index-DchqyDBQ.d.ts → index-CqWtZgzO.d.ts} +1 -1
- package/dist/index.cjs +432 -951
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +423 -930
- package/dist/services.cjs +82 -60
- package/dist/services.d.mts +1 -1
- package/dist/services.d.ts +1 -1
- package/dist/services.js +82 -60
- package/dist/storage.cjs +331 -854
- package/dist/storage.d.mts +58 -128
- package/dist/storage.d.ts +58 -128
- package/dist/storage.js +323 -848
- package/dist/upload.d.mts +1 -1
- package/dist/upload.d.ts +1 -1
- package/package.json +32 -27
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var JSEncrypt = require('jsencrypt');
|
|
4
|
+
var CryptoJS = require('crypto-js');
|
|
3
5
|
var Qs = require('qs');
|
|
4
6
|
var axios = require('axios');
|
|
5
7
|
|
|
6
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
9
|
|
|
10
|
+
var JSEncrypt__default = /*#__PURE__*/_interopDefault(JSEncrypt);
|
|
11
|
+
var CryptoJS__default = /*#__PURE__*/_interopDefault(CryptoJS);
|
|
8
12
|
var Qs__default = /*#__PURE__*/_interopDefault(Qs);
|
|
9
13
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
10
14
|
|
|
@@ -769,8 +773,8 @@ function parseUrl(url) {
|
|
|
769
773
|
}
|
|
770
774
|
}
|
|
771
775
|
function buildUrl(parts) {
|
|
772
|
-
const { protocol = "https:", host = "", pathname = "/", search = "", hash
|
|
773
|
-
return `${protocol}//${host}${pathname}${search}${
|
|
776
|
+
const { protocol = "https:", host = "", pathname = "/", search = "", hash = "" } = parts;
|
|
777
|
+
return `${protocol}//${host}${pathname}${search}${hash}`;
|
|
774
778
|
}
|
|
775
779
|
function getQueryParams(url) {
|
|
776
780
|
const searchParams = url ? new URL(url).searchParams : new URLSearchParams(window.location.search);
|
|
@@ -1500,14 +1504,6 @@ async function uploadFile(file, options) {
|
|
|
1500
1504
|
const uploader = createUploader(file, options);
|
|
1501
1505
|
return uploader.upload();
|
|
1502
1506
|
}
|
|
1503
|
-
|
|
1504
|
-
// src/helper/crypto/index.ts
|
|
1505
|
-
async function sha256(data) {
|
|
1506
|
-
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1507
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
1508
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1509
|
-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1510
|
-
}
|
|
1511
1507
|
function base64Encode(data) {
|
|
1512
1508
|
if (typeof data === "string") {
|
|
1513
1509
|
return btoa(unescape(encodeURIComponent(data)));
|
|
@@ -1519,967 +1515,444 @@ function base64Encode(data) {
|
|
|
1519
1515
|
}
|
|
1520
1516
|
return btoa(binary);
|
|
1521
1517
|
}
|
|
1522
|
-
function
|
|
1523
|
-
|
|
1524
|
-
return decodeURIComponent(escape(atob(data)));
|
|
1525
|
-
} catch {
|
|
1526
|
-
throw new Error("Invalid Base64 string");
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
function generateUUID() {
|
|
1530
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
1531
|
-
return crypto.randomUUID();
|
|
1532
|
-
}
|
|
1533
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
1534
|
-
const r = Math.random() * 16 | 0;
|
|
1535
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
1536
|
-
return v.toString(16);
|
|
1537
|
-
});
|
|
1518
|
+
function generateRandomAESKeyString() {
|
|
1519
|
+
return CryptoJS__default.default.lib.WordArray.random(256 / 8).toString();
|
|
1538
1520
|
}
|
|
1539
|
-
function
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1521
|
+
function encryptJsonWithAES(data, aesKey) {
|
|
1522
|
+
try {
|
|
1523
|
+
const jsonString = JSON.stringify(data);
|
|
1524
|
+
return CryptoJS__default.default.AES.encrypt(jsonString, aesKey).toString();
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
throw new Error(
|
|
1527
|
+
"[encryption] AES encryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
|
|
1528
|
+
);
|
|
1543
1529
|
}
|
|
1544
|
-
return result;
|
|
1545
1530
|
}
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1531
|
+
function decryptJsonWithAES(encryptedData, aesKey) {
|
|
1532
|
+
try {
|
|
1533
|
+
const decrypted = CryptoJS__default.default.AES.decrypt(encryptedData, aesKey);
|
|
1534
|
+
const jsonString = decrypted.toString(CryptoJS__default.default.enc.Utf8);
|
|
1535
|
+
if (!jsonString) {
|
|
1536
|
+
throw new Error("decrypted JSON string is empty");
|
|
1537
|
+
}
|
|
1538
|
+
return JSON.parse(jsonString);
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
throw new Error(
|
|
1541
|
+
"[encryption] AES decryptJsonWithAES failed: " + (error instanceof Error ? error.message : String(error))
|
|
1542
|
+
);
|
|
1552
1543
|
}
|
|
1553
|
-
return Math.abs(hashValue);
|
|
1554
1544
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
{
|
|
1561
|
-
name: "RSA-OAEP",
|
|
1562
|
-
modulusLength,
|
|
1563
|
-
publicExponent: new Uint8Array([1, 0, 1]),
|
|
1564
|
-
hash: "SHA-256"
|
|
1565
|
-
},
|
|
1566
|
-
true,
|
|
1567
|
-
["encrypt", "decrypt"]
|
|
1568
|
-
);
|
|
1569
|
-
return {
|
|
1570
|
-
publicKey: keyPair.publicKey,
|
|
1571
|
-
privateKey: keyPair.privateKey
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
async function rsaEncrypt(data, publicKey) {
|
|
1575
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1576
|
-
throw new Error("Web Crypto API is not available");
|
|
1577
|
-
}
|
|
1578
|
-
const encoder = new TextEncoder();
|
|
1579
|
-
const dataBuffer = encoder.encode(data);
|
|
1580
|
-
const maxChunkSize = 245;
|
|
1581
|
-
const chunks = [];
|
|
1582
|
-
for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {
|
|
1583
|
-
const chunk2 = dataBuffer.slice(i, i + maxChunkSize);
|
|
1584
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
1585
|
-
{
|
|
1586
|
-
name: "RSA-OAEP"
|
|
1587
|
-
},
|
|
1588
|
-
publicKey,
|
|
1589
|
-
chunk2
|
|
1545
|
+
function getEnvPublicKey() {
|
|
1546
|
+
const key = undefined.VITE_RSA_PUBLIC_KEY?.trim();
|
|
1547
|
+
if (!key) {
|
|
1548
|
+
throw new Error(
|
|
1549
|
+
"[encryption] VITE_RSA_PUBLIC_KEY is not set. Please configure RSA public key in environment variables."
|
|
1590
1550
|
);
|
|
1591
|
-
chunks.push(encrypted);
|
|
1592
1551
|
}
|
|
1593
|
-
|
|
1594
|
-
const merged = new Uint8Array(totalLength);
|
|
1595
|
-
let offset = 0;
|
|
1596
|
-
for (const chunk2 of chunks) {
|
|
1597
|
-
merged.set(new Uint8Array(chunk2), offset);
|
|
1598
|
-
offset += chunk2.byteLength;
|
|
1599
|
-
}
|
|
1600
|
-
return base64Encode(merged.buffer);
|
|
1552
|
+
return key;
|
|
1601
1553
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
const encryptedArray = new Uint8Array(binaryString.length);
|
|
1608
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
1609
|
-
encryptedArray[i] = binaryString.charCodeAt(i);
|
|
1610
|
-
}
|
|
1611
|
-
const chunkSize = 256;
|
|
1612
|
-
const chunks = [];
|
|
1613
|
-
for (let i = 0; i < encryptedArray.length; i += chunkSize) {
|
|
1614
|
-
const chunk2 = encryptedArray.slice(i, i + chunkSize);
|
|
1615
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
1616
|
-
{
|
|
1617
|
-
name: "RSA-OAEP"
|
|
1618
|
-
},
|
|
1619
|
-
privateKey,
|
|
1620
|
-
chunk2
|
|
1554
|
+
function getEnvPrivateKey() {
|
|
1555
|
+
const key = undefined.VITE_RSA_PRIVATE_KEY?.trim();
|
|
1556
|
+
if (!key) {
|
|
1557
|
+
throw new Error(
|
|
1558
|
+
"[encryption] VITE_RSA_PRIVATE_KEY is not set. Please configure RSA private key in environment variables."
|
|
1621
1559
|
);
|
|
1622
|
-
const decoder = new TextDecoder();
|
|
1623
|
-
chunks.push(decoder.decode(decrypted));
|
|
1624
1560
|
}
|
|
1625
|
-
return
|
|
1561
|
+
return key;
|
|
1626
1562
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1563
|
+
function base64ToPem(base64Key, type) {
|
|
1564
|
+
const chunks = [];
|
|
1565
|
+
for (let i = 0; i < base64Key.length; i += 64) {
|
|
1566
|
+
chunks.push(base64Key.slice(i, i + 64));
|
|
1630
1567
|
}
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1568
|
+
const body = chunks.join("\n");
|
|
1569
|
+
const header = type === "PUBLIC" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
|
|
1570
|
+
const footer = type === "PUBLIC" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
|
|
1571
|
+
return `${header}
|
|
1572
|
+
${body}
|
|
1573
|
+
${footer}`;
|
|
1633
1574
|
}
|
|
1634
|
-
async function
|
|
1635
|
-
|
|
1636
|
-
|
|
1575
|
+
async function rsaEncrypt(plain, publicKeyPem) {
|
|
1576
|
+
try {
|
|
1577
|
+
const encrypt = new JSEncrypt__default.default();
|
|
1578
|
+
let publicKeyString;
|
|
1579
|
+
if (typeof publicKeyPem === "string") {
|
|
1580
|
+
const trimmedKey = publicKeyPem.trim();
|
|
1581
|
+
if (!trimmedKey) {
|
|
1582
|
+
throw new Error("Public key string is empty");
|
|
1583
|
+
}
|
|
1584
|
+
if (trimmedKey.includes("-----BEGIN")) {
|
|
1585
|
+
publicKeyString = trimmedKey;
|
|
1586
|
+
} else {
|
|
1587
|
+
publicKeyString = base64ToPem(trimmedKey, "PUBLIC");
|
|
1588
|
+
}
|
|
1589
|
+
} else if (publicKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
|
|
1590
|
+
const base64Key = publicKeyPem.toString();
|
|
1591
|
+
publicKeyString = base64ToPem(base64Key, "PUBLIC");
|
|
1592
|
+
} else {
|
|
1593
|
+
try {
|
|
1594
|
+
const exported = await crypto.subtle.exportKey("spki", publicKeyPem);
|
|
1595
|
+
const base64Key = base64Encode(exported);
|
|
1596
|
+
publicKeyString = base64ToPem(base64Key, "PUBLIC");
|
|
1597
|
+
} catch (error) {
|
|
1598
|
+
throw new Error(
|
|
1599
|
+
"Failed to export CryptoKey. In non-HTTPS environment, use string publicKey (Base64 or PEM) directly. " + (error instanceof Error ? error.message : String(error))
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
encrypt.setPublicKey(publicKeyString);
|
|
1604
|
+
const MAX_ENCRYPT_LENGTH = 200;
|
|
1605
|
+
if (plain.length > MAX_ENCRYPT_LENGTH) {
|
|
1606
|
+
const chunks = [];
|
|
1607
|
+
for (let i = 0; i < plain.length; i += MAX_ENCRYPT_LENGTH) {
|
|
1608
|
+
const chunk2 = plain.slice(i, i + MAX_ENCRYPT_LENGTH);
|
|
1609
|
+
const encrypted2 = encrypt.encrypt(chunk2);
|
|
1610
|
+
if (!encrypted2) {
|
|
1611
|
+
throw new Error(
|
|
1612
|
+
`RSA encryption failed for chunk ${i / MAX_ENCRYPT_LENGTH + 1}. Public key may be invalid or JSEncrypt may not be loaded correctly.`
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
chunks.push(encrypted2);
|
|
1616
|
+
}
|
|
1617
|
+
return chunks.join("|");
|
|
1618
|
+
}
|
|
1619
|
+
const encrypted = encrypt.encrypt(plain);
|
|
1620
|
+
if (!encrypted) {
|
|
1621
|
+
throw new Error(
|
|
1622
|
+
`RSA encryption failed. Public key may be invalid or JSEncrypt may not be loaded correctly. Plain text length: ${plain.length} bytes.`
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
return encrypted;
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
if (error instanceof Error) {
|
|
1628
|
+
if (error.message.includes("RSA encryption failed") || error.message.includes("Public key")) {
|
|
1629
|
+
throw error;
|
|
1630
|
+
}
|
|
1631
|
+
throw new Error(`[encryption] rsaEncrypt failed: ${error.message}`);
|
|
1632
|
+
}
|
|
1633
|
+
throw new Error(`[encryption] rsaEncrypt failed: ${String(error)}`);
|
|
1637
1634
|
}
|
|
1638
|
-
const exported = await crypto.subtle.exportKey("pkcs8", privateKey);
|
|
1639
|
-
return base64Encode(exported);
|
|
1640
1635
|
}
|
|
1641
|
-
async function
|
|
1642
|
-
|
|
1643
|
-
|
|
1636
|
+
async function rsaDecrypt(cipher, privateKeyPem) {
|
|
1637
|
+
const decrypt = new JSEncrypt__default.default();
|
|
1638
|
+
let privateKeyString;
|
|
1639
|
+
if (typeof privateKeyPem === "string") {
|
|
1640
|
+
if (privateKeyPem.includes("-----BEGIN")) {
|
|
1641
|
+
privateKeyString = privateKeyPem.trim();
|
|
1642
|
+
} else {
|
|
1643
|
+
const trimmed = privateKeyPem.trim();
|
|
1644
|
+
privateKeyString = base64ToPem(trimmed, "PRIVATE");
|
|
1645
|
+
}
|
|
1646
|
+
} else if (privateKeyPem instanceof CryptoJS__default.default.lib.WordArray) {
|
|
1647
|
+
const base64Key = privateKeyPem.toString();
|
|
1648
|
+
privateKeyString = base64ToPem(base64Key, "PRIVATE");
|
|
1649
|
+
} else {
|
|
1650
|
+
try {
|
|
1651
|
+
const exported = await crypto.subtle.exportKey("pkcs8", privateKeyPem);
|
|
1652
|
+
const base64Key = base64Encode(exported);
|
|
1653
|
+
privateKeyString = base64ToPem(base64Key, "PRIVATE");
|
|
1654
|
+
} catch (error) {
|
|
1655
|
+
throw new Error(
|
|
1656
|
+
"[encryption] rsaDecrypt: failed to export CryptoKey. In non-HTTPS environment, use PEM string directly."
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1644
1659
|
}
|
|
1645
|
-
|
|
1646
|
-
const
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
{
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
true,
|
|
1655
|
-
["encrypt"]
|
|
1656
|
-
);
|
|
1657
|
-
}
|
|
1658
|
-
async function importPrivateKey(keyData) {
|
|
1659
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1660
|
-
throw new Error("Web Crypto API is not available");
|
|
1660
|
+
decrypt.setPrivateKey(privateKeyString);
|
|
1661
|
+
const chunks = cipher.split("|");
|
|
1662
|
+
const decryptedChunks = [];
|
|
1663
|
+
for (const chunk2 of chunks) {
|
|
1664
|
+
const decrypted = decrypt.decrypt(chunk2);
|
|
1665
|
+
if (!decrypted) {
|
|
1666
|
+
throw new Error("[encryption] rsaDecrypt: RSA decryption failed");
|
|
1667
|
+
}
|
|
1668
|
+
decryptedChunks.push(decrypted);
|
|
1661
1669
|
}
|
|
1662
|
-
|
|
1663
|
-
const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
|
|
1664
|
-
return crypto.subtle.importKey(
|
|
1665
|
-
"pkcs8",
|
|
1666
|
-
keyArray.buffer,
|
|
1667
|
-
{
|
|
1668
|
-
name: "RSA-OAEP",
|
|
1669
|
-
hash: "SHA-256"
|
|
1670
|
-
},
|
|
1671
|
-
true,
|
|
1672
|
-
["decrypt"]
|
|
1673
|
-
);
|
|
1670
|
+
return decryptedChunks.join("");
|
|
1674
1671
|
}
|
|
1675
|
-
async function
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
}
|
|
1679
|
-
return crypto.subtle.generateKey(
|
|
1680
|
-
{
|
|
1681
|
-
name: "HMAC",
|
|
1682
|
-
hash: "SHA-256"
|
|
1683
|
-
},
|
|
1684
|
-
true,
|
|
1685
|
-
["sign", "verify"]
|
|
1686
|
-
);
|
|
1672
|
+
async function encryptWithEnvPublicKey(plain) {
|
|
1673
|
+
const publicKey = getEnvPublicKey();
|
|
1674
|
+
return rsaEncrypt(plain, publicKey);
|
|
1687
1675
|
}
|
|
1688
|
-
async function
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
}
|
|
1692
|
-
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1693
|
-
const signature = await crypto.subtle.sign("HMAC", key, buffer);
|
|
1694
|
-
return base64Encode(signature);
|
|
1676
|
+
async function decryptWithEnvPrivateKey(cipher) {
|
|
1677
|
+
const privateKey = getEnvPrivateKey();
|
|
1678
|
+
return rsaDecrypt(cipher, privateKey);
|
|
1695
1679
|
}
|
|
1696
|
-
async function
|
|
1680
|
+
async function generateRSAKeyPair(modulusLength = 2048) {
|
|
1697
1681
|
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1698
|
-
throw new Error(
|
|
1699
|
-
|
|
1700
|
-
try {
|
|
1701
|
-
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1702
|
-
const signatureBuffer = new Uint8Array(
|
|
1703
|
-
atob(signature).split("").map((char) => char.charCodeAt(0))
|
|
1682
|
+
throw new Error(
|
|
1683
|
+
"[encryption] Web Crypto API is not available. Cannot generate RSA key pair. Please use fixed key pair from environment variables."
|
|
1704
1684
|
);
|
|
1705
|
-
return await crypto.subtle.verify("HMAC", key, signatureBuffer, dataBuffer);
|
|
1706
|
-
} catch {
|
|
1707
|
-
return false;
|
|
1708
1685
|
}
|
|
1709
|
-
|
|
1710
|
-
async function deriveKeyFromPassword(password, salt, iterations = 1e5, keyLength = 256) {
|
|
1711
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1712
|
-
throw new Error("Web Crypto API is not available");
|
|
1713
|
-
}
|
|
1714
|
-
const saltBuffer = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
|
|
1715
|
-
const passwordKey = await crypto.subtle.importKey(
|
|
1716
|
-
"raw",
|
|
1717
|
-
new TextEncoder().encode(password),
|
|
1718
|
-
"PBKDF2",
|
|
1719
|
-
false,
|
|
1720
|
-
["deriveBits", "deriveKey"]
|
|
1721
|
-
);
|
|
1722
|
-
return crypto.subtle.deriveKey(
|
|
1686
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
1723
1687
|
{
|
|
1724
|
-
name: "
|
|
1725
|
-
|
|
1726
|
-
|
|
1688
|
+
name: "RSA-OAEP",
|
|
1689
|
+
modulusLength,
|
|
1690
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
1727
1691
|
hash: "SHA-256"
|
|
1728
1692
|
},
|
|
1729
|
-
|
|
1730
|
-
{
|
|
1731
|
-
name: "AES-GCM",
|
|
1732
|
-
length: keyLength
|
|
1733
|
-
},
|
|
1734
|
-
false,
|
|
1693
|
+
true,
|
|
1735
1694
|
["encrypt", "decrypt"]
|
|
1736
1695
|
);
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
}
|
|
1742
|
-
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1743
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1744
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
1745
|
-
{
|
|
1746
|
-
name: "AES-GCM",
|
|
1747
|
-
iv
|
|
1748
|
-
},
|
|
1749
|
-
key,
|
|
1750
|
-
dataBuffer
|
|
1751
|
-
);
|
|
1696
|
+
const publicKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
|
1697
|
+
const privateKeyDer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
1698
|
+
const publicKeyBase64 = base64Encode(publicKeyDer);
|
|
1699
|
+
const privateKeyBase64 = base64Encode(privateKeyDer);
|
|
1752
1700
|
return {
|
|
1753
|
-
|
|
1754
|
-
|
|
1701
|
+
publicKey: base64ToPem(publicKeyBase64, "PUBLIC"),
|
|
1702
|
+
privateKey: base64ToPem(privateKeyBase64, "PRIVATE")
|
|
1755
1703
|
};
|
|
1756
1704
|
}
|
|
1757
|
-
async function aesGCMDecrypt(encryptedData, iv, key) {
|
|
1758
|
-
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
1759
|
-
throw new Error("Web Crypto API is not available");
|
|
1760
|
-
}
|
|
1761
|
-
const encryptedBuffer = new Uint8Array(
|
|
1762
|
-
atob(encryptedData).split("").map((char) => char.charCodeAt(0))
|
|
1763
|
-
);
|
|
1764
|
-
const ivBuffer = new Uint8Array(
|
|
1765
|
-
atob(iv).split("").map((char) => char.charCodeAt(0))
|
|
1766
|
-
);
|
|
1767
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
1768
|
-
{
|
|
1769
|
-
name: "AES-GCM",
|
|
1770
|
-
iv: ivBuffer
|
|
1771
|
-
},
|
|
1772
|
-
key,
|
|
1773
|
-
encryptedBuffer
|
|
1774
|
-
);
|
|
1775
|
-
return new TextDecoder().decode(decrypted);
|
|
1776
|
-
}
|
|
1777
1705
|
|
|
1778
1706
|
// src/browser/SecureStorage/index.ts
|
|
1779
|
-
var
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
var initOptions = {
|
|
1783
|
-
autoGenerateKeys: true,
|
|
1784
|
-
persistKeys: false,
|
|
1785
|
-
keyStorageKey: void 0,
|
|
1786
|
-
// 将自动生成随机键名
|
|
1787
|
-
keyEncryptionPassword: void 0,
|
|
1788
|
-
pbkdf2Iterations: 1e5,
|
|
1789
|
-
keyModulusLength: 2048,
|
|
1790
|
-
enableHMAC: true,
|
|
1791
|
-
enableTimestampValidation: true,
|
|
1792
|
-
timestampMaxAge: 7 * 24 * 60 * 60 * 1e3,
|
|
1793
|
-
// 7 天
|
|
1794
|
-
isProduction: false
|
|
1707
|
+
var CONFIG = {
|
|
1708
|
+
NAMESPACE: "sec_b_",
|
|
1709
|
+
DEFAULT_EXPIRE: null
|
|
1795
1710
|
};
|
|
1796
|
-
var
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
return `_sk_${generateRandomString(32)}_${Date.now()}`;
|
|
1801
|
-
}
|
|
1802
|
-
async function initializeStorageKeys(options = {}) {
|
|
1803
|
-
if (options.forceReinitialize) {
|
|
1804
|
-
globalKeyPair = null;
|
|
1805
|
-
globalHMACKey = null;
|
|
1806
|
-
keyPairInitialized = false;
|
|
1807
|
-
actualKeyStorageKey = null;
|
|
1808
|
-
keyUsageCount = 0;
|
|
1809
|
-
initializationPromise = null;
|
|
1810
|
-
} else if (keyPairInitialized && globalKeyPair) {
|
|
1811
|
-
initOptions = { ...initOptions, ...options };
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
initOptions = { ...initOptions, ...options };
|
|
1815
|
-
if (initOptions.keyStorageKey) {
|
|
1816
|
-
actualKeyStorageKey = initOptions.keyStorageKey;
|
|
1817
|
-
} else {
|
|
1818
|
-
actualKeyStorageKey = generateKeyStorageKey();
|
|
1819
|
-
}
|
|
1820
|
-
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
1821
|
-
try {
|
|
1822
|
-
const storedKeys = window.localStorage.getItem(actualKeyStorageKey);
|
|
1823
|
-
if (storedKeys) {
|
|
1824
|
-
const keyData = JSON.parse(storedKeys);
|
|
1825
|
-
if (keyData.encrypted && keyData.privateKeyEncrypted && keyData.salt) {
|
|
1826
|
-
if (!initOptions.keyEncryptionPassword) {
|
|
1827
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1828
|
-
console.error("Encrypted keys found but no password provided");
|
|
1829
|
-
}
|
|
1830
|
-
throw new Error("Password required to decrypt stored keys");
|
|
1831
|
-
}
|
|
1832
|
-
const saltArray = new Uint8Array(
|
|
1833
|
-
atob(keyData.salt).split("").map((char) => char.charCodeAt(0))
|
|
1834
|
-
);
|
|
1835
|
-
const iterations = keyData.pbkdf2Iterations || initOptions.pbkdf2Iterations;
|
|
1836
|
-
const derivedKey = await deriveKeyFromPassword(
|
|
1837
|
-
initOptions.keyEncryptionPassword,
|
|
1838
|
-
saltArray.buffer,
|
|
1839
|
-
iterations
|
|
1840
|
-
);
|
|
1841
|
-
const decryptedPrivateKey = await aesGCMDecrypt(
|
|
1842
|
-
keyData.privateKeyEncrypted,
|
|
1843
|
-
keyData.iv,
|
|
1844
|
-
derivedKey
|
|
1845
|
-
);
|
|
1846
|
-
globalKeyPair = {
|
|
1847
|
-
publicKey: await importPublicKey(keyData.publicKey),
|
|
1848
|
-
privateKey: await importPrivateKey(decryptedPrivateKey)
|
|
1849
|
-
};
|
|
1850
|
-
} else if (keyData.publicKey && keyData.privateKey) {
|
|
1851
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1852
|
-
console.warn(
|
|
1853
|
-
"\u26A0\uFE0F SECURITY WARNING: Loading unencrypted keys from storage. Consider re-initializing with password encryption."
|
|
1854
|
-
);
|
|
1855
|
-
}
|
|
1856
|
-
globalKeyPair = {
|
|
1857
|
-
publicKey: await importPublicKey(keyData.publicKey),
|
|
1858
|
-
privateKey: await importPrivateKey(keyData.privateKey)
|
|
1859
|
-
};
|
|
1860
|
-
}
|
|
1861
|
-
if (globalKeyPair) {
|
|
1862
|
-
keyPairInitialized = true;
|
|
1863
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1864
|
-
globalHMACKey = await generateHMACKey();
|
|
1865
|
-
}
|
|
1866
|
-
return;
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
} catch (error) {
|
|
1870
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1871
|
-
console.warn("Failed to load persisted keys, will generate new ones");
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
if (initOptions.autoGenerateKeys && !globalKeyPair) {
|
|
1876
|
-
try {
|
|
1877
|
-
globalKeyPair = await generateRSAKeyPair(initOptions.keyModulusLength);
|
|
1878
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1879
|
-
globalHMACKey = await generateHMACKey();
|
|
1880
|
-
}
|
|
1881
|
-
keyPairInitialized = true;
|
|
1882
|
-
keyUsageCount = 0;
|
|
1883
|
-
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
1884
|
-
try {
|
|
1885
|
-
const publicKeyStr = await exportPublicKey(globalKeyPair.publicKey);
|
|
1886
|
-
if (initOptions.keyEncryptionPassword) {
|
|
1887
|
-
const privateKeyStr = await exportPrivateKey(globalKeyPair.privateKey);
|
|
1888
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
1889
|
-
const derivedKey = await deriveKeyFromPassword(
|
|
1890
|
-
initOptions.keyEncryptionPassword,
|
|
1891
|
-
salt.buffer,
|
|
1892
|
-
initOptions.pbkdf2Iterations
|
|
1893
|
-
);
|
|
1894
|
-
const { encrypted, iv } = await aesGCMEncrypt(privateKeyStr, derivedKey);
|
|
1895
|
-
const keyData = {
|
|
1896
|
-
encrypted: true,
|
|
1897
|
-
publicKey: publicKeyStr,
|
|
1898
|
-
privateKeyEncrypted: encrypted,
|
|
1899
|
-
iv,
|
|
1900
|
-
salt: base64Encode(salt.buffer),
|
|
1901
|
-
pbkdf2Iterations: initOptions.pbkdf2Iterations
|
|
1902
|
-
};
|
|
1903
|
-
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
1904
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.info) {
|
|
1905
|
-
console.info("\u2705 Keys encrypted and stored securely");
|
|
1906
|
-
}
|
|
1907
|
-
} else {
|
|
1908
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
1909
|
-
console.warn(
|
|
1910
|
-
"\u26A0\uFE0F SECURITY WARNING: Storing private keys without encryption! Private keys will be stored in plain text (Base64 encoded). Provide keyEncryptionPassword for secure storage."
|
|
1911
|
-
);
|
|
1912
|
-
}
|
|
1913
|
-
const keyData = {
|
|
1914
|
-
publicKey: publicKeyStr,
|
|
1915
|
-
privateKey: await exportPrivateKey(globalKeyPair.privateKey)
|
|
1916
|
-
};
|
|
1917
|
-
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
1918
|
-
}
|
|
1919
|
-
} catch (error) {
|
|
1920
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1921
|
-
console.error("Failed to persist keys");
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
} catch (error) {
|
|
1926
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1927
|
-
console.error("Failed to generate storage keys");
|
|
1928
|
-
}
|
|
1929
|
-
throw error;
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
1933
|
-
globalHMACKey = await generateHMACKey();
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
function setStorageKeyPair(keyPair) {
|
|
1937
|
-
globalKeyPair = keyPair;
|
|
1938
|
-
keyPairInitialized = true;
|
|
1939
|
-
}
|
|
1940
|
-
function getStorageKeyPair() {
|
|
1941
|
-
return globalKeyPair;
|
|
1942
|
-
}
|
|
1943
|
-
function clearPersistedKeys() {
|
|
1944
|
-
if (typeof window !== "undefined" && window.localStorage && actualKeyStorageKey) {
|
|
1945
|
-
try {
|
|
1946
|
-
window.localStorage.removeItem(actualKeyStorageKey);
|
|
1947
|
-
actualKeyStorageKey = null;
|
|
1948
|
-
keyPairInitialized = false;
|
|
1949
|
-
globalKeyPair = null;
|
|
1950
|
-
globalHMACKey = null;
|
|
1951
|
-
keyUsageCount = 0;
|
|
1952
|
-
} catch (error) {
|
|
1953
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
1954
|
-
console.error("Failed to clear persisted keys");
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
function getKeyUsageCount() {
|
|
1960
|
-
return keyUsageCount;
|
|
1961
|
-
}
|
|
1962
|
-
function resetKeyUsageCount() {
|
|
1963
|
-
keyUsageCount = 0;
|
|
1964
|
-
}
|
|
1965
|
-
async function ensureKeyPair() {
|
|
1966
|
-
if (globalKeyPair) {
|
|
1967
|
-
keyUsageCount++;
|
|
1968
|
-
return;
|
|
1969
|
-
}
|
|
1970
|
-
if (!initOptions.autoGenerateKeys) {
|
|
1971
|
-
return;
|
|
1972
|
-
}
|
|
1973
|
-
if (initializationPromise) {
|
|
1974
|
-
await initializationPromise;
|
|
1975
|
-
if (globalKeyPair) {
|
|
1976
|
-
keyUsageCount++;
|
|
1711
|
+
var SecureStorage = class _SecureStorage {
|
|
1712
|
+
static get instances() {
|
|
1713
|
+
if (!this._instances) {
|
|
1714
|
+
this._instances = /* @__PURE__ */ new Map();
|
|
1977
1715
|
}
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
initializationPromise = initializeStorageKeys();
|
|
1981
|
-
try {
|
|
1982
|
-
await initializationPromise;
|
|
1983
|
-
} finally {
|
|
1984
|
-
initializationPromise = null;
|
|
1985
|
-
}
|
|
1986
|
-
if (globalKeyPair) {
|
|
1987
|
-
keyUsageCount++;
|
|
1716
|
+
return this._instances;
|
|
1988
1717
|
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
if (expectedType === "array" && !Array.isArray(parsed)) {
|
|
1997
|
-
throw new Error("Expected array but got different type");
|
|
1998
|
-
}
|
|
1999
|
-
return parsed;
|
|
2000
|
-
} catch (error) {
|
|
2001
|
-
if (error instanceof SyntaxError) {
|
|
2002
|
-
throw new Error(`Invalid JSON format: ${error.message}`);
|
|
1718
|
+
constructor(options) {
|
|
1719
|
+
const storageType = options.storage === window.sessionStorage ? "session" : "local";
|
|
1720
|
+
const namespace = options.namespace || CONFIG.NAMESPACE;
|
|
1721
|
+
const instanceKey = `${storageType}_${namespace}`;
|
|
1722
|
+
const existingInstance = _SecureStorage.instances.get(instanceKey);
|
|
1723
|
+
if (existingInstance) {
|
|
1724
|
+
return existingInstance;
|
|
2003
1725
|
}
|
|
2004
|
-
|
|
1726
|
+
this.namespace = namespace;
|
|
1727
|
+
this.storage = options.storage || window.localStorage;
|
|
1728
|
+
this.defaultExpire = options.defaultExpire ?? CONFIG.DEFAULT_EXPIRE;
|
|
1729
|
+
this.instanceKey = instanceKey;
|
|
1730
|
+
_SecureStorage.instances.set(instanceKey, this);
|
|
2005
1731
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
if (initOptions.timestampMaxAge === 0) {
|
|
2012
|
-
return true;
|
|
2013
|
-
}
|
|
2014
|
-
const now = Date.now();
|
|
2015
|
-
const age = now - timestamp;
|
|
2016
|
-
const maxAge = initOptions.timestampMaxAge || 7 * 24 * 60 * 60 * 1e3;
|
|
2017
|
-
if (age < -maxClockSkew) {
|
|
2018
|
-
return false;
|
|
2019
|
-
}
|
|
2020
|
-
return age >= -maxClockSkew && age <= maxAge;
|
|
2021
|
-
}
|
|
2022
|
-
async function encryptValue(value, publicKey, hmacKey) {
|
|
2023
|
-
const encryptedData = await rsaEncrypt(value, publicKey);
|
|
2024
|
-
if (hmacKey) {
|
|
2025
|
-
const hmac = await computeHMAC(encryptedData, hmacKey);
|
|
2026
|
-
const item = {
|
|
2027
|
-
data: encryptedData,
|
|
2028
|
-
hmac,
|
|
2029
|
-
encrypted: true,
|
|
2030
|
-
timestamp: Date.now()
|
|
2031
|
-
};
|
|
2032
|
-
return JSON.stringify(item);
|
|
2033
|
-
}
|
|
2034
|
-
return encryptedData;
|
|
2035
|
-
}
|
|
2036
|
-
async function handleDecryptError(error, encryptedStr, key) {
|
|
2037
|
-
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
2038
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
2039
|
-
console.error(
|
|
2040
|
-
"\u26A0\uFE0F SECURITY ALERT: Data integrity check failed! Data may have been tampered with."
|
|
2041
|
-
);
|
|
2042
|
-
}
|
|
2043
|
-
throw error;
|
|
1732
|
+
/**
|
|
1733
|
+
* 获取单例实例(静态方法)
|
|
1734
|
+
*/
|
|
1735
|
+
static getInstance(options) {
|
|
1736
|
+
return new _SecureStorage(options);
|
|
2044
1737
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
1738
|
+
/**
|
|
1739
|
+
* 清除所有单例实例(用于测试或重置)
|
|
1740
|
+
*/
|
|
1741
|
+
static clearInstances() {
|
|
1742
|
+
_SecureStorage.instances.clear();
|
|
2050
1743
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
);
|
|
2057
|
-
}
|
|
2058
|
-
return decryptedStr;
|
|
2059
|
-
} catch {
|
|
2060
|
-
throw error;
|
|
1744
|
+
/**
|
|
1745
|
+
* 获取当前所有实例的数量(用于调试)
|
|
1746
|
+
*/
|
|
1747
|
+
static getInstanceCount() {
|
|
1748
|
+
return _SecureStorage.instances.size;
|
|
2061
1749
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
console.warn(
|
|
2068
|
-
`\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}" without encryption keys.`
|
|
2069
|
-
);
|
|
2070
|
-
}
|
|
2071
|
-
return decryptedStr;
|
|
2072
|
-
} catch (error) {
|
|
2073
|
-
throw new Error(`Failed to decode storage value for key "${key}"`);
|
|
1750
|
+
/**
|
|
1751
|
+
* 获取完整键名(带命名空间)
|
|
1752
|
+
*/
|
|
1753
|
+
_getFullKey(key) {
|
|
1754
|
+
return `${this.namespace}${key}`;
|
|
2074
1755
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
if (
|
|
2080
|
-
|
|
2081
|
-
const isValid = await verifyHMAC(item.data, item.hmac, hmacKey);
|
|
2082
|
-
if (!isValid) {
|
|
2083
|
-
throw new Error("HMAC verification failed: data may have been tampered with");
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
if (item.timestamp && !validateTimestamp(item.timestamp)) {
|
|
2087
|
-
throw new Error("Data timestamp validation failed: data may be expired or replayed");
|
|
2088
|
-
}
|
|
2089
|
-
return await rsaDecrypt(item.data, privateKey);
|
|
2090
|
-
}
|
|
2091
|
-
return await rsaDecrypt(encryptedValue, privateKey);
|
|
2092
|
-
} catch (error) {
|
|
2093
|
-
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
2094
|
-
throw error;
|
|
2095
|
-
}
|
|
2096
|
-
throw new Error("Failed to decrypt storage value: invalid format or corrupted data");
|
|
1756
|
+
/**
|
|
1757
|
+
* 检查数据是否过期
|
|
1758
|
+
*/
|
|
1759
|
+
_isExpired(expire) {
|
|
1760
|
+
if (!expire) return false;
|
|
1761
|
+
return Date.now() > expire;
|
|
2097
1762
|
}
|
|
2098
|
-
}
|
|
2099
|
-
var localStorage = {
|
|
2100
1763
|
/**
|
|
2101
1764
|
* 设置值
|
|
2102
|
-
* @param key - 键
|
|
2103
|
-
* @param value - 值
|
|
2104
|
-
* @param options - 选项(过期时间、密钥等)
|
|
2105
|
-
* @returns Promise<void>
|
|
2106
1765
|
*/
|
|
2107
|
-
async set(key, value,
|
|
2108
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
2109
|
-
throw new Error("localStorage is not available");
|
|
2110
|
-
}
|
|
2111
|
-
const { expiry, publicKey } = options;
|
|
2112
|
-
const item = {
|
|
2113
|
-
value,
|
|
2114
|
-
expiry: expiry ? Date.now() + expiry : void 0
|
|
2115
|
-
};
|
|
1766
|
+
async set(key, value, expire = this.defaultExpire) {
|
|
2116
1767
|
try {
|
|
2117
|
-
const
|
|
2118
|
-
|
|
2119
|
-
const
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
1768
|
+
const fullKey = this._getFullKey(key);
|
|
1769
|
+
const aesKey = generateRandomAESKeyString();
|
|
1770
|
+
const storageData = {
|
|
1771
|
+
value,
|
|
1772
|
+
expire: expire ? Date.now() + expire : null,
|
|
1773
|
+
timestamp: Date.now()
|
|
1774
|
+
};
|
|
1775
|
+
const encryptedData = encryptJsonWithAES(storageData, aesKey);
|
|
1776
|
+
const encryptedKey = await encryptWithEnvPublicKey(aesKey);
|
|
1777
|
+
const finalData = {
|
|
1778
|
+
data: encryptedData,
|
|
1779
|
+
key: encryptedKey,
|
|
1780
|
+
version: "1.0"
|
|
1781
|
+
};
|
|
1782
|
+
this.storage.setItem(fullKey, JSON.stringify(finalData));
|
|
1783
|
+
return true;
|
|
2132
1784
|
} catch (error) {
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
}
|
|
2136
|
-
throw error;
|
|
1785
|
+
console.error("[SecureStorage] \u4FDD\u5B58\u5931\u8D25:", error);
|
|
1786
|
+
return false;
|
|
2137
1787
|
}
|
|
2138
|
-
}
|
|
1788
|
+
}
|
|
2139
1789
|
/**
|
|
2140
1790
|
* 获取值
|
|
2141
|
-
* @param key - 键
|
|
2142
|
-
* @param options - 选项(默认值、私钥等)
|
|
2143
|
-
* @returns Promise<T | undefined> 值或默认值
|
|
2144
1791
|
*/
|
|
2145
|
-
async get(key,
|
|
2146
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
2147
|
-
return options.defaultValue;
|
|
2148
|
-
}
|
|
2149
|
-
const { defaultValue, privateKey } = options;
|
|
1792
|
+
async get(key, defaultValue = null) {
|
|
2150
1793
|
try {
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
const keyToUse = privateKey || globalKeyPair?.privateKey;
|
|
2156
|
-
if (keyToUse) {
|
|
2157
|
-
try {
|
|
2158
|
-
decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
|
|
2159
|
-
} catch (error) {
|
|
2160
|
-
try {
|
|
2161
|
-
decryptedStr = await handleDecryptError(error, encryptedStr, key);
|
|
2162
|
-
} catch {
|
|
2163
|
-
return defaultValue;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
} else {
|
|
2167
|
-
try {
|
|
2168
|
-
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
2169
|
-
} catch {
|
|
2170
|
-
return defaultValue;
|
|
2171
|
-
}
|
|
1794
|
+
const fullKey = this._getFullKey(key);
|
|
1795
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1796
|
+
if (!storedData) {
|
|
1797
|
+
return defaultValue;
|
|
2172
1798
|
}
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2175
|
-
|
|
1799
|
+
const finalData = JSON.parse(storedData);
|
|
1800
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1801
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1802
|
+
if (this._isExpired(storageData.expire)) {
|
|
1803
|
+
this.remove(key);
|
|
2176
1804
|
return defaultValue;
|
|
2177
1805
|
}
|
|
2178
|
-
return
|
|
1806
|
+
return storageData.value;
|
|
2179
1807
|
} catch (error) {
|
|
2180
|
-
|
|
2181
|
-
console.warn(`Failed to parse storage item for key "${key}"`);
|
|
2182
|
-
}
|
|
1808
|
+
console.error("[SecureStorage] \u8BFB\u53D6\u5931\u8D25:", error);
|
|
2183
1809
|
return defaultValue;
|
|
2184
1810
|
}
|
|
2185
|
-
}
|
|
1811
|
+
}
|
|
2186
1812
|
/**
|
|
2187
|
-
*
|
|
2188
|
-
* @param key - 键
|
|
1813
|
+
* 删除值
|
|
2189
1814
|
*/
|
|
2190
1815
|
remove(key) {
|
|
2191
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
2192
1816
|
try {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
console.error("localStorage.remove error");
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
},
|
|
2200
|
-
/**
|
|
2201
|
-
* 清空所有值
|
|
2202
|
-
*/
|
|
2203
|
-
clear() {
|
|
2204
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
2205
|
-
try {
|
|
2206
|
-
window.localStorage.clear();
|
|
1817
|
+
const fullKey = this._getFullKey(key);
|
|
1818
|
+
this.storage.removeItem(fullKey);
|
|
1819
|
+
return true;
|
|
2207
1820
|
} catch (error) {
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
}
|
|
1821
|
+
console.error("[SecureStorage] \u79FB\u9664\u5931\u8D25:", error);
|
|
1822
|
+
return false;
|
|
2211
1823
|
}
|
|
2212
|
-
}
|
|
1824
|
+
}
|
|
2213
1825
|
/**
|
|
2214
|
-
*
|
|
2215
|
-
* @returns 键数组
|
|
1826
|
+
* 检查键是否存在
|
|
2216
1827
|
*/
|
|
2217
|
-
|
|
2218
|
-
if (typeof window === "undefined" || !window.localStorage) return [];
|
|
2219
|
-
const keys2 = [];
|
|
1828
|
+
async has(key) {
|
|
2220
1829
|
try {
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
1830
|
+
const fullKey = this._getFullKey(key);
|
|
1831
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1832
|
+
if (!storedData) return false;
|
|
1833
|
+
const finalData = JSON.parse(storedData);
|
|
1834
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1835
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1836
|
+
if (this._isExpired(storageData.expire)) {
|
|
1837
|
+
this.remove(key);
|
|
1838
|
+
return false;
|
|
2224
1839
|
}
|
|
1840
|
+
return true;
|
|
2225
1841
|
} catch (error) {
|
|
2226
|
-
|
|
2227
|
-
console.error("localStorage.keys error");
|
|
2228
|
-
}
|
|
1842
|
+
return false;
|
|
2229
1843
|
}
|
|
2230
|
-
return keys2;
|
|
2231
1844
|
}
|
|
2232
|
-
};
|
|
2233
|
-
var sessionStorage = {
|
|
2234
1845
|
/**
|
|
2235
|
-
*
|
|
2236
|
-
* @param key - 键
|
|
2237
|
-
* @param value - 值
|
|
2238
|
-
* @param options - 选项(公钥等)
|
|
2239
|
-
* @returns Promise<void>
|
|
1846
|
+
* 清空所有数据
|
|
2240
1847
|
*/
|
|
2241
|
-
|
|
2242
|
-
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
2243
|
-
throw new Error("sessionStorage is not available");
|
|
2244
|
-
}
|
|
2245
|
-
const { publicKey } = options;
|
|
1848
|
+
clear() {
|
|
2246
1849
|
try {
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
} else {
|
|
2254
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
2255
|
-
console.warn(
|
|
2256
|
-
`\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
|
|
2257
|
-
);
|
|
1850
|
+
let count = 0;
|
|
1851
|
+
const keys2 = [];
|
|
1852
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
1853
|
+
const key = this.storage.key(i);
|
|
1854
|
+
if (key && key.startsWith(this.namespace)) {
|
|
1855
|
+
keys2.push(key);
|
|
2258
1856
|
}
|
|
2259
|
-
const encoded = base64Encode(jsonStr);
|
|
2260
|
-
window.sessionStorage.setItem(key, encoded);
|
|
2261
1857
|
}
|
|
1858
|
+
keys2.forEach((key) => {
|
|
1859
|
+
this.storage.removeItem(key);
|
|
1860
|
+
count++;
|
|
1861
|
+
});
|
|
1862
|
+
return count;
|
|
2262
1863
|
} catch (error) {
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
}
|
|
2266
|
-
throw error;
|
|
1864
|
+
console.error("[SecureStorage] \u6E05\u7A7A\u5931\u8D25:", error);
|
|
1865
|
+
return 0;
|
|
2267
1866
|
}
|
|
2268
|
-
}
|
|
1867
|
+
}
|
|
2269
1868
|
/**
|
|
2270
|
-
*
|
|
2271
|
-
* @param key - 键
|
|
2272
|
-
* @param options - 选项(默认值、私钥等)
|
|
2273
|
-
* @returns Promise<T | undefined> 值或默认值
|
|
1869
|
+
* 获取所有键名
|
|
2274
1870
|
*/
|
|
2275
|
-
|
|
2276
|
-
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
2277
|
-
return options.defaultValue;
|
|
2278
|
-
}
|
|
2279
|
-
const { defaultValue, privateKey } = options;
|
|
1871
|
+
keys() {
|
|
2280
1872
|
try {
|
|
2281
|
-
const
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
if (keyToUse) {
|
|
2287
|
-
try {
|
|
2288
|
-
decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
|
|
2289
|
-
} catch (error) {
|
|
2290
|
-
try {
|
|
2291
|
-
decryptedStr = await handleDecryptError(error, encryptedStr, key);
|
|
2292
|
-
} catch {
|
|
2293
|
-
return defaultValue;
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
} else {
|
|
2297
|
-
try {
|
|
2298
|
-
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
2299
|
-
} catch {
|
|
2300
|
-
return defaultValue;
|
|
1873
|
+
const keys2 = [];
|
|
1874
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
1875
|
+
const key = this.storage.key(i);
|
|
1876
|
+
if (key && key.startsWith(this.namespace)) {
|
|
1877
|
+
keys2.push(key.substring(this.namespace.length));
|
|
2301
1878
|
}
|
|
2302
1879
|
}
|
|
2303
|
-
return
|
|
2304
|
-
} catch {
|
|
2305
|
-
return defaultValue;
|
|
2306
|
-
}
|
|
2307
|
-
},
|
|
2308
|
-
/**
|
|
2309
|
-
* 移除值
|
|
2310
|
-
* @param key - 键
|
|
2311
|
-
*/
|
|
2312
|
-
remove(key) {
|
|
2313
|
-
if (typeof window === "undefined" || !window.sessionStorage) return;
|
|
2314
|
-
try {
|
|
2315
|
-
window.sessionStorage.removeItem(key);
|
|
1880
|
+
return keys2;
|
|
2316
1881
|
} catch (error) {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
},
|
|
2322
|
-
/**
|
|
2323
|
-
* 清空所有值
|
|
2324
|
-
*/
|
|
2325
|
-
clear() {
|
|
2326
|
-
if (typeof window === "undefined" || !window.sessionStorage) return;
|
|
2327
|
-
try {
|
|
2328
|
-
window.sessionStorage.clear();
|
|
2329
|
-
} catch (error) {
|
|
2330
|
-
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
2331
|
-
console.error("sessionStorage.clear error");
|
|
2332
|
-
}
|
|
1882
|
+
console.error("[SecureStorage] \u83B7\u53D6\u952E\u540D\u5931\u8D25:", error);
|
|
1883
|
+
return [];
|
|
2333
1884
|
}
|
|
2334
1885
|
}
|
|
2335
|
-
};
|
|
2336
|
-
var cookie = {
|
|
2337
|
-
/**
|
|
2338
|
-
* 设置Cookie
|
|
2339
|
-
* @param key - 键
|
|
2340
|
-
* @param value - 值
|
|
2341
|
-
* @param options - Cookie选项
|
|
2342
|
-
*/
|
|
2343
|
-
set(key, value, options = {}) {
|
|
2344
|
-
if (typeof document === "undefined") {
|
|
2345
|
-
throw new Error("document is not available");
|
|
2346
|
-
}
|
|
2347
|
-
let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
2348
|
-
if (options.expires) {
|
|
2349
|
-
const expiresDate = options.expires instanceof Date ? options.expires : new Date(Date.now() + options.expires * 24 * 60 * 60 * 1e3);
|
|
2350
|
-
cookieStr += `; expires=${expiresDate.toUTCString()}`;
|
|
2351
|
-
}
|
|
2352
|
-
if (options.path) cookieStr += `; path=${options.path}`;
|
|
2353
|
-
if (options.domain) cookieStr += `; domain=${options.domain}`;
|
|
2354
|
-
if (options.secure) cookieStr += "; secure";
|
|
2355
|
-
if (options.sameSite) cookieStr += `; sameSite=${options.sameSite}`;
|
|
2356
|
-
document.cookie = cookieStr;
|
|
2357
|
-
},
|
|
2358
|
-
/**
|
|
2359
|
-
* 获取Cookie
|
|
2360
|
-
* @param key - 键
|
|
2361
|
-
* @returns Cookie值
|
|
2362
|
-
*/
|
|
2363
|
-
get(key) {
|
|
2364
|
-
if (typeof document === "undefined") return void 0;
|
|
2365
|
-
const name = encodeURIComponent(key);
|
|
2366
|
-
const cookies = document.cookie.split(";");
|
|
2367
|
-
for (const cookieStr of cookies) {
|
|
2368
|
-
const trimmed = cookieStr.trim();
|
|
2369
|
-
const eqIndex = trimmed.indexOf("=");
|
|
2370
|
-
if (eqIndex === -1) continue;
|
|
2371
|
-
const cookieKey = trimmed.substring(0, eqIndex).trim();
|
|
2372
|
-
const cookieValue = trimmed.substring(eqIndex + 1).trim();
|
|
2373
|
-
if (cookieKey === name) {
|
|
2374
|
-
const decoded = decodeURIComponent(cookieValue);
|
|
2375
|
-
return decoded === "" ? void 0 : decoded;
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
return void 0;
|
|
2379
|
-
},
|
|
2380
|
-
/**
|
|
2381
|
-
* 移除Cookie
|
|
2382
|
-
* @param key - 键
|
|
2383
|
-
* @param options - Cookie选项
|
|
2384
|
-
*/
|
|
2385
|
-
remove(key, options = {}) {
|
|
2386
|
-
cookie.set(key, "", {
|
|
2387
|
-
...options,
|
|
2388
|
-
expires: /* @__PURE__ */ new Date(0)
|
|
2389
|
-
});
|
|
2390
|
-
},
|
|
2391
1886
|
/**
|
|
2392
|
-
*
|
|
2393
|
-
* @returns Cookie对象
|
|
1887
|
+
* 获取数据条数
|
|
2394
1888
|
*/
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
const cookies = {};
|
|
2398
|
-
document.cookie.split(";").forEach((cookieStr) => {
|
|
2399
|
-
const trimmed = cookieStr.trim();
|
|
2400
|
-
if (!trimmed) return;
|
|
2401
|
-
const eqIndex = trimmed.indexOf("=");
|
|
2402
|
-
if (eqIndex === -1) return;
|
|
2403
|
-
const key = trimmed.substring(0, eqIndex).trim();
|
|
2404
|
-
const value = trimmed.substring(eqIndex + 1).trim();
|
|
2405
|
-
if (key && value) {
|
|
2406
|
-
const decodedKey = decodeURIComponent(key);
|
|
2407
|
-
const decodedValue = decodeURIComponent(value);
|
|
2408
|
-
if (decodedValue !== "") {
|
|
2409
|
-
cookies[decodedKey] = decodedValue;
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
});
|
|
2413
|
-
return cookies;
|
|
1889
|
+
size() {
|
|
1890
|
+
return this.keys().length;
|
|
2414
1891
|
}
|
|
2415
|
-
};
|
|
2416
|
-
var secureStorage = {
|
|
2417
1892
|
/**
|
|
2418
|
-
*
|
|
2419
|
-
* @param key - 键
|
|
2420
|
-
* @param value - 值
|
|
2421
|
-
* @param options - 选项(过期时间、公钥等)
|
|
2422
|
-
* @returns Promise<void>
|
|
1893
|
+
* 清理过期数据
|
|
2423
1894
|
*/
|
|
2424
|
-
async
|
|
1895
|
+
async clearExpired() {
|
|
2425
1896
|
try {
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
1897
|
+
let count = 0;
|
|
1898
|
+
const keys2 = this.keys();
|
|
1899
|
+
for (const key of keys2) {
|
|
1900
|
+
const fullKey = this._getFullKey(key);
|
|
1901
|
+
try {
|
|
1902
|
+
const storedData = this.storage.getItem(fullKey);
|
|
1903
|
+
if (storedData) {
|
|
1904
|
+
const finalData = JSON.parse(storedData);
|
|
1905
|
+
const aesKey = await decryptWithEnvPrivateKey(finalData.key);
|
|
1906
|
+
const storageData = decryptJsonWithAES(finalData.data, aesKey);
|
|
1907
|
+
if (this._isExpired(storageData.expire)) {
|
|
1908
|
+
this.remove(key);
|
|
1909
|
+
count++;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
} catch (error) {
|
|
1913
|
+
this.remove(key);
|
|
1914
|
+
count++;
|
|
2433
1915
|
}
|
|
2434
1916
|
}
|
|
1917
|
+
return count;
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
console.error("[SecureStorage] \u6E05\u7406\u8FC7\u671F\u6570\u636E\u5931\u8D25:", error);
|
|
1920
|
+
return 0;
|
|
2435
1921
|
}
|
|
2436
|
-
}
|
|
2437
|
-
/**
|
|
2438
|
-
* 获取值
|
|
2439
|
-
* @param key - 键
|
|
2440
|
-
* @param options - 选项(默认值、私钥等)
|
|
2441
|
-
* @returns Promise<T | undefined> 值或默认值
|
|
2442
|
-
*/
|
|
2443
|
-
async get(key, options = {}) {
|
|
2444
|
-
const localValue = await localStorage.get(key, options);
|
|
2445
|
-
if (localValue !== void 0) return localValue;
|
|
2446
|
-
const sessionValue = await sessionStorage.get(key, options);
|
|
2447
|
-
return sessionValue !== void 0 ? sessionValue : options.defaultValue;
|
|
2448
|
-
},
|
|
2449
|
-
/**
|
|
2450
|
-
* 移除值
|
|
2451
|
-
* @param key - 键
|
|
2452
|
-
*/
|
|
2453
|
-
remove(key) {
|
|
2454
|
-
localStorage.remove(key);
|
|
2455
|
-
sessionStorage.remove(key);
|
|
2456
|
-
},
|
|
1922
|
+
}
|
|
2457
1923
|
/**
|
|
2458
|
-
*
|
|
1924
|
+
* 获取实例的唯一标识
|
|
2459
1925
|
*/
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
},
|
|
1926
|
+
getInstanceKey() {
|
|
1927
|
+
return this.instanceKey;
|
|
1928
|
+
}
|
|
2464
1929
|
/**
|
|
2465
|
-
*
|
|
2466
|
-
* @returns 键名数组
|
|
1930
|
+
* 检查是否为单例实例
|
|
2467
1931
|
*/
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
const sessionKeys = [];
|
|
2471
|
-
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
2472
|
-
try {
|
|
2473
|
-
for (let i = 0; i < window.sessionStorage.length; i++) {
|
|
2474
|
-
const key = window.sessionStorage.key(i);
|
|
2475
|
-
if (key) sessionKeys.push(key);
|
|
2476
|
-
}
|
|
2477
|
-
} catch (error) {
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
return Array.from(/* @__PURE__ */ new Set([...localKeys, ...sessionKeys]));
|
|
1932
|
+
isSingleton() {
|
|
1933
|
+
return _SecureStorage.instances.has(this.instanceKey);
|
|
2481
1934
|
}
|
|
2482
1935
|
};
|
|
1936
|
+
var _defaultSecureStorage = null;
|
|
1937
|
+
function _getDefaultSecureStorage() {
|
|
1938
|
+
if (typeof window === "undefined") {
|
|
1939
|
+
throw new Error("[SecureStorage] secureStorage is only available in browser environments.");
|
|
1940
|
+
}
|
|
1941
|
+
if (!_defaultSecureStorage) {
|
|
1942
|
+
_defaultSecureStorage = new SecureStorage({});
|
|
1943
|
+
}
|
|
1944
|
+
return _defaultSecureStorage;
|
|
1945
|
+
}
|
|
1946
|
+
var secureStorage = new Proxy({}, {
|
|
1947
|
+
get(_target, prop) {
|
|
1948
|
+
const instance = _getDefaultSecureStorage();
|
|
1949
|
+
const value = instance[prop];
|
|
1950
|
+
if (typeof value === "function") {
|
|
1951
|
+
return value.bind(instance);
|
|
1952
|
+
}
|
|
1953
|
+
return value;
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
2483
1956
|
|
|
2484
1957
|
// src/browser/network/index.ts
|
|
2485
1958
|
async function fetchWithRetry(url, options = {}, retryCount = 3, retryDelay = 1e3) {
|
|
@@ -4075,6 +3548,48 @@ function setCommonParams(params) {
|
|
|
4075
3548
|
async function flush() {
|
|
4076
3549
|
await getTracker().flush();
|
|
4077
3550
|
}
|
|
3551
|
+
function convertRes2Blob(response) {
|
|
3552
|
+
const contentDisposition = response.headers["content-disposition"];
|
|
3553
|
+
let fileName = "file.xlsx";
|
|
3554
|
+
if (contentDisposition) {
|
|
3555
|
+
const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
|
|
3556
|
+
if (rfc5987Match) {
|
|
3557
|
+
fileName = decodeURIComponent(rfc5987Match[1]);
|
|
3558
|
+
} else {
|
|
3559
|
+
const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
|
|
3560
|
+
if (standardMatch) {
|
|
3561
|
+
fileName = standardMatch[1];
|
|
3562
|
+
fileName = fileName.replace(/^['"]|['"]$/g, "");
|
|
3563
|
+
try {
|
|
3564
|
+
fileName = decodeURIComponent(fileName);
|
|
3565
|
+
} catch (e) {
|
|
3566
|
+
try {
|
|
3567
|
+
fileName = decodeURI(fileName);
|
|
3568
|
+
} catch (e2) {
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
fileName = fileName.trim().replace(/^_+|_+$/g, "");
|
|
3574
|
+
}
|
|
3575
|
+
const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
|
|
3576
|
+
if (typeof window.navigator.msSaveBlob !== "undefined") {
|
|
3577
|
+
window.navigator.msSaveBlob(blob, fileName);
|
|
3578
|
+
} else {
|
|
3579
|
+
const blobURL = window.URL.createObjectURL(blob);
|
|
3580
|
+
const tempLink = document.createElement("a");
|
|
3581
|
+
tempLink.style.display = "none";
|
|
3582
|
+
tempLink.href = blobURL;
|
|
3583
|
+
tempLink.setAttribute("download", fileName);
|
|
3584
|
+
if (typeof tempLink.download === "undefined") {
|
|
3585
|
+
tempLink.setAttribute("target", "_blank");
|
|
3586
|
+
}
|
|
3587
|
+
document.body.appendChild(tempLink);
|
|
3588
|
+
tempLink.click();
|
|
3589
|
+
document.body.removeChild(tempLink);
|
|
3590
|
+
window.URL.revokeObjectURL(blobURL);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
4078
3593
|
var serviceEventHandlers = {
|
|
4079
3594
|
message: null
|
|
4080
3595
|
};
|
|
@@ -4108,11 +3623,28 @@ function filterEmptyKey(params, emptyString) {
|
|
|
4108
3623
|
}
|
|
4109
3624
|
});
|
|
4110
3625
|
}
|
|
3626
|
+
function isFileUpload(data) {
|
|
3627
|
+
if (!data) return false;
|
|
3628
|
+
if (data instanceof FormData) return true;
|
|
3629
|
+
if (data instanceof File) return true;
|
|
3630
|
+
if (data instanceof Blob) return true;
|
|
3631
|
+
if (data instanceof ArrayBuffer) return true;
|
|
3632
|
+
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
3633
|
+
const obj = data;
|
|
3634
|
+
return Object.values(obj).some(
|
|
3635
|
+
(value) => value instanceof File || value instanceof Blob || value instanceof FormData
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3638
|
+
return false;
|
|
3639
|
+
}
|
|
4111
3640
|
function getTransformRequest(contentType) {
|
|
4112
3641
|
if (!contentType) {
|
|
4113
3642
|
return (data) => Qs__default.default.stringify(data);
|
|
4114
3643
|
}
|
|
4115
3644
|
const normalizedType = contentType.toLowerCase().trim();
|
|
3645
|
+
if (normalizedType.startsWith("multipart/form-data")) {
|
|
3646
|
+
return void 0;
|
|
3647
|
+
}
|
|
4116
3648
|
if (normalizedType.startsWith("application/json")) {
|
|
4117
3649
|
return (data) => JSON.stringify(data);
|
|
4118
3650
|
}
|
|
@@ -4133,6 +3665,15 @@ var service = axios__default.default.create({
|
|
|
4133
3665
|
});
|
|
4134
3666
|
service.interceptors.request.use(
|
|
4135
3667
|
async (config) => {
|
|
3668
|
+
const isFileUploadRequest = isFileUpload(config.data);
|
|
3669
|
+
if (isFileUploadRequest) {
|
|
3670
|
+
if (config.headers) {
|
|
3671
|
+
delete config.headers["Content-Type"];
|
|
3672
|
+
delete config.headers["content-type"];
|
|
3673
|
+
}
|
|
3674
|
+
config.transformRequest = void 0;
|
|
3675
|
+
return config;
|
|
3676
|
+
}
|
|
4136
3677
|
const contentType = config.headers?.["Content-Type"] || config.headers?.["content-type"];
|
|
4137
3678
|
if (!config.transformRequest && contentType) {
|
|
4138
3679
|
const transformRequest = getTransformRequest(
|
|
@@ -4145,9 +3686,11 @@ service.interceptors.request.use(
|
|
|
4145
3686
|
config.transformRequest = (data) => Qs__default.default.stringify(data);
|
|
4146
3687
|
}
|
|
4147
3688
|
if (config.method === "post" || config.method === "put" || config.method === "patch") {
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
3689
|
+
if (config.data && typeof config.data === "object" && !isFileUploadRequest) {
|
|
3690
|
+
const params = { ...config.data || {} };
|
|
3691
|
+
filterEmptyKey(params, config.emptyParams ?? false);
|
|
3692
|
+
config.data = params;
|
|
3693
|
+
}
|
|
4151
3694
|
} else if (config.method === "get" || config.method === "delete") {
|
|
4152
3695
|
if (!config.disableTimestamp) {
|
|
4153
3696
|
config.params = {
|
|
@@ -4231,19 +3774,7 @@ service.interceptors.response.use(
|
|
|
4231
3774
|
if (status === 401) {
|
|
4232
3775
|
handle401Error(config, error);
|
|
4233
3776
|
}
|
|
4234
|
-
if (
|
|
4235
|
-
config._retryCount = config._retryCount || 0;
|
|
4236
|
-
if (config._retryCount < 2) {
|
|
4237
|
-
config._retryCount++;
|
|
4238
|
-
console.debug(
|
|
4239
|
-
`[request] aborted, retry attempt #${config._retryCount} -> ${config.url || ""}`
|
|
4240
|
-
);
|
|
4241
|
-
return new Promise((resolve) => setTimeout(resolve, 300)).then(
|
|
4242
|
-
() => service.request(config)
|
|
4243
|
-
);
|
|
4244
|
-
}
|
|
4245
|
-
}
|
|
4246
|
-
if (!config?.hidden && !isAborted) {
|
|
3777
|
+
if (!config?.hidden && !isAborted && status !== 401) {
|
|
4247
3778
|
let friendly = messageText;
|
|
4248
3779
|
if (status === 403) friendly = "No permission";
|
|
4249
3780
|
else if (status === 502) friendly = "System is upgrading or unavailable";
|
|
@@ -4269,7 +3800,7 @@ service.json = function(url, params, config) {
|
|
|
4269
3800
|
};
|
|
4270
3801
|
return service.post(
|
|
4271
3802
|
url,
|
|
4272
|
-
|
|
3803
|
+
params,
|
|
4273
3804
|
newConfig
|
|
4274
3805
|
);
|
|
4275
3806
|
};
|
|
@@ -4278,7 +3809,13 @@ service.put = function(url, params, config) {
|
|
|
4278
3809
|
headers: { "Content-Type": "application/json", ...config?.headers },
|
|
4279
3810
|
...config
|
|
4280
3811
|
};
|
|
4281
|
-
|
|
3812
|
+
const data = Array.isArray(params) ? params : params;
|
|
3813
|
+
return service.request({
|
|
3814
|
+
url,
|
|
3815
|
+
method: "put",
|
|
3816
|
+
data,
|
|
3817
|
+
...newConfig
|
|
3818
|
+
}).then((res) => res);
|
|
4282
3819
|
};
|
|
4283
3820
|
service.arrayGet = function(url, config) {
|
|
4284
3821
|
if (config) {
|
|
@@ -4307,48 +3844,6 @@ service.download = function(url, config = {}) {
|
|
|
4307
3844
|
return service.post(url, params, newConfig).then(() => void 0);
|
|
4308
3845
|
}
|
|
4309
3846
|
};
|
|
4310
|
-
function convertRes2Blob(response) {
|
|
4311
|
-
const contentDisposition = response.headers["content-disposition"];
|
|
4312
|
-
let fileName = "file.xlsx";
|
|
4313
|
-
if (contentDisposition) {
|
|
4314
|
-
const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
|
|
4315
|
-
if (rfc5987Match) {
|
|
4316
|
-
fileName = decodeURIComponent(rfc5987Match[1]);
|
|
4317
|
-
} else {
|
|
4318
|
-
const standardMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
|
|
4319
|
-
if (standardMatch) {
|
|
4320
|
-
fileName = standardMatch[1];
|
|
4321
|
-
fileName = fileName.replace(/^['"]|['"]$/g, "");
|
|
4322
|
-
try {
|
|
4323
|
-
fileName = decodeURIComponent(fileName);
|
|
4324
|
-
} catch (e) {
|
|
4325
|
-
try {
|
|
4326
|
-
fileName = decodeURI(fileName);
|
|
4327
|
-
} catch (e2) {
|
|
4328
|
-
}
|
|
4329
|
-
}
|
|
4330
|
-
}
|
|
4331
|
-
}
|
|
4332
|
-
fileName = fileName.trim().replace(/^_+|_+$/g, "");
|
|
4333
|
-
}
|
|
4334
|
-
const blob = new Blob([response.data], { type: "application/vnd.ms-excel;charset=utf-8" });
|
|
4335
|
-
if (typeof window.navigator.msSaveBlob !== "undefined") {
|
|
4336
|
-
window.navigator.msSaveBlob(blob, fileName);
|
|
4337
|
-
} else {
|
|
4338
|
-
const blobURL = window.URL.createObjectURL(blob);
|
|
4339
|
-
const tempLink = document.createElement("a");
|
|
4340
|
-
tempLink.style.display = "none";
|
|
4341
|
-
tempLink.href = blobURL;
|
|
4342
|
-
tempLink.setAttribute("download", fileName);
|
|
4343
|
-
if (typeof tempLink.download === "undefined") {
|
|
4344
|
-
tempLink.setAttribute("target", "_blank");
|
|
4345
|
-
}
|
|
4346
|
-
document.body.appendChild(tempLink);
|
|
4347
|
-
tempLink.click();
|
|
4348
|
-
document.body.removeChild(tempLink);
|
|
4349
|
-
window.URL.revokeObjectURL(blobURL);
|
|
4350
|
-
}
|
|
4351
|
-
}
|
|
4352
3847
|
|
|
4353
3848
|
exports.$ = $;
|
|
4354
3849
|
exports.$$ = $$;
|
|
@@ -4359,6 +3854,7 @@ exports.Graph = Graph;
|
|
|
4359
3854
|
exports.LRUCache = LRUCache;
|
|
4360
3855
|
exports.LinkedList = LinkedList;
|
|
4361
3856
|
exports.Queue = Queue;
|
|
3857
|
+
exports.SecureStorage = SecureStorage;
|
|
4362
3858
|
exports.Stack = Stack;
|
|
4363
3859
|
exports.Tracker = Tracker;
|
|
4364
3860
|
exports.UploadStatus = UploadStatus;
|
|
@@ -4366,9 +3862,6 @@ exports.addClass = addClass;
|
|
|
4366
3862
|
exports.addDays = addDays;
|
|
4367
3863
|
exports.addMonths = addMonths;
|
|
4368
3864
|
exports.addYears = addYears;
|
|
4369
|
-
exports.aesGCMDecrypt = aesGCMDecrypt;
|
|
4370
|
-
exports.aesGCMEncrypt = aesGCMEncrypt;
|
|
4371
|
-
exports.base64Decode = base64Decode;
|
|
4372
3865
|
exports.base64Encode = base64Encode;
|
|
4373
3866
|
exports.batch = batch;
|
|
4374
3867
|
exports.binarySearch = binarySearch;
|
|
@@ -4382,11 +3875,8 @@ exports.ceil = ceil;
|
|
|
4382
3875
|
exports.checkOnline = checkOnline;
|
|
4383
3876
|
exports.chunk = chunk;
|
|
4384
3877
|
exports.clamp = clamp;
|
|
4385
|
-
exports.clearPersistedKeys = clearPersistedKeys;
|
|
4386
3878
|
exports.compact = compact;
|
|
4387
|
-
exports.computeHMAC = computeHMAC;
|
|
4388
3879
|
exports.contrast = contrast;
|
|
4389
|
-
exports.cookie = cookie;
|
|
4390
3880
|
exports.copyToClipboard = copyToClipboard;
|
|
4391
3881
|
exports.createTracker = createTracker;
|
|
4392
3882
|
exports.createTranslator = createTranslator;
|
|
@@ -4394,19 +3884,20 @@ exports.createUploader = createUploader;
|
|
|
4394
3884
|
exports.csvToJson = csvToJson;
|
|
4395
3885
|
exports.darken = darken;
|
|
4396
3886
|
exports.debounce = debounce;
|
|
3887
|
+
exports.decryptJsonWithAES = decryptJsonWithAES;
|
|
3888
|
+
exports.decryptWithEnvPrivateKey = decryptWithEnvPrivateKey;
|
|
4397
3889
|
exports.deepClone = deepClone;
|
|
4398
3890
|
exports.deepMerge = deepMerge;
|
|
4399
3891
|
exports.defaults = defaults;
|
|
4400
|
-
exports.deriveKeyFromPassword = deriveKeyFromPassword;
|
|
4401
3892
|
exports.diffDays = diffDays;
|
|
4402
3893
|
exports.difference = difference;
|
|
4403
3894
|
exports.downloadFile = downloadFile;
|
|
4404
3895
|
exports.drop = drop;
|
|
4405
3896
|
exports.dropWhile = dropWhile;
|
|
3897
|
+
exports.encryptJsonWithAES = encryptJsonWithAES;
|
|
3898
|
+
exports.encryptWithEnvPublicKey = encryptWithEnvPublicKey;
|
|
4406
3899
|
exports.endOfDay = endOfDay;
|
|
4407
3900
|
exports.escapeHtml = escapeHtml;
|
|
4408
|
-
exports.exportPrivateKey = exportPrivateKey;
|
|
4409
|
-
exports.exportPublicKey = exportPublicKey;
|
|
4410
3901
|
exports.factorial = factorial;
|
|
4411
3902
|
exports.fetchWithRetry = fetchWithRetry;
|
|
4412
3903
|
exports.fetchWithTimeout = fetchWithTimeout;
|
|
@@ -4426,35 +3917,29 @@ exports.formatNumber = formatNumber;
|
|
|
4426
3917
|
exports.formatNumberI18n = formatNumberI18n;
|
|
4427
3918
|
exports.formatRelativeTime = formatRelativeTime;
|
|
4428
3919
|
exports.gcd = gcd;
|
|
4429
|
-
exports.generateHMACKey = generateHMACKey;
|
|
4430
3920
|
exports.generateRSAKeyPair = generateRSAKeyPair;
|
|
4431
|
-
exports.
|
|
4432
|
-
exports.generateUUID = generateUUID;
|
|
3921
|
+
exports.generateRandomAESKeyString = generateRandomAESKeyString;
|
|
4433
3922
|
exports.get = get;
|
|
4434
3923
|
exports.getDateFormatByGMT = getDateFormatByGMT;
|
|
4435
3924
|
exports.getElementOffset = getElementOffset;
|
|
3925
|
+
exports.getEnvPrivateKey = getEnvPrivateKey;
|
|
3926
|
+
exports.getEnvPublicKey = getEnvPublicKey;
|
|
4436
3927
|
exports.getFileExtension = getFileExtension;
|
|
4437
3928
|
exports.getFileNameWithoutExtension = getFileNameWithoutExtension;
|
|
4438
|
-
exports.getKeyUsageCount = getKeyUsageCount;
|
|
4439
3929
|
exports.getLocale = getLocale;
|
|
4440
3930
|
exports.getQuarter = getQuarter;
|
|
4441
3931
|
exports.getQueryParams = getQueryParams;
|
|
4442
3932
|
exports.getRelativeTime = getRelativeTime;
|
|
4443
3933
|
exports.getScrollPosition = getScrollPosition;
|
|
4444
|
-
exports.getStorageKeyPair = getStorageKeyPair;
|
|
4445
3934
|
exports.getStyle = getStyle;
|
|
4446
3935
|
exports.getTimeFromGMT = getTimeFromGMT;
|
|
4447
3936
|
exports.getTracker = getTracker;
|
|
4448
3937
|
exports.getWeekNumber = getWeekNumber;
|
|
4449
3938
|
exports.groupBy = groupBy;
|
|
4450
|
-
exports.hash = hash;
|
|
4451
3939
|
exports.hexToRgb = hexToRgb;
|
|
4452
3940
|
exports.highlight = highlight;
|
|
4453
3941
|
exports.hslToRgb = hslToRgb;
|
|
4454
|
-
exports.importPrivateKey = importPrivateKey;
|
|
4455
|
-
exports.importPublicKey = importPublicKey;
|
|
4456
3942
|
exports.initTracker = initTracker;
|
|
4457
|
-
exports.initializeStorageKeys = initializeStorageKeys;
|
|
4458
3943
|
exports.intersection = intersection;
|
|
4459
3944
|
exports.invert = invert;
|
|
4460
3945
|
exports.isAbsoluteUrl = isAbsoluteUrl;
|
|
@@ -4514,7 +3999,6 @@ exports.removeAccents = removeAccents;
|
|
|
4514
3999
|
exports.removeClass = removeClass;
|
|
4515
4000
|
exports.removeQueryParams = removeQueryParams;
|
|
4516
4001
|
exports.request = request;
|
|
4517
|
-
exports.resetKeyUsageCount = resetKeyUsageCount;
|
|
4518
4002
|
exports.retry = retry;
|
|
4519
4003
|
exports.rgbToHex = rgbToHex;
|
|
4520
4004
|
exports.rgbToHsl = rgbToHsl;
|
|
@@ -4528,10 +4012,8 @@ exports.set = set;
|
|
|
4528
4012
|
exports.setCommonParams = setCommonParams;
|
|
4529
4013
|
exports.setQueryParams = setQueryParams;
|
|
4530
4014
|
exports.setServiceEventHandlers = setServiceEventHandlers;
|
|
4531
|
-
exports.setStorageKeyPair = setStorageKeyPair;
|
|
4532
4015
|
exports.setStyle = setStyle;
|
|
4533
4016
|
exports.setUserInfo = setUserInfo;
|
|
4534
|
-
exports.sha256 = sha256;
|
|
4535
4017
|
exports.shuffle = shuffle;
|
|
4536
4018
|
exports.slugify = slugify;
|
|
4537
4019
|
exports.snakeCase = snakeCase;
|
|
@@ -4559,7 +4041,6 @@ exports.unzip = unzip;
|
|
|
4559
4041
|
exports.updateQueryParams = updateQueryParams;
|
|
4560
4042
|
exports.uploadFile = uploadFile;
|
|
4561
4043
|
exports.values = values;
|
|
4562
|
-
exports.verifyHMAC = verifyHMAC;
|
|
4563
4044
|
exports.xmlToJson = xmlToJson;
|
|
4564
4045
|
exports.yamlToJson = yamlToJson;
|
|
4565
4046
|
exports.zip = zip;
|