Init
This commit is contained in:
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
vol
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
vol
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM node:21-alpine
|
||||||
|
RUN mkdir /app
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN mkdir dist
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
VOLUME /app/vol
|
||||||
|
|
||||||
|
CMD npm run start
|
||||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
main:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
HTTP_PORT: 52080
|
||||||
|
HTTPS_PORT: 52443 # comment this if https is not required
|
||||||
|
# VOLUME_PATH: '/app/vol' # Not required because its '/app/vol' by default
|
||||||
|
CERTS_PATH: /etc/letsencrypt/live/ # Will be ignored because LOAD_CERTS_FROM_VOLUME is 1
|
||||||
|
LOAD_CERTS_FROM_VOLUME: 1 # CERTS_PATH will be inherited from VOLUME_PATH
|
||||||
|
networks:
|
||||||
|
- your_network # choose network with your containers
|
||||||
|
volumes:
|
||||||
|
- ../volumes/proxy:/app/vol
|
||||||
|
ports:
|
||||||
|
- 52080:52080
|
||||||
|
- 52443:52443
|
||||||
|
|
||||||
|
networks:
|
||||||
|
# choose network with your containers
|
||||||
|
your_network:
|
||||||
|
external: true
|
||||||
877
package-lock.json
generated
Normal file
877
package-lock.json
generated
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
{
|
||||||
|
"name": "proxy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "proxy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/http-proxy": "^1.17.13",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"http-proxy": "^1.18.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.20",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/body-parser": {
|
||||||
|
"version": "1.19.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz",
|
||||||
|
"integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/connect": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/connect": {
|
||||||
|
"version": "3.4.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz",
|
||||||
|
"integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/express": {
|
||||||
|
"version": "4.17.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz",
|
||||||
|
"integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/body-parser": "*",
|
||||||
|
"@types/express-serve-static-core": "^4.17.33",
|
||||||
|
"@types/qs": "*",
|
||||||
|
"@types/serve-static": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/express-serve-static-core": {
|
||||||
|
"version": "4.17.39",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz",
|
||||||
|
"integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/qs": "*",
|
||||||
|
"@types/range-parser": "*",
|
||||||
|
"@types/send": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/http-errors": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/http-proxy": {
|
||||||
|
"version": "1.17.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz",
|
||||||
|
"integrity": "sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/mime": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.8.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
|
||||||
|
"integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/qs": {
|
||||||
|
"version": "6.9.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz",
|
||||||
|
"integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/range-parser": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/send": {
|
||||||
|
"version": "0.17.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz",
|
||||||
|
"integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/mime": "^1",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/serve-static": {
|
||||||
|
"version": "1.15.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz",
|
||||||
|
"integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/http-errors": "*",
|
||||||
|
"@types/mime": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||||
|
},
|
||||||
|
"node_modules/body-parser": {
|
||||||
|
"version": "1.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||||
|
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "3.1.2",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"qs": "6.11.0",
|
||||||
|
"raw-body": "2.5.1",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8",
|
||||||
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/body-parser/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bytes": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bind": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"set-function-length": "^1.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/content-disposition": {
|
||||||
|
"version": "0.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "5.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/content-type": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||||
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/define-data-property": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/depd": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/destroy": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8",
|
||||||
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
|
},
|
||||||
|
"node_modules/encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||||
|
},
|
||||||
|
"node_modules/etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||||
|
},
|
||||||
|
"node_modules/express": {
|
||||||
|
"version": "4.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||||
|
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.8",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.20.1",
|
||||||
|
"content-disposition": "0.5.4",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.5.0",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "1.2.0",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.7",
|
||||||
|
"qs": "6.11.0",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"safe-buffer": "5.2.1",
|
||||||
|
"send": "0.18.0",
|
||||||
|
"serve-static": "1.15.0",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/express/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/finalhandler": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/finalhandler/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||||
|
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/forwarded": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"has-proto": "^1.0.1",
|
||||||
|
"has-symbols": "^1.0.3",
|
||||||
|
"hasown": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.1.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-property-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-errors": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"toidentifier": "1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-proxy": {
|
||||||
|
"version": "1.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
||||||
|
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": "^4.0.0",
|
||||||
|
"follow-redirects": "^1.0.0",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"node_modules/ipaddr.js": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||||
|
},
|
||||||
|
"node_modules/methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-inspect": {
|
||||||
|
"version": "1.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||||
|
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/on-finished": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||||
|
},
|
||||||
|
"node_modules/proxy-addr": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||||
|
"dependencies": {
|
||||||
|
"forwarded": "0.2.0",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/raw-body": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "3.1.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"node_modules/send": {
|
||||||
|
"version": "0.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||||
|
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"statuses": "2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/send/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
|
},
|
||||||
|
"node_modules/send/node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"node_modules/serve-static": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||||
|
"dependencies": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"send": "0.18.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/set-function-length": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.1.1",
|
||||||
|
"get-intrinsic": "^1.2.1",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/setprototypeof": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||||
|
},
|
||||||
|
"node_modules/side-channel": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.0",
|
||||||
|
"get-intrinsic": "^1.0.2",
|
||||||
|
"object-inspect": "^1.9.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/statuses": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/toidentifier": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
|
"node_modules/unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "proxy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "tsc && node dist/index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/http-proxy": "^1.17.13",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"http-proxy": "^1.18.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.20",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
284
src/config.ts
Normal file
284
src/config.ts
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
import { readFile, writeFile } from "fs/promises"
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
|
||||||
|
interface Dict<T> {
|
||||||
|
[key: string]: T
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cors = string[] | '*'
|
||||||
|
type Nullable<T> = T | null
|
||||||
|
|
||||||
|
type DomainCertName = string
|
||||||
|
|
||||||
|
export const CERT_NAME = 'cert-name'
|
||||||
|
|
||||||
|
|
||||||
|
export class Config implements IConfig {
|
||||||
|
volumePath: string
|
||||||
|
domains: Dict<ConfigAtDomain>
|
||||||
|
|
||||||
|
constructor(cfg: IConfig, volumePath: string) {
|
||||||
|
this.volumePath = volumePath
|
||||||
|
|
||||||
|
for (const key of Object.keys(cfg))
|
||||||
|
this[key] = cfg[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefault(): IConfig {
|
||||||
|
return {
|
||||||
|
domains: {
|
||||||
|
"test.com": {
|
||||||
|
[CERT_NAME]: "test.com",
|
||||||
|
"/": {
|
||||||
|
domain: "web-docker-container",
|
||||||
|
port: 8080
|
||||||
|
},
|
||||||
|
"/api/": {
|
||||||
|
domain: "api-docker-container",
|
||||||
|
port: 8081,
|
||||||
|
cors: ["test.com"],
|
||||||
|
relative: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mirror.test.com": {
|
||||||
|
[CERT_NAME]: "test.com",
|
||||||
|
"/": {
|
||||||
|
domain: "test.cc",
|
||||||
|
https: true,
|
||||||
|
cors: '*',
|
||||||
|
rewriteDomain: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"static.test.com": {
|
||||||
|
[CERT_NAME]: "test.com",
|
||||||
|
"/": {
|
||||||
|
folder: "volFolder",
|
||||||
|
cors: '*'
|
||||||
|
},
|
||||||
|
"/extra/": {
|
||||||
|
folder: "extraVolFolder",
|
||||||
|
cors: ['test.com', 'test.cc'],
|
||||||
|
relative: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async save(cfg: IConfig, volumePath: string) {
|
||||||
|
await writeFile(Config.configPath(volumePath), JSON.stringify(cfg, undefined, 2))
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
static async load(volumePath: string) {
|
||||||
|
return new Config(
|
||||||
|
await readFile(Config.configPath(volumePath), 'utf8')
|
||||||
|
.then(
|
||||||
|
res => JSON.parse(res) as IConfig,
|
||||||
|
async err => await Config.save(Config.getDefault(), volumePath)
|
||||||
|
),
|
||||||
|
volumePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static isStatic(atPath: ConfigAtPathRaw): atPath is ConfigAtPathStatic {
|
||||||
|
return "folder" in atPath
|
||||||
|
}
|
||||||
|
|
||||||
|
private static configPath(volumePath: string) { return join(resolve(volumePath), 'config.json') }
|
||||||
|
private static fixPath(path: string) { return (path.startsWith('/') ? '' : '/') + path + (!path.length || path.endsWith('/') ? '' : '/') }
|
||||||
|
private static splitPath(path: string) {
|
||||||
|
const fixedPath = Config.fixPath(path)
|
||||||
|
return fixedPath.length < 2 ? [] : fixedPath.substring(1, fixedPath.length - 1).split('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
repr(): IConfig {
|
||||||
|
return {
|
||||||
|
domains: this.domains,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCerts(): Dict<Nullable<DomainCertName>> {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(this.domains)
|
||||||
|
.map(([key, val]) =>
|
||||||
|
[
|
||||||
|
key,
|
||||||
|
typeof val[CERT_NAME] == 'string' ? val[CERT_NAME] : null
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getProxyData(domain: string, path: string): Nullable<ConfigAtPath> {
|
||||||
|
const atDomain = this.domains[domain]
|
||||||
|
if (!atDomain)
|
||||||
|
return null
|
||||||
|
|
||||||
|
const [atPath, exclude] = this.getConfigFromDomain(atDomain, path)
|
||||||
|
if (!atPath)
|
||||||
|
return null
|
||||||
|
if (Config.isStatic(atPath))
|
||||||
|
return {
|
||||||
|
exclude,
|
||||||
|
folder: atPath.folder,
|
||||||
|
cors: atPath.cors || [domain],
|
||||||
|
relative: atPath.relative
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
exclude,
|
||||||
|
domain: atPath.domain,
|
||||||
|
port: atPath.port || 0,
|
||||||
|
headers: atPath.headers || {},
|
||||||
|
https: atPath.https,
|
||||||
|
cors: atPath.cors || [domain],
|
||||||
|
rewriteDomain: atPath.rewriteDomain,
|
||||||
|
relative: atPath.relative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getConfigFromDomain(atDomain: ConfigAtDomain, _path: string): [Nullable<ConfigAtPathRaw>, number] {
|
||||||
|
const path = Config.splitPath(_path)
|
||||||
|
|
||||||
|
const atPaths = this.getPaths(atDomain)
|
||||||
|
|
||||||
|
for (const [key, atPath] of atPaths)
|
||||||
|
if (this.comparePaths(path, key))
|
||||||
|
return [atPath, atPath.relative ? Config.fixPath(key.join('/')).length : 1]
|
||||||
|
|
||||||
|
return [null, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
private comparePaths(reqPath: string[], cfgPath: string[]) {
|
||||||
|
if (reqPath.length < cfgPath.length)
|
||||||
|
return false
|
||||||
|
|
||||||
|
for (let i = 0; i < cfgPath.length; i++)
|
||||||
|
if (reqPath[i] != cfgPath[i])
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPaths(atDomain: ConfigAtDomain) {
|
||||||
|
return Object.entries(atDomain)
|
||||||
|
.filter(([key, val]) =>
|
||||||
|
(key != CERT_NAME) && (typeof val != 'string'))
|
||||||
|
.map(([key, val]) =>
|
||||||
|
[Config.splitPath(key), val] as [string[], ConfigAtPathRaw])
|
||||||
|
.sort(([a], [b]) =>
|
||||||
|
b.length - a.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
await Config.save(this.repr(), this.volumePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfigAtPath = Required<ConfigAtPathRaw> & ConfigAtPathCommon
|
||||||
|
|
||||||
|
export type ConfigAtPathRaw = ConfigAtPathDynamic | ConfigAtPathStatic
|
||||||
|
|
||||||
|
export interface ConfigAtPathCommon {
|
||||||
|
/**
|
||||||
|
* Auto calculated field
|
||||||
|
*/
|
||||||
|
exclude: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigAtPathStatic {
|
||||||
|
/**
|
||||||
|
* Will redirect request to local file from this **folder**
|
||||||
|
*
|
||||||
|
* **Note:** this folder must be in volume folder
|
||||||
|
*/
|
||||||
|
folder: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed origins
|
||||||
|
*
|
||||||
|
* Use **"*"** to disable cors
|
||||||
|
*
|
||||||
|
* **Default:** [req.hostname]
|
||||||
|
*/
|
||||||
|
cors?: Cors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove path from request path
|
||||||
|
*
|
||||||
|
* **Example:** ({"test.com": "/api/": {"folder": "volFolder"}})
|
||||||
|
* * *true* - Get **http://test.com/api/test** => Get **./volume/volFolder/test**
|
||||||
|
* * *false* - Get **http://test.com/api/test** => Get **./volume/volFolder/volFolder/test**
|
||||||
|
*
|
||||||
|
* **Default:** false
|
||||||
|
*/
|
||||||
|
relative?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigAtPathDynamic {
|
||||||
|
/**
|
||||||
|
* Will redirect request to http server with this **domain** or **ip**
|
||||||
|
*/
|
||||||
|
domain: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will redirect request to http server with this **port**
|
||||||
|
*
|
||||||
|
* **Default:**
|
||||||
|
* * *for http* - 80
|
||||||
|
* * *for https* - 443
|
||||||
|
*/
|
||||||
|
port?: number
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will redirect request to http server with this **headers**
|
||||||
|
*
|
||||||
|
* **Default:** {}
|
||||||
|
*/
|
||||||
|
headers?: { [key: string]: string }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use https protocol instead of http when redirecting
|
||||||
|
*
|
||||||
|
* **Default:** false
|
||||||
|
*/
|
||||||
|
https?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed origins
|
||||||
|
*
|
||||||
|
* Use **"*"** to disable cors
|
||||||
|
*
|
||||||
|
* **Default:** [req.hostname]
|
||||||
|
*/
|
||||||
|
cors?: Cors
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use domain (or ip) of http server instead of requested domain
|
||||||
|
*
|
||||||
|
* **Example:**
|
||||||
|
* * *true* - Get **req.hostname** => Get **config.domain** with header Host=**config.domain**
|
||||||
|
* * *false* - Get **req.hostname** => Get **config.domain** with header Host=**req.hostname**
|
||||||
|
*
|
||||||
|
* **Default:** false
|
||||||
|
*/
|
||||||
|
rewriteDomain?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove path from request path
|
||||||
|
*
|
||||||
|
* **Example:** ({"test.com": "/api/": {"domain": "localhost"}})
|
||||||
|
* * *true* - Get **http://test.com/api/test** => Get **http:/localhost/test**
|
||||||
|
* * *false* - Get **http://test.com/api/test** => Get **http:/localhost/api/test**
|
||||||
|
*
|
||||||
|
* **Default:** false
|
||||||
|
*/
|
||||||
|
relative?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigAtDomain extends Dict<ConfigAtPathRaw | DomainCertName> { }
|
||||||
|
|
||||||
|
export interface IConfig {
|
||||||
|
domains: Dict<ConfigAtDomain>
|
||||||
|
}
|
||||||
41
src/config_old.ts
Normal file
41
src/config_old.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
default?: ConfigProxyPaths
|
||||||
|
hosts: {
|
||||||
|
[domain: string]: ConfigProxyPaths
|
||||||
|
},
|
||||||
|
certs: ConfigCerts
|
||||||
|
}
|
||||||
|
export interface ConfigCerts {
|
||||||
|
[folder: string]: {
|
||||||
|
domain: string
|
||||||
|
allowSubdomains: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface ConfigProxyPaths {
|
||||||
|
[path: string]: ConfigProxy | ConfigProxyStatic
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cors = string[] | '*'
|
||||||
|
|
||||||
|
export interface ConfigProxy {
|
||||||
|
domain: string
|
||||||
|
port?: number
|
||||||
|
headers?: { [key: string]: string }
|
||||||
|
https?: boolean
|
||||||
|
cors?: Cors
|
||||||
|
useNativeDomain?: boolean
|
||||||
|
relative?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigProxyStatic {
|
||||||
|
static: string
|
||||||
|
cors?: Cors
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getConfig(path: string): Config {
|
||||||
|
return JSON.parse(
|
||||||
|
fs.readFileSync(path, 'utf8')
|
||||||
|
)
|
||||||
|
}
|
||||||
28
src/env.ts
Normal file
28
src/env.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import 'process'
|
||||||
|
|
||||||
|
interface EnvFunc {
|
||||||
|
(def: number): number
|
||||||
|
(def: string): string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnvFuncGeneric = (def: string | number) => string | number
|
||||||
|
|
||||||
|
interface Env {
|
||||||
|
readonly [key: string]: EnvFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env: Env = new Proxy<Env>({}, {
|
||||||
|
get(_, prop: string, cast): EnvFuncGeneric {
|
||||||
|
return def => {
|
||||||
|
const val: string = process.env[prop]
|
||||||
|
if (!val) return def
|
||||||
|
|
||||||
|
switch (typeof def) {
|
||||||
|
case 'number':
|
||||||
|
return Number.isNaN(val) ? def : Number(val)
|
||||||
|
default:
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
40
src/https.ts
Normal file
40
src/https.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { RequestListener } from "http";
|
||||||
|
import https from "https";
|
||||||
|
import tls from "tls";
|
||||||
|
import fs from "fs";
|
||||||
|
import { Config } from "./config";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
|
||||||
|
export function getHttps(cfg: Config, letsencryptPath: string) {
|
||||||
|
const secureContexts = getSecureContexts(cfg, letsencryptPath)
|
||||||
|
|
||||||
|
const options: https.ServerOptions = {
|
||||||
|
// A function that will be called if the client supports SNI TLS extension.
|
||||||
|
SNICallback: (domain, cb) => {
|
||||||
|
|
||||||
|
const ctx = secureContexts[domain];
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
ctx ? cb(null, ctx) : cb(Error("No ctx"));
|
||||||
|
} else if (ctx) {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (app: RequestListener) => https.createServer(options, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSecureContexts(config: Config, letsencryptPath: string) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(config.getCerts())
|
||||||
|
.filter(([_, value]) => value)
|
||||||
|
.map(([key, value]) =>
|
||||||
|
[key, tls.createSecureContext({
|
||||||
|
key: fs.readFileSync(join(letsencryptPath, value, "/privkey1.pem")),
|
||||||
|
cert: fs.readFileSync(join(letsencryptPath, value, "/fullchain1.pem")),
|
||||||
|
ca: null,
|
||||||
|
})])
|
||||||
|
)
|
||||||
|
}
|
||||||
35
src/index.ts
Normal file
35
src/index.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { getHttps } from "./https";
|
||||||
|
import { createServer as httpServer } from 'http'
|
||||||
|
import { Config } from "./config";
|
||||||
|
import getProxy from "./proxy";
|
||||||
|
import { env } from "./env";
|
||||||
|
|
||||||
|
|
||||||
|
const HTTP_PORT = env.HTTP_PORT(52080),
|
||||||
|
HTTPS_PORT = env.HTTPS_PORT(-1),
|
||||||
|
VOLUME_PATH = env.VOLUME_PATH('./vol'),
|
||||||
|
CERTS_PATH = env.LOAD_CERTS_FROM_VOLUME(0) ? VOLUME_PATH : env.CERTS_PATH('/etc/letsencrypt/live/')
|
||||||
|
|
||||||
|
console.log("ENV Loaded:", {
|
||||||
|
HTTP_PORT,
|
||||||
|
HTTPS_PORT: HTTPS_PORT + (HTTPS_PORT == -1 ? ' (WITHOUT HTTPS)' : ''),
|
||||||
|
VOLUME_PATH,
|
||||||
|
CERTS_PATH: CERTS_PATH + (env.LOAD_CERTS_FROM_VOLUME(0) ? ' (Loaded from VOLUME_PATH)' : ''),
|
||||||
|
})
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const config = await Config.load(VOLUME_PATH),
|
||||||
|
{ app, upgrade } = getProxy(config)
|
||||||
|
|
||||||
|
httpServer(app).on('upgrade', upgrade).listen(HTTP_PORT, function () {
|
||||||
|
console.log("Listening http on port: " + HTTP_PORT)
|
||||||
|
});
|
||||||
|
if (HTTPS_PORT != -1) {
|
||||||
|
const httpsServer = await getHttps(config, CERTS_PATH)
|
||||||
|
httpsServer(app).on('upgrade', upgrade).listen(HTTPS_PORT, function () {
|
||||||
|
console.log("Listening https on port: " + HTTPS_PORT)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
132
src/proxy.ts
Normal file
132
src/proxy.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import express, { Request, Response } from "express";
|
||||||
|
|
||||||
|
import { Duplex } from "node:stream"
|
||||||
|
import { IncomingMessage } from 'http';
|
||||||
|
import httpProxy from 'http-proxy';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import { resolve, join } from 'path';
|
||||||
|
import { lstat } from 'fs';
|
||||||
|
import { Config, ConfigAtPath } from "./config";
|
||||||
|
|
||||||
|
|
||||||
|
export default function getProxy(cfg: Config) {
|
||||||
|
const volumePath = resolve(cfg.volumePath),
|
||||||
|
app = express(),
|
||||||
|
proxy = httpProxy.createProxyServer(),
|
||||||
|
corsOptionsDelegate = function (req: Request, callback: (a: any, b: { origin: boolean }) => null) {
|
||||||
|
const proxyData = getProxyData(req),
|
||||||
|
_origin = (req.header('Origin') || req.header('Referer') || 'None').split('//'),
|
||||||
|
origin = _origin[_origin.length - 1].split('/')[0].split(':')[0]
|
||||||
|
|
||||||
|
let answer = false
|
||||||
|
|
||||||
|
if (proxyData)
|
||||||
|
if (proxyData.cors == '*')
|
||||||
|
answer = true
|
||||||
|
else
|
||||||
|
for (const domain of proxyData.cors)
|
||||||
|
if (origin.endsWith(domain)) {
|
||||||
|
answer = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.res)
|
||||||
|
req.res.locals.cors = answer
|
||||||
|
|
||||||
|
callback(null, { origin: answer });
|
||||||
|
};
|
||||||
|
|
||||||
|
function prettyLog(data: any) {
|
||||||
|
console.log(JSON.stringify(data, undefined, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRequest(proxyData: ConfigAtPath, req: Request, res: Response) {
|
||||||
|
const log: { [key: string]: any } = {
|
||||||
|
'IN_HOST': req.hostname,
|
||||||
|
'IN_PATH': req.path,
|
||||||
|
'IN_QUERY': req.query,
|
||||||
|
'IN_HEADERS': req.headers,
|
||||||
|
'CORS_ALLOWED': !!res.locals.cors
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.isStatic(proxyData)) {
|
||||||
|
log.PROXY_TYPE = 'STATIC'
|
||||||
|
const path = join(volumePath, proxyData.folder, '/' + req.path.substring(proxyData.exclude));
|
||||||
|
log.FILE_PATH = path
|
||||||
|
lstat(path, (err, stat) => {
|
||||||
|
if (err || !stat.isFile()) {
|
||||||
|
log.FILE_FOUND = false
|
||||||
|
prettyLog(log)
|
||||||
|
|
||||||
|
res.status(404).send('<h1>File not exist</h1>')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.FILE_FOUND = false
|
||||||
|
prettyLog(log)
|
||||||
|
|
||||||
|
res.sendFile(path)
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domain = req.headers.host = proxyData.rewriteDomain ? proxyData.domain : req.hostname
|
||||||
|
const headers = req.headers = { ...proxyData.headers, ...req.headers }
|
||||||
|
const target = (proxyData.https ? 'https://' : 'http://') + proxyData.domain + (proxyData.port ? ':' + proxyData.port : '')
|
||||||
|
|
||||||
|
req.url = '/' + req.url.substring(proxyData.exclude)
|
||||||
|
|
||||||
|
log.PROXY_TYPE = 'DYNAMIC'
|
||||||
|
log.REDIRECTED_TO = target
|
||||||
|
log.OUT_HOST = domain
|
||||||
|
log.OUT_PATH = req.path
|
||||||
|
log.OUT_HEADERS = headers
|
||||||
|
prettyLog(log)
|
||||||
|
|
||||||
|
proxy.web(req, res, {
|
||||||
|
target,
|
||||||
|
hostRewrite: domain
|
||||||
|
}, () => res.status(503).send('Service unavailable'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProxyData(req: { hostname: string, path: string }): ConfigAtPath {
|
||||||
|
return cfg.getProxyData(req.hostname, req.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(cors(corsOptionsDelegate))
|
||||||
|
|
||||||
|
app.use((req, res) => {
|
||||||
|
const proxyData = getProxyData(req);
|
||||||
|
if (proxyData)
|
||||||
|
return handleRequest(proxyData, req, res)
|
||||||
|
|
||||||
|
prettyLog({
|
||||||
|
'IN_HOST': req.hostname,
|
||||||
|
'IN_PATH': req.path,
|
||||||
|
'IN_QUERY': req.query,
|
||||||
|
'IN_HEADERS': req.headers,
|
||||||
|
'PROXY_TYPE': 'NOT_FOUND'
|
||||||
|
})
|
||||||
|
res.status(404).send('Not found')
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
upgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => {
|
||||||
|
|
||||||
|
const { hostname, pathname: path } = new URL(req.url, `http://${req.headers.host}`);
|
||||||
|
const proxyData = getProxyData({
|
||||||
|
hostname,
|
||||||
|
path
|
||||||
|
})
|
||||||
|
|
||||||
|
if (proxyData && !Config.isStatic(proxyData))
|
||||||
|
proxy.ws(req, socket, head, {
|
||||||
|
ws: true,
|
||||||
|
target: 'ws://' + proxyData.domain + (proxyData.port ? ':' + proxyData.port : ''),
|
||||||
|
hostRewrite: proxyData.rewriteDomain ? proxyData.domain : hostname
|
||||||
|
}, () => null)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es6",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"es2015"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user