Bläddra i källkod

Merge remote-tracking branch 'upstream/vite-middle' into file-middle

yourname 4 månader sedan
förälder
incheckning
ad15195627
6 ändrade filer med 252 tillägg och 302 borttagningar
  1. 3 2
      package.json
  2. 73 141
      pnpm-lock.yaml
  3. 157 159
      server.js
  4. 1 0
      src/server/api.ts
  5. 2 0
      src/server/index.tsx
  6. 16 0
      vite.config.ts

+ 3 - 2
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "monitor",
+  "name": "d8d-starter",
   "private": true,
   "type": "module",
   "scripts": {
@@ -24,7 +24,7 @@
     "compression": "^1.8.0",
     "dayjs": "^1.11.13",
     "debug": "^4.4.1",
-    "dotenv": "^16.5.0",
+    "dotenv": "^17.2.1",
     "formdata-node": "^6.0.3",
     "hono": "^4.8.5",
     "ioredis": "^5.6.1",
@@ -55,6 +55,7 @@
     "tsx": "^4.20.3",
     "typescript": "~5.8.3",
     "vite": "^7.0.0",
+    "vite-plugin-iframe-communicator": "^0.0.10",
     "vite-progress-tracking-plugin": "^0.0.2"
   }
 }

+ 73 - 141
pnpm-lock.yaml

@@ -48,11 +48,8 @@ importers:
         specifier: ^4.4.1
         version: 4.4.1
       dotenv:
-        specifier: ^16.5.0
-        version: 16.6.1
-      formdata-node:
-        specifier: ^6.0.3
-        version: 6.0.3
+        specifier: ^17.2.1
+        version: 17.2.1
       hono:
         specifier: ^4.8.5
         version: 4.8.9
@@ -111,6 +108,9 @@ importers:
       '@types/debug':
         specifier: ^4.1.12
         version: 4.1.12
+      '@types/express':
+        specifier: ^5.0.3
+        version: 5.0.3
       '@types/node':
         specifier: ^24.0.10
         version: 24.1.0
@@ -138,6 +138,9 @@ importers:
       vite:
         specifier: ^7.0.0
         version: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0)
+      vite-plugin-iframe-communicator:
+        specifier: ^0.0.10
+        version: 0.0.10(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0))
       vite-progress-tracking-plugin:
         specifier: ^0.0.2
         version: 0.0.2(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0))
@@ -794,12 +797,30 @@ packages:
     peerDependencies:
       react: ^18 || ^19
 
+  '@types/body-parser@1.19.6':
+    resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
+
+  '@types/connect@3.4.38':
+    resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
   '@types/debug@4.1.12':
     resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
 
   '@types/estree@1.0.8':
     resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
 
+  '@types/express-serve-static-core@5.0.7':
+    resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==}
+
+  '@types/express@5.0.3':
+    resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==}
+
+  '@types/http-errors@2.0.5':
+    resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
+
+  '@types/mime@1.3.5':
+    resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
+
   '@types/ms@2.1.0':
     resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
 
@@ -819,9 +840,6 @@ packages:
     peerDependencies:
       vite: ^4 || ^5 || ^6 || ^7
 
-  '@zxing/text-encoding@0.9.0':
-    resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
-
   ansi-regex@5.0.1:
     resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
     engines: {node: '>=8'}
@@ -879,9 +897,6 @@ packages:
     resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
     engines: {node: '>= 18'}
 
-  block-stream2@2.1.0:
-    resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==}
-
   brace-expansion@2.0.2:
     resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
 
@@ -974,6 +989,9 @@ packages:
   csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
+  d8d-iframe-communicator@0.0.10:
+    resolution: {integrity: sha512-VuBVcXE2ZdW6eSvJ13TBM1nkau6I8RzIcPpHF9XopLiLvx6AylIujVHEFQ4Fl6kGuABnh6VdulMCkC4zr7R0zA==}
+
   dayjs@1.11.13:
     resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
 
@@ -1026,6 +1044,10 @@ packages:
     resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
     engines: {node: '>=12'}
 
+  dotenv@17.2.1:
+    resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==}
+    engines: {node: '>=12'}
+
   dunder-proto@1.0.1:
     resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
     engines: {node: '>= 0.4'}
@@ -1071,13 +1093,6 @@ packages:
     resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
     engines: {node: '>=6'}
 
