diff --git a/.gitignore b/.gitignore index 5bb8b22..0f972d5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist coverage .DS_Store +upload \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..eaf6f94 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@abearxiong:registry=https://npm.pkg.github.com \ No newline at end of file diff --git a/app.config.json5 b/app.config.json5 new file mode 100644 index 0000000..264255d --- /dev/null +++ b/app.config.json5 @@ -0,0 +1,7 @@ +{ + api: { + host: 'codeflow.xiongxiao.me', + }, + domain: 'kevisual.xiongxiao.me', + resources: 'minio.xiongxiao.me/resources', +} diff --git a/package.json b/package.json index 89f59ad..1e5d239 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,21 @@ "main": "index.js", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "dev": "nodemon --exec tsx src/index.ts" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@types/node": "^22.7.4", + "nodemon": "^3.1.7", "rollup": "^4.24.0", "typescript": "^5.6.2" + }, + "dependencies": { + "@abearxiong/use-config": "^0.0.2", + "@abearxiong/use-file-store": "^0.0.1", + "ioredis": "^5.4.1", + "nanoid": "^5.0.7" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b046fd..ae2b627 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,26 @@ settings: importers: .: + dependencies: + '@abearxiong/use-config': + specifier: ^0.0.2 + version: 0.0.2 + '@abearxiong/use-file-store': + specifier: ^0.0.1 + version: 0.0.1(typescript@5.6.2) + ioredis: + specifier: ^5.4.1 + version: 5.4.1 + nanoid: + specifier: ^5.0.7 + version: 5.0.7 devDependencies: '@types/node': specifier: ^22.7.4 version: 22.7.4 + nodemon: + specifier: ^3.1.7 + version: 3.1.7 rollup: specifier: ^4.24.0 version: 4.24.0 @@ -20,6 +36,36 @@ importers: packages: + '@abearxiong/use-config@0.0.2': + resolution: {integrity: sha512-IBOmeP46ykbDlkplFS65UsAHjyPDKnvS2oqbkpLWhbSwDbF5zhBnD4ibsFZKPCyc3lMlPeRqYva4x6puX3E/qQ==, tarball: https://npm.pkg.github.com/download/@abearxiong/use-config/0.0.2/59fbeec8c8e086ec48e55024fe39020b079e6fa5} + + '@abearxiong/use-file-store@0.0.1': + resolution: {integrity: sha512-65ZQBHxwr76sAFG+Xd4IQstx8dERhkaX5MLqtqJ0f9m+2NnS/klNe0t4q9tgjMWAEWQxHjnPShpHWzkCENaDnQ==, tarball: https://npm.pkg.github.com/download/@abearxiong/use-file-store/0.0.1/f171e398c078d4940c1ddedf5ad529d17b0eec32} + + '@ioredis/commands@1.2.0': + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@rollup/rollup-android-arm-eabi@4.24.0': resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] @@ -112,29 +158,509 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@22.7.4': resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} + '@webassemblyjs/ast@1.12.1': + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + + '@webassemblyjs/floating-point-hex-parser@1.11.6': + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + + '@webassemblyjs/helper-api-error@1.11.6': + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + + '@webassemblyjs/helper-buffer@1.12.1': + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + + '@webassemblyjs/helper-numbers@1.11.6': + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + + '@webassemblyjs/helper-wasm-bytecode@1.11.6': + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + + '@webassemblyjs/helper-wasm-section@1.12.1': + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + + '@webassemblyjs/ieee754@1.11.6': + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + + '@webassemblyjs/leb128@1.11.6': + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + + '@webassemblyjs/utf8@1.11.6': + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + + '@webassemblyjs/wasm-edit@1.12.1': + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + + '@webassemblyjs/wasm-gen@1.12.1': + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + + '@webassemblyjs/wasm-opt@1.12.1': + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + + '@webassemblyjs/wasm-parser@1.12.1': + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + + '@webassemblyjs/wast-printer@1.12.1': + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.0: + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + caniuse-lite@1.0.30001667: + resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + electron-to-chromium@1.5.32: + resolution: {integrity: sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==} + + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ioredis@5.4.1: + resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} + engines: {node: '>=12.22.0'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@5.0.7: + resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==} + engines: {node: ^18 || >=20} + hasBin: true + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + nodemon@3.1.7: + resolution: {integrity: sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==} + engines: {node: '>=10'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + rollup@4.24.0: resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.3.10: + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.34.1: + resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==} + engines: {node: '>=10'} + hasBin: true + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + ts-loader@9.5.1: + resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + typescript@5.6.2: resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} hasBin: true + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + + webpack@5.95.0: + resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + snapshots: + '@abearxiong/use-config@0.0.2': {} + + '@abearxiong/use-file-store@0.0.1(typescript@5.6.2)': + dependencies: + json5: 2.2.3 + ts-loader: 9.5.1(typescript@5.6.2)(webpack@5.95.0) + webpack: 5.95.0 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - typescript + - uglify-js + - webpack-cli + + '@ioredis/commands@1.2.0': {} + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true @@ -185,13 +711,338 @@ snapshots: '@types/estree@1.0.6': {} + '@types/json-schema@7.0.15': {} + '@types/node@22.7.4': dependencies: undici-types: 6.19.8 + '@webassemblyjs/ast@1.12.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + + '@webassemblyjs/floating-point-hex-parser@1.11.6': {} + + '@webassemblyjs/helper-api-error@1.11.6': {} + + '@webassemblyjs/helper-buffer@1.12.1': {} + + '@webassemblyjs/helper-numbers@1.11.6': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} + + '@webassemblyjs/helper-wasm-section@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 + + '@webassemblyjs/ieee754@1.11.6': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.11.6': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.11.6': {} + + '@webassemblyjs/wasm-edit@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 + + '@webassemblyjs/wasm-gen@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wasm-opt@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + + '@webassemblyjs/wasm-parser@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wast-printer@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-attributes@1.9.5(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.0: + dependencies: + caniuse-lite: 1.0.30001667 + electron-to-chromium: 1.5.32 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.0) + + buffer-from@1.1.2: {} + + caniuse-lite@1.0.30001667: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chrome-trace-event@1.0.4: {} + + cluster-key-slot@1.1.2: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@2.20.3: {} + + concat-map@0.0.1: {} + + debug@4.3.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + denque@2.1.0: {} + + electron-to-chromium@1.5.32: {} + + enhanced-resolve@5.17.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + es-module-lexer@1.5.4: {} + + escalade@3.2.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + events@3.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + fsevents@2.3.3: optional: true + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + graceful-fs@4.2.11: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + ignore-by-default@1.0.1: {} + + ioredis@5.4.1: + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.7(supports-color@5.5.0) + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.7.4 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json5@2.2.3: {} + + loader-runner@4.3.0: {} + + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + + merge-stream@2.0.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + ms@2.1.3: {} + + nanoid@5.0.7: {} + + neo-async@2.6.2: {} + + node-releases@2.0.18: {} + + nodemon@3.1.7: + dependencies: + chokidar: 3.6.0 + debug: 4.3.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.6.3 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + normalize-path@3.0.0: {} + + picocolors@1.1.0: {} + + picomatch@2.3.1: {} + + pstree.remy@1.1.8: {} + + punycode@2.3.1: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + rollup@4.24.0: dependencies: '@types/estree': 1.0.6 @@ -214,6 +1065,130 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 + safe-buffer@5.2.1: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + semver@7.6.3: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.6.3 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + standard-as-callback@2.1.0: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + tapable@2.2.1: {} + + terser-webpack-plugin@5.3.10(webpack@5.95.0): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.34.1 + webpack: 5.95.0 + + terser@5.34.1: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + touch@3.1.1: {} + + ts-loader@9.5.1(typescript@5.6.2)(webpack@5.95.0): + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.17.1 + micromatch: 4.0.8 + semver: 7.6.3 + source-map: 0.7.4 + typescript: 5.6.2 + webpack: 5.95.0 + typescript@5.6.2: {} + undefsafe@2.0.5: {} + undici-types@6.19.8: {} + + update-browserslist-db@1.1.1(browserslist@4.24.0): + dependencies: + browserslist: 4.24.0 + escalade: 3.2.0 + picocolors: 1.1.0 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + watchpack@2.4.2: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + webpack-sources@3.2.3: {} + + webpack@5.95.0: + dependencies: + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.24.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.95.0) + watchpack: 2.4.2 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js diff --git a/src/index.ts b/src/index.ts index e69de29..77cfe5e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,15 @@ +import http from 'http'; +import { handleRequest } from './module/index.ts'; +import { useConfig } from '@abearxiong/use-config'; +useConfig(); +const server = http.createServer((req, res) => { + // res.writeHead(200, { 'Content-Type': 'text/plain' }); + // const pathname = new URL(req.url, `http://${dns.hostName}`).pathname; + handleRequest(req, res); + // res.write(`Request from ${dns.hostName} with IP: ${dns.ip}\n`); + // res.end('Hello World\n'); +}); + +server.listen(3005, () => { + console.log('Server running at http://localhost:3005/'); +}); diff --git a/src/module/get-content-type.ts b/src/module/get-content-type.ts new file mode 100644 index 0000000..d592ba3 --- /dev/null +++ b/src/module/get-content-type.ts @@ -0,0 +1,18 @@ +import path from 'path'; +// 获取文件的 content-type +export const getContentType = (filePath: string) => { + const extname = path.extname(filePath); + const contentType = { + '.html': 'text/html', + '.js': 'text/javascript', + '.css': 'text/css', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.wav': 'audio/wav', + '.mp4': 'video/mp4', + }; + return contentType[extname] || 'application/octet-stream'; +}; diff --git a/src/module/get-user-app.ts b/src/module/get-user-app.ts new file mode 100644 index 0000000..07d6a19 --- /dev/null +++ b/src/module/get-user-app.ts @@ -0,0 +1,283 @@ +import path from 'path'; +import { redis, subscriber } from './redis/redis.ts'; +import { useFileStore } from '@abearxiong/use-file-store'; +import { useConfig } from '@abearxiong/use-config'; +import fs from 'fs'; +import crypto from 'crypto'; +import { nanoid } from 'nanoid'; +import { pipeline } from 'stream'; +import { promisify } from 'util'; +const pipelineAsync = promisify(pipeline); + +const { resources } = useConfig<{ resources: string }>(); +const fileStore = useFileStore('upload'); + +const demoData = { + user: 'root', + key: 'codeflow', + appType: 'web-single', // + version: '1.0.0', + domain: null, + type: 'local', + data: { + files: [ + { + name: 'index.html', + path: 'codeflow/index.html', + }, + { + name: 'assets/index-14y4J8dP.js', + path: 'codeflow/assets/index-14y4J8dP.js', + }, + { + name: 'assets/index-C-libw4a.css', + path: 'codeflow/assets/index-C-libw4a.css', + }, + ], + }, +}; +const demoData2 = { + user: 'root', + key: 'codeflow', + appType: 'web-single', // + version: '0.0.1', + domain: null, + type: 'oss', // 是否使用oss + data: { + files: [ + { + name: 'index.html', + path: 'root/codeflow/0.0.1/index.html', + }, + { + name: 'assets/index-14y4J8dP.js', + path: 'root/codeflow/0.0.1/assets/index-14y4J8dP.js', + }, + { + name: 'assets/index-C-libw4a.css', + path: 'root/codeflow/0.0.1/assets/index-C-libw4a.css', + }, + ], + }, +}; +type UserAppOptions = { + user: string; + app: string; +}; +export class UserApp { + user: string; + app: string; + constructor(options: UserAppOptions) { + this.user = options.user; + this.app = options.app; + } + async getExist() { + const app = this.app; + const user = this.user; + const key = 'user:app:exist:' + app + ':' + user; + const value = await redis.get(key); + return value; + } + async getCache() { + const app = this.app; + const user = this.user; + const key = 'user:app:' + app + ':' + user; + const value = await redis.get(key); + if (!value) { + return null; + } + } + async getFile(appFileUrl: string) { + const app = this.app; + const user = this.user; + const key = 'user:app:set:' + app + ':' + user; + const value = await redis.hget(key, appFileUrl); + return value; + } + async setCacheData() { + const app = this.app; + const user = this.user; + const key = 'user:app:' + app + ':' + user; + // 如果demoData 不存在则返回 + if (!demoData2) { + return false; + } + const value = await downloadUserAppFiles(user, app, demoData2); + const valueIndexHtml = value.data.files.find((file) => file.name === 'index.html'); + await redis.set(key, JSON.stringify(value)); + await redis.set('user:app:exist:' + app + ':' + user, valueIndexHtml.path, 'EX', 60 * 60 * 24 * 7); // 24小时 + const files = value.data.files; + // await redis.hset(key, 'files', JSON.stringify(files)); + const data = {}; + + // 将文件名和路径添加到 `data` 对象中 + files.forEach((file) => { + data[file.name] = file.path; + }); + await redis.hset('user:app:set:' + app + ':' + user, data); + + return true; + } + async getAllCacheData() { + const app = this.app; + const user = this.user; + const key = 'user:app:' + app + ':' + user; + const value = await redis.get(key); + console.log('getAllCacheData', JSON.parse(value)); + const exist = await redis.get('user:app:exist:' + app + ':' + user); + console.log('getAllCacheData:exist', exist); + const files = await redis.hgetall('user:app:set:' + app + ':' + user); + console.log('getAllCacheData:files', files); + } + async clearCacheData() { + const app = this.app; + const user = this.user; + const key = 'user:app:' + app + ':' + user; + await redis.del(key); + await redis.del('user:app:exist:' + app + ':' + user); + await redis.del('user:app:set:' + app + ':' + user); + console.log('clear user data', key); + // 删除所有文件 + deleteUserAppFiles(user, app); + } + async getData() { + return demoData; + } + async close() { + // 关闭连接 + await redis.quit(); + } +} +export const downloadUserAppFiles = async (user: string, app: string, data: typeof demoData) => { + const { + data: { files, ...dataRest }, + ...rest + } = data; + const uploadFiles = path.join(fileStore, user, app); + if (!checkFileExistsSync(uploadFiles)) { + fs.mkdirSync(uploadFiles, { recursive: true }); + } + const newFiles = []; + if (data.type === 'local') { + // local copy file + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const copyFile = path.join(fileStore, file.path); + const destFile = path.join(uploadFiles, file.name); + const destDir = path.dirname(destFile); // 获取目标文件所在的目录路径 + // 检查目录是否存在,如果不存在则创建 + if (!checkFileExistsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); // 递归创建目录 + } + fs.copyFileSync(copyFile, destFile); + // const etag = await setEtag(fs.readFileSync(destFile, 'utf-8')); + const etag = nanoid(); + newFiles.push({ + name: file.name, + path: destFile.replace(fileStore, '') + '||' + etag, + }); + } + } + if (data.type === 'oss') { + const serverPath = 'https://' + resources + '/'; + // server download file + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const destFile = path.join(uploadFiles, file.name); + const destDir = path.dirname(destFile); // 获取目标文件所在的目录路径 + // 检查目录是否存在,如果不存在则创建 + if (!checkFileExistsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); // 递归创建目录 + } + // 下载文件到 destFile + await downloadFile(serverPath + file.path, destFile); + const etag = nanoid(); + newFiles.push({ + name: file.name, + path: destFile.replace(fileStore, '') + '||' + etag, + }); + } + } + + return { + ...rest, + data: { + ...dataRest, + files: newFiles, + }, + }; +}; +export const checkFileExistsSync = (filePath: string) => { + try { + // 使用 F_OK 检查文件或目录是否存在 + fs.accessSync(filePath, fs.constants.F_OK); + return true; + } catch (err) { + return false; + } +}; +export const deleteUserAppFiles = async (user: string, app: string) => { + const uploadFiles = path.join(fileStore, user, app); + try { + fs.rmSync(uploadFiles, { recursive: true }); + } catch (err) { + console.error('deleteUserAppFiles', err); + } + // console.log('deleteUserAppFiles', res); +}; +async function downloadFile(fileUrl: string, destFile: string) { + const res = await fetch(fileUrl); + + if (!res.ok) { + throw new Error(`Failed to fetch ${fileUrl}: ${res.statusText}`); + } + console.log('destFile', destFile); + const destStream = fs.createWriteStream(destFile); + + // 使用 `pipeline` 将 `res.body` 中的数据传递给 `destStream` + await pipelineAsync(res.body, destStream); + + console.log(`File downloaded to ${destFile}`); +} + +export const clearAllUserApp = async () => { + // redis 删除 所有的 user:app:* + const keys = await redis.keys('user:app:*'); + console.log('clearAllUserApp', keys); + if (keys.length > 0) { + const pipeline = redis.pipeline(); + keys.forEach((key) => pipeline.del(key)); // 将每个键的删除操作添加到 pipeline 中 + await pipeline.exec(); // 执行 pipeline 中的所有命令 + console.log('All keys deleted successfully using pipeline'); + } +}; +export const setEtag = async (fileContent: string) => { + const eTag = crypto.createHash('md5').update(fileContent).digest('hex'); + return eTag; +}; + +// redis 监听 user:app:exist:*的过期 +subscriber.on('ready', () => { + console.log('Subscriber is ready and connected.'); +}); + +// 订阅 Redis 频道 +subscriber.subscribe('__keyevent@0__:expired', (err, count) => { + if (err) { + console.error('Failed to subscribe: ', err); + } else { + console.log(`Subscribed to ${count} channel(s). Waiting for expired events...`); + } +}); + +// 监听消息事件 +subscriber.on('message', (channel, message) => { + // 检查是否匹配 user:app:exist:* 模式 + if (message.startsWith('user:app:exist:')) { + const [_user, _app, _exist, app, user] = message.split(':'); + // 在这里执行你的逻辑,例如清理缓存或通知用户 + console.log('User app exist key expired:', app, user); + const userApp = new UserApp({ user, app }); + userApp.clearCacheData(); + } +}); diff --git a/src/module/index.ts b/src/module/index.ts new file mode 100644 index 0000000..0bcfafc --- /dev/null +++ b/src/module/index.ts @@ -0,0 +1,167 @@ +import { getDNS, isLocalhost } from '@/utils/dns.ts'; +import http from 'http'; +import { UserApp } from './get-user-app.ts'; +import { useFileStore } from '@abearxiong/use-file-store'; +import path from 'path'; +import fs from 'fs'; +import { useConfig } from '@abearxiong/use-config'; +import { redis } from './redis/redis.ts'; +import { getContentType } from './get-content-type.ts'; +const { api, domain } = useConfig<{ + api: { + host: string; + }; + domain: string; +}>(); + +const fileStore = useFileStore('upload'); +console.log('filePath', fileStore); +const noProxyUrl = ['/', '/favicon.ico']; +export const handleRequest = async (req: http.IncomingMessage, res: http.ServerResponse) => { + const dns = getDNS(req); + + let user, app; + let domainApp = false; + if (isLocalhost(dns.hostName)) { + // 本地开发环境 测试 + // user = 'root'; + // app = 'codeflow'; + // domainApp = true; + } else { + // 生产环境 + // 验证域名 + if (dns.hostName !== domain) { + // redis获取域名对应的用户和应用 + domainApp = true; + const key = 'domain:' + dns.hostName; + const value = await redis.get(key); + if (!value) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.write('Invalid domain\n'); + return res.end(); + } + const [_user, _app] = value.split(':'); + if (!_user || !_app) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.write('Invalid domain, Config error\n'); + return res.end(); + } + user = _user; + app = _app; + } + } + const url = req.url; + if (!domainApp && noProxyUrl.includes(req.url)) { + res.write('No proxy for this URL\n'); + return res.end(); + } + if (!domainApp) { + // 原始url地址 + const urls = url.split('/'); + if (urls.length < 3) { + console.log('urls errpr', urls); + res.writeHead(404, { 'Content-Type': 'text/html' }); + res.write('Invalid Proxy URL\n'); + return res.end(); + } + const [_, _user, _app] = urls; + if (!_user || !_app) { + res.write('Invalid URL\n'); + return res.end(); + } + user = _user; + app = _app; + } + const [_, _api] = req.url.split('/'); + if (_api === 'api') { + // 代理到 http://codeflow.xiongxiao.me/api + // 设置代理请求的目标 URL 和请求头 + const options = { + host: api.host, + path: req.url, + method: 'POST', + headers: { + 'Content-Type': req.headers['content-type'], + Authroization: req.headers?.['authorization'] || '', + }, + }; + // 创建代理请求 + const proxyReq = http.request(options, (proxyRes) => { + // 将代理服务器的响应头和状态码返回给客户端 + res.writeHead(proxyRes.statusCode, proxyRes.headers); + // 将代理响应流写入客户端响应 + proxyRes.pipe(res, { end: true }); + }); + // 处理代理请求的错误事件 + proxyReq.on('error', (err) => { + console.error(`Proxy request error: ${err.message}`); + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.write(`Proxy request error: ${err.message}`); + }); + // 处理 POST 请求的请求体(传递数据到目标服务器) + req.pipe(proxyReq, { end: true }); + return; + } + const userApp = new UserApp({ user, app }); + let isExist = await userApp.getExist(); + if (!isExist) { + try { + const hasApp = await userApp.setCacheData(); + if (!hasApp) { + res.writeHead(404, { 'Content-Type': 'text/html' }); + res.write('Not Found App\n'); + res.end(); + return; + } + } catch (error) { + console.error('setCacheData error', error); + res.writeHead(500, { 'Content-Type': 'text/html' }); + res.write('Server Error\n'); + res.end(); + return; + } + + isExist = await userApp.getExist(); + if (!isExist) { + res.writeHead(404, { 'Content-Type': 'text/html' }); + res.write('Not Found App Index Page\n'); + res.end(); + return; + } + } + const indexFile = isExist; + let appFileUrl: string; + if (domainApp) { + appFileUrl = (url + '').replace(`/`, ''); + } else { + appFileUrl = (url + '').replace(`/${user}/${app}/`, ''); + } + const appFile = await userApp.getFile(appFileUrl); + if (!appFile) { + const [indexFilePath, etag] = indexFile.split('||'); + // 不存在的文件,返回indexFile的文件 + res.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-cache' }); + const filePath = path.join(fileStore, indexFilePath); + const readStream = fs.createReadStream(filePath); + readStream.pipe(res); + return; + } else { + const [appFilePath, eTag] = appFile.split('||'); + // 检查 If-None-Match 头判断缓存是否有效 + if (req.headers['if-none-match'] === eTag) { + res.statusCode = 304; // 内容未修改 + res.end(); + return; + } + const filePath = path.join(fileStore, appFilePath); + let contentType = getContentType(filePath); + res.writeHead(200, { + 'Content-Type': contentType, + 'Cache-Control': 'public, max-age=3600', // 设置缓存时间为 1 小时 + ETag: eTag, + }); + const readStream = fs.createReadStream(filePath); + readStream.pipe(res); + return; + } +}; diff --git a/src/module/redis/access-time.ts b/src/module/redis/access-time.ts new file mode 100644 index 0000000..f5e166c --- /dev/null +++ b/src/module/redis/access-time.ts @@ -0,0 +1,28 @@ +import { redis } from './redis.ts'; + +/** + * 更新键的访问时间 + * @param key + */ +export const accessKeyWithTimestamp = async (key: string) => { + const value = await redis.get(key); + if (value !== null) { + // 记录上一次访问时间(使用当前 Unix 时间戳) + await redis.hset('key_last_access_time', key, Math.floor(Date.now() / 1000)); + } + return value; +}; +/** + * 更新键的访问计数 + * @param key + * @returns + */ +export const accessKeyAndCount = async (key: string) => { + const value = await redis.get(key); + if (value !== null) { + // 增加访问计数 + await redis.incr(`access_count:${key}`); + } + return value; +}; + diff --git a/src/module/redis/redis.ts b/src/module/redis/redis.ts new file mode 100644 index 0000000..0f9ee16 --- /dev/null +++ b/src/module/redis/redis.ts @@ -0,0 +1,48 @@ +import { Redis } from 'ioredis'; +import { useConfig } from '@abearxiong/use-config'; + +const config = useConfig<{ + redis: ConstructorParameters; +}>(); +// 配置 Redis 连接 +export const redis = new Redis({ + host: 'localhost', // Redis 服务器的主机名或 IP 地址 + port: 6379, // Redis 服务器的端口号 + // password: 'your_password', // Redis 的密码 (如果有) + db: 0, // 要使用的 Redis 数据库索引 (0-15) + keyPrefix: '', // key 前缀 + retryStrategy(times) { + // 连接重试策略 + return Math.min(times * 50, 2000); // 每次重试时延迟增加 + }, + maxRetriesPerRequest: null, // 允许请求重试的次数 (如果需要无限次重试) + ...config.redis, +}); +export const subscriber = redis.duplicate(); // 创建一个订阅者连接 + +async function ensureKeyspaceNotifications() { + try { + // 获取当前的 `notify-keyspace-events` 配置 + const currentConfig = (await redis.config('GET', 'notify-keyspace-events')) as string[]; + + // 检查返回的数组长度是否大于1,表示获取成功 + if (currentConfig && currentConfig.length > 1) { + const currentSetting = currentConfig[1]; // 值在数组的第二个元素 + // 检查当前配置是否包含 "Ex" + if (!currentSetting.includes('E') || !currentSetting.includes('x')) { + console.log('Keyspace notifications are not fully enabled. Setting correct value...'); + await redis.config('SET', 'notify-keyspace-events', 'Ex'); + console.log('Keyspace notifications enabled with setting "Ex".'); + } else { + // console.log('Keyspace notifications are already correctly configured.'); + } + } else { + console.error('Failed to get the current notify-keyspace-events setting.'); + } + } catch (err) { + console.error('Error while configuring Redis keyspace notifications:', err); + } +} + +// 确保键空间通知被正确设置 +ensureKeyspaceNotifications().catch(console.error); diff --git a/src/scripts/copy.ts b/src/scripts/copy.ts new file mode 100644 index 0000000..046fa8c --- /dev/null +++ b/src/scripts/copy.ts @@ -0,0 +1,41 @@ +import { UserApp, clearAllUserApp } from '../module/get-user-app.ts'; +import { redis } from '../module/redis/redis.ts'; +import path from 'path'; +import { useFileStore } from '@abearxiong/use-file-store'; +const filePath = useFileStore('upload'); + +const main = async () => { + const userApp = new UserApp({ user: 'root', app: 'codeflow' }); + const res = await userApp.setCacheData(); + console.log(res); + // userApp.close(); + process.exit(0); +}; + +// main(); + +const getAll = async () => { + const userApp = new UserApp({ user: 'root', app: 'codeflow' }); + const res = await userApp.getAllCacheData(); + userApp.close(); +}; + +// getAll(); + +// console.log('path', path.join(filePath, '/module/get-user-app.ts')); + +const clearData = async () => { + const userApp = new UserApp({ user: 'root', app: 'codeflow' }); + const res = await userApp.clearCacheData(); + process.exit(0); +}; + +// clearData(); +clearAllUserApp(); + +const expireData = async () => { + await redis.set('user:app:exist:' + 'codeflow:root', 'value', 'EX', 2); + process.exit(0); +}; + +// expireData(); diff --git a/src/utils/dns.ts b/src/utils/dns.ts new file mode 100644 index 0000000..39a6c63 --- /dev/null +++ b/src/utils/dns.ts @@ -0,0 +1,11 @@ +import http from 'http'; + +export const getDNS = (req: http.IncomingMessage) => { + const hostName = req.headers.host; + const ip = req.socket.remoteAddress; + return { hostName, ip }; +}; + +export const isLocalhost = (hostName: string) => { + return hostName.includes('localhost'); +};