From bbdf9f087d4d4d89bded7a6d222ed151658eadff Mon Sep 17 00:00:00 2001 From: abearxiong Date: Thu, 5 Mar 2026 03:58:46 +0800 Subject: [PATCH] feat: enhance WebSocket proxy with user connection management and status reporting - Updated StudioOpts type to include infoList for user connection status. - Added rendering of connection info in createStudioAppListHtml. - Modified self-restart logic to use a specific app path. - Improved WebSocket connection handling in wsProxyManager, including user registration and ID management. - Implemented connection status checks and responses in UserV1Proxy. - Introduced renderServerHtml function to inject server data into HTML responses. - Refactored page-proxy request handling for better URL management. --- package.json | 10 +- pnpm-lock.yaml | 422 +++++++++++----------- src/modules/html/render-server-html.ts | 6 + src/modules/html/studio-app-list/index.ts | 14 +- src/modules/self-restart.ts | 2 +- src/modules/v1-ws-proxy/index.ts | 52 ++- src/modules/v1-ws-proxy/manager.ts | 131 ++++++- src/modules/v1-ws-proxy/proxy.ts | 71 +++- src/routes-simple/page-proxy.ts | 13 +- 9 files changed, 451 insertions(+), 270 deletions(-) create mode 100644 src/modules/html/render-server-html.ts diff --git a/package.json b/package.json index ff111f7..861a9f2 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ ], "license": "UNLICENSED", "dependencies": { - "@kevisual/ai": "^0.0.24", + "@kevisual/ai": "^0.0.26", "@kevisual/auth": "^2.0.3", "@kevisual/js-filter": "^0.0.5", "@kevisual/query": "^0.0.52", @@ -58,7 +58,7 @@ "xml2js": "^0.6.2" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.1000.0", + "@aws-sdk/client-s3": "^3.1001.0", "@kevisual/api": "^0.0.60", "@kevisual/cnb": "^0.0.33", "@kevisual/context": "^0.0.8", @@ -67,11 +67,11 @@ "@kevisual/logger": "^0.0.4", "@kevisual/oss": "0.0.20", "@kevisual/permission": "^0.0.4", - "@kevisual/router": "0.0.84", + "@kevisual/router": "0.0.85", "@kevisual/types": "^0.0.12", "@kevisual/use-config": "^1.0.30", "@types/archiver": "^7.0.0", - "@types/bun": "^1.3.9", + "@types/bun": "^1.3.10", "@types/crypto-js": "^4.2.2", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^25.3.3", @@ -83,7 +83,7 @@ "crypto-js": "^4.2.0", "dayjs": "^1.11.19", "dotenv": "^17.3.1", - "es-toolkit": "^1.44.0", + "es-toolkit": "^1.45.1", "ioredis": "^5.10.0", "jsonwebtoken": "^9.0.3", "nanoid": "^5.1.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8693ad0..616c24a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,8 +14,8 @@ importers: .: dependencies: '@kevisual/ai': - specifier: ^0.0.24 - version: 0.0.24 + specifier: ^0.0.26 + version: 0.0.26 '@kevisual/auth': specifier: ^2.0.3 version: 2.0.3 @@ -45,10 +45,10 @@ importers: version: 0.31.9 drizzle-orm: specifier: ^0.45.1 - version: 0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.9)(pg@8.19.0) + version: 0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.10)(pg@8.19.0) drizzle-zod: specifier: ^0.8.3 - version: 0.8.3(drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.9)(pg@8.19.0))(zod@4.3.6) + version: 0.8.3(drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.10)(pg@8.19.0))(zod@4.3.6) eventemitter3: specifier: ^5.0.4 version: 5.0.4 @@ -63,8 +63,8 @@ importers: version: 0.6.2 devDependencies: '@aws-sdk/client-s3': - specifier: ^3.1000.0 - version: 3.1000.0 + specifier: ^3.1001.0 + version: 3.1001.0 '@kevisual/api': specifier: ^0.0.60 version: 0.0.60(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -90,8 +90,8 @@ importers: specifier: ^0.0.4 version: 0.0.4 '@kevisual/router': - specifier: 0.0.84 - version: 0.0.84 + specifier: 0.0.85 + version: 0.0.85 '@kevisual/types': specifier: ^0.0.12 version: 0.0.12 @@ -102,8 +102,8 @@ importers: specifier: ^7.0.0 version: 7.0.0 '@types/bun': - specifier: ^1.3.9 - version: 1.3.9 + specifier: ^1.3.10 + version: 1.3.10 '@types/crypto-js': specifier: ^4.2.2 version: 4.2.2 @@ -138,8 +138,8 @@ importers: specifier: ^17.3.1 version: 17.3.1 es-toolkit: - specifier: ^1.44.0 - version: 1.44.0 + specifier: ^1.45.1 + version: 1.45.1 ioredis: specifier: ^5.9.3 version: 5.9.3 @@ -218,48 +218,48 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1000.0': - resolution: {integrity: sha512-7kPy33qNGq3NfwHC0412T6LDK1bp4+eiPzetX0sVd9cpTSXuQDKpoOFnB0Njj6uZjJDcLS3n2OeyarwwgkQ0Ow==} + '@aws-sdk/client-s3@3.1001.0': + resolution: {integrity: sha512-uKgFjQuBjMcd0iigLQwnqIp9gOy/5TGBxa42rcb6l5byDt1mrwOe6fyWTEUEJaNHG2LKYSPUibteGvM1zfm0Rw==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.15': - resolution: {integrity: sha512-AlC0oQ1/mdJ8vCIqu524j5RB7M8i8E24bbkZmya1CuiQxkY7SdIZAyw7NDNMGaNINQFq/8oGRMX0HeOfCVsl/A==} + '@aws-sdk/core@3.973.16': + resolution: {integrity: sha512-Nasoyb5K4jfvncTKQyA13q55xHoz9as01NVYP05B0Kzux/X5UhMn3qXsZDyWOSXkfSCAIrMBKmVVWbI0vUapdQ==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.3': resolution: {integrity: sha512-UExeK+EFiq5LAcbHm96CQLSia+5pvpUVSAsVApscBzayb7/6dJBJKwV4/onsk4VbWSmqxDMcfuTD+pC4RxgZHg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.13': - resolution: {integrity: sha512-6ljXKIQ22WFKyIs1jbORIkGanySBHaPPTOI4OxACP5WXgbcR0nDYfqNJfXEGwCK7IzHdNbCSFsNKKs0qCexR8Q==} + '@aws-sdk/credential-provider-env@3.972.14': + resolution: {integrity: sha512-PvnBY9rwBuLh9MEsAng28DG+WKl+txerKgf4BU9IPAqYI7FBIo1x6q/utLf4KLyQYgSy1TLQnbQuXx5xfBGASg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.15': - resolution: {integrity: sha512-dJuSTreu/T8f24SHDNTjd7eQ4rabr0TzPh2UTCwYexQtzG3nTDKm1e5eIdhiroTMDkPEJeY+WPkA6F9wod/20A==} + '@aws-sdk/credential-provider-http@3.972.16': + resolution: {integrity: sha512-m/QAcvw5OahqGPjeAnKtgfWgjLxeWOYj7JSmxKK6PLyKp2S/t2TAHI6EELEzXnIz28RMgbQLukJkVAqPASVAGQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.13': - resolution: {integrity: sha512-JKSoGb7XeabZLBJptpqoZIFbROUIS65NuQnEHGOpuT9GuuZwag2qciKANiDLFiYk4u8nSrJC9JIOnWKVvPVjeA==} + '@aws-sdk/credential-provider-ini@3.972.14': + resolution: {integrity: sha512-EGA7ufqNpZKZcD0RwM6gRDEQgwAf19wQ99R1ptdWYDJAnpcMcWiFyT0RIrgiZFLD28CwJmYjnra75hChnEveWA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.13': - resolution: {integrity: sha512-RtYcrxdnJHKY8MFQGLltCURcjuMjnaQpAxPE6+/QEdDHHItMKZgabRe/KScX737F9vJMQsmJy9EmMOkCnoC1JQ==} + '@aws-sdk/credential-provider-login@3.972.14': + resolution: {integrity: sha512-P2kujQHAoV7irCTv6EGyReKFofkHCjIK+F0ZYf5UxeLeecrCwtrDkHoO2Vjsv/eRUumaKblD8czuk3CLlzwGDw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.14': - resolution: {integrity: sha512-WqoC2aliIjQM/L3oFf6j+op/enT2i9Cc4UTxxMEKrJNECkq4/PlKE5BOjSYFcq6G9mz65EFbXJh7zOU4CvjSKQ==} + '@aws-sdk/credential-provider-node@3.972.15': + resolution: {integrity: sha512-59NBJgTcQ2FC94T+SWkN5UQgViFtrLnkswSKhG5xbjPAotOXnkEF2Bf0bfUV1F3VaXzqAPZJoZ3bpg4rr8XD5Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.13': - resolution: {integrity: sha512-rsRG0LQA4VR+jnDyuqtXi2CePYSmfm5GNL9KxiW8DSe25YwJSr06W8TdUfONAC+rjsTI+aIH2rBGG5FjMeANrw==} + '@aws-sdk/credential-provider-process@3.972.14': + resolution: {integrity: sha512-KAF5LBkJInUPaR9dJDw8LqmbPDRTLyXyRoWVGcJQ+DcN9rxVKBRzAK+O4dTIvQtQ7xaIDZ2kY7zUmDlz6CCXdw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.13': - resolution: {integrity: sha512-fr0UU1wx8kNHDhTQBXioc/YviSW8iXuAxHvnH7eQUtn8F8o/FU3uu6EUMvAQgyvn7Ne5QFnC0Cj0BFlwCk+RFw==} + '@aws-sdk/credential-provider-sso@3.972.14': + resolution: {integrity: sha512-LQzIYrNABnZzkyuIguFa3VVOox9UxPpRW6PL+QYtRHaGl1Ux/+Zi54tAVK31VdeBKPKU3cxqeu8dbOgNqy+naw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.13': - resolution: {integrity: sha512-a6iFMh1pgUH0TdcouBppLJUfPM7Yd3R9S1xFodPtCRoLqCz2RQFA3qjA8x4112PVYXEd4/pHX2eihapq39w0rA==} + '@aws-sdk/credential-provider-web-identity@3.972.14': + resolution: {integrity: sha512-rOwB3vXHHHnGvAOjTgQETxVAsWjgF61XlbGd/ulvYo7EpdXs8cbIHE3PGih9tTj/65ZOegSqZGFqLaKntaI9Kw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.972.6': @@ -270,8 +270,8 @@ packages: resolution: {integrity: sha512-QMdffpU+GkSGC+bz6WdqlclqIeCsOfgX8JFZ5xvwDtX+UTj4mIXm3uXu7Ko6dBseRcJz1FA6T9OmlAAY6JgJUg==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.973.1': - resolution: {integrity: sha512-QLXsxsI6VW8LuGK+/yx699wzqP/NMCGk/hSGP+qtB+Lcff+23UlbahyouLlk+nfT7Iu021SkXBhnAuVd6IZcPw==} + '@aws-sdk/middleware-flexible-checksums@3.973.2': + resolution: {integrity: sha512-KM6QujWdasNjRLG+f7YEqEY5D36vR6Govm7nPIwxjILpb5rJ0pPJZpYY1nrzgtlxwJIYAznfBK5YXoLOHKHyfQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.6': @@ -290,32 +290,32 @@ packages: resolution: {integrity: sha512-dY4v3of5EEMvik6+UDwQ96KfUFDk8m1oZDdkSc5lwi4o7rFrjnv0A+yTV+gu230iybQZnKgDLg/rt2P3H+Vscw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.15': - resolution: {integrity: sha512-WDLgssevOU5BFx1s8jA7jj6cE5HuImz28sy9jKOaVtz0AW1lYqSzotzdyiybFaBcQTs5zxXOb2pUfyMxgEKY3Q==} + '@aws-sdk/middleware-sdk-s3@3.972.16': + resolution: {integrity: sha512-U4K1rqyJYvT/zgTI3+rN+MToa51dFnnq1VSsVJuJWPNEKcEnuZVqf7yTpkJJMkYixVW5TTi1dgupd+nmJ0JyWw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.6': resolution: {integrity: sha512-acvMUX9jF4I2Ew+Z/EA6gfaFaz9ehci5wxBmXCZeulLuv8m+iGf6pY9uKz8TPjg39bdAz3hxoE0eLP8Qz+IYlA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.15': - resolution: {integrity: sha512-ABlFVcIMmuRAwBT+8q5abAxOr7WmaINirDJBnqGY5b5jSDo00UMlg/G4a0xoAgwm6oAECeJcwkvDlxDwKf58fQ==} + '@aws-sdk/middleware-user-agent@3.972.16': + resolution: {integrity: sha512-AmVxtxn8ZkNJbuPu3KKfW9IkJgTgcEtgSwbo0NVcAb31iGvLgHXj2nbbyrUDfh2fx8otXmqL+qw1lRaTi+V3vA==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.3': - resolution: {integrity: sha512-AU5TY1V29xqwg/MxmA2odwysTez+ccFAhmfRJk+QZT5HNv90UTA9qKd1J9THlsQkvmH7HWTEV1lDNxkQO5PzNw==} + '@aws-sdk/nested-clients@3.996.4': + resolution: {integrity: sha512-NowB1HfOnWC4kwZOnTg8E8rSL0U+RSjSa++UtEV4ipoH6JOjMLnHyGilqwl+Pe1f0Al6v9yMkSJ/8Ot0f578CQ==} engines: {node: '>=20.0.0'} '@aws-sdk/region-config-resolver@3.972.6': resolution: {integrity: sha512-Aa5PusHLXAqLTX1UKDvI3pHQJtIsF7Q+3turCHqfz/1F61/zDMWfbTC8evjhrrYVAtz9Vsv3SJ/waSUeu7B6gw==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.3': - resolution: {integrity: sha512-gQYI/Buwp0CAGQxY7mR5VzkP56rkWq2Y1ROkFuXh5XY94DsSjJw62B3I0N0lysQmtwiL2ht2KHI9NylM/RP4FA==} + '@aws-sdk/signature-v4-multi-region@3.996.4': + resolution: {integrity: sha512-MGa8ro0onekYIiesHX60LwKdkxK3Kd61p7TTbLwZemBqlnD9OLrk9sXZdFOIxXanJ+3AaJnV/jiX866eD/4PDg==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.999.0': - resolution: {integrity: sha512-cx0hHUlgXULfykx4rdu/ciNAJaa3AL5xz3rieCz7NKJ68MJwlj3664Y8WR5MGgxfyYJBdamnkjNSx5Kekuc0cg==} + '@aws-sdk/token-providers@3.1001.0': + resolution: {integrity: sha512-09XAq/uIYgeZhohuGRrR/R+ek3+ljFNdzWCXdqb9rlIERDjSfNiLjTtpHgSK1xTPmC5G4yWoEAyMfTXiggS6wA==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.4': @@ -337,8 +337,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.6': resolution: {integrity: sha512-Fwr/llD6GOrFgQnKaI2glhohdGuBDfHfora6iG9qsBBBR8xv1SdCSwbtf5CWlUdCw5X7g76G/9Hf0Inh0EmoxA==} - '@aws-sdk/util-user-agent-node@3.973.0': - resolution: {integrity: sha512-A9J2G4Nf236e9GpaC1JnA8wRn6u6GjnOXiTwBLA6NUJhlBTIGfrTy+K1IazmF8y+4OFdW3O5TZlhyspJMqiqjA==} + '@aws-sdk/util-user-agent-node@3.973.1': + resolution: {integrity: sha512-kmgbDqT7aCBEVrqESM2JUjbf0zhDUQ7wnt3q1RuVS+3mglrcfVb2bwkbmf38npOyyPGtQPV5dWN3m+sSFAVAgQ==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -346,8 +346,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.8': - resolution: {integrity: sha512-Ql8elcUdYCha83Ol7NznBsgN5GVZnv3vUd86fEc6waU6oUdY0T1O9NODkEEOS/Uaogr87avDrUC6DSeM4oXjZg==} + '@aws-sdk/xml-builder@3.972.9': + resolution: {integrity: sha512-ItnlMgSqkPrUfJs7EsvU/01zw5UeIb2tNPhD09LBLHbg+g+HDiKibSLwpkuz/ZIlz4F2IMn+5XgE4AK/pfPuog==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.3': @@ -816,8 +816,8 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@kevisual/ai@0.0.24': - resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==} + '@kevisual/ai@0.0.26': + resolution: {integrity: sha512-lhaMpxi+vgqPdyBKiuNbSil4hy13tNLbDiqCtG0qUXKtvoowK6xMx269pSSYkYBivczM8g8I0XEouuJceUpJPg==} '@kevisual/api@0.0.60': resolution: {integrity: sha512-NTFDx1ns/iGli2fUJLJZRWu8nf5VkXV+sOQUqGGAJvrvGATvXSuITu6mD4P/aDQakx4hzQUPr9wDTZoNk7+RqQ==} @@ -852,15 +852,9 @@ packages: '@kevisual/oss@0.0.20': resolution: {integrity: sha512-apsE+D79kNQrgdvgGzoS/kGEEfrO58WoHSFpUQOL1leuGdcmiT6pKLhBaWiAkmiJkacLBRwUNUTjz6xI+4liBg==} - '@kevisual/permission@0.0.3': - resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==} - '@kevisual/permission@0.0.4': resolution: {integrity: sha512-zwBYPnT/z21W4q2wkklJrxvoYBYWG/+a3iXFDKqXQAnDOcxm/SU1f1N6FQb9KxGKl36/fclVlhxlxqszvKCenQ==} - '@kevisual/query@0.0.38': - resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==} - '@kevisual/query@0.0.39': resolution: {integrity: sha512-3UEPBIvtdykNkrby3hvrgrHdgd17Uq+Pnr4zs+JBzATkU2eKaOqtTUJqdyIEwuySCwzGTxrnlUzWP4tziDQDLQ==} @@ -873,6 +867,9 @@ packages: '@kevisual/router@0.0.84': resolution: {integrity: sha512-l/TUFuqTJegB/S3FZQRBMUoz0Spvg8EzV3C/kBi/VO9KKCzjqZDVvhZJJbTQh9879CBY6vUy1ajo9WcLYnwbNA==} + '@kevisual/router@0.0.85': + resolution: {integrity: sha512-ihSzPXHOMSOnZD/+Eso4yZMt4MoUXyLdfRHhXJGg90+sJBr/BjsmgAokit4pI9gWU+Rs/3JqQ2/aqA43FHtGoA==} + '@kevisual/types@0.0.12': resolution: {integrity: sha512-zJXH2dosir3jVrQ6QG4i0+iLQeT9gJ3H+cKXs8ReWboxBSYzUZO78XssVeVrFPsJ33iaAqo4q3DWbSS1dWGn7Q==} @@ -966,8 +963,8 @@ packages: resolution: {integrity: sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.6': - resolution: {integrity: sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==} + '@smithy/core@3.23.7': + resolution: {integrity: sha512-/+ldRdtiO5Cb26afAZOG1FZM0x7D4AYdjpyOv2OScJw+4C7X+OLdRnNKF5UyUE0VpPgSKr3rnF/kvprRA4h2kg==} engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.2.10': @@ -994,8 +991,8 @@ packages: resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.11': - resolution: {integrity: sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==} + '@smithy/fetch-http-handler@5.3.12': + resolution: {integrity: sha512-muS5tFw+A/uo+U+yig06vk1776UFM+aAp9hFM8efI4ZcHhTcgv6NTeK4x7ltHeMPBwnhEjcf0MULTyxNkSNxDw==} engines: {node: '>=18.0.0'} '@smithy/hash-blob-browser@4.2.11': @@ -1030,12 +1027,12 @@ packages: resolution: {integrity: sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.20': - resolution: {integrity: sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==} + '@smithy/middleware-endpoint@4.4.21': + resolution: {integrity: sha512-CoVGZaqIC0tEjz0ga3ciwCMA5fd/4lIOwO2wx0fH+cTi1zxSFZnMJbIiIF9G1d4vRSDyTupDrpS3FKBBJGkRZg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.37': - resolution: {integrity: sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==} + '@smithy/middleware-retry@4.4.38': + resolution: {integrity: sha512-WdHvdhjE6Fj78vxFwDKFDwlqGOGRUWrwGeuENUbTVE46Su9mnQM+dXHtbnCaQvwuSYrRsjpe8zUsFpwUp/azlA==} engines: {node: '>=18.0.0'} '@smithy/middleware-serde@4.2.11': @@ -1050,8 +1047,8 @@ packages: resolution: {integrity: sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.12': - resolution: {integrity: sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==} + '@smithy/node-http-handler@4.4.13': + resolution: {integrity: sha512-o8CP8w6tlUA0lk+Qfwm6Ed0jCWk3bEY6iBOJjdBaowbXKCSClk8zIHQvUL6RUZMvuNafF27cbRCMYqw6O1v4aA==} engines: {node: '>=18.0.0'} '@smithy/property-provider@4.2.10': @@ -1082,8 +1079,8 @@ packages: resolution: {integrity: sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.0': - resolution: {integrity: sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==} + '@smithy/smithy-client@4.12.1': + resolution: {integrity: sha512-Xf9UFHlAihewfkmLNZ6I/Ek6kcYBKoU3cbRS9Z4q++9GWoW0YFbAHs7wMbuXm+nGuKHZ5OKheZMuDdaWPv8DJw==} engines: {node: '>=18.0.0'} '@smithy/types@4.13.0': @@ -1118,12 +1115,12 @@ packages: resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.36': - resolution: {integrity: sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==} + '@smithy/util-defaults-mode-browser@4.3.37': + resolution: {integrity: sha512-JlPZhV1kQCGNJgofRTU6E8kHrjCKsb6cps8gco8QDVaFl7biFYzHg0p1x89ytIWyVyCkY3nOpO8tJPM47Vqlww==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.39': - resolution: {integrity: sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==} + '@smithy/util-defaults-mode-node@4.2.40': + resolution: {integrity: sha512-BM5cPEsyxHdYYO4Da77E94lenhaVPNUzBTyCGDkcw/n/mE8Q1cfHwr+n/w2bNPuUsPC30WaW5/hGKWOTKqw8kw==} engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.3.1': @@ -1142,8 +1139,8 @@ packages: resolution: {integrity: sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.15': - resolution: {integrity: sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==} + '@smithy/util-stream@4.5.16': + resolution: {integrity: sha512-c7awZV6cxY0czgDDSr+Bz0XfRtg8AwW2BWhrHhLJISrpmwv8QzA2qzTllWyMVNdy1+UJr9vCm29hzuh3l8TTFw==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.2.1': @@ -1172,8 +1169,8 @@ packages: '@types/archiver@7.0.0': resolution: {integrity: sha512-/3vwGwx9n+mCQdYZ2IKGGHEFL30I96UgBlk8EtRDDFQ9uxM1l4O5Ci6r00EMAkiDaTqD9DQ6nVrWRICnBPtzzg==} - '@types/bun@1.3.9': - resolution: {integrity: sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==} + '@types/bun@1.3.10': + resolution: {integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==} '@types/busboy@1.5.4': resolution: {integrity: sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==} @@ -1339,8 +1336,8 @@ packages: bullmq@5.70.1: resolution: {integrity: sha512-HjfGHfICkAClrFL0Y07qNbWcmiOCv1l+nusupXUjrvTPuDEyPEJ23MP0lUwUs/QEy1a3pWt/P/sCsSZ1RjRK+w==} - bun-types@1.3.9: - resolution: {integrity: sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==} + bun-types@1.3.10: + resolution: {integrity: sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg==} busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} @@ -1644,6 +1641,9 @@ packages: es-toolkit@1.44.0: resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + esbuild-register@3.6.0: resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} peerDependencies: @@ -1723,8 +1723,11 @@ packages: fast-json-patch@3.1.1: resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} - fast-xml-parser@5.3.6: - resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} + + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} hasBin: true fclone@1.0.11: @@ -2707,76 +2710,76 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1000.0': + '@aws-sdk/client-s3@3.1001.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.15 - '@aws-sdk/credential-provider-node': 3.972.14 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/credential-provider-node': 3.972.15 '@aws-sdk/middleware-bucket-endpoint': 3.972.6 '@aws-sdk/middleware-expect-continue': 3.972.6 - '@aws-sdk/middleware-flexible-checksums': 3.973.1 + '@aws-sdk/middleware-flexible-checksums': 3.973.2 '@aws-sdk/middleware-host-header': 3.972.6 '@aws-sdk/middleware-location-constraint': 3.972.6 '@aws-sdk/middleware-logger': 3.972.6 '@aws-sdk/middleware-recursion-detection': 3.972.6 - '@aws-sdk/middleware-sdk-s3': 3.972.15 + '@aws-sdk/middleware-sdk-s3': 3.972.16 '@aws-sdk/middleware-ssec': 3.972.6 - '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/middleware-user-agent': 3.972.16 '@aws-sdk/region-config-resolver': 3.972.6 - '@aws-sdk/signature-v4-multi-region': 3.996.3 + '@aws-sdk/signature-v4-multi-region': 3.996.4 '@aws-sdk/types': 3.973.4 '@aws-sdk/util-endpoints': 3.996.3 '@aws-sdk/util-user-agent-browser': 3.972.6 - '@aws-sdk/util-user-agent-node': 3.973.0 + '@aws-sdk/util-user-agent-node': 3.973.1 '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 + '@smithy/core': 3.23.7 '@smithy/eventstream-serde-browser': 4.2.10 '@smithy/eventstream-serde-config-resolver': 4.3.10 '@smithy/eventstream-serde-node': 4.2.10 - '@smithy/fetch-http-handler': 5.3.11 + '@smithy/fetch-http-handler': 5.3.12 '@smithy/hash-blob-browser': 4.2.11 '@smithy/hash-node': 4.2.10 '@smithy/hash-stream-node': 4.2.10 '@smithy/invalid-dependency': 4.2.10 '@smithy/md5-js': 4.2.10 '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-endpoint': 4.4.21 + '@smithy/middleware-retry': 4.4.38 '@smithy/middleware-serde': 4.2.11 '@smithy/middleware-stack': 4.2.10 '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 + '@smithy/node-http-handler': 4.4.13 '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 '@smithy/url-parser': 4.2.10 '@smithy/util-base64': 4.3.1 '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-defaults-mode-browser': 4.3.37 + '@smithy/util-defaults-mode-node': 4.2.40 '@smithy/util-endpoints': 3.3.1 '@smithy/util-middleware': 4.2.10 '@smithy/util-retry': 4.2.10 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 '@smithy/util-utf8': 4.2.1 '@smithy/util-waiter': 4.2.10 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.15': + '@aws-sdk/core@3.973.16': dependencies: '@aws-sdk/types': 3.973.4 - '@aws-sdk/xml-builder': 3.972.8 - '@smithy/core': 3.23.6 + '@aws-sdk/xml-builder': 3.972.9 + '@smithy/core': 3.23.7 '@smithy/node-config-provider': 4.3.10 '@smithy/property-provider': 4.2.10 '@smithy/protocol-http': 5.3.10 '@smithy/signature-v4': 5.3.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 '@smithy/util-base64': 4.3.1 '@smithy/util-middleware': 4.2.10 @@ -2788,37 +2791,37 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.13': + '@aws-sdk/credential-provider-env@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.15': + '@aws-sdk/credential-provider-http@3.972.16': dependencies: - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/types': 3.973.4 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 + '@smithy/fetch-http-handler': 5.3.12 + '@smithy/node-http-handler': 4.4.13 '@smithy/property-provider': 4.2.10 '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.13': + '@aws-sdk/credential-provider-ini@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/credential-provider-env': 3.972.13 - '@aws-sdk/credential-provider-http': 3.972.15 - '@aws-sdk/credential-provider-login': 3.972.13 - '@aws-sdk/credential-provider-process': 3.972.13 - '@aws-sdk/credential-provider-sso': 3.972.13 - '@aws-sdk/credential-provider-web-identity': 3.972.13 - '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/credential-provider-env': 3.972.14 + '@aws-sdk/credential-provider-http': 3.972.16 + '@aws-sdk/credential-provider-login': 3.972.14 + '@aws-sdk/credential-provider-process': 3.972.14 + '@aws-sdk/credential-provider-sso': 3.972.14 + '@aws-sdk/credential-provider-web-identity': 3.972.14 + '@aws-sdk/nested-clients': 3.996.4 '@aws-sdk/types': 3.973.4 '@smithy/credential-provider-imds': 4.2.10 '@smithy/property-provider': 4.2.10 @@ -2828,10 +2831,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.13': + '@aws-sdk/credential-provider-login@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/nested-clients': 3.996.4 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/protocol-http': 5.3.10 @@ -2841,14 +2844,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.14': + '@aws-sdk/credential-provider-node@3.972.15': dependencies: - '@aws-sdk/credential-provider-env': 3.972.13 - '@aws-sdk/credential-provider-http': 3.972.15 - '@aws-sdk/credential-provider-ini': 3.972.13 - '@aws-sdk/credential-provider-process': 3.972.13 - '@aws-sdk/credential-provider-sso': 3.972.13 - '@aws-sdk/credential-provider-web-identity': 3.972.13 + '@aws-sdk/credential-provider-env': 3.972.14 + '@aws-sdk/credential-provider-http': 3.972.16 + '@aws-sdk/credential-provider-ini': 3.972.14 + '@aws-sdk/credential-provider-process': 3.972.14 + '@aws-sdk/credential-provider-sso': 3.972.14 + '@aws-sdk/credential-provider-web-identity': 3.972.14 '@aws-sdk/types': 3.973.4 '@smithy/credential-provider-imds': 4.2.10 '@smithy/property-provider': 4.2.10 @@ -2858,20 +2861,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.13': + '@aws-sdk/credential-provider-process@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/shared-ini-file-loader': 4.4.5 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.13': + '@aws-sdk/credential-provider-sso@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/token-providers': 3.999.0 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/nested-clients': 3.996.4 + '@aws-sdk/token-providers': 3.1001.0 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/shared-ini-file-loader': 4.4.5 @@ -2880,10 +2883,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.13': + '@aws-sdk/credential-provider-web-identity@3.972.14': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/nested-clients': 3.996.4 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/shared-ini-file-loader': 4.4.5 @@ -2909,12 +2912,12 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.973.1': + '@aws-sdk/middleware-flexible-checksums@3.973.2': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/crc64-nvme': 3.972.3 '@aws-sdk/types': 3.973.4 '@smithy/is-array-buffer': 4.2.1 @@ -2922,7 +2925,7 @@ snapshots: '@smithy/protocol-http': 5.3.10 '@smithy/types': 4.13.0 '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 @@ -2953,20 +2956,20 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.15': + '@aws-sdk/middleware-sdk-s3@3.972.16': dependencies: - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/types': 3.973.4 '@aws-sdk/util-arn-parser': 3.972.2 - '@smithy/core': 3.23.6 + '@smithy/core': 3.23.7 '@smithy/node-config-provider': 4.3.10 '@smithy/protocol-http': 5.3.10 '@smithy/signature-v4': 5.3.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 '@smithy/util-config-provider': 4.2.1 '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 @@ -2976,51 +2979,51 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.15': + '@aws-sdk/middleware-user-agent@3.972.16': dependencies: - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/types': 3.973.4 '@aws-sdk/util-endpoints': 3.996.3 - '@smithy/core': 3.23.6 + '@smithy/core': 3.23.7 '@smithy/protocol-http': 5.3.10 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.3': + '@aws-sdk/nested-clients@3.996.4': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.15 + '@aws-sdk/core': 3.973.16 '@aws-sdk/middleware-host-header': 3.972.6 '@aws-sdk/middleware-logger': 3.972.6 '@aws-sdk/middleware-recursion-detection': 3.972.6 - '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/middleware-user-agent': 3.972.16 '@aws-sdk/region-config-resolver': 3.972.6 '@aws-sdk/types': 3.973.4 '@aws-sdk/util-endpoints': 3.996.3 '@aws-sdk/util-user-agent-browser': 3.972.6 - '@aws-sdk/util-user-agent-node': 3.973.0 + '@aws-sdk/util-user-agent-node': 3.973.1 '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 - '@smithy/fetch-http-handler': 5.3.11 + '@smithy/core': 3.23.7 + '@smithy/fetch-http-handler': 5.3.12 '@smithy/hash-node': 4.2.10 '@smithy/invalid-dependency': 4.2.10 '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-endpoint': 4.4.21 + '@smithy/middleware-retry': 4.4.38 '@smithy/middleware-serde': 4.2.11 '@smithy/middleware-stack': 4.2.10 '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 + '@smithy/node-http-handler': 4.4.13 '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 '@smithy/url-parser': 4.2.10 '@smithy/util-base64': 4.3.1 '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-defaults-mode-browser': 4.3.37 + '@smithy/util-defaults-mode-node': 4.2.40 '@smithy/util-endpoints': 3.3.1 '@smithy/util-middleware': 4.2.10 '@smithy/util-retry': 4.2.10 @@ -3037,19 +3040,19 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.3': + '@aws-sdk/signature-v4-multi-region@3.996.4': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.15 + '@aws-sdk/middleware-sdk-s3': 3.972.16 '@aws-sdk/types': 3.973.4 '@smithy/protocol-http': 5.3.10 '@smithy/signature-v4': 5.3.10 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.999.0': + '@aws-sdk/token-providers@3.1001.0': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/core': 3.973.16 + '@aws-sdk/nested-clients': 3.996.4 '@aws-sdk/types': 3.973.4 '@smithy/property-provider': 4.2.10 '@smithy/shared-ini-file-loader': 4.4.5 @@ -3086,18 +3089,18 @@ snapshots: bowser: 2.13.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.0': + '@aws-sdk/util-user-agent-node@3.973.1': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/middleware-user-agent': 3.972.16 '@aws-sdk/types': 3.973.4 '@smithy/node-config-provider': 4.3.10 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.8': + '@aws-sdk/xml-builder@3.972.9': dependencies: '@smithy/types': 4.13.0 - fast-xml-parser: 5.3.6 + fast-xml-parser: 5.4.1 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.3': {} @@ -3347,11 +3350,11 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@kevisual/ai@0.0.24': + '@kevisual/ai@0.0.26': dependencies: '@kevisual/logger': 0.0.4 - '@kevisual/permission': 0.0.3 - '@kevisual/query': 0.0.38 + '@kevisual/permission': 0.0.4 + '@kevisual/query': 0.0.52 '@kevisual/api@0.0.60(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: @@ -3359,7 +3362,7 @@ snapshots: '@kevisual/js-filter': 0.0.5 '@kevisual/load': 0.0.6 '@paralleldrive/cuid2': 3.3.0 - es-toolkit: 1.44.0 + es-toolkit: 1.45.1 eventemitter3: 5.0.4 fuse.js: 7.1.0 nanoid: 5.1.6 @@ -3381,7 +3384,7 @@ snapshots: '@kevisual/query': 0.0.52 '@kevisual/router': 0.0.84 '@kevisual/use-config': 1.0.30(dotenv@17.3.1) - es-toolkit: 1.44.0 + es-toolkit: 1.45.1 nanoid: 5.1.6 unstorage: 1.17.4(ioredis@5.9.3) ws: '@kevisual/ws@8.19.0' @@ -3443,14 +3446,8 @@ snapshots: '@kevisual/oss@0.0.20': {} - '@kevisual/permission@0.0.3': {} - '@kevisual/permission@0.0.4': {} - '@kevisual/query@0.0.38': - dependencies: - tslib: 2.8.1 - '@kevisual/query@0.0.39': dependencies: tslib: 2.8.1 @@ -3463,7 +3460,11 @@ snapshots: '@kevisual/router@0.0.84': dependencies: - es-toolkit: 1.44.0 + es-toolkit: 1.45.1 + + '@kevisual/router@0.0.85': + dependencies: + es-toolkit: 1.45.1 '@kevisual/types@0.0.12': {} @@ -3580,7 +3581,7 @@ snapshots: '@smithy/util-middleware': 4.2.10 tslib: 2.8.1 - '@smithy/core@3.23.6': + '@smithy/core@3.23.7': dependencies: '@smithy/middleware-serde': 4.2.11 '@smithy/protocol-http': 5.3.10 @@ -3588,7 +3589,7 @@ snapshots: '@smithy/util-base64': 4.3.1 '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 '@smithy/util-utf8': 4.2.1 '@smithy/uuid': 1.1.1 tslib: 2.8.1 @@ -3631,7 +3632,7 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.11': + '@smithy/fetch-http-handler@5.3.12': dependencies: '@smithy/protocol-http': 5.3.10 '@smithy/querystring-builder': 4.2.10 @@ -3684,9 +3685,9 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.20': + '@smithy/middleware-endpoint@4.4.21': dependencies: - '@smithy/core': 3.23.6 + '@smithy/core': 3.23.7 '@smithy/middleware-serde': 4.2.11 '@smithy/node-config-provider': 4.3.10 '@smithy/shared-ini-file-loader': 4.4.5 @@ -3695,12 +3696,12 @@ snapshots: '@smithy/util-middleware': 4.2.10 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.37': + '@smithy/middleware-retry@4.4.38': dependencies: '@smithy/node-config-provider': 4.3.10 '@smithy/protocol-http': 5.3.10 '@smithy/service-error-classification': 4.2.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 '@smithy/util-middleware': 4.2.10 '@smithy/util-retry': 4.2.10 @@ -3725,7 +3726,7 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.12': + '@smithy/node-http-handler@4.4.13': dependencies: '@smithy/abort-controller': 4.2.10 '@smithy/protocol-http': 5.3.10 @@ -3774,14 +3775,14 @@ snapshots: '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/smithy-client@4.12.0': + '@smithy/smithy-client@4.12.1': dependencies: - '@smithy/core': 3.23.6 - '@smithy/middleware-endpoint': 4.4.20 + '@smithy/core': 3.23.7 + '@smithy/middleware-endpoint': 4.4.21 '@smithy/middleware-stack': 4.2.10 '@smithy/protocol-http': 5.3.10 '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@smithy/util-stream': 4.5.16 tslib: 2.8.1 '@smithy/types@4.13.0': @@ -3822,20 +3823,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.36': + '@smithy/util-defaults-mode-browser@4.3.37': dependencies: '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.39': + '@smithy/util-defaults-mode-node@4.2.40': dependencies: '@smithy/config-resolver': 4.4.9 '@smithy/credential-provider-imds': 4.2.10 '@smithy/node-config-provider': 4.3.10 '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 + '@smithy/smithy-client': 4.12.1 '@smithy/types': 4.13.0 tslib: 2.8.1 @@ -3860,10 +3861,10 @@ snapshots: '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-stream@4.5.15': + '@smithy/util-stream@4.5.16': dependencies: - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 + '@smithy/fetch-http-handler': 5.3.12 + '@smithy/node-http-handler': 4.4.13 '@smithy/types': 4.13.0 '@smithy/util-base64': 4.3.1 '@smithy/util-buffer-from': 4.2.1 @@ -3901,9 +3902,9 @@ snapshots: dependencies: '@types/readdir-glob': 1.1.5 - '@types/bun@1.3.9': + '@types/bun@1.3.10': dependencies: - bun-types: 1.3.9 + bun-types: 1.3.10 '@types/busboy@1.5.4': dependencies: @@ -4091,7 +4092,7 @@ snapshots: transitivePeerDependencies: - supports-color - bun-types@1.3.9: + bun-types@1.3.10: dependencies: '@types/node': 25.3.3 @@ -4245,16 +4246,16 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.9)(pg@8.19.0): + drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.10)(pg@8.19.0): optionalDependencies: '@types/pg': 8.18.0 better-sqlite3: 12.6.2 - bun-types: 1.3.9 + bun-types: 1.3.10 pg: 8.19.0 - drizzle-zod@0.8.3(drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.9)(pg@8.19.0))(zod@4.3.6): + drizzle-zod@0.8.3(drizzle-orm@0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.10)(pg@8.19.0))(zod@4.3.6): dependencies: - drizzle-orm: 0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.9)(pg@8.19.0) + drizzle-orm: 0.45.1(@types/pg@8.18.0)(better-sqlite3@12.6.2)(bun-types@1.3.10)(pg@8.19.0) zod: 4.3.6 eastasianwidth@0.2.0: {} @@ -4284,6 +4285,8 @@ snapshots: es-toolkit@1.44.0: {} + es-toolkit@1.45.1: {} + esbuild-register@3.6.0(esbuild@0.25.12): dependencies: debug: 4.4.3 @@ -4417,8 +4420,11 @@ snapshots: fast-json-patch@3.1.1: {} - fast-xml-parser@5.3.6: + fast-xml-builder@1.0.0: {} + + fast-xml-parser@5.4.1: dependencies: + fast-xml-builder: 1.0.0 strnum: 2.1.2 fclone@1.0.11: {} diff --git a/src/modules/html/render-server-html.ts b/src/modules/html/render-server-html.ts new file mode 100644 index 0000000..ede40c7 --- /dev/null +++ b/src/modules/html/render-server-html.ts @@ -0,0 +1,6 @@ +export const renderServerHtml = (data: any, html: string) => { + if (html.includes('')) { + return html.replace('', ``); + } + return html; +} \ No newline at end of file diff --git a/src/modules/html/studio-app-list/index.ts b/src/modules/html/studio-app-list/index.ts index 3a7cd0d..042b4bf 100644 --- a/src/modules/html/studio-app-list/index.ts +++ b/src/modules/html/studio-app-list/index.ts @@ -1,8 +1,18 @@ -type StudioOpts = { user: string, userAppKey?: string; appIds: string[] } +type StudioOpts = { + user: string, + userAppKey?: string; + appIds: string[] + infoList?: { + user: string; + id: string; + status: 'waiting' | 'connected' | 'closed'; + }[] +} export const createStudioAppListHtml = (opts: StudioOpts) => { const user = opts.user!; const userAppKey = opts?.userAppKey; let showUserAppKey = userAppKey; + const infos = opts.infoList || []; if (showUserAppKey && showUserAppKey.startsWith(user + '--')) { showUserAppKey = showUserAppKey.replace(user + '--', ''); } @@ -333,7 +343,7 @@ export const createStudioAppListHtml = (opts: StudioOpts) => { ` : ''} ${appListContent} - +
${JSON.stringify(infos, null, 2)}
diff --git a/src/modules/self-restart.ts b/src/modules/self-restart.ts index 6952bef..d4a5a6c 100644 --- a/src/modules/self-restart.ts +++ b/src/modules/self-restart.ts @@ -1,7 +1,7 @@ import childProcess from 'child_process'; export const selfRestart = async () => { - const appName = 'code-center'; + const appName = 'root/code-center'; // 检测 pm2 是否安装和是否有 appName 这个应用 try { const res = childProcess.execSync(`pm2 list`); diff --git a/src/modules/v1-ws-proxy/index.ts b/src/modules/v1-ws-proxy/index.ts index 055330d..90c7c29 100644 --- a/src/modules/v1-ws-proxy/index.ts +++ b/src/modules/v1-ws-proxy/index.ts @@ -3,46 +3,40 @@ import { getLoginUserByToken } from '@/modules/auth.ts'; import { logger } from '../logger.ts'; export const wsProxyManager = new WsProxyManager(); import { WebSocketListenerFun } from '@kevisual/router/src/server/server-type.ts' +// 生成一个随机六位字符串作为注册 ID +const generateRegistryId = () => { + return Math.random().toString(36).substring(2, 8); +} export const wssFun: WebSocketListenerFun = async (req, res) => { // do nothing, just to enable ws upgrade event const { id, ws, token, data, emitter } = req; // console.log('req', req) const { type } = data || {}; + if (type === 'registryClient') { const loginUser = await getLoginUserByToken(token); - if (!loginUser?.tokenUser) { - logger.debug('未登录,断开连接'); - ws.send(JSON.stringify({ code: 401, message: '未登录' })); - setTimeout(() => { - ws.close(401, 'Unauthorized'); - }, 1000); + let isLogin = false; + let user = ''; + if (loginUser?.tokenUser) { + isLogin = true; + user = loginUser?.tokenUser?.username; + } else { + logger.debug('未登录,请求等待用户验证', data); + user = data?.username || ''; + } + if (!user) { + logger.debug('未提供用户名,无法注册 ws 连接'); + ws.close(); return; } - const user = loginUser?.tokenUser?.username; - const userApp = user + '--' + id; - logger.debug('注册 ws 连接', userApp); - const wsMessage = wsProxyManager.get(userApp); - if (wsMessage) { - logger.debug('ws 连接已存在,关闭旧连接', userApp); - wsMessage.ws.close(); - wsProxyManager.unregister(userApp); - await new Promise((resolve) => setTimeout(resolve, 200)); + let userApp = user + '--' + id; + // TODO: 如果存在, 而且之前的那个关闭了,不需要验证,直接覆盖和复用. + let wsConnect = await wsProxyManager.createNewConnection({ ws, user, userApp, isLogin }); + if (wsConnect.isNew) { + logger.debug('新连接注册成功', userApp); } // @ts-ignore - wsProxyManager.register(userApp, { user, ws }); - ws.send( - JSON.stringify({ - type: 'connected', - user: user, - id, - }), - ); - emitter.once('close--' + id, () => { - logger.debug('ws emitter closed'); - wsProxyManager.unregister(userApp); - }); - // @ts-ignore - ws.data.userApp = userApp; + ws.data.userApp = wsConnect.id; return; } // @ts-ignore diff --git a/src/modules/v1-ws-proxy/manager.ts b/src/modules/v1-ws-proxy/manager.ts index e7ee504..4011593 100644 --- a/src/modules/v1-ws-proxy/manager.ts +++ b/src/modules/v1-ws-proxy/manager.ts @@ -1,7 +1,9 @@ -import { nanoid } from 'nanoid'; +import { customAlphabet } from 'nanoid'; import { WebSocket } from 'ws'; import { logger } from '../logger.ts'; import { EventEmitter } from 'eventemitter3'; +const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const nanoid = customAlphabet(letters, 10); class WsMessage { ws: WebSocket; @@ -9,11 +11,16 @@ class WsMessage { emitter: EventEmitter; private pingTimer?: NodeJS.Timeout; private readonly PING_INTERVAL = 30000; // 30 秒发送一次 ping - - constructor({ ws, user }: WssMessageOptions) { + id?: string; + status?: 'waiting' | 'connected' | 'closed'; + manager: WsProxyManager; + constructor({ ws, user, id, isLogin, manager }: WssMessageOptions) { this.ws = ws; this.user = user; + this.id = id; this.emitter = new EventEmitter(); + this.manager = manager; + this.status = isLogin ? 'connected' : 'waiting'; this.startPing(); } @@ -39,12 +46,44 @@ class WsMessage { this.stopPing(); this.emitter.removeAllListeners(); } + isClosed() { + return this.ws.readyState === WebSocket.CLOSED; + } async sendResponse(data: any) { if (data.id) { this.emitter.emit(data.id, data?.data); } } + async sendConnected() { + const id = this.id; + const user = this.user; + const data = { type: 'verified', user, id }; + if (this.ws.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(data)); + this.status = 'connected'; + } + if (id.includes('-registry-')) { + const newId = id.split('-registry-')[0]; + this.manager.changeId(id, newId); + const ws = this.ws; + // @ts-ignore + if (this.ws?.data) { + // @ts-ignore + this.ws.data.userApp = newId; + } + } + } + getInfo() { + const shortAppId = this.id ? this.id.split('--')[1] : ''; + return { + user: this.user, + id: this.id, + status: this.status, + shortAppId, + pathname: this.id ? `/${this.user}/v1/${shortAppId}` : '', + }; + } async sendData(data: any, context?: any, opts?: { timeout?: number }) { if (this.ws.readyState !== WebSocket.OPEN) { return { code: 500, message: 'WebSocket is not open' }; @@ -64,6 +103,7 @@ class WsMessage { const msg = { path: data?.path, key: data?.key, id: data?.id }; return new Promise((resolve) => { const timer = setTimeout(() => { + console.log('ws-proxy sendData timeout', msg); resolve({ code: 500, message: `运行超时,执行的id: ${id},参数是${JSON.stringify(msg)}`, @@ -79,7 +119,12 @@ class WsMessage { type WssMessageOptions = { ws: WebSocket; user?: string; + id?: string; + realId?: string; + isLogin?: boolean; + manager: WsProxyManager; }; + export class WsProxyManager { wssMap: Map = new Map(); PING_INTERVAL = 30000; // 30 秒检查一次连接状态 @@ -89,7 +134,7 @@ export class WsProxyManager { } this.checkConnceted(); } - register(id: string, opts?: { ws: WebSocket; user: string }) { + register(id: string, opts?: { ws: WebSocket; user: string, id?: string }) { if (this.wssMap.has(id)) { const value = this.wssMap.get(id); if (value) { @@ -100,8 +145,24 @@ export class WsProxyManager { const [username, appId] = id.split('--'); const url = new URL(`/${username}/v1/${appId}`, 'https://kevisual.cn/'); console.log('WsProxyManager register', id, '访问地址', url.toString()); - const value = new WsMessage({ ws: opts?.ws, user: opts?.user }); + const value = new WsMessage({ ...opts, manager: this } as WssMessageOptions); this.wssMap.set(id, value); + return value; + } + changeId(oldId: string, newId: string) { + const value = this.wssMap.get(oldId); + const originalValue = this.wssMap.get(newId); + if (originalValue) { + logger.debug(`WsProxyManager changeId: ${newId} already exists, close old connection`); + originalValue.ws.close(); + originalValue.destroy(); + } + if (value) { + this.wssMap.delete(oldId); + this.wssMap.set(newId, value); + value.id = newId; + logger.debug(`WsProxyManager changeId: ${oldId} -> ${newId}`); + } } unregister(id: string) { const value = this.wssMap.get(id); @@ -117,9 +178,33 @@ export class WsProxyManager { } return Array.from(this.wssMap.keys()); } + getIdsInfo(beginWith?: string) { + const ids = this.getIds(beginWith); + const infoList = this.getInfoList(ids); + return { + ids, + infoList, + }; + } + getInfoList(ids: string[]) { + return ids.map(id => { + const value = this.wssMap.get(id); + if (value) { + return value.getInfo(); + } + return null; + }).filter(Boolean); + } get(id: string) { return this.wssMap.get(id); } + createId(id: string) { + if (!this.wssMap.has(id)) { + return id; + } + const newId = id + '-' + nanoid(6); + return newId; + } checkConnceted() { const that = this; setTimeout(() => { @@ -132,4 +217,40 @@ export class WsProxyManager { that.checkConnceted(); }, this.PING_INTERVAL); } + async createNewConnection(opts: { ws: any; user: string, userApp: string, isLogin?: boolean }) { + const id = opts.userApp; + let realId: string = id; + const isLogin = opts.isLogin || false; + const has = this.wssMap.has(id); + const registryId = '-registry-' + generateRegistryId(); // 生成一个随机六位字符串作为注册 ID + let isNeedVerify = !isLogin; + if (has) { + const value = this.wssMap.get(id); + if (value) { + if (value.isClosed()) { + // 短时间内还在, 等于简单重启了一下应用,不需要重新注册. + logger.debug('之前的连接已关闭,复用注册 ID 连接 ws', id); + this.unregister(id); + await new Promise(resolve => setTimeout(resolve, 100)); + const wsMessage = this.register(id, { ws: opts.ws, user: opts.user, id }); + wsMessage.sendConnected(); + return { wsMessage, isNew: false, id: id }; + } else { + // 没有关闭,需要重新注册鉴权一下, 生成新的 id 连接. + isNeedVerify = true; + } + } + } + // 没有连接, 直接注册新的连接. + if (isNeedVerify) { + realId = id + registryId; + logger.debug('未登录用户,使用临时注册 ID 连接 ws', realId); + } + const wsMessage = this.register(realId, { ws: opts.ws, user: opts.user, id: realId }); + return { wsMessage, isNew: true, id: realId }; + } } +// 生成一个随机六位字符串作为注册 ID +const generateRegistryId = () => { + return Math.random().toString(36).substring(2, 8); +} \ No newline at end of file diff --git a/src/modules/v1-ws-proxy/proxy.ts b/src/modules/v1-ws-proxy/proxy.ts index b4b37b7..8ae89b1 100644 --- a/src/modules/v1-ws-proxy/proxy.ts +++ b/src/modules/v1-ws-proxy/proxy.ts @@ -7,14 +7,17 @@ import { getLoginUser } from '@/modules/auth.ts'; import { createStudioAppListHtml } from '../html/studio-app-list/index.ts'; import { omit } from 'es-toolkit'; import { baseProxyUrl, proxyDomain } from '../domain.ts'; +import { renderServerHtml } from '../html/render-server-html.ts'; type ProxyOptions = { createNotFoundPage: (msg?: string) => any; }; export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opts?: ProxyOptions) => { - const { url } = req; + const { url, method } = req; const _url = new URL(url || '', `http://localhost`); const { pathname, searchParams } = _url; + const isGet = method === 'GET'; + let [user, app, userAppKey] = pathname.split('/').slice(1); if (!user || !app) { opts?.createNotFoundPage?.('应用未找到'); @@ -32,14 +35,9 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt if (!userAppKey) { if (isAdmin) { - // 获取所有的管理员的应用列表 - const ids = wsProxyManager.getIds(user + '--'); - const html = createStudioAppListHtml({ user, appIds: ids, userAppKey }); - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end(html); - return; + return handleRequest(req, res, { user, app, userAppKey, isAdmin }); } else { - opts?.createNotFoundPage?.('没有访问应用权限'); + opts?.createNotFoundPage?.('应用访问失败'); return false; } } @@ -57,14 +55,19 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt } logger.debug('data', data); const client = wsProxyManager.get(userAppKey); - const ids = wsProxyManager.getIds(user + '--'); + const { ids, infoList } = wsProxyManager.getIdsInfo(user + '--'); if (!client) { - if (isAdmin) { - const html = createStudioAppListHtml({ user, appIds: ids, userAppKey }); - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end(html); + if (isGet) { + if (isAdmin) { + const html = createStudioAppListHtml({ user, appIds: ids, userAppKey, infoList }); + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(html); + } else { + opts?.createNotFoundPage?.('应用访问失败'); + } } else { - opts?.createNotFoundPage?.('应用访问失败'); + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: '应用访问失败' })); } return false; } @@ -80,6 +83,11 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt if (!isAdmin) { message = omit(data, ['token', 'cookies']); } + if (client.status === 'waiting') { + res.writeHead(603, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: '应用没有鉴权' })); + return true; + } const value = await client.sendData(message, { state: { tokenUser: omit(loginUser.tokenUser, ['oauthExpand']) }, }); @@ -91,3 +99,38 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt opts?.createNotFoundPage?.('应用未启动'); return true; }; + +const handleRequest = async (req: IncomingMessage, res: ServerResponse, opts?: { user?: string, app?: string, userAppKey?: string, isAdmin?: boolean }) => { + const { user, userAppKey } = opts || {}; + const isGet = req.method === 'GET'; + // 获取所有的管理员的应用列表 + const { ids, infoList } = wsProxyManager.getIdsInfo(user + '--'); + if (isGet) { + const html = createStudioAppListHtml({ user, appIds: ids, userAppKey, infoList }); + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(html); + return; + } else { + const url = new URL(req.url || '', 'http://localhost'); + const path = url.searchParams.get('path'); + if (path) { + const appId = url.searchParams.get('appId') || ''; + const client = wsProxyManager.get(appId!)!; + if (!client) { + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: '应用未找到' })); + return; + } + if (path === 'connected') { + + client.sendConnected(); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ code: 200, message: '应用已连接' })); + return; + } + } + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ code: 200, data: { ids, infoList } })); + return; + } +} \ No newline at end of file diff --git a/src/routes-simple/page-proxy.ts b/src/routes-simple/page-proxy.ts index 70b293d..a873b0c 100644 --- a/src/routes-simple/page-proxy.ts +++ b/src/routes-simple/page-proxy.ts @@ -184,7 +184,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR /** * url是pathname的路径 */ - const url = pathname; + const url = pathname || ''; if (!domainApp && noProxyUrl.includes(url)) { if (url === '/') { rediretHome(req, res); @@ -312,13 +312,14 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR const indexFile = isExist.indexFilePath; // 已经必定存在了 try { let appFileUrl: string; - if (domainApp) { - appFileUrl = (url + '').replace(`/`, ''); - } else { - appFileUrl = (url + '').replace(`/${user}/${app}/`, ''); - } + + appFileUrl = url.replace(`/${user}/${app}/`, ''); appFileUrl = decodeURIComponent(appFileUrl); // Decode URL components let appFile = await userApp.getFile(appFileUrl); + if (!appFile && domainApp) { + const domainAppFileUrl = url.replace(`/`, ''); + appFile = await userApp.getFile(domainAppFileUrl); + } if (!appFile && url.endsWith('/')) { appFile = await userApp.getFile(appFileUrl + 'index.html'); } else if (!appFile && !url.endsWith('/')) {