-  eventemitter3@5.0.1:
-    resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
-
-  fast-xml-parser@4.5.3:
-    resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
-    hasBin: true
-
   fdir@6.4.6:
     resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
     peerDependencies:
@@ -1086,10 +1101,6 @@ packages:
       picomatch:
         optional: true
 
-  filter-obj@1.1.0:
-    resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
-    engines: {node: '>=0.10.0'}
-
   follow-redirects@1.15.9:
     resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
     engines: {node: '>=4.0'}
@@ -1111,10 +1122,6 @@ packages:
     resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
     engines: {node: '>= 6'}
 
-  formdata-node@6.0.3:
-    resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==}
-    engines: {node: '>= 18'}
-
   fsevents@2.3.3:
     resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -1181,18 +1188,6 @@ packages:
   inherits@2.0.4:
     resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
 
-  ioredis@5.6.1:
-    resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==}
-    engines: {node: '>=12.22.0'}
-
-  ipaddr.js@2.2.0:
-    resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
-    engines: {node: '>= 10'}
-
-  is-arguments@1.2.0:
-    resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
-    engines: {node: '>= 0.4'}
-
   is-callable@1.2.7:
     resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
     engines: {node: '>= 0.4'}
@@ -1468,10 +1463,6 @@ packages:
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
-  query-string@7.1.3:
-    resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
-    engines: {node: '>=6'}
-
   rc-cascader@3.34.0:
     resolution: {integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==}
     peerDependencies:
@@ -1842,19 +1833,6 @@ packages:
     resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
     engines: {node: '>= 0.6'}
 
-  standard-as-callback@2.1.0:
-    resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
-
-  stream-chain@2.2.5:
-    resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==}
-
-  stream-json@1.9.1:
-    resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==}
-
-  strict-uri-encode@2.0.0:
-    resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
-    engines: {node: '>=4'}
-
   string-convert@0.2.1:
     resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
 
@@ -1995,12 +1973,6 @@ packages:
   undici-types@7.8.0:
     resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
 
-  util-deprecate@1.0.2:
-    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-
-  util@0.12.5:
-    resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
-
   uuid@11.1.0:
     resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
     hasBin: true
@@ -2009,6 +1981,11 @@ packages:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
     engines: {node: '>= 0.8'}
 
+  vite-plugin-iframe-communicator@0.0.10:
+    resolution: {integrity: sha512-gvgqH+GueeKAxRuW/oQR7bXjTf0W5svSz2P/6UgeknWrAHczexTZOuAEwhisPVqeOlwomwLwkof0dddQPKHF2A==}
+    peerDependencies:
+      vite: ^7.0.0
+
   vite-progress-tracking-plugin@0.0.2:
     resolution: {integrity: sha512-tm5KIwicitTSw+7EVZT3rJ7gLkv7LNloFAxUhJRYCvZ3jQh85l3/GYHHMwl2HbuAaPDYEQOUB2w5IM3fC/a12w==}
     peerDependencies:
@@ -2074,14 +2051,6 @@ packages:
     resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
     engines: {node: '>=12'}
 
-  xml2js@0.6.2:
-    resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
-    engines: {node: '>=4.0.0'}
-
-  xmlbuilder@11.0.1:
-    resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
-    engines: {node: '>=4.0'}
-
   y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
@@ -2601,12 +2570,38 @@ snapshots:
       '@tanstack/query-core': 5.83.0
       react: 19.1.0
 
+  '@types/debug@4.1.12':
+    dependencies:
+      '@types/connect': 3.4.38
+      '@types/node': 24.1.0
+
+  '@types/connect@3.4.38':
+    dependencies:
+      '@types/node': 24.1.0
+
   '@types/debug@4.1.12':
     dependencies:
       '@types/ms': 2.1.0
 
   '@types/estree@1.0.8': {}
 
+  '@types/express-serve-static-core@5.0.7':
+    dependencies:
+      '@types/node': 24.1.0
+      '@types/qs': 6.14.0
+      '@types/range-parser': 1.2.7
+      '@types/send': 0.17.5
+
+  '@types/express@5.0.3':
+    dependencies:
+      '@types/body-parser': 1.19.6
+      '@types/express-serve-static-core': 5.0.7
+      '@types/serve-static': 1.15.8
+
+  '@types/http-errors@2.0.5': {}
+
+  '@types/mime@1.3.5': {}
+
   '@types/ms@2.1.0': {}
 
   '@types/node@24.1.0':
