diff --git a/bun.config.mjs b/bun.config.mjs new file mode 100644 index 0000000..ded78cb --- /dev/null +++ b/bun.config.mjs @@ -0,0 +1,37 @@ +// @ts-check +import { resolvePath } from '@kevisual/use-config/env'; +import { execSync } from 'node:child_process'; + +const entry = 'src/index.ts'; +const naming = 'app'; +const external = ['sequelize', 'pg', 'sqlite3', 'ioredis', 'pm2']; +/** + * @type {import('bun').BuildConfig} + */ +await Bun.build({ + target: 'node', + format: 'esm', + entrypoints: [resolvePath(entry, { meta: import.meta })], + outdir: resolvePath('./dist', { meta: import.meta }), + naming: { + entry: `${naming}.js`, + }, + external, + env: 'KEVISUAL_*', +}); + +// const cmd = `dts -i src/index.ts -o app.d.ts`; +// const cmd = `dts -i ${entry} -o ${naming}.d.ts`; +// execSync(cmd, { stdio: 'inherit' }); + +// await Bun.build({ +// target: 'node', +// format: 'esm', +// entrypoints: [resolvePath('./src/run.ts', { meta: import.meta })], +// outdir: resolvePath('./dist', { meta: import.meta }), +// naming: { +// entry: `${'run'}.js`, +// }, +// external, +// env: 'KEVISUAL_*', +// }); diff --git a/package.json b/package.json index df88da7..960b371 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "basename": "/root/page-proxy", "app": { "key": "page-proxy", - "entry": "dist/app.mjs", + "entry": "dist/app.js", "type": "pm2-system-app", "files": [ "dist" @@ -17,10 +17,10 @@ "dist" ], "scripts": { - "watch": "rollup -c --watch", - "dev": "cross-env NODE_ENV=development nodemon --ignore proxy-upload --exec tsx src/index.ts", - "dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ", - "build": "rimraf dist && rollup -c", + "dev": "bun run --watch --hot --inspect src/index.ts", + "cmd": "bun run src/run.ts ", + "prebuild": "rimraf dist", + "build": "NODE_ENV=production bun bun.config.mjs", "start": "pm2 start dist/app.mjs --name page-proxy", "release": "node ./scripts/release/index.mjs", "deploy": "envision pack -p -u", @@ -31,7 +31,7 @@ "author": "", "license": "ISC", "devDependencies": { - "@kevisual/logger": "^0.0.3", + "@kevisual/logger": "^0.0.4", "@kevisual/oss": "^0.0.11", "@rollup/plugin-commonjs": "^28.0.3", "@rollup/plugin-json": "^6.1.0", @@ -39,22 +39,24 @@ "@rollup/plugin-typescript": "^12.1.2", "@types/busboy": "^1.5.4", "@types/http-proxy": "^1.17.16", - "@types/node": "^22.15.17", + "@types/node": "^22.15.21", "@types/send": "^0.17.4", + "@types/ws": "^8.18.1", "concurrently": "^9.1.2", "cross-env": "^7.0.3", "nodemon": "^3.1.10", - "rollup": "^4.40.2", + "rollup": "^4.41.0", "tslib": "^2.8.1", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "ws": "npm:@kevisual/ws" }, "dependencies": { "@kevisual/code-center-module": "0.0.18", - "@kevisual/permission": "^0.0.1", - "@kevisual/query": "^0.0.17", + "@kevisual/permission": "^0.0.3", + "@kevisual/query": "^0.0.18", "@kevisual/query-config": "^0.0.2", - "@kevisual/router": "0.0.14", - "@kevisual/use-config": "^1.0.15", + "@kevisual/router": "0.0.21", + "@kevisual/use-config": "^1.0.17", "@types/lodash-es": "^4.17.12", "archiver": "^7.0.1", "busboy": "^1.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68b0d03..6578980 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,22 +13,22 @@ importers: dependencies: '@kevisual/code-center-module': specifier: 0.0.18 - version: 0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.14)(@kevisual/use-config@1.0.15(dotenv@16.4.7))(ioredis@5.6.1)(pg@8.13.3)(sequelize@6.37.7(pg@8.13.3)) + version: 0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.21)(@kevisual/use-config@1.0.17(dotenv@16.4.7))(ioredis@5.6.1)(pg@8.13.3)(sequelize@6.37.7(pg@8.13.3)) '@kevisual/permission': - specifier: ^0.0.1 - version: 0.0.1 + specifier: ^0.0.3 + version: 0.0.3 '@kevisual/query': - specifier: ^0.0.17 - version: 0.0.17(ws@8.18.0)(zod@3.24.2) + specifier: ^0.0.18 + version: 0.0.18(@kevisual/ws@8.0.0)(zod@3.24.2) '@kevisual/query-config': specifier: ^0.0.2 - version: 0.0.2(@kevisual/query@0.0.17(ws@8.18.0)(zod@3.24.2)) + version: 0.0.2(@kevisual/query@0.0.18(@kevisual/ws@8.0.0)(zod@3.24.2)) '@kevisual/router': - specifier: 0.0.14 - version: 0.0.14 + specifier: 0.0.21 + version: 0.0.21 '@kevisual/use-config': - specifier: ^1.0.15 - version: 1.0.15(dotenv@16.4.7) + specifier: ^1.0.17 + version: 1.0.17(dotenv@16.4.7) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -61,23 +61,23 @@ importers: version: 6.37.7(pg@8.13.3) devDependencies: '@kevisual/logger': - specifier: ^0.0.3 - version: 0.0.3 + specifier: ^0.0.4 + version: 0.0.4 '@kevisual/oss': specifier: ^0.0.11 version: 0.0.11 '@rollup/plugin-commonjs': specifier: ^28.0.3 - version: 28.0.3(rollup@4.40.2) + version: 28.0.3(rollup@4.41.0) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.40.2) + version: 6.1.0(rollup@4.41.0) '@rollup/plugin-node-resolve': specifier: ^16.0.1 - version: 16.0.1(rollup@4.40.2) + version: 16.0.1(rollup@4.41.0) '@rollup/plugin-typescript': specifier: ^12.1.2 - version: 12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.3) + version: 12.1.2(rollup@4.41.0)(tslib@2.8.1)(typescript@5.8.3) '@types/busboy': specifier: ^1.5.4 version: 1.5.4 @@ -85,11 +85,14 @@ importers: specifier: ^1.17.16 version: 1.17.16 '@types/node': - specifier: ^22.15.17 - version: 22.15.17 + specifier: ^22.15.21 + version: 22.15.21 '@types/send': specifier: ^0.17.4 version: 0.17.4 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -100,14 +103,17 @@ importers: specifier: ^3.1.10 version: 3.1.10 rollup: - specifier: ^4.40.2 - version: 4.40.2 + specifier: ^4.41.0 + version: 4.41.0 tslib: specifier: ^2.8.1 version: 2.8.1 typescript: specifier: ^5.8.3 version: 5.8.3 + ws: + specifier: npm:@kevisual/ws + version: '@kevisual/ws@8.0.0' packages: @@ -137,31 +143,35 @@ packages: '@kevisual/load@0.0.6': resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} - '@kevisual/logger@0.0.3': - resolution: {integrity: sha512-8emqxg+ab62WAK6VY4FQqetXPSSVKFAjGctD1NDbdnxt7YWuI/PyuDltCpsVz+uvWpV1dO5OKZOoHU7ow59Omw==} + '@kevisual/logger@0.0.4': + resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==} '@kevisual/oss@0.0.11': resolution: {integrity: sha512-oxC6KVTNeDUaoAAVHyNVhSg9JPj1FzwbP5PkYvTj2VHC3PvFtvG//m6Zii7yKpFv7itKzMuS7/9xDMdI5lPhaA==} - '@kevisual/permission@0.0.1': - resolution: {integrity: sha512-nSX2LzbPkU3YAMegbUFGU8tfmtFb7dcF5edqzm+gI6crcyCL1JzIB9HAYNEeEVIljLxuREwM/vVg9aFmF4cz9Q==} + '@kevisual/permission@0.0.3': + resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==} '@kevisual/query-config@0.0.2': resolution: {integrity: sha512-JcDq/gS0Y1FQfGm9OxNGZwxm4Pgmu24NEvWrZ6TLf3VCk/WsaOtzkNX5pf2rnwxF9FETaLAwa0g2nAYVLsELRA==} peerDependencies: '@kevisual/query': ^0.0.13 - '@kevisual/query@0.0.17': - resolution: {integrity: sha512-WMvWM+3pNlPKNhoxPX9fldMp1tOeJrkRM/tXA4bvOnftIoX2yeI4v0wTpbGJXES/bLlo7OC2kV8SeKF0K6dnxQ==} + '@kevisual/query@0.0.18': + resolution: {integrity: sha512-I2vHTu0I6AyD9PJyr+vxyp9jIJ6rd2EZqLVHTv/+zrVKVc2SS76Tg7aGNkmAFqqLSCB8kLLsmMGtSJU1Qb8VVg==} - '@kevisual/router@0.0.14': - resolution: {integrity: sha512-eHOSD6gzs/Ed9DDTq9VCEM3uw3Wh1vPFlyPo91cEe2rA44t8oJm8g905qmwHVUEjbo5s/rA306ZW/ub06jlNlg==} + '@kevisual/router@0.0.21': + resolution: {integrity: sha512-XKTxbNO924cT18UOAGplWErZ+hMze8Y53F2jYCk18v4jsdsvjRho5uXXjJb6HSVsuITMtQR4R3rG0IcM3jkDKQ==} - '@kevisual/use-config@1.0.15': - resolution: {integrity: sha512-bLWdGMOPHgIKV4qY3U18cLoOKmSBL72K1wL0MneyEsqj9jRXoc98OMMyQm2/BlBddFTL1olOfByRET2DvwmWAA==} + '@kevisual/use-config@1.0.17': + resolution: {integrity: sha512-EsuMJ5bhAbdERvpD55td1diRxx4kSxtYVaIHo0vDvnLetuXLfq+j2DPGmWl/oRdO48op0dme5oo1DctCqpgYcQ==} peerDependencies: dotenv: ^16.4.7 + '@kevisual/ws@8.0.0': + resolution: {integrity: sha512-jlFxSlXUEz93cFW+UYT5BXv/rFVgiMQnIfqRYZ0gj1hSP8PMGRqMqUoHSLfKvfRRS4jseLSvTTeEKSQpZJtURg==} + engines: {node: '>=10.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -215,103 +225,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.40.2': - resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + '@rollup/rollup-android-arm-eabi@4.41.0': + resolution: {integrity: sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.40.2': - resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + '@rollup/rollup-android-arm64@4.41.0': + resolution: {integrity: sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.40.2': - resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + '@rollup/rollup-darwin-arm64@4.41.0': + resolution: {integrity: sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.40.2': - resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + '@rollup/rollup-darwin-x64@4.41.0': + resolution: {integrity: sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.40.2': - resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + '@rollup/rollup-freebsd-arm64@4.41.0': + resolution: {integrity: sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.40.2': - resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + '@rollup/rollup-freebsd-x64@4.41.0': + resolution: {integrity: sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': - resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.40.2': - resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + '@rollup/rollup-linux-arm-musleabihf@4.41.0': + resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.40.2': - resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + '@rollup/rollup-linux-arm64-gnu@4.41.0': + resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.40.2': - resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + '@rollup/rollup-linux-arm64-musl@4.41.0': + resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': - resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': + resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': - resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': + resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.40.2': - resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + '@rollup/rollup-linux-riscv64-gnu@4.41.0': + resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.40.2': - resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + '@rollup/rollup-linux-riscv64-musl@4.41.0': + resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.40.2': - resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + '@rollup/rollup-linux-s390x-gnu@4.41.0': + resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.40.2': - resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + '@rollup/rollup-linux-x64-gnu@4.41.0': + resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.40.2': - resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + '@rollup/rollup-linux-x64-musl@4.41.0': + resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.40.2': - resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + '@rollup/rollup-win32-arm64-msvc@4.41.0': + resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.40.2': - resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + '@rollup/rollup-win32-ia32-msvc@4.41.0': + resolution: {integrity: sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.40.2': - resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + '@rollup/rollup-win32-x64-msvc@4.41.0': + resolution: {integrity: sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==} cpu: [x64] os: [win32] @@ -357,8 +367,8 @@ packages: '@types/node@18.19.86': resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==} - '@types/node@22.15.17': - resolution: {integrity: sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==} + '@types/node@22.15.21': + resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -369,6 +379,9 @@ packages: '@types/validator@13.12.2': resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@zxing/text-encoding@0.9.0': resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} @@ -977,8 +990,8 @@ packages: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} - openai@4.91.1: - resolution: {integrity: sha512-DbjrR0hIMQFbxz8+3qBsfPJnh3+I/skPgoSlT7f9eiZuhGBUissPQULNgx6gHNkLoZ3uS0uYS6eXPUdtg4nHzw==} + openai@4.102.0: + resolution: {integrity: sha512-CWk15CMhPSHNZnjz+6rwVYV551xaC8CwOd7/zxImrC1btEo37dX/Ii5tBKWfqqxqyzpJ6p3Y4bICzzKhW03WhQ==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -1120,8 +1133,8 @@ packages: retry-as-promised@7.1.1: resolution: {integrity: sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==} - rollup@4.40.2: - resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + rollup@4.41.0: + resolution: {integrity: sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1413,18 +1426,6 @@ packages: utf-8-validate: optional: true - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xml2js@0.6.2: resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} @@ -1473,11 +1474,11 @@ snapshots: '@kevisual/auth@1.0.5': {} - '@kevisual/code-center-module@0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.14)(@kevisual/use-config@1.0.15(dotenv@16.4.7))(ioredis@5.6.1)(pg@8.13.3)(sequelize@6.37.7(pg@8.13.3))': + '@kevisual/code-center-module@0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.21)(@kevisual/use-config@1.0.17(dotenv@16.4.7))(ioredis@5.6.1)(pg@8.13.3)(sequelize@6.37.7(pg@8.13.3))': dependencies: '@kevisual/auth': 1.0.5 - '@kevisual/router': 0.0.14 - '@kevisual/use-config': 1.0.15(dotenv@16.4.7) + '@kevisual/router': 0.0.21 + '@kevisual/use-config': 1.0.17(dotenv@16.4.7) ioredis: 5.6.1 nanoid: 5.1.5 pg: 8.13.3 @@ -1493,40 +1494,42 @@ snapshots: dependencies: eventemitter3: 5.0.1 - '@kevisual/logger@0.0.3': {} + '@kevisual/logger@0.0.4': {} '@kevisual/oss@0.0.11': {} - '@kevisual/permission@0.0.1': {} + '@kevisual/permission@0.0.3': {} - '@kevisual/query-config@0.0.2(@kevisual/query@0.0.17(ws@8.18.0)(zod@3.24.2))': + '@kevisual/query-config@0.0.2(@kevisual/query@0.0.18(@kevisual/ws@8.0.0)(zod@3.24.2))': dependencies: - '@kevisual/query': 0.0.17(ws@8.18.0)(zod@3.24.2) + '@kevisual/query': 0.0.18(@kevisual/ws@8.0.0)(zod@3.24.2) - '@kevisual/query@0.0.17(ws@8.18.0)(zod@3.24.2)': + '@kevisual/query@0.0.18(@kevisual/ws@8.0.0)(zod@3.24.2)': dependencies: - openai: 4.91.1(ws@8.18.0)(zod@3.24.2) + openai: 4.102.0(@kevisual/ws@8.0.0)(zod@3.24.2) transitivePeerDependencies: - encoding - ws - zod - '@kevisual/router@0.0.14': + '@kevisual/router@0.0.21': dependencies: path-to-regexp: 8.2.0 selfsigned: 2.4.1 - '@kevisual/use-config@1.0.15(dotenv@16.4.7)': + '@kevisual/use-config@1.0.17(dotenv@16.4.7)': dependencies: '@kevisual/load': 0.0.6 dotenv: 16.4.7 + '@kevisual/ws@8.0.0': {} + '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/plugin-commonjs@28.0.3(rollup@4.40.2)': + '@rollup/plugin-commonjs@28.0.3(rollup@4.41.0)': dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.40.2) + '@rollup/pluginutils': 5.1.2(rollup@4.41.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.0(picomatch@4.0.2) @@ -1534,110 +1537,110 @@ snapshots: magic-string: 0.30.11 picomatch: 4.0.2 optionalDependencies: - rollup: 4.40.2 + rollup: 4.41.0 - '@rollup/plugin-json@6.1.0(rollup@4.40.2)': + '@rollup/plugin-json@6.1.0(rollup@4.41.0)': dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.40.2) + '@rollup/pluginutils': 5.1.2(rollup@4.41.0) optionalDependencies: - rollup: 4.40.2 + rollup: 4.41.0 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.40.2)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.41.0)': dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.40.2) + '@rollup/pluginutils': 5.1.2(rollup@4.41.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 4.40.2 + rollup: 4.41.0 - '@rollup/plugin-typescript@12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.8.3)': + '@rollup/plugin-typescript@12.1.2(rollup@4.41.0)(tslib@2.8.1)(typescript@5.8.3)': dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.40.2) + '@rollup/pluginutils': 5.1.2(rollup@4.41.0) resolve: 1.22.8 typescript: 5.8.3 optionalDependencies: - rollup: 4.40.2 + rollup: 4.41.0 tslib: 2.8.1 - '@rollup/pluginutils@5.1.2(rollup@4.40.2)': + '@rollup/pluginutils@5.1.2(rollup@4.41.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.40.2 + rollup: 4.41.0 - '@rollup/rollup-android-arm-eabi@4.40.2': + '@rollup/rollup-android-arm-eabi@4.41.0': optional: true - '@rollup/rollup-android-arm64@4.40.2': + '@rollup/rollup-android-arm64@4.41.0': optional: true - '@rollup/rollup-darwin-arm64@4.40.2': + '@rollup/rollup-darwin-arm64@4.41.0': optional: true - '@rollup/rollup-darwin-x64@4.40.2': + '@rollup/rollup-darwin-x64@4.41.0': optional: true - '@rollup/rollup-freebsd-arm64@4.40.2': + '@rollup/rollup-freebsd-arm64@4.41.0': optional: true - '@rollup/rollup-freebsd-x64@4.40.2': + '@rollup/rollup-freebsd-x64@4.41.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.40.2': + '@rollup/rollup-linux-arm-musleabihf@4.41.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.40.2': + '@rollup/rollup-linux-arm64-gnu@4.41.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.40.2': + '@rollup/rollup-linux-arm64-musl@4.41.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.40.2': + '@rollup/rollup-linux-riscv64-gnu@4.41.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.40.2': + '@rollup/rollup-linux-riscv64-musl@4.41.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.40.2': + '@rollup/rollup-linux-s390x-gnu@4.41.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.2': + '@rollup/rollup-linux-x64-gnu@4.41.0': optional: true - '@rollup/rollup-linux-x64-musl@4.40.2': + '@rollup/rollup-linux-x64-musl@4.41.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.40.2': + '@rollup/rollup-win32-arm64-msvc@4.41.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.40.2': + '@rollup/rollup-win32-ia32-msvc@4.41.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.40.2': + '@rollup/rollup-win32-x64-msvc@4.41.0': optional: true '@socket.io/component-emitter@3.1.2': {} '@types/busboy@1.5.4': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 '@types/cors@2.8.17': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 '@types/debug@4.1.12': dependencies: @@ -1649,7 +1652,7 @@ snapshots: '@types/http-proxy@1.17.16': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 '@types/lodash-es@4.17.12': dependencies: @@ -1663,18 +1666,18 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 form-data: 4.0.2 '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 '@types/node@18.19.86': dependencies: undici-types: 5.26.5 - '@types/node@22.15.17': + '@types/node@22.15.21': dependencies: undici-types: 6.21.0 @@ -1683,10 +1686,14 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.15.17 + '@types/node': 22.15.21 '@types/validator@13.12.2': {} + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.15.21 + '@zxing/text-encoding@0.9.0': optional: true @@ -1944,7 +1951,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.17 - '@types/node': 22.15.17 + '@types/node': 22.15.21 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -2288,7 +2295,7 @@ snapshots: dependencies: ee-first: 1.1.1 - openai@4.91.1(ws@8.18.0)(zod@3.24.2): + openai@4.102.0(@kevisual/ws@8.0.0)(zod@3.24.2): dependencies: '@types/node': 18.19.86 '@types/node-fetch': 2.6.12 @@ -2298,7 +2305,7 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - ws: 8.18.0 + ws: '@kevisual/ws@8.0.0' zod: 3.24.2 transitivePeerDependencies: - encoding @@ -2428,30 +2435,30 @@ snapshots: retry-as-promised@7.1.1: {} - rollup@4.40.2: + rollup@4.41.0: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.2 - '@rollup/rollup-android-arm64': 4.40.2 - '@rollup/rollup-darwin-arm64': 4.40.2 - '@rollup/rollup-darwin-x64': 4.40.2 - '@rollup/rollup-freebsd-arm64': 4.40.2 - '@rollup/rollup-freebsd-x64': 4.40.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 - '@rollup/rollup-linux-arm-musleabihf': 4.40.2 - '@rollup/rollup-linux-arm64-gnu': 4.40.2 - '@rollup/rollup-linux-arm64-musl': 4.40.2 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 - '@rollup/rollup-linux-riscv64-gnu': 4.40.2 - '@rollup/rollup-linux-riscv64-musl': 4.40.2 - '@rollup/rollup-linux-s390x-gnu': 4.40.2 - '@rollup/rollup-linux-x64-gnu': 4.40.2 - '@rollup/rollup-linux-x64-musl': 4.40.2 - '@rollup/rollup-win32-arm64-msvc': 4.40.2 - '@rollup/rollup-win32-ia32-msvc': 4.40.2 - '@rollup/rollup-win32-x64-msvc': 4.40.2 + '@rollup/rollup-android-arm-eabi': 4.41.0 + '@rollup/rollup-android-arm64': 4.41.0 + '@rollup/rollup-darwin-arm64': 4.41.0 + '@rollup/rollup-darwin-x64': 4.41.0 + '@rollup/rollup-freebsd-arm64': 4.41.0 + '@rollup/rollup-freebsd-x64': 4.41.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.0 + '@rollup/rollup-linux-arm-musleabihf': 4.41.0 + '@rollup/rollup-linux-arm64-gnu': 4.41.0 + '@rollup/rollup-linux-arm64-musl': 4.41.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-musl': 4.41.0 + '@rollup/rollup-linux-s390x-gnu': 4.41.0 + '@rollup/rollup-linux-x64-gnu': 4.41.0 + '@rollup/rollup-linux-x64-musl': 4.41.0 + '@rollup/rollup-win32-arm64-msvc': 4.41.0 + '@rollup/rollup-win32-ia32-msvc': 4.41.0 + '@rollup/rollup-win32-x64-msvc': 4.41.0 fsevents: 2.3.3 rxjs@7.8.2: @@ -2726,7 +2733,7 @@ snapshots: wkx@0.5.0: dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.21 wrap-ansi@7.0.0: dependencies: @@ -2742,9 +2749,6 @@ snapshots: ws@8.17.1: {} - ws@8.18.0: - optional: true - xml2js@0.6.2: dependencies: sax: 1.4.1 diff --git a/src/index.ts b/src/index.ts index 5dd6747..a6649c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,8 @@ import { config } from './module/config.ts'; import { app } from './app.ts'; import './route/route.ts'; import net from 'net'; +import { WssApp } from './module/ws-proxy/index.ts'; + const port = config?.proxy?.port || 3005; app @@ -21,10 +23,16 @@ app.listen(port, () => { app.server.on(handleRequest); +const wssApp = new WssApp(); const main = () => { console.log('Upgrade initialization started'); - app.server.server.on('upgrade', (req, socket, head) => { + app.server.server.on('upgrade', async (req, socket, head) => { + const isUpgrade = wssApp.upgrade(req, socket, head); + if (isUpgrade) { + console.log('WebSocket upgrade successful for path:', req.url); + return; + } const proxyApiList = config?.apiList || []; const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path)); @@ -40,12 +48,12 @@ const main = () => { const proxySocket = net.connect(options.port, options.hostname, () => { proxySocket.write( `GET ${options.path} HTTP/1.1\r\n` + - `Host: ${options.hostname}\r\n` + - `Connection: Upgrade\r\n` + - `Upgrade: websocket\r\n` + - `Sec-WebSocket-Key: ${req.headers['sec-websocket-key']}\r\n` + - `Sec-WebSocket-Version: ${req.headers['sec-websocket-version']}\r\n` + - `\r\n` + `Host: ${options.hostname}\r\n` + + `Connection: Upgrade\r\n` + + `Upgrade: websocket\r\n` + + `Sec-WebSocket-Key: ${req.headers['sec-websocket-key']}\r\n` + + `Sec-WebSocket-Version: ${req.headers['sec-websocket-version']}\r\n` + + `\r\n`, ); proxySocket.pipe(socket); socket.pipe(proxySocket); diff --git a/src/module/index.ts b/src/module/index.ts index 7d789bc..0a58912 100644 --- a/src/module/index.ts +++ b/src/module/index.ts @@ -13,6 +13,7 @@ import { getLoginUser } from '@/middleware/auth.ts'; import { rediretHome } from './user-home/index.ts'; import { aiProxy } from './proxy/ai-proxy.ts'; import { logger } from './logger.ts'; +import { UserV1Proxy } from './ws-proxy/proxy.ts'; const domain = config?.proxy?.domain; const allowedOrigins = config?.proxy?.allowedOrigin || []; @@ -221,8 +222,8 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR res.write('Server Error\n'); res.end(); }; - const createNotFoundPage = async (msg?: string) => { - res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); + const createNotFoundPage = async (msg?: string, code = 404) => { + res.writeHead(code, { 'Content-Type': 'text/html; charset=utf-8' }); res.write(msg || 'Not Found App\n'); res.end(); }; @@ -231,6 +232,11 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR createNotFoundPage, }); } + if (user !== 'api' && app === 'v1') { + return UserV1Proxy(req, res, { + createNotFoundPage, + }); + } const userApp = new UserApp({ user, app }); let isExist = await userApp.getExist(); diff --git a/src/module/ws-proxy/index.ts b/src/module/ws-proxy/index.ts new file mode 100644 index 0000000..4008846 --- /dev/null +++ b/src/module/ws-proxy/index.ts @@ -0,0 +1,72 @@ +import { WebSocketServer } from 'ws'; +import { nanoid } from 'nanoid'; +import { WsProxyManager } from './manager.ts'; +import { getLoginUser } from '@/middleware/auth.ts'; +export const wsProxyManager = new WsProxyManager(); + +export const upgrade = async (request: any, socket: any, head: any) => { + const req = request as any; + const url = new URL(req.url, 'http://localhost'); + const id = url.searchParams.get('id'); + if (url.pathname === '/ws/proxy') { + console.log('upgrade', request.url, id); + wss.handleUpgrade(req, socket, head, (ws) => { + // 这里手动触发 connection 事件 + // @ts-ignore + wss.emit('connection', ws, req); + }); + return true; + } + return false; +}; +export const wss = new WebSocketServer({ + noServer: true, + path: '/ws/proxy', +}); + +wss.on('connection', async (ws, req) => { + console.log('connected', req.url); + // const user = await getLoginUser(req); + // if (!user) { + // ws.send( + // JSON.stringify({ + // type: 'error', + // message: 'Invalid authorization', + // }), + // ); + // ws.close(); + // return; + // } + const url = new URL(req.url, 'http://localhost'); + const id = url?.searchParams?.get('id') || nanoid(); + const user = 'root'; + wsProxyManager.register(id, { user, ws }); + ws.send( + JSON.stringify({ + type: 'connected', + id, + }), + ); + ws.on('message', async (event: Buffer) => { + const eventData = event.toString(); + if (!eventData) { + return; + } + const data = JSON.parse(eventData); + console.log('message', data); + }); + ws.on('close', () => { + console.log('ws closed'); + wsProxyManager.unregister(id, user); + }); +}); + +export class WssApp { + wss: WebSocketServer; + constructor() { + this.wss = wss; + } + upgrade(request: any, socket: any, head: any) { + return upgrade(request, socket, head); + } +} diff --git a/src/module/ws-proxy/manager.ts b/src/module/ws-proxy/manager.ts new file mode 100644 index 0000000..2edc3b2 --- /dev/null +++ b/src/module/ws-proxy/manager.ts @@ -0,0 +1,84 @@ +import { nanoid } from 'nanoid'; +import { WebSocket } from 'ws'; +import { logger } from '../logger.ts'; +class WsMessage { + ws: WebSocket; + user?: string; + constructor({ ws, user }: WssMessageOptions) { + this.ws = ws; + this.user = user; + } + async sendData(data: any, opts?: { timeout?: number }) { + if (this.ws.readyState !== WebSocket.OPEN) { + return { code: 500, message: 'WebSocket is not open' }; + } + const timeout = opts?.timeout || 10 * 6 * 1000; // 10 minutes + const id = nanoid(); + const message = JSON.stringify({ + id, + type: 'proxy', + data, + }); + logger.info('ws-proxy sendData', message); + this.ws.send(message); + return new Promise((resolve) => { + const timer = setTimeout(() => { + resolve({ + code: 500, + message: 'timeout', + }); + }, timeout); + this.ws.once('message', (event: Buffer) => { + const eventData = event.toString(); + if (!eventData) { + return; + } + const data = JSON.parse(eventData); + if (data.id === id) { + resolve(data.data); + clearTimeout(timer); + } + }); + }); + } +} +type WssMessageOptions = { + ws: WebSocket; + user?: string; +}; +export class WsProxyManager { + wssMap: Map = new Map(); + constructor() {} + getId(id: string, user?: string) { + return id + '/' + user; + } + register(id: string, opts?: { ws: WebSocket; user: string }) { + const _id = this.getId(id, opts?.user || ''); + if (this.wssMap.has(_id)) { + const value = this.wssMap.get(_id); + if (value) { + value.ws.close(); + } + } + const value = new WsMessage({ ws: opts?.ws, user: opts?.user }); + this.wssMap.set(_id, value); + } + unregister(id: string, user?: string) { + const _id = this.getId(id, user || ''); + const value = this.wssMap.get(_id); + if (value) { + value.ws.close(); + } + this.wssMap.delete(_id); + } + getIds() { + return Array.from(this.wssMap.keys()); + } + get(id: string, user?: string) { + if (user) { + const _id = this.getId(id, user); + return this.wssMap.get(_id); + } + return this.wssMap.get(id); + } +} diff --git a/src/module/ws-proxy/proxy.ts b/src/module/ws-proxy/proxy.ts new file mode 100644 index 0000000..7e39004 --- /dev/null +++ b/src/module/ws-proxy/proxy.ts @@ -0,0 +1,35 @@ +import { IncomingMessage, ServerResponse } from 'http'; +import { wsProxyManager } from './index.ts'; + +import { App } from '@kevisual/router'; +import { log } from 'console'; +import { logger } from '../logger.ts'; + +type ProxyOptions = { + createNotFoundPage: (msg?: string) => any; +}; +export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opts?: ProxyOptions) => { + const { url } = req; + const { pathname } = new URL(url || '', `http://localhost`); + const [user, app, userAppKey] = pathname.split('/').slice(1); + if (!user || !app || !userAppKey) { + opts?.createNotFoundPage?.('应用未启动'); + return false; + } + const data = await App.handleRequest(req, res); + logger.debug('data', data); + const client = wsProxyManager.get(userAppKey, user); + const ids = wsProxyManager.getIds(); + if (!client) { + opts?.createNotFoundPage?.(`应用未启动, 未找到应用, ${userAppKey}, ${ids.join(',')}`); + return false; + } + const value = await client.sendData(data); + if (value) { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(value)); + return true; + } + opts?.createNotFoundPage?.('应用未启动'); + return true; +};