Fix Broken EventSource/Server-Sent Events

This commit is contained in:
Caileb 2025-05-27 15:26:35 -05:00
parent c0e3781244
commit b525cc0dd0
4 changed files with 232 additions and 104 deletions

View file

@ -9,6 +9,7 @@
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express-http-proxy": "^2.1.1", "express-http-proxy": "^2.1.1",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"http-proxy-middleware": "^3.0.5",
"level": "^10.0.0", "level": "^10.0.0",
"level-ttl": "^3.1.1", "level-ttl": "^3.1.1",
"maxmind": "^4.3.25", "maxmind": "^4.3.25",
@ -27,6 +28,10 @@
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
"@types/http-proxy": ["@types/http-proxy@1.17.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w=="],
"@types/node": ["@types/node@22.15.23", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-7Ec1zaFPF4RJ0eXu1YT/xgiebqwqoJz8rYPDi/O2BcZ++Wpt0Kq9cl0eg6NN6bYbPnR67ZLo7St5Q3UK0SnARw=="],
"abstract-level": ["abstract-level@3.1.0", "", { "dependencies": { "buffer": "^6.0.3", "is-buffer": "^2.0.5", "level-supports": "^6.2.0", "level-transcoder": "^1.0.1", "maybe-combine-errors": "^1.0.0", "module-error": "^1.0.1" } }, "sha512-j2e+TsAxy7Ri+0h7dJqwasymgt0zHBWX4+nMk3XatyuqgHfdstBJ9wsMfbiGwE1O+QovRyPcVAqcViMYdyPaaw=="], "abstract-level": ["abstract-level@3.1.0", "", { "dependencies": { "buffer": "^6.0.3", "is-buffer": "^2.0.5", "level-supports": "^6.2.0", "level-transcoder": "^1.0.1", "maybe-combine-errors": "^1.0.0", "module-error": "^1.0.1" } }, "sha512-j2e+TsAxy7Ri+0h7dJqwasymgt0zHBWX4+nMk3XatyuqgHfdstBJ9wsMfbiGwE1O+QovRyPcVAqcViMYdyPaaw=="],
"after": ["after@0.8.2", "", {}, "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="], "after": ["after@0.8.2", "", {}, "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="],
@ -37,6 +42,8 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"browser-level": ["browser-level@3.0.0", "", { "dependencies": { "abstract-level": "^3.1.0" } }, "sha512-kGXtLh29jMwqKaskz5xeDLtCtN1KBz/DbQSqmvH7QdJiyGRC7RAM8PPg6gvUiNMa+wVnaxS9eSmEtP/f5ajOVw=="], "browser-level": ["browser-level@3.0.0", "", { "dependencies": { "abstract-level": "^3.1.0" } }, "sha512-kGXtLh29jMwqKaskz5xeDLtCtN1KBz/DbQSqmvH7QdJiyGRC7RAM8PPg6gvUiNMa+wVnaxS9eSmEtP/f5ajOVw=="],
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
@ -65,12 +72,16 @@
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
"http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="], "http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="],
"http-proxy-middleware": ["http-proxy-middleware@3.0.5", "", { "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", "http-proxy": "^1.18.1", "is-glob": "^4.0.3", "is-plain-object": "^5.0.0", "micromatch": "^4.0.8" } }, "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg=="],
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
@ -79,6 +90,14 @@
"is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="], "is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"level": ["level@10.0.0", "", { "dependencies": { "abstract-level": "^3.1.0", "browser-level": "^3.0.0", "classic-level": "^3.0.0" } }, "sha512-aZJvdfRr/f0VBbSRF5C81FHON47ZsC2TkGxbBezXpGGXAUEL/s6+GP73nnhAYRSCIqUNsmJjfeOF4lzRDKbUig=="], "level": ["level@10.0.0", "", { "dependencies": { "abstract-level": "^3.1.0", "browser-level": "^3.0.0", "classic-level": "^3.0.0" } }, "sha512-aZJvdfRr/f0VBbSRF5C81FHON47ZsC2TkGxbBezXpGGXAUEL/s6+GP73nnhAYRSCIqUNsmJjfeOF4lzRDKbUig=="],
@ -97,6 +116,8 @@
"maybe-combine-errors": ["maybe-combine-errors@1.0.0", "", {}, "sha512-eefp6IduNPT6fVdwPp+1NgD0PML1NU5P6j1Mj5nz1nidX8/sWY7119WL8vTAHgqfsY74TzW0w1XPgdYEKkGZ5A=="], "maybe-combine-errors": ["maybe-combine-errors@1.0.0", "", {}, "sha512-eefp6IduNPT6fVdwPp+1NgD0PML1NU5P6j1Mj5nz1nidX8/sWY7119WL8vTAHgqfsY74TzW0w1XPgdYEKkGZ5A=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
"minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
@ -113,6 +134,8 @@
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="],
"process-nextick-args": ["process-nextick-args@1.0.7", "", {}, "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw=="], "process-nextick-args": ["process-nextick-args@1.0.7", "", {}, "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw=="],
@ -143,10 +166,14 @@
"tiny-lru": ["tiny-lru@11.2.11", "", {}, "sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA=="], "tiny-lru": ["tiny-lru@11.2.11", "", {}, "sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@ -154,5 +181,7 @@
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"http-proxy-middleware/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
} }
} }