@@ -2629,9 +2624,6 @@ snapshots:
     transitivePeerDependencies:
       - '@swc/helpers'
 
-  '@zxing/text-encoding@0.9.0':
-    optional: true
-
   ansi-regex@5.0.1: {}
 
   ansi-regex@6.1.0: {}
@@ -2731,10 +2723,6 @@ snapshots:
       node-addon-api: 8.5.0
       node-gyp-build: 4.8.4
 
-  block-stream2@2.1.0:
-    dependencies:
-      readable-stream: 3.6.2
-
   brace-expansion@2.0.2:
     dependencies:
       balanced-match: 1.0.2
@@ -2829,6 +2817,8 @@ snapshots:
 
   csstype@3.1.3: {}
 
+  d8d-iframe-communicator@0.0.10: {}
+
   dayjs@1.11.13: {}
 
   debug@2.6.9:
@@ -2857,6 +2847,8 @@ snapshots:
 
   dotenv@16.6.1: {}
 
+  dotenv@17.2.1: {}
+
   dunder-proto@1.0.1:
     dependencies:
       call-bind-apply-helpers: 1.0.2
@@ -2924,18 +2916,10 @@ snapshots:
 
   escalade@3.2.0: {}
 
-  eventemitter3@5.0.1: {}
-
-  fast-xml-parser@4.5.3:
-    dependencies:
-      strnum: 1.1.2
-
   fdir@6.4.6(picomatch@4.0.3):
     optionalDependencies:
       picomatch: 4.0.3
 
-  filter-obj@1.1.0: {}
-
   follow-redirects@1.15.9(debug@4.4.1):
     optionalDependencies:
       debug: 4.4.1
@@ -2957,8 +2941,6 @@ snapshots:
       hasown: 2.0.2
       mime-types: 2.1.35
 
-  formdata-node@6.0.3: {}
-
   fsevents@2.3.3:
     optional: true
 
@@ -3029,27 +3011,6 @@ snapshots:
 
   inherits@2.0.4: {}
 
-  ioredis@5.6.1:
-    dependencies:
-      '@ioredis/commands': 1.2.0
-      cluster-key-slot: 1.1.2
-      debug: 4.4.1
-      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
-
-  ipaddr.js@2.2.0: {}
-
-  is-arguments@1.2.0:
-    dependencies:
-      call-bound: 1.0.4
-      has-tostringtag: 1.0.2
-
   is-callable@1.2.7: {}
 
   is-fullwidth-code-point@3.0.0: {}
@@ -3291,13 +3252,6 @@ snapshots:
 
   proxy-from-env@1.1.0: {}
 
-  query-string@7.1.3:
-    dependencies:
-      decode-uri-component: 0.2.2
-      filter-obj: 1.1.0
-      split-on-first: 1.1.0
-      strict-uri-encode: 2.0.0
-
   rc-cascader@3.34.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
     dependencies:
       '@babel/runtime': 7.28.2
@@ -3757,16 +3711,6 @@ snapshots:
 
   sqlstring@2.3.3: {}
 
-  standard-as-callback@2.1.0: {}
-
-  stream-chain@2.2.5: {}
-
-  stream-json@1.9.1:
-    dependencies:
-      stream-chain: 2.2.5
-
-  strict-uri-encode@2.0.0: {}
-
   string-convert@0.2.1: {}
 
   string-width@4.2.3:
@@ -3874,20 +3818,15 @@ snapshots:
 
   undici-types@7.8.0: {}
 
-  util-deprecate@1.0.2: {}
-
-  util@0.12.5:
-    dependencies:
-      inherits: 2.0.4
-      is-arguments: 1.2.0
-      is-generator-function: 1.1.0
-      is-typed-array: 1.1.15
-      which-typed-array: 1.1.19
-
   uuid@11.1.0: {}
 
   vary@1.1.2: {}
 
+  vite-plugin-iframe-communicator@0.0.10(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0)):
+    dependencies:
+      d8d-iframe-communicator: 0.0.10
+      vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0)
+
   vite-progress-tracking-plugin@0.0.2(vite@7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0)):
     dependencies:
       vite: 7.0.6(@types/node@24.1.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.8.0)
@@ -3940,13 +3879,6 @@ snapshots:
       string-width: 5.1.2
       strip-ansi: 7.1.0
 
-  xml2js@0.6.2:
-    dependencies:
-      sax: 1.4.1
-      xmlbuilder: 11.0.1
-
-  xmlbuilder@11.0.1: {}
-
   y18n@5.0.8: {}
 
   yallist@5.0.0: {}

+ 157 - 159
server.js

@@ -1,20 +1,19 @@
+import 'dotenv/config'
 import fs from 'node:fs/promises';
 import { URL } from 'node:url';
 import { Transform } from 'node:stream';
 import { Readable } from 'node:stream';
-import { pipeline } from 'node:stream/promises';
 import { Hono } from 'hono';
-import { logger } from 'hono/logger'; // 引入 Hono 日志中间件
+import { logger } from 'hono/logger';
 import { createServer as createNodeServer } from 'node:http';
 import process from 'node:process';
 import { createAdaptorServer } from '@hono/node-server'  
 
-
 // 创建 Hono 应用
-const app = new Hono();// API路由
+const app = new Hono();
 
-// 全局使用 Hono 日志中间件(记录所有请求)
-app.use('*', logger()); // 新增:添加请求日志中间件
+// 全局使用 Hono 日志中间件
+app.use('*', logger());
 
 // 常量定义
 const isProduction = process.env.NODE_ENV === 'production';
@@ -29,17 +28,17 @@ console.log(`端口: ${port}`);
 console.log(`基础路径: ${base}`);
 console.log('========================================');
 
-// 解析基础路径为 URL 对象,方便后续处理
+// 解析基础路径为 URL 对象
 const baseUrl = new URL(base, `http://localhost:${port}`);
 console.log(`基础URL解析完成: ${baseUrl.href}`);
 
-// 创建服务器实例  
+// 创建服务器实例
 console.log('正在创建服务器实例...');
 const parentServer = createAdaptorServer({  
   fetch: app.fetch,  
   createServer: createNodeServer,  
   port: port,  
-})  
+})
 console.log('服务器实例创建成功');
 
 // 生产环境中间件