159
package-lock.json generated
View file

@ -11,6 +11,7 @@
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express-http-proxy": "^2.1.1", "express-http-proxy": "^2.1.1",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"http-proxy-middleware": "^3.0.5",
"level": "^10.0.0", "level": "^10.0.0",
"level-ttl": "^3.1.1", "level-ttl": "^3.1.1",
"maxmind": "^4.3.25", "maxmind": "^4.3.25",
@ -41,6 +42,24 @@
"node": ">=18.0.0" "node": ">=18.0.0"
} }
}, },
"node_modules/@types/http-proxy": {
"version": "1.17.16",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz",
"integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "22.15.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.23.tgz",
"integrity": "sha512-7Ec1zaFPF4RJ0eXu1YT/xgiebqwqoJz8rYPDi/O2BcZ++Wpt0Kq9cl0eg6NN6bYbPnR67ZLo7St5Q3UK0SnARw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/abstract-level": { "node_modules/abstract-level": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-3.1.0.tgz", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-3.1.0.tgz",
@ -97,6 +116,18 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browser-level": { "node_modules/browser-level": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/browser-level/-/browser-level-3.0.0.tgz", "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-3.0.0.tgz",
@ -241,6 +272,18 @@
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.15.9", "version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
@ -291,6 +334,40 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/http-proxy-middleware": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
"integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==",
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.15",
"debug": "^4.3.6",
"http-proxy": "^1.18.1",
"is-glob": "^4.0.3",
"is-plain-object": "^5.0.0",
"micromatch": "^4.0.8"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/http-proxy-middleware/node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -352,6 +429,45 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -449,6 +565,19 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/minipass": { "node_modules/minipass": {
"version": "7.1.2", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@ -527,6 +656,18 @@
"node-gyp-build-test": "build-test.js" "node-gyp-build-test": "build-test.js"
} }
}, },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prettier": { "node_modules/prettier": {
"version": "2.8.8", "version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
@ -676,6 +817,18 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -691,6 +844,12 @@
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

View file

@ -15,6 +15,7 @@
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express-http-proxy": "^2.1.1", "express-http-proxy": "^2.1.1",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"http-proxy-middleware": "^3.0.5",
"level": "^10.0.0", "level": "^10.0.0",
"level-ttl": "^3.1.1", "level-ttl": "^3.1.1",
"maxmind": "^4.3.25", "maxmind": "^4.3.25",

View file