@@ -62,18 +61,18 @@ if (!isProduction) {
   console.log('开发环境: 初始化 Vite 开发服务器...');
   const { createServer } = await import('vite');
   vite = await createServer({
-    server: { middlewareMode: {
+    server: { 
+      middlewareMode: {
         server: parentServer
       },
       hmr: {  
         port: 8081,
-        clientPort: 443, // 或其他可用端口  
+        clientPort: 443,
         path: 'vite-hmr'
       },
       proxy: {
         '/vite-hmr': {
           target: 'ws://localhost:8081',
-          // 代理 WebSocket
           ws: true,
         },
       },
@@ -84,169 +83,77 @@ if (!isProduction) {
   console.log('Vite 开发服务器初始化完成');
 }
 
-// 加载 API 路由
-if (!isProduction) {
-  console.log('开发环境: 从 Vite 加载 API 路由...');
-  const apiModule = await vite.ssrLoadModule('./src/server/api.ts');
-  app.route('/', apiModule.default);
-  console.log('API 路由加载完成');
-}else{
-  console.log('生产环境: 加载编译后的 API 路由...');
-  const api = (await import('./dist/api/api.js')).default
-  app.route('/', api);
-  console.log('API 路由加载完成');
-}
-
-// 请求处理中间件 - 通用逻辑
-app.use(async (c, next) => {
-  try {
-    // 使用 c.env 获取原生请求响应对象
-    const req = c.env.incoming;
-    const res = c.env.outgoing;
-    const url = new URL(req.url, `http://${req.headers.host}`);
-    const path = url.pathname;
 
-    // 检查是否匹配基础路径
-    if (!path.startsWith(baseUrl.pathname)) {
-      return c.text('未找到', 404);
-    }
+// 开发环境模板处理函数 - 仅处理模板转换
+const processDevTemplate = async (template) => {
+  if (!isProduction && vite) {
+    console.log('开发环境: 处理模板...');
+    const processedTemplate = await vite.transformIndexHtml('/', template);
+    console.log('开发环境模板处理完成');
+    return processedTemplate;
+  }
+  return template;
+};
 
-    // 开发环境:使用 Vite 中间件
-    if (!isProduction && vite) {
-      // 使用 Vite 中间件处理请求
-      const handled = await new Promise((resolve) => {
-        vite.middlewares(req, res, () => resolve(false));
-      });
-      
-      // 如果 Vite 中间件已经处理了请求,直接返回
-      if (handled) {
-        return c.body;
-      }
-    } 
-    // 生产环境:使用 compression 和 sirv 中间件
-    else if (isProduction) {
-      // 先尝试 compression 中间件
-      const compressed = await new Promise((resolve) => {
-        compressionMiddleware(req, res, () => resolve(false));
-      });
-      
-      if (compressed) {
-        return c.body;
-      }
-      
-      // 再尝试 sirv 中间件处理静态文件
-      const served = await new Promise((resolve) => {
-        sirvMiddleware(req, res, () => resolve(false));
-      });
-      
-      if (served) {
-        return c.body;
-      }
+// 生产环境模板处理函数 - 仅处理资源路径替换
+const processProdTemplate = async (template) => {
+  console.log('生产环境: 处理模板资源路径...');
+  try {
+    // 读取 manifest
+    const manifestPath = new URL('./dist/client/.vite/manifest.json', import.meta.url);
+    const manifestContent = await fs.readFile(manifestPath, 'utf-8');
+    const manifest = JSON.parse(manifestContent);
+    console.log('生产环境: 成功读取 manifest.json');
+
+    // 获取 src/client/index.tsx 对应的资源信息
+    const indexManifest = manifest['src/client/index.tsx'];
+    if (!indexManifest) {
+      throw new Error('manifest 中未找到 src/client/index.tsx 入口配置');
     }
 
-    await next()
-  } catch (e) {
-    if (!isProduction && vite) {
-      vite.ssrFixStacktrace(e);
+    // 获取 src/style.css 对应的资源信息
+    const styleManifest = manifest['src/style.css'];
+    if (!styleManifest) {
+      throw new Error('manifest 中未找到 src/style.css 入口配置');
     }
-    console.error('请求处理中间件错误:', e.stack);
-    return c.text('服务器内部错误', 500);
-  }
-});
 
-// 请求处理中间件 - SSR 渲染逻辑
-app.use(async (c) => {
-  
-  try {
-    // 使用 c.env 获取原生请求响应对象
-    const req = c.env.incoming;
-    const res = c.env.outgoing;
-    const url = new URL(req.url, `http://${req.headers.host}`);
-    const path = url.pathname;
+    const cssPath = new URL(styleManifest.file, baseUrl).pathname;
+    const cssLinks = `<link href="${cssPath}" rel="stylesheet" />`;
 
-    // 检查是否匹配基础路径
-    if (!path.startsWith(baseUrl.pathname)) {
-      return c.text('未找到', 404);
-    }
+    // 替换入口脚本
+    const jsEntryPath = new URL(indexManifest.file, baseUrl).pathname;
+    const entryScript = `<script type="module" src="${jsEntryPath}"></script>`;
 
-    // 处理基础路径
-    const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/';
-    console.log(`处理请求: ${normalizedUrl}`);
+    // 执行替换
+    const processedTemplate = template
+      .replace(/<link href="\/src\/style.css" rel="stylesheet"\/>/, cssLinks)
+      .replace(/<script type="module" src="\/src\/client\/index.tsx"><\/script>/, entryScript);
 
-    // 处理所有其他请求的 SSR 逻辑
-    /** @type {string} */
-    let template;
-    /** @type {import('./src/server/index.tsx').render} */
-    let render;
-    
-    if (!isProduction && vite) {
-      console.log('开发环境: 加载模板和渲染函数...');
-      // 开发环境:读取最新模板并转换
-      const module = (await vite.ssrLoadModule('/src/server/index.tsx'));
-      template = module.template;
-      template = await vite.transformIndexHtml(normalizedUrl, template);
-      render = module.render;
-      console.log('开发环境模板处理完成');
-    } else {
-      // 生产环境:使用缓存的模板
-      console.log('生产环境: 加载编译后的模板和渲染函数...');
-      const module = await import('./dist/server/index.js');
-      
-      // 读取 manifest.json 并处理模板
-      try {
-        // 读取 manifest
-        const manifestPath = new URL('./dist/client/.vite/manifest.json', import.meta.url);
-        const manifestContent = await fs.readFile(manifestPath, 'utf-8');
-        const manifest = JSON.parse(manifestContent);
-        console.log('生产环境: 成功读取 manifest.json');
-
-        // 获取 index.html 对应的资源信息
-        const indexManifest = manifest['index.html'];
-        if (!indexManifest) {
-          throw new Error('manifest 中未找到 index.html 入口配置');
-        }
-
-        template = module.template;
-
-        // 替换 CSS 链接
-        const cssLinks = indexManifest.css?.map(cssFile => {
-          // 结合基础路径生成完整 URL(处理 base 前缀)
-          const cssUrl = new URL(cssFile, baseUrl).pathname;
-          return `<link href="${cssUrl}" rel="stylesheet" />`;
-        }).join('\n') || ''; // 无 CSS 则清空
-
-        // 替换入口脚本
-        const jsEntryPath = new URL(indexManifest.file, baseUrl).pathname;
-        const entryScript = `<script type="module" src="${jsEntryPath}"></script>`;
-
-        // 执行替换
-        template = template
-          .replace(/<link href="\/src\/style.css" rel="stylesheet"\/>/, cssLinks)
-          .replace(/<script type="module" src="\/src\/client\/index.tsx"><\/script>/, entryScript);
-
-        console.log('生产环境模板处理完成');
-
-      } catch (err) {
-        console.error('生产环境模板处理失败:', err);
-        throw err; // 终止启动,避免使用错误模板
-      }
+    console.log('生产环境模板处理完成');
+    return processedTemplate;
 
-      render = module.render;
-    }
+  } catch (err) {
+    console.error('生产环境模板处理失败:', err);
+    throw err;
+  }
+};
 
+// SSR 渲染中间件函数
+const createSsrHandler = (template, render, normalizedUrl) => {
+  return async (c) => {
     let didError = false;
     let abortController;
 
     // 创建一个可读流用于 SSR 渲染内容
     const [htmlStart, htmlEnd] = template.split(`<!--app-html-->`);
     const ssrStream = new Readable({ read: () => {} });
-    
+
     // 写入 HTML 头部
     ssrStream.push(htmlStart);
 
     // 设置响应头和状态码
     c.header('Content-Type', 'text/html');
-    
+
     // 处理渲染
     const { pipe, abort } = render(normalizedUrl, {
       onShellError() {
@@ -266,7 +173,7 @@ app.use(async (c) => {
         });
 
         pipe(transformStream);
-        
+
         // 当 transformStream 完成时,添加 HTML 尾部
         transformStream.on('finish', () => {
           ssrStream.push(htmlEnd);
@@ -294,15 +201,106 @@ app.use(async (c) => {
         clearTimeout(abortTimeout);
       }
     });
+  };
+};
+
+// 统一的请求处理中间件 - 合并 API 和 SSR 渲染逻辑
+app.use(async (c) => {
+  try {
+    // 使用 c.env 获取原生请求响应对象
+    const req = c.env.incoming;
+    const res = c.env.outgoing;
+    const url = new URL(req.url, `http://${req.headers.host}`);
+    const path = url.pathname;
+
+    // 检查是否匹配基础路径
+    if (!path.startsWith(baseUrl.pathname)) {
+      return c.text('未找到', 404);
+    }
+
+    // 处理基础路径
+    const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/';
+    console.log(`处理请求: ${normalizedUrl}`);
+
+    // 开发环境:使用 Vite 中间件
+    if (!isProduction && vite) {
+      // 使用 Vite 中间件处理请求
+      const handled = await new Promise((resolve) => {
+        vite.middlewares(req, res, () => resolve(false));
+      });
+      
+      // 如果 Vite 中间件已经处理了请求,直接返回
+      if (handled) {
+        return c.body;
+      }
+
+
+      // 动态加载最新 API 模块
+      const apiModule = await vite.ssrLoadModule('./src/server/index.tsx');
+      
+      // 创建临时子应用并挂载路由
+      const apiApp = new Hono();
+      apiApp.route('/', apiModule.api);
+      
+      // 处理开发环境模板
+      const template = await processDevTemplate(apiModule.template);
+      apiApp.get('*', createSsrHandler(template, apiModule.render, normalizedUrl));
+
+      // 直接由子应用处理 API 请求
+      return apiApp.fetch(c.req.raw, {
+        ...c.env,
+        // 传递原始请求对象
+        incoming: c.env.incoming,
+        outgoing: c.env.outgoing
+      });
+    }
+    // 生产环境:使用 compression 和 sirv 中间件
+    else if (isProduction) {
+      // 先尝试 compression 中间件
+      const compressed = await new Promise((resolve) => {
+        compressionMiddleware(req, res, () => resolve(false));
+      });
+      
+      if (compressed) {
+        return c.body;
+      }
+      
+      // 再尝试 sirv 中间件处理静态文件
+      const served = await new Promise((resolve) => {
+        sirvMiddleware(req, res, () => resolve(false));
+      });
+      
+      if (served) {
+        return c.body;
+      }
+
+      // 生产环境:使用缓存的 API 路由
+      const { api, template: rawTemplate, render } = (await import('./dist/server/index.js'));
+      const apiApp = new Hono();
+      apiApp.route('/', api);
+
+      // 处理生产环境模板
+      const template = await processProdTemplate(rawTemplate);
+      apiApp.get('*', createSsrHandler(template, render, normalizedUrl));
+
+      return apiApp.fetch(c.req.raw, {
+        ...c.env,
+        incoming: c.env.incoming,
+        outgoing: c.env.outgoing
+      });
+    }
+
   } catch (e) {
     if (!isProduction && vite) {
       vite.ssrFixStacktrace(e);
     }
-    console.error('SSR 处理错误:', e.stack);
+    console.error('请求处理错误:', e.stack);
     return c.text('服务器内部错误', 500);
   }
 });
 
+// 移除生产环境的 API 路由预加载(已在统一中间件中处理)
+// 移除动态 API 路由中间件(已合并到统一中间件中)
 
 // 启动服务器
 console.log('准备启动服务器...');
@@ -322,7 +320,7 @@ const shutdownServer = async () => {
   if (!isProduction && vite) {
     console.log('正在关闭 Vite 开发服务器(包括 HMR 服务)...');
     try {
-      await vite.close(); // 关闭 Vite 实例,会自动终止 HMR 服务(8081 端口)
+      await vite.close();
       console.log('Vite 开发服务器已关闭');
     } catch (err) {
       console.error('关闭 Vite 服务器时出错:', err);
@@ -340,6 +338,6 @@ const shutdownServer = async () => {
   });
 };
 
-// 处理进程终止信号(包括 Ctrl+C)
-process.on('SIGINT', shutdownServer);  // 处理 Ctrl+C
-process.on('SIGTERM', shutdownServer); // 处理 kill 命令
+// 处理进程终止信号
+process.on('SIGINT', shutdownServer);
+process.on('SIGTERM', shutdownServer);

+ 1 - 0
src/server/api.ts

@@ -38,6 +38,7 @@ api.openAPIRegistry.registerComponent('securitySchemes','bearerAuth',{
 })
 
 // OpenAPI documentation endpoint
+// !import.meta.env.PROD
 if(1){
   api.doc31('/doc', {
     openapi: '3.1.0',

+ 2 - 0
src/server/index.tsx

@@ -23,3 +23,5 @@ export const template = renderToStaticMarkup(
   <Rooter />
 );
 
+// 导出 API 路由
+export { default as api } from './api';

+ 16 - 0
vite.config.ts

@@ -2,6 +2,7 @@ import { defineConfig } from 'vite'
 import react from '@vitejs/plugin-react-swc'
 import tailwindcss from '@tailwindcss/vite'
 import { progressTrackingPlugin } from 'vite-progress-tracking-plugin';
+import iframeCommunicationPlugin from 'vite-plugin-iframe-communicator';
 
 // https://vite.dev/config/
 export default defineConfig({
@@ -11,6 +12,9 @@ export default defineConfig({
     }),
     tailwindcss(),
     progressTrackingPlugin(),
+    iframeCommunicationPlugin({
+      hostOrigin: '*', // 可信的主页面源
+    })
   ],
   server: {
     allowedHosts:true
@@ -21,4 +25,16 @@ export default defineConfig({
       '@': '/src',
     },
   },
+  // 构建配置
+  build: {
+    // 对于SSR,我们不需要默认的index.html入口
+    // 为客户端构建指定JS入口文件
+    rollupOptions: {
+      input: {
+        // 这里指定你的客户端入口JS文件
+        main: 'src/client/index.tsx',
+        styles: 'src/style.css',
+      },
+    },
+  },
 })