@ -11,7 +11,7 @@ const upstreamTimeout = proxyConfig.Timeouts.UpstreamTimeoutMs;
// Build proxy mappings from array format // Build proxy mappings from array format
const proxyMappings = {}; const proxyMappings = {};
proxyConfig.Mapping.forEach((mapping) => { proxyConfig.Mapping.forEach(mapping => {
proxyMappings[mapping.Host] = mapping.Target; proxyMappings[mapping.Host] = mapping.Target;
}); });
@ -61,148 +61,94 @@ async function createProxyResponse(targetURL, request) {
const outgoingHeaders = new Headers(request.headers); const outgoingHeaders = new Headers(request.headers);
outgoingHeaders.delete('host'); outgoingHeaders.delete('host');
// Set proper host header for the target // Set proper host header for the target
const targetHost = new URL(targetURL).host; const targetHost = new URL(targetURL).host;
outgoingHeaders.set('host', targetHost); outgoingHeaders.set('host', targetHost);
// Forward the original host as X-Forwarded-Host for applications that need it // Forward the original host as X-Forwarded-Host for applications that need it
outgoingHeaders.set('x-forwarded-host', request.headers.get('host')); outgoingHeaders.set('x-forwarded-host', request.headers.get('host'));
outgoingHeaders.set('x-forwarded-proto', url.protocol.replace(':', '')); outgoingHeaders.set('x-forwarded-proto', url.protocol.replace(':', ''));
// Preserve important headers for authentication
// Don't delete content-length or transfer-encoding here, handle them properly below
const options = { const options = {
method: request.method, method: request.method,
headers: outgoingHeaders, headers: outgoingHeaders,
// Follow redirects automatically for GET; forward redirects for non-GET // Always use manual redirect to let client handle it
// Absolute requirement: DONT REMOVE redirect: 'manual',
redirect: request.method === 'GET' ? 'follow' : 'manual', // Don't decode compressed responses - let the client handle it
credentials: 'include', decompress: false,
}; };
const isChunked = request.headers.get('transfer-encoding')?.toLowerCase() === 'chunked'; // Handle request body
if (request.body && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method)) {
// Define methods that can legitimately have request bodies options.body = request.body;
const methodsWithBody = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
if (methodsWithBody.has(request.method) && request.body) {
if (isChunked) {
logs.plugin('proxy', `De-chunking request body for ${request.method} ${request.url}`);
try {
const bodyBuffer = await request.arrayBuffer();
options.body = bodyBuffer;
outgoingHeaders.set('content-length', String(bodyBuffer.byteLength));
outgoingHeaders.delete('transfer-encoding');
} catch (bufferError) {
logs.error('proxy', `Error buffering chunked request body: ${bufferError}`);
return new Response('Error processing chunked request body', { status: 500 });
}
} else {
// For non-chunked bodies, preserve the body stream
options.body = request.body;
// Keep the original content-length if it exists
if (request.headers.has('content-length')) {
outgoingHeaders.set('content-length', request.headers.get('content-length'));
}
}
} }
// Add a timeout controller for the upstream fetch // Add timeout
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
logs.warn( logs.warn('proxy', `Upstream request to ${fullTargetURL} timed out after ${upstreamTimeout}ms`);
'proxy',
`Upstream request to ${fullTargetURL} timed out after ${upstreamTimeout}ms`,
);
controller.abort(); controller.abort();
}, upstreamTimeout); }, upstreamTimeout);
let response; let response;
try { try {
response = await fetch(fullTargetURL, { response = await fetch(fullTargetURL, { ...options, signal: controller.signal });
...options,
signal: controller.signal,
verbose: true,
});
} catch (fetchErr) { } catch (fetchErr) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (fetchErr.name === 'AbortError') { if (fetchErr.name === 'AbortError') {
logs.error('proxy', `Upstream fetch aborted for ${fullTargetURL} (likely due to timeout)`); logs.error('proxy', `Upstream fetch aborted for ${fullTargetURL} (likely due to timeout)`);
return new Response('Gateway Timeout', { status: 504 }); return new Response('Gateway Timeout', { status: 504 });
} }
throw fetchErr; logs.error('proxy', `Fetch error: ${fetchErr.message}`);
return new Response('Bad Gateway', { status: 502 });
} }
clearTimeout(timeoutId); clearTimeout(timeoutId);
const latency = Date.now() - startTime; const latency = Date.now() - startTime;
logs.plugin('proxy', `Proxied request to: ${fullTargetURL} (${response.status} ${response.statusText}) (${latency}ms)`);
logs.plugin(
'proxy',
`Proxied request to: ${fullTargetURL} (${response.status} ${response.statusText}) (${latency}ms)`,
);
const responseHeaders = new Headers(response.headers); const responseHeaders = new Headers(response.headers);
// Remove hop-by-hop headers // Remove hop-by-hop headers
HOP_BY_HOP_HEADERS.forEach((h) => responseHeaders.delete(h)); HOP_BY_HOP_HEADERS.forEach((h) => responseHeaders.delete(h));
// Remove content-encoding and content-length headers // IMPORTANT: Don't remove content-encoding or modify the body
// This is necessary because Bun/fetch automatically decompresses the response body // Let the response stream through as-is for SSE compatibility
// but leaves the content-encoding header, causing the browser to try to decompress already decompressed content
responseHeaders.delete('content-encoding');
responseHeaders.delete('content-length');
// Add proxy information // Add proxy information
responseHeaders.set('X-Proxy-Latency', `${latency}ms`); responseHeaders.set('X-Proxy-Latency', `${latency}ms`);
// Handle Set-Cookie headers - rewrite domain if needed // Handle Set-Cookie headers - rewrite domain if needed
const setCookieHeaders = response.headers.getSetCookie ? response.headers.getSetCookie() : []; const setCookieHeaders = response.headers.getSetCookie ? response.headers.getSetCookie() : [];
if (setCookieHeaders.length > 0) { if (setCookieHeaders.length > 0) {
responseHeaders.delete('set-cookie'); responseHeaders.delete('set-cookie');
setCookieHeaders.forEach((cookieStr) => { setCookieHeaders.forEach(cookieStr => {
// Parse and potentially rewrite the cookie domain
let modifiedCookie = cookieStr; let modifiedCookie = cookieStr;
// Remove domain restrictions that might prevent the cookie from working // Remove domain restrictions
modifiedCookie = modifiedCookie.replace(/;\s*domain=[^;]*/gi, ''); modifiedCookie = modifiedCookie.replace(/;\s*domain=[^;]*/gi, '');
// If the cookie has SameSite=None, ensure it also has Secure // Handle SameSite for local development
if (modifiedCookie.match(/samesite\s*=\s*none/i) && !modifiedCookie.match(/secure/i)) {
modifiedCookie += '; Secure';
}
// For local development, you might need to adjust SameSite
if (url.protocol === 'http:' && modifiedCookie.match(/samesite\s*=\s*none/i)) { if (url.protocol === 'http:' && modifiedCookie.match(/samesite\s*=\s*none/i)) {
modifiedCookie = modifiedCookie.replace(/;\s*samesite=[^;]*/gi, '; SameSite=Lax'); modifiedCookie = modifiedCookie.replace(/;\s*samesite=[^;]*/gi, '; SameSite=Lax');
modifiedCookie = modifiedCookie.replace(/;\s*secure/gi, ''); modifiedCookie = modifiedCookie.replace(/;\s*secure/gi, '');
} }
responseHeaders.append('set-cookie', modifiedCookie); responseHeaders.append('set-cookie', modifiedCookie);
}); });
} }
// Return response with original body stream
return new Response(response.body, { return new Response(response.body, {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
headers: responseHeaders, headers: responseHeaders,
}); });
} catch (err) { } catch (err) {
logs.error( logs.error('proxy', `Proxy error processing ${request.method} ${request.url}: ${err.message}`);
'proxy', logs.error('proxy', `Full error details: ${err.stack}`);
`Proxy error processing ${request.method} ${request.url}: ${err.message}${
err.cause ? ' - Cause: ' + err.cause : ''
}`,
);
let causeDetails = '';
if (err.cause) {
causeDetails = typeof err.cause === 'object' ? JSON.stringify(err.cause) : String(err.cause);
}
logs.error(
'proxy',
`Full error details: ${err.stack}${err.cause ? '\nCause: ' + causeDetails : ''}`,
);
return new Response('Bad Gateway', { status: 502 }); return new Response('Bad Gateway', { status: 502 });
} }
} }
@ -228,30 +174,23 @@ function proxyMiddleware() {
if (upgradeHeader === 'websocket') { if (upgradeHeader === 'websocket') {
const targetUrl = new URL(url.pathname + url.search, target); const targetUrl = new URL(url.pathname + url.search, target);
targetUrl.protocol = targetUrl.protocol.replace(/^http/, 'ws'); targetUrl.protocol = targetUrl.protocol.replace(/^http/, 'ws');
// Forward important headers for WebSocket // Forward important headers for WebSocket
const wsHeaders = new Headers(); const wsHeaders = {};
if (request.headers.has('cookie')) wsHeaders.set('Cookie', request.headers.get('cookie')); ['cookie', 'authorization', 'origin', 'sec-websocket-protocol', 'sec-websocket-extensions']
if (request.headers.has('authorization')) .forEach(header => {
wsHeaders.set('Authorization', request.headers.get('authorization')); const value = request.headers.get(header);
if (request.headers.has('origin')) wsHeaders.set('Origin', request.headers.get('origin')); if (value) wsHeaders[header] = value;
if (request.headers.has('sec-websocket-protocol')) });
wsHeaders.set('Sec-WebSocket-Protocol', request.headers.get('sec-websocket-protocol'));
if (request.headers.has('sec-websocket-extensions'))
wsHeaders.set('Sec-WebSocket-Extensions', request.headers.get('sec-websocket-extensions'));
let upstream; let upstream;
try { try {
// Convert Headers object to a plain object for the WebSocket constructor upstream = await connectUpstreamWebSocket(targetUrl.toString(), wsHeaders);
const plainWsHeaders = {};
for (const [key, value] of wsHeaders) {
plainWsHeaders[key] = value;
}
upstream = await connectUpstreamWebSocket(targetUrl.toString(), plainWsHeaders);
} catch (err) { } catch (err) {
logs.error('proxy', `Upstream WebSocket connection failed: ${err}`); logs.error('proxy', `Upstream WebSocket connection failed: ${err}`);
return new Response('Bad Gateway', { status: 502 }); return new Response('Bad Gateway', { status: 502 });
} }
// Upgrade incoming client connection and attach upstream socket // Upgrade incoming client connection and attach upstream socket
const ok = server.upgrade(request, { data: { upstream } }); const ok = server.upgrade(request, { data: { upstream } });
if (!ok) { if (!ok) {