AAZZ khanon commited on
Commit
fe91fe3
0 Parent(s):

Duplicate from khanon/oai-proxy

Browse files

Co-authored-by: anon <khanon@users.noreply.huggingface.co>

.env.example ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copy this file to .env and fill in the values.
2
+
3
+ # Uncomment the following line and replace the value with your own secret key
4
+ # to control access to the proxy server
5
+ # PROXY_KEY=your-secret-key
6
+
7
+ # Set your OpenAI API key below.
8
+ OPENAI_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
9
+
10
+ # You can also set a base-64 encoded JSON array of keys matching this schema:
11
+ # Trial keys will be prioritized. GPT4-enabled keys are necessary to use GPT-4.
12
+ # For example:
13
+ # [
14
+ # { "key": "your-openai-key-1", "isTrial": true, "isGpt4": false },
15
+ # { "key": "your-openai-key-2", "isTrial": false, "isGpt4": false },
16
+ # { "key": "your-openai-key-3", "isTrial": false, "isGpt4": true }
17
+ # ]
18
+ # Encoded in base-64, this would look like:
19
+ # OPENAI_KEYS=WwogeyAia2V5IjogInlvdXItb3BlbmFpLWtleS0xIiwgImlzVHJpYWwiOiB0cnVlLCAiaXNHcHQ0IjogZmFsc2UgfSwKIHsgImtleSI6ICJ5b3VyLW9wZW5haS1rZXktMiIsICJpc1RyaWFsIjogZmFsc2UsICJpc0dwdDQiOiBmYWxzZSB9LAogeyAia2V5IjogInlvdXItb3BlbmFpLWtleS0zIiwgImlzVHJpYWwiOiBmYWxzZSwgImlzR3B0NCI6IHRydWUgfQpd
20
+
.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .vscode
2
+ .env
3
+ build
4
+ node_modules
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-bullseye-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm install
7
+
8
+ COPY . .
9
+ RUN npm run build
10
+
11
+ EXPOSE 7860
12
+
13
+ CMD [ "npm", "start" ]
README.md ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: oai-reverse-proxy
3
+ emoji: 🔁
4
+ colorFrom: green
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ duplicated_from: khanon/oai-proxy
9
+ ---
10
+ <!-- -->
11
+ # OAI Reverse Proxy Server
12
+
13
+ Simple reverse proxy server for the OpenAI API.
14
+
15
+ ## What is this?
16
+ If you have an API key you want to share with a friend, you can use this to keep your key safe while still allowing them to generate text with the API.
17
+
18
+ ## Why?
19
+ OpenAI keys have full account permissions. They can revoke themselves, generate new keys, modify spend quotas, etc. You absolutely should not share them.
20
+
21
+ If you still want to share access to your key, you can put it behind this proxy to ensure it can't be used to do anything but generate text. You can also set a separate key on the proxy to gatekeep access.
22
+
23
+ ## How to use
24
+
25
+ ### 1. Get an API key
26
+ - Go to [OpenAI](https://openai.com/) and sign up for an account.
27
+
28
+ ### 2. Clone this Huggingface repository to your account
29
+ - Go to [Huggingface](https://huggingface.co/) and sign up for an account.
30
+ - Once logged in, click on the `+` button in the top right corner and select `Duplicate this Space`.
31
+
32
+ ![Duplicate Space](https://files.catbox.moe/3n6ubn.png)
33
+
34
+ ### 3. Set your OpenAI API key as a secret
35
+ - Click the Settings button in the top right corner of your repository.
36
+ - Scroll down to the `Repository Secrets` section and click `New Secret`.
37
+
38
+ ![Secrets](https://files.catbox.moe/irrp2p.png)
39
+
40
+ - Enter `OPENAI_KEY` as the name and your OpenAI API key as the value.
41
+
42
+ ![New Secret](https://files.catbox.moe/ka6s1a.png)
43
+
44
+ **Do not paste the key into `server.js`!** That file is public and anyone can see it. Leave it alone; it will load the key from the secret you just created.
45
+
46
+ ### 4. Deploy the server
47
+ - Your server should automatically deploy when you add the secret, but if not you can select `Factory Reset` from that same Settings menu.
48
+
49
+ ### 5. Share the link
50
+ - The Service Info section below should show the URL for your server. You can share this with anyone to safely give them access to your OpenAI API key.
51
+ - Your friend doesn't need any OpenAI API key of their own, they just need your link.
52
+ - However, if you want to protect access to the server, you can add another secret called `PROXY_KEY`. This key will need to be passed in the Authentication header of every request to the server, just like an OpenAI API key.
53
+
54
+ **Note:** The `keys` section in the serverinfo screen may not correctly identify keys as trial/paid/GPT-4 unless you use the more advanced configuration described in `.env.example`.
package-lock.json ADDED
@@ -0,0 +1,1629 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "oai-reverse-proxy",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "oai-reverse-proxy",
9
+ "version": "1.0.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "cors": "^2.8.5",
13
+ "dotenv": "^16.0.3",
14
+ "express": "^4.18.2",
15
+ "http-proxy-middleware": "^3.0.0-beta.1",
16
+ "pino-http": "^8.3.3",
17
+ "showdown": "^2.1.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/cors": "^2.8.13",
21
+ "@types/express": "^4.17.17",
22
+ "@types/showdown": "^2.0.0",
23
+ "nodemon": "^2.0.22",
24
+ "ts-node": "^10.9.1",
25
+ "typescript": "^5.0.4"
26
+ }
27
+ },
28
+ "node_modules/@cspotcode/source-map-support": {
29
+ "version": "0.8.1",
30
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
31
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
32
+ "dev": true,
33
+ "dependencies": {
34
+ "@jridgewell/trace-mapping": "0.3.9"
35
+ },
36
+ "engines": {
37
+ "node": ">=12"
38
+ }
39
+ },
40
+ "node_modules/@jridgewell/resolve-uri": {
41
+ "version": "3.1.1",
42
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
43
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
44
+ "dev": true,
45
+ "engines": {
46
+ "node": ">=6.0.0"
47
+ }
48
+ },
49
+ "node_modules/@jridgewell/sourcemap-codec": {
50
+ "version": "1.4.15",
51
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
52
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
53
+ "dev": true
54
+ },
55
+ "node_modules/@jridgewell/trace-mapping": {
56
+ "version": "0.3.9",
57
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
58
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
59
+ "dev": true,
60
+ "dependencies": {
61
+ "@jridgewell/resolve-uri": "^3.0.3",
62
+ "@jridgewell/sourcemap-codec": "^1.4.10"
63
+ }
64
+ },
65
+ "node_modules/@tsconfig/node10": {
66
+ "version": "1.0.9",
67
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
68
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
69
+ "dev": true
70
+ },
71
+ "node_modules/@tsconfig/node12": {
72
+ "version": "1.0.11",
73
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
74
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
75
+ "dev": true
76
+ },
77
+ "node_modules/@tsconfig/node14": {
78
+ "version": "1.0.3",
79
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
80
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
81
+ "dev": true
82
+ },
83
+ "node_modules/@tsconfig/node16": {
84
+ "version": "1.0.3",
85
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
86
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
87
+ "dev": true
88
+ },
89
+ "node_modules/@types/body-parser": {
90
+ "version": "1.19.2",
91
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
92
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
93
+ "dev": true,
94
+ "dependencies": {
95
+ "@types/connect": "*",
96
+ "@types/node": "*"
97
+ }
98
+ },
99
+ "node_modules/@types/connect": {
100
+ "version": "3.4.35",
101
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
102
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
103
+ "dev": true,
104
+ "dependencies": {
105
+ "@types/node": "*"
106
+ }
107
+ },
108
+ "node_modules/@types/cors": {
109
+ "version": "2.8.13",
110
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
111
+ "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
112
+ "dev": true,
113
+ "dependencies": {
114
+ "@types/node": "*"
115
+ }
116
+ },
117
+ "node_modules/@types/express": {
118
+ "version": "4.17.17",
119
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
120
+ "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
121
+ "dev": true,
122
+ "dependencies": {
123
+ "@types/body-parser": "*",
124
+ "@types/express-serve-static-core": "^4.17.33",
125
+ "@types/qs": "*",
126
+ "@types/serve-static": "*"
127
+ }
128
+ },
129
+ "node_modules/@types/express-serve-static-core": {
130
+ "version": "4.17.33",
131
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
132
+ "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
133
+ "dev": true,
134
+ "dependencies": {
135
+ "@types/node": "*",
136
+ "@types/qs": "*",
137
+ "@types/range-parser": "*"
138
+ }
139
+ },
140
+ "node_modules/@types/http-proxy": {
141
+ "version": "1.17.10",
142
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz",
143
+ "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==",
144
+ "dependencies": {
145
+ "@types/node": "*"
146
+ }
147
+ },
148
+ "node_modules/@types/mime": {
149
+ "version": "3.0.1",
150
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
151
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
152
+ "dev": true
153
+ },
154
+ "node_modules/@types/node": {
155
+ "version": "18.15.11",
156
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
157
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
158
+ },
159
+ "node_modules/@types/qs": {
160
+ "version": "6.9.7",
161
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
162
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
163
+ "dev": true
164
+ },
165
+ "node_modules/@types/range-parser": {
166
+ "version": "1.2.4",
167
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
168
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
169
+ "dev": true
170
+ },
171
+ "node_modules/@types/serve-static": {
172
+ "version": "1.15.1",
173
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
174
+ "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
175
+ "dev": true,
176
+ "dependencies": {
177
+ "@types/mime": "*",
178
+ "@types/node": "*"
179
+ }
180
+ },
181
+ "node_modules/@types/showdown": {
182
+ "version": "2.0.0",
183
+ "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
184
+ "integrity": "sha512-70xBJoLv+oXjB5PhtA8vo7erjLDp9/qqI63SRHm4REKrwuPOLs8HhXwlZJBJaB4kC18cCZ1UUZ6Fb/PLFW4TCA==",
185
+ "dev": true
186
+ },
187
+ "node_modules/abbrev": {
188
+ "version": "1.1.1",
189
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
190
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
191
+ "dev": true
192
+ },
193
+ "node_modules/abort-controller": {
194
+ "version": "3.0.0",
195
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
196
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
197
+ "dependencies": {
198
+ "event-target-shim": "^5.0.0"
199
+ },
200
+ "engines": {
201
+ "node": ">=6.5"
202
+ }
203
+ },
204
+ "node_modules/accepts": {
205
+ "version": "1.3.8",
206
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
207
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
208
+ "dependencies": {
209
+ "mime-types": "~2.1.34",
210
+ "negotiator": "0.6.3"
211
+ },
212
+ "engines": {
213
+ "node": ">= 0.6"
214
+ }
215
+ },
216
+ "node_modules/acorn": {
217
+ "version": "8.8.2",
218
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
219
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
220
+ "dev": true,
221
+ "bin": {
222
+ "acorn": "bin/acorn"
223
+ },
224
+ "engines": {
225
+ "node": ">=0.4.0"
226
+ }
227
+ },
228
+ "node_modules/acorn-walk": {
229
+ "version": "8.2.0",
230
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
231
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
232
+ "dev": true,
233
+ "engines": {
234
+ "node": ">=0.4.0"
235
+ }
236
+ },
237
+ "node_modules/anymatch": {
238
+ "version": "3.1.3",
239
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
240
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
241
+ "dev": true,
242
+ "dependencies": {
243
+ "normalize-path": "^3.0.0",
244
+ "picomatch": "^2.0.4"
245
+ },
246
+ "engines": {
247
+ "node": ">= 8"
248
+ }
249
+ },
250
+ "node_modules/arg": {
251
+ "version": "4.1.3",
252
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
253
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
254
+ "dev": true
255
+ },
256
+ "node_modules/array-flatten": {
257
+ "version": "1.1.1",
258
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
259
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
260
+ },
261
+ "node_modules/atomic-sleep": {
262
+ "version": "1.0.0",
263
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
264
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
265
+ "engines": {
266
+ "node": ">=8.0.0"
267
+ }
268
+ },
269
+ "node_modules/balanced-match": {
270
+ "version": "1.0.2",
271
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
272
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
273
+ "dev": true
274
+ },
275
+ "node_modules/base64-js": {
276
+ "version": "1.5.1",
277
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
278
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
279
+ "funding": [
280
+ {
281
+ "type": "github",
282
+ "url": "https://github.com/sponsors/feross"
283
+ },
284
+ {
285
+ "type": "patreon",
286
+ "url": "https://www.patreon.com/feross"
287
+ },
288
+ {
289
+ "type": "consulting",
290
+ "url": "https://feross.org/support"
291
+ }
292
+ ]
293
+ },
294
+ "node_modules/binary-extensions": {
295
+ "version": "2.2.0",
296
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
297
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
298
+ "dev": true,
299
+ "engines": {
300
+ "node": ">=8"
301
+ }
302
+ },
303
+ "node_modules/body-parser": {
304
+ "version": "1.20.1",
305
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
306
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
307
+ "dependencies": {
308
+ "bytes": "3.1.2",
309
+ "content-type": "~1.0.4",
310
+ "debug": "2.6.9",
311
+ "depd": "2.0.0",
312
+ "destroy": "1.2.0",
313
+ "http-errors": "2.0.0",
314
+ "iconv-lite": "0.4.24",
315
+ "on-finished": "2.4.1",
316
+ "qs": "6.11.0",
317
+ "raw-body": "2.5.1",
318
+ "type-is": "~1.6.18",
319
+ "unpipe": "1.0.0"
320
+ },
321
+ "engines": {
322
+ "node": ">= 0.8",
323
+ "npm": "1.2.8000 || >= 1.4.16"
324
+ }
325
+ },
326
+ "node_modules/brace-expansion": {
327
+ "version": "1.1.11",
328
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
329
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
330
+ "dev": true,
331
+ "dependencies": {
332
+ "balanced-match": "^1.0.0",
333
+ "concat-map": "0.0.1"
334
+ }
335
+ },
336
+ "node_modules/braces": {
337
+ "version": "3.0.2",
338
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
339
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
340
+ "dependencies": {
341
+ "fill-range": "^7.0.1"
342
+ },
343
+ "engines": {
344
+ "node": ">=8"
345
+ }
346
+ },
347
+ "node_modules/buffer": {
348
+ "version": "6.0.3",
349
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
350
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
351
+ "funding": [
352
+ {
353
+ "type": "github",
354
+ "url": "https://github.com/sponsors/feross"
355
+ },
356
+ {
357
+ "type": "patreon",
358
+ "url": "https://www.patreon.com/feross"
359
+ },
360
+ {
361
+ "type": "consulting",
362
+ "url": "https://feross.org/support"
363
+ }
364
+ ],
365
+ "dependencies": {
366
+ "base64-js": "^1.3.1",
367
+ "ieee754": "^1.2.1"
368
+ }
369
+ },
370
+ "node_modules/bytes": {
371
+ "version": "3.1.2",
372
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
373
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
374
+ "engines": {
375
+ "node": ">= 0.8"
376
+ }
377
+ },
378
+ "node_modules/call-bind": {
379
+ "version": "1.0.2",
380
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
381
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
382
+ "dependencies": {
383
+ "function-bind": "^1.1.1",
384
+ "get-intrinsic": "^1.0.2"
385
+ },
386
+ "funding": {
387
+ "url": "https://github.com/sponsors/ljharb"
388
+ }
389
+ },
390
+ "node_modules/chokidar": {
391
+ "version": "3.5.3",
392
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
393
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
394
+ "dev": true,
395
+ "funding": [
396
+ {
397
+ "type": "individual",
398
+ "url": "https://paulmillr.com/funding/"
399
+ }
400
+ ],
401
+ "dependencies": {
402
+ "anymatch": "~3.1.2",
403
+ "braces": "~3.0.2",
404
+ "glob-parent": "~5.1.2",
405
+ "is-binary-path": "~2.1.0",
406
+ "is-glob": "~4.0.1",
407
+ "normalize-path": "~3.0.0",
408
+ "readdirp": "~3.6.0"
409
+ },
410
+ "engines": {
411
+ "node": ">= 8.10.0"
412
+ },
413
+ "optionalDependencies": {
414
+ "fsevents": "~2.3.2"
415
+ }
416
+ },
417
+ "node_modules/commander": {
418
+ "version": "9.5.0",
419
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
420
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
421
+ "engines": {
422
+ "node": "^12.20.0 || >=14"
423
+ }
424
+ },
425
+ "node_modules/concat-map": {
426
+ "version": "0.0.1",
427
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
428
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
429
+ "dev": true
430
+ },
431
+ "node_modules/content-disposition": {
432
+ "version": "0.5.4",
433
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
434
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
435
+ "dependencies": {
436
+ "safe-buffer": "5.2.1"
437
+ },
438
+ "engines": {
439
+ "node": ">= 0.6"
440
+ }
441
+ },
442
+ "node_modules/content-type": {
443
+ "version": "1.0.5",
444
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
445
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
446
+ "engines": {
447
+ "node": ">= 0.6"
448
+ }
449
+ },
450
+ "node_modules/cookie": {
451
+ "version": "0.5.0",
452
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
453
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
454
+ "engines": {
455
+ "node": ">= 0.6"
456
+ }
457
+ },
458
+ "node_modules/cookie-signature": {
459
+ "version": "1.0.6",
460
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
461
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
462
+ },
463
+ "node_modules/cors": {
464
+ "version": "2.8.5",
465
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
466
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
467
+ "dependencies": {
468
+ "object-assign": "^4",
469
+ "vary": "^1"
470
+ },
471
+ "engines": {
472
+ "node": ">= 0.10"
473
+ }
474
+ },
475
+ "node_modules/create-require": {
476
+ "version": "1.1.1",
477
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
478
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
479
+ "dev": true
480
+ },
481
+ "node_modules/debug": {
482
+ "version": "2.6.9",
483
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
484
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
485
+ "dependencies": {
486
+ "ms": "2.0.0"
487
+ }
488
+ },
489
+ "node_modules/depd": {
490
+ "version": "2.0.0",
491
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
492
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
493
+ "engines": {
494
+ "node": ">= 0.8"
495
+ }
496
+ },
497
+ "node_modules/destroy": {
498
+ "version": "1.2.0",
499
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
500
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
501
+ "engines": {
502
+ "node": ">= 0.8",
503
+ "npm": "1.2.8000 || >= 1.4.16"
504
+ }
505
+ },
506
+ "node_modules/diff": {
507
+ "version": "4.0.2",
508
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
509
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
510
+ "dev": true,
511
+ "engines": {
512
+ "node": ">=0.3.1"
513
+ }
514
+ },
515
+ "node_modules/dotenv": {
516
+ "version": "16.0.3",
517
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
518
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
519
+ "engines": {
520
+ "node": ">=12"
521
+ }
522
+ },
523
+ "node_modules/ee-first": {
524
+ "version": "1.1.1",
525
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
526
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
527
+ },
528
+ "node_modules/encodeurl": {
529
+ "version": "1.0.2",
530
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
531
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
532
+ "engines": {
533
+ "node": ">= 0.8"
534
+ }
535
+ },
536
+ "node_modules/escape-html": {
537
+ "version": "1.0.3",
538
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
539
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
540
+ },
541
+ "node_modules/etag": {
542
+ "version": "1.8.1",
543
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
544
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
545
+ "engines": {
546
+ "node": ">= 0.6"
547
+ }
548
+ },
549
+ "node_modules/event-target-shim": {
550
+ "version": "5.0.1",
551
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
552
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
553
+ "engines": {
554
+ "node": ">=6"
555
+ }
556
+ },
557
+ "node_modules/eventemitter3": {
558
+ "version": "4.0.7",
559
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
560
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
561
+ },
562
+ "node_modules/events": {
563
+ "version": "3.3.0",
564
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
565
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
566
+ "engines": {
567
+ "node": ">=0.8.x"
568
+ }
569
+ },
570
+ "node_modules/express": {
571
+ "version": "4.18.2",
572
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
573
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
574
+ "dependencies": {
575
+ "accepts": "~1.3.8",
576
+ "array-flatten": "1.1.1",
577
+ "body-parser": "1.20.1",
578
+ "content-disposition": "0.5.4",
579
+ "content-type": "~1.0.4",
580
+ "cookie": "0.5.0",
581
+ "cookie-signature": "1.0.6",
582
+ "debug": "2.6.9",
583
+ "depd": "2.0.0",
584
+ "encodeurl": "~1.0.2",
585
+ "escape-html": "~1.0.3",
586
+ "etag": "~1.8.1",
587
+ "finalhandler": "1.2.0",
588
+ "fresh": "0.5.2",
589
+ "http-errors": "2.0.0",
590
+ "merge-descriptors": "1.0.1",
591
+ "methods": "~1.1.2",
592
+ "on-finished": "2.4.1",
593
+ "parseurl": "~1.3.3",
594
+ "path-to-regexp": "0.1.7",
595
+ "proxy-addr": "~2.0.7",
596
+ "qs": "6.11.0",
597
+ "range-parser": "~1.2.1",
598
+ "safe-buffer": "5.2.1",
599
+ "send": "0.18.0",
600
+ "serve-static": "1.15.0",
601
+ "setprototypeof": "1.2.0",
602
+ "statuses": "2.0.1",
603
+ "type-is": "~1.6.18",
604
+ "utils-merge": "1.0.1",
605
+ "vary": "~1.1.2"
606
+ },
607
+ "engines": {
608
+ "node": ">= 0.10.0"
609
+ }
610
+ },
611
+ "node_modules/fast-redact": {
612
+ "version": "3.1.2",
613
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz",
614
+ "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==",
615
+ "engines": {
616
+ "node": ">=6"
617
+ }
618
+ },
619
+ "node_modules/fill-range": {
620
+ "version": "7.0.1",
621
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
622
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
623
+ "dependencies": {
624
+ "to-regex-range": "^5.0.1"
625
+ },
626
+ "engines": {
627
+ "node": ">=8"
628
+ }
629
+ },
630
+ "node_modules/finalhandler": {
631
+ "version": "1.2.0",
632
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
633
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
634
+ "dependencies": {
635
+ "debug": "2.6.9",
636
+ "encodeurl": "~1.0.2",
637
+ "escape-html": "~1.0.3",
638
+ "on-finished": "2.4.1",
639
+ "parseurl": "~1.3.3",
640
+ "statuses": "2.0.1",
641
+ "unpipe": "~1.0.0"
642
+ },
643
+ "engines": {
644
+ "node": ">= 0.8"
645
+ }
646
+ },
647
+ "node_modules/follow-redirects": {
648
+ "version": "1.15.2",
649
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
650
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
651
+ "funding": [
652
+ {
653
+ "type": "individual",
654
+ "url": "https://github.com/sponsors/RubenVerborgh"
655
+ }
656
+ ],
657
+ "engines": {
658
+ "node": ">=4.0"
659
+ },
660
+ "peerDependenciesMeta": {
661
+ "debug": {
662
+ "optional": true
663
+ }
664
+ }
665
+ },
666
+ "node_modules/forwarded": {
667
+ "version": "0.2.0",
668
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
669
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
670
+ "engines": {
671
+ "node": ">= 0.6"
672
+ }
673
+ },
674
+ "node_modules/fresh": {
675
+ "version": "0.5.2",
676
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
677
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
678
+ "engines": {
679
+ "node": ">= 0.6"
680
+ }
681
+ },
682
+ "node_modules/fsevents": {
683
+ "version": "2.3.2",
684
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
685
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
686
+ "dev": true,
687
+ "hasInstallScript": true,
688
+ "optional": true,
689
+ "os": [
690
+ "darwin"
691
+ ],
692
+ "engines": {
693
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
694
+ }
695
+ },
696
+ "node_modules/function-bind": {
697
+ "version": "1.1.1",
698
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
699
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
700
+ },
701
+ "node_modules/get-caller-file": {
702
+ "version": "2.0.5",
703
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
704
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
705
+ "engines": {
706
+ "node": "6.* || 8.* || >= 10.*"
707
+ }
708
+ },
709
+ "node_modules/get-intrinsic": {
710
+ "version": "1.2.0",
711
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
712
+ "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
713
+ "dependencies": {
714
+ "function-bind": "^1.1.1",
715
+ "has": "^1.0.3",
716
+ "has-symbols": "^1.0.3"
717
+ },
718
+ "funding": {
719
+ "url": "https://github.com/sponsors/ljharb"
720
+ }
721
+ },
722
+ "node_modules/glob-parent": {
723
+ "version": "5.1.2",
724
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
725
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
726
+ "dev": true,
727
+ "dependencies": {
728
+ "is-glob": "^4.0.1"
729
+ },
730
+ "engines": {
731
+ "node": ">= 6"
732
+ }
733
+ },
734
+ "node_modules/has": {
735
+ "version": "1.0.3",
736
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
737
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
738
+ "dependencies": {
739
+ "function-bind": "^1.1.1"
740
+ },
741
+ "engines": {
742
+ "node": ">= 0.4.0"
743
+ }
744
+ },
745
+ "node_modules/has-flag": {
746
+ "version": "3.0.0",
747
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
748
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
749
+ "dev": true,
750
+ "engines": {
751
+ "node": ">=4"
752
+ }
753
+ },
754
+ "node_modules/has-symbols": {
755
+ "version": "1.0.3",
756
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
757
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
758
+ "engines": {
759
+ "node": ">= 0.4"
760
+ },
761
+ "funding": {
762
+ "url": "https://github.com/sponsors/ljharb"
763
+ }
764
+ },
765
+ "node_modules/http-errors": {
766
+ "version": "2.0.0",
767
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
768
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
769
+ "dependencies": {
770
+ "depd": "2.0.0",
771
+ "inherits": "2.0.4",
772
+ "setprototypeof": "1.2.0",
773
+ "statuses": "2.0.1",
774
+ "toidentifier": "1.0.1"
775
+ },
776
+ "engines": {
777
+ "node": ">= 0.8"
778
+ }
779
+ },
780
+ "node_modules/http-proxy": {
781
+ "version": "1.18.1",
782
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
783
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
784
+ "dependencies": {
785
+ "eventemitter3": "^4.0.0",
786
+ "follow-redirects": "^1.0.0",
787
+ "requires-port": "^1.0.0"
788
+ },
789
+ "engines": {
790
+ "node": ">=8.0.0"
791
+ }
792
+ },
793
+ "node_modules/http-proxy-middleware": {
794
+ "version": "3.0.0-beta.1",
795
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0-beta.1.tgz",
796
+ "integrity": "sha512-hdiTlVVoaxncf239csnEpG5ew2lRWnoNR1PMWOO6kYulSphlrfLs5JFZtFVH3R5EUWSZNMkeUqvkvfctuWaK8A==",
797
+ "dependencies": {
798
+ "@types/http-proxy": "^1.17.10",
799
+ "debug": "^4.3.4",
800
+ "http-proxy": "^1.18.1",
801
+ "is-glob": "^4.0.1",
802
+ "is-plain-obj": "^3.0.0",
803
+ "micromatch": "^4.0.5"
804
+ },
805
+ "engines": {
806
+ "node": ">=12.0.0"
807
+ }
808
+ },
809
+ "node_modules/http-proxy-middleware/node_modules/debug": {
810
+ "version": "4.3.4",
811
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
812
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
813
+ "dependencies": {
814
+ "ms": "2.1.2"
815
+ },
816
+ "engines": {
817
+ "node": ">=6.0"
818
+ },
819
+ "peerDependenciesMeta": {
820
+ "supports-color": {
821
+ "optional": true
822
+ }
823
+ }
824
+ },
825
+ "node_modules/http-proxy-middleware/node_modules/ms": {
826
+ "version": "2.1.2",
827
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
828
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
829
+ },
830
+ "node_modules/iconv-lite": {
831
+ "version": "0.4.24",
832
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
833
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
834
+ "dependencies": {
835
+ "safer-buffer": ">= 2.1.2 < 3"
836
+ },
837
+ "engines": {
838
+ "node": ">=0.10.0"
839
+ }
840
+ },
841
+ "node_modules/ieee754": {
842
+ "version": "1.2.1",
843
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
844
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
845
+ "funding": [
846
+ {
847
+ "type": "github",
848
+ "url": "https://github.com/sponsors/feross"
849
+ },
850
+ {
851
+ "type": "patreon",
852
+ "url": "https://www.patreon.com/feross"
853
+ },
854
+ {
855
+ "type": "consulting",
856
+ "url": "https://feross.org/support"
857
+ }
858
+ ]
859
+ },
860
+ "node_modules/ignore-by-default": {
861
+ "version": "1.0.1",
862
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
863
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
864
+ "dev": true
865
+ },
866
+ "node_modules/inherits": {
867
+ "version": "2.0.4",
868
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
869
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
870
+ },
871
+ "node_modules/ipaddr.js": {
872
+ "version": "1.9.1",
873
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
874
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
875
+ "engines": {
876
+ "node": ">= 0.10"
877
+ }
878
+ },
879
+ "node_modules/is-binary-path": {
880
+ "version": "2.1.0",
881
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
882
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
883
+ "dev": true,
884
+ "dependencies": {
885
+ "binary-extensions": "^2.0.0"
886
+ },
887
+ "engines": {
888
+ "node": ">=8"
889
+ }
890
+ },
891
+ "node_modules/is-extglob": {
892
+ "version": "2.1.1",
893
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
894
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
895
+ "engines": {
896
+ "node": ">=0.10.0"
897
+ }
898
+ },
899
+ "node_modules/is-glob": {
900
+ "version": "4.0.3",
901
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
902
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
903
+ "dependencies": {
904
+ "is-extglob": "^2.1.1"
905
+ },
906
+ "engines": {
907
+ "node": ">=0.10.0"
908
+ }
909
+ },
910
+ "node_modules/is-number": {
911
+ "version": "7.0.0",
912
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
913
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
914
+ "engines": {
915
+ "node": ">=0.12.0"
916
+ }
917
+ },
918
+ "node_modules/is-plain-obj": {
919
+ "version": "3.0.0",
920
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
921
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
922
+ "engines": {
923
+ "node": ">=10"
924
+ },
925
+ "funding": {
926
+ "url": "https://github.com/sponsors/sindresorhus"
927
+ }
928
+ },
929
+ "node_modules/make-error": {
930
+ "version": "1.3.6",
931
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
932
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
933
+ "dev": true
934
+ },
935
+ "node_modules/media-typer": {
936
+ "version": "0.3.0",
937
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
938
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
939
+ "engines": {
940
+ "node": ">= 0.6"
941
+ }
942
+ },
943
+ "node_modules/merge-descriptors": {
944
+ "version": "1.0.1",
945
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
946
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
947
+ },
948
+ "node_modules/methods": {
949
+ "version": "1.1.2",
950
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
951
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
952
+ "engines": {
953
+ "node": ">= 0.6"
954
+ }
955
+ },
956
+ "node_modules/micromatch": {
957
+ "version": "4.0.5",
958
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
959
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
960
+ "dependencies": {
961
+ "braces": "^3.0.2",
962
+ "picomatch": "^2.3.1"
963
+ },
964
+ "engines": {
965
+ "node": ">=8.6"
966
+ }
967
+ },
968
+ "node_modules/mime": {
969
+ "version": "1.6.0",
970
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
971
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
972
+ "bin": {
973
+ "mime": "cli.js"
974
+ },
975
+ "engines": {
976
+ "node": ">=4"
977
+ }
978
+ },
979
+ "node_modules/mime-db": {
980
+ "version": "1.52.0",
981
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
982
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
983
+ "engines": {
984
+ "node": ">= 0.6"
985
+ }
986
+ },
987
+ "node_modules/mime-types": {
988
+ "version": "2.1.35",
989
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
990
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
991
+ "dependencies": {
992
+ "mime-db": "1.52.0"
993
+ },
994
+ "engines": {
995
+ "node": ">= 0.6"
996
+ }
997
+ },
998
+ "node_modules/minimatch": {
999
+ "version": "3.1.2",
1000
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1001
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1002
+ "dev": true,
1003
+ "dependencies": {
1004
+ "brace-expansion": "^1.1.7"
1005
+ },
1006
+ "engines": {
1007
+ "node": "*"
1008
+ }
1009
+ },
1010
+ "node_modules/ms": {
1011
+ "version": "2.0.0",
1012
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1013
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1014
+ },
1015
+ "node_modules/negotiator": {
1016
+ "version": "0.6.3",
1017
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1018
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1019
+ "engines": {
1020
+ "node": ">= 0.6"
1021
+ }
1022
+ },
1023
+ "node_modules/nodemon": {
1024
+ "version": "2.0.22",
1025
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
1026
+ "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
1027
+ "dev": true,
1028
+ "dependencies": {
1029
+ "chokidar": "^3.5.2",
1030
+ "debug": "^3.2.7",
1031
+ "ignore-by-default": "^1.0.1",
1032
+ "minimatch": "^3.1.2",
1033
+ "pstree.remy": "^1.1.8",
1034
+ "semver": "^5.7.1",
1035
+ "simple-update-notifier": "^1.0.7",
1036
+ "supports-color": "^5.5.0",
1037
+ "touch": "^3.1.0",
1038
+ "undefsafe": "^2.0.5"
1039
+ },
1040
+ "bin": {
1041
+ "nodemon": "bin/nodemon.js"
1042
+ },
1043
+ "engines": {
1044
+ "node": ">=8.10.0"
1045
+ },
1046
+ "funding": {
1047
+ "type": "opencollective",
1048
+ "url": "https://opencollective.com/nodemon"
1049
+ }
1050
+ },
1051
+ "node_modules/nodemon/node_modules/debug": {
1052
+ "version": "3.2.7",
1053
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
1054
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
1055
+ "dev": true,
1056
+ "dependencies": {
1057
+ "ms": "^2.1.1"
1058
+ }
1059
+ },
1060
+ "node_modules/nodemon/node_modules/ms": {
1061
+ "version": "2.1.3",
1062
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1063
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1064
+ "dev": true
1065
+ },
1066
+ "node_modules/nopt": {
1067
+ "version": "1.0.10",
1068
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1069
+ "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
1070
+ "dev": true,
1071
+ "dependencies": {
1072
+ "abbrev": "1"
1073
+ },
1074
+ "bin": {
1075
+ "nopt": "bin/nopt.js"
1076
+ },
1077
+ "engines": {
1078
+ "node": "*"
1079
+ }
1080
+ },
1081
+ "node_modules/normalize-path": {
1082
+ "version": "3.0.0",
1083
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1084
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1085
+ "dev": true,
1086
+ "engines": {
1087
+ "node": ">=0.10.0"
1088
+ }
1089
+ },
1090
+ "node_modules/object-assign": {
1091
+ "version": "4.1.1",
1092
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1093
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1094
+ "engines": {
1095
+ "node": ">=0.10.0"
1096
+ }
1097
+ },
1098
+ "node_modules/object-inspect": {
1099
+ "version": "1.12.3",
1100
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
1101
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
1102
+ "funding": {
1103
+ "url": "https://github.com/sponsors/ljharb"
1104
+ }
1105
+ },
1106
+ "node_modules/on-exit-leak-free": {
1107
+ "version": "2.1.0",
1108
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz",
1109
+ "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w=="
1110
+ },
1111
+ "node_modules/on-finished": {
1112
+ "version": "2.4.1",
1113
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1114
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1115
+ "dependencies": {
1116
+ "ee-first": "1.1.1"
1117
+ },
1118
+ "engines": {
1119
+ "node": ">= 0.8"
1120
+ }
1121
+ },
1122
+ "node_modules/parseurl": {
1123
+ "version": "1.3.3",
1124
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1125
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1126
+ "engines": {
1127
+ "node": ">= 0.8"
1128
+ }
1129
+ },
1130
+ "node_modules/path-to-regexp": {
1131
+ "version": "0.1.7",
1132
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1133
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1134
+ },
1135
+ "node_modules/picomatch": {
1136
+ "version": "2.3.1",
1137
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1138
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1139
+ "engines": {
1140
+ "node": ">=8.6"
1141
+ },
1142
+ "funding": {
1143
+ "url": "https://github.com/sponsors/jonschlinkert"
1144
+ }
1145
+ },
1146
+ "node_modules/pino": {
1147
+ "version": "8.11.0",
1148
+ "resolved": "https://registry.npmjs.org/pino/-/pino-8.11.0.tgz",
1149
+ "integrity": "sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==",
1150
+ "dependencies": {
1151
+ "atomic-sleep": "^1.0.0",
1152
+ "fast-redact": "^3.1.1",
1153
+ "on-exit-leak-free": "^2.1.0",
1154
+ "pino-abstract-transport": "v1.0.0",
1155
+ "pino-std-serializers": "^6.0.0",
1156
+ "process-warning": "^2.0.0",
1157
+ "quick-format-unescaped": "^4.0.3",
1158
+ "real-require": "^0.2.0",
1159
+ "safe-stable-stringify": "^2.3.1",
1160
+ "sonic-boom": "^3.1.0",
1161
+ "thread-stream": "^2.0.0"
1162
+ },
1163
+ "bin": {
1164
+ "pino": "bin.js"
1165
+ }
1166
+ },
1167
+ "node_modules/pino-abstract-transport": {
1168
+ "version": "1.0.0",
1169
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz",
1170
+ "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==",
1171
+ "dependencies": {
1172
+ "readable-stream": "^4.0.0",
1173
+ "split2": "^4.0.0"
1174
+ }
1175
+ },
1176
+ "node_modules/pino-http": {
1177
+ "version": "8.3.3",
1178
+ "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.3.3.tgz",
1179
+ "integrity": "sha512-p4umsNIXXVu95HD2C8wie/vXH7db5iGRpc+yj1/ZQ3sRtTQLXNjoS6Be5+eI+rQbqCRxen/7k/KSN+qiZubGDw==",
1180
+ "dependencies": {
1181
+ "get-caller-file": "^2.0.5",
1182
+ "pino": "^8.0.0",
1183
+ "pino-std-serializers": "^6.0.0",
1184
+ "process-warning": "^2.0.0"
1185
+ }
1186
+ },
1187
+ "node_modules/pino-std-serializers": {
1188
+ "version": "6.1.0",
1189
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz",
1190
+ "integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g=="
1191
+ },
1192
+ "node_modules/process": {
1193
+ "version": "0.11.10",
1194
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
1195
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
1196
+ "engines": {
1197
+ "node": ">= 0.6.0"
1198
+ }
1199
+ },
1200
+ "node_modules/process-warning": {
1201
+ "version": "2.2.0",
1202
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz",
1203
+ "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg=="
1204
+ },
1205
+ "node_modules/proxy-addr": {
1206
+ "version": "2.0.7",
1207
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1208
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1209
+ "dependencies": {
1210
+ "forwarded": "0.2.0",
1211
+ "ipaddr.js": "1.9.1"
1212
+ },
1213
+ "engines": {
1214
+ "node": ">= 0.10"
1215
+ }
1216
+ },
1217
+ "node_modules/pstree.remy": {
1218
+ "version": "1.1.8",
1219
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1220
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1221
+ "dev": true
1222
+ },
1223
+ "node_modules/qs": {
1224
+ "version": "6.11.0",
1225
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1226
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1227
+ "dependencies": {
1228
+ "side-channel": "^1.0.4"
1229
+ },
1230
+ "engines": {
1231
+ "node": ">=0.6"
1232
+ },
1233
+ "funding": {
1234
+ "url": "https://github.com/sponsors/ljharb"
1235
+ }
1236
+ },
1237
+ "node_modules/quick-format-unescaped": {
1238
+ "version": "4.0.4",
1239
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
1240
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
1241
+ },
1242
+ "node_modules/range-parser": {
1243
+ "version": "1.2.1",
1244
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1245
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1246
+ "engines": {
1247
+ "node": ">= 0.6"
1248
+ }
1249
+ },
1250
+ "node_modules/raw-body": {
1251
+ "version": "2.5.1",
1252
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
1253
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
1254
+ "dependencies": {
1255
+ "bytes": "3.1.2",
1256
+ "http-errors": "2.0.0",
1257
+ "iconv-lite": "0.4.24",
1258
+ "unpipe": "1.0.0"
1259
+ },
1260
+ "engines": {
1261
+ "node": ">= 0.8"
1262
+ }
1263
+ },
1264
+ "node_modules/readable-stream": {
1265
+ "version": "4.3.0",
1266
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
1267
+ "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
1268
+ "dependencies": {
1269
+ "abort-controller": "^3.0.0",
1270
+ "buffer": "^6.0.3",
1271
+ "events": "^3.3.0",
1272
+ "process": "^0.11.10"
1273
+ },
1274
+ "engines": {
1275
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1276
+ }
1277
+ },
1278
+ "node_modules/readdirp": {
1279
+ "version": "3.6.0",
1280
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1281
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1282
+ "dev": true,
1283
+ "dependencies": {
1284
+ "picomatch": "^2.2.1"
1285
+ },
1286
+ "engines": {
1287
+ "node": ">=8.10.0"
1288
+ }
1289
+ },
1290
+ "node_modules/real-require": {
1291
+ "version": "0.2.0",
1292
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
1293
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
1294
+ "engines": {
1295
+ "node": ">= 12.13.0"
1296
+ }
1297
+ },
1298
+ "node_modules/requires-port": {
1299
+ "version": "1.0.0",
1300
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
1301
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
1302
+ },
1303
+ "node_modules/safe-buffer": {
1304
+ "version": "5.2.1",
1305
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1306
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1307
+ "funding": [
1308
+ {
1309
+ "type": "github",
1310
+ "url": "https://github.com/sponsors/feross"
1311
+ },
1312
+ {
1313
+ "type": "patreon",
1314
+ "url": "https://www.patreon.com/feross"
1315
+ },
1316
+ {
1317
+ "type": "consulting",
1318
+ "url": "https://feross.org/support"
1319
+ }
1320
+ ]
1321
+ },
1322
+ "node_modules/safe-stable-stringify": {
1323
+ "version": "2.4.3",
1324
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
1325
+ "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
1326
+ "engines": {
1327
+ "node": ">=10"
1328
+ }
1329
+ },
1330
+ "node_modules/safer-buffer": {
1331
+ "version": "2.1.2",
1332
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1333
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1334
+ },
1335
+ "node_modules/semver": {
1336
+ "version": "5.7.1",
1337
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1338
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1339
+ "dev": true,
1340
+ "bin": {
1341
+ "semver": "bin/semver"
1342
+ }
1343
+ },
1344
+ "node_modules/send": {
1345
+ "version": "0.18.0",
1346
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1347
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1348
+ "dependencies": {
1349
+ "debug": "2.6.9",
1350
+ "depd": "2.0.0",
1351
+ "destroy": "1.2.0",
1352
+ "encodeurl": "~1.0.2",
1353
+ "escape-html": "~1.0.3",
1354
+ "etag": "~1.8.1",
1355
+ "fresh": "0.5.2",
1356
+ "http-errors": "2.0.0",
1357
+ "mime": "1.6.0",
1358
+ "ms": "2.1.3",
1359
+ "on-finished": "2.4.1",
1360
+ "range-parser": "~1.2.1",
1361
+ "statuses": "2.0.1"
1362
+ },
1363
+ "engines": {
1364
+ "node": ">= 0.8.0"
1365
+ }
1366
+ },
1367
+ "node_modules/send/node_modules/ms": {
1368
+ "version": "2.1.3",
1369
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1370
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1371
+ },
1372
+ "node_modules/serve-static": {
1373
+ "version": "1.15.0",
1374
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1375
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1376
+ "dependencies": {
1377
+ "encodeurl": "~1.0.2",
1378
+ "escape-html": "~1.0.3",
1379
+ "parseurl": "~1.3.3",
1380
+ "send": "0.18.0"
1381
+ },
1382
+ "engines": {
1383
+ "node": ">= 0.8.0"
1384
+ }
1385
+ },
1386
+ "node_modules/setprototypeof": {
1387
+ "version": "1.2.0",
1388
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1389
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1390
+ },
1391
+ "node_modules/showdown": {
1392
+ "version": "2.1.0",
1393
+ "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
1394
+ "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
1395
+ "dependencies": {
1396
+ "commander": "^9.0.0"
1397
+ },
1398
+ "bin": {
1399
+ "showdown": "bin/showdown.js"
1400
+ },
1401
+ "funding": {
1402
+ "type": "individual",
1403
+ "url": "https://www.paypal.me/tiviesantos"
1404
+ }
1405
+ },
1406
+ "node_modules/side-channel": {
1407
+ "version": "1.0.4",
1408
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1409
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1410
+ "dependencies": {
1411
+ "call-bind": "^1.0.0",
1412
+ "get-intrinsic": "^1.0.2",
1413
+ "object-inspect": "^1.9.0"
1414
+ },
1415
+ "funding": {
1416
+ "url": "https://github.com/sponsors/ljharb"
1417
+ }
1418
+ },
1419
+ "node_modules/simple-update-notifier": {
1420
+ "version": "1.1.0",
1421
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
1422
+ "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
1423
+ "dev": true,
1424
+ "dependencies": {
1425
+ "semver": "~7.0.0"
1426
+ },
1427
+ "engines": {
1428
+ "node": ">=8.10.0"
1429
+ }
1430
+ },
1431
+ "node_modules/simple-update-notifier/node_modules/semver": {
1432
+ "version": "7.0.0",
1433
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
1434
+ "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
1435
+ "dev": true,
1436
+ "bin": {
1437
+ "semver": "bin/semver.js"
1438
+ }
1439
+ },
1440
+ "node_modules/sonic-boom": {
1441
+ "version": "3.3.0",
1442
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz",
1443
+ "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==",
1444
+ "dependencies": {
1445
+ "atomic-sleep": "^1.0.0"
1446
+ }
1447
+ },
1448
+ "node_modules/split2": {
1449
+ "version": "4.2.0",
1450
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
1451
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
1452
+ "engines": {
1453
+ "node": ">= 10.x"
1454
+ }
1455
+ },
1456
+ "node_modules/statuses": {
1457
+ "version": "2.0.1",
1458
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1459
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1460
+ "engines": {
1461
+ "node": ">= 0.8"
1462
+ }
1463
+ },
1464
+ "node_modules/supports-color": {
1465
+ "version": "5.5.0",
1466
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1467
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1468
+ "dev": true,
1469
+ "dependencies": {
1470
+ "has-flag": "^3.0.0"
1471
+ },
1472
+ "engines": {
1473
+ "node": ">=4"
1474
+ }
1475
+ },
1476
+ "node_modules/thread-stream": {
1477
+ "version": "2.3.0",
1478
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz",
1479
+ "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==",
1480
+ "dependencies": {
1481
+ "real-require": "^0.2.0"
1482
+ }
1483
+ },
1484
+ "node_modules/to-regex-range": {
1485
+ "version": "5.0.1",
1486
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1487
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1488
+ "dependencies": {
1489
+ "is-number": "^7.0.0"
1490
+ },
1491
+ "engines": {
1492
+ "node": ">=8.0"
1493
+ }
1494
+ },
1495
+ "node_modules/toidentifier": {
1496
+ "version": "1.0.1",
1497
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1498
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1499
+ "engines": {
1500
+ "node": ">=0.6"
1501
+ }
1502
+ },
1503
+ "node_modules/touch": {
1504
+ "version": "3.1.0",
1505
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
1506
+ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
1507
+ "dev": true,
1508
+ "dependencies": {
1509
+ "nopt": "~1.0.10"
1510
+ },
1511
+ "bin": {
1512
+ "nodetouch": "bin/nodetouch.js"
1513
+ }
1514
+ },
1515
+ "node_modules/ts-node": {
1516
+ "version": "10.9.1",
1517
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
1518
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
1519
+ "dev": true,
1520
+ "dependencies": {
1521
+ "@cspotcode/source-map-support": "^0.8.0",
1522
+ "@tsconfig/node10": "^1.0.7",
1523
+ "@tsconfig/node12": "^1.0.7",
1524
+ "@tsconfig/node14": "^1.0.0",
1525
+ "@tsconfig/node16": "^1.0.2",
1526
+ "acorn": "^8.4.1",
1527
+ "acorn-walk": "^8.1.1",
1528
+ "arg": "^4.1.0",
1529
+ "create-require": "^1.1.0",
1530
+ "diff": "^4.0.1",
1531
+ "make-error": "^1.1.1",
1532
+ "v8-compile-cache-lib": "^3.0.1",
1533
+ "yn": "3.1.1"
1534
+ },
1535
+ "bin": {
1536
+ "ts-node": "dist/bin.js",
1537
+ "ts-node-cwd": "dist/bin-cwd.js",
1538
+ "ts-node-esm": "dist/bin-esm.js",
1539
+ "ts-node-script": "dist/bin-script.js",
1540
+ "ts-node-transpile-only": "dist/bin-transpile.js",
1541
+ "ts-script": "dist/bin-script-deprecated.js"
1542
+ },
1543
+ "peerDependencies": {
1544
+ "@swc/core": ">=1.2.50",
1545
+ "@swc/wasm": ">=1.2.50",
1546
+ "@types/node": "*",
1547
+ "typescript": ">=2.7"
1548
+ },
1549
+ "peerDependenciesMeta": {
1550
+ "@swc/core": {
1551
+ "optional": true
1552
+ },
1553
+ "@swc/wasm": {
1554
+ "optional": true
1555
+ }
1556
+ }
1557
+ },
1558
+ "node_modules/type-is": {
1559
+ "version": "1.6.18",
1560
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1561
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1562
+ "dependencies": {
1563
+ "media-typer": "0.3.0",
1564
+ "mime-types": "~2.1.24"
1565
+ },
1566
+ "engines": {
1567
+ "node": ">= 0.6"
1568
+ }
1569
+ },
1570
+ "node_modules/typescript": {
1571
+ "version": "5.0.4",
1572
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
1573
+ "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
1574
+ "dev": true,
1575
+ "bin": {
1576
+ "tsc": "bin/tsc",
1577
+ "tsserver": "bin/tsserver"
1578
+ },
1579
+ "engines": {
1580
+ "node": ">=12.20"
1581
+ }
1582
+ },
1583
+ "node_modules/undefsafe": {
1584
+ "version": "2.0.5",
1585
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1586
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1587
+ "dev": true
1588
+ },
1589
+ "node_modules/unpipe": {
1590
+ "version": "1.0.0",
1591
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1592
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1593
+ "engines": {
1594
+ "node": ">= 0.8"
1595
+ }
1596
+ },
1597
+ "node_modules/utils-merge": {
1598
+ "version": "1.0.1",
1599
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1600
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1601
+ "engines": {
1602
+ "node": ">= 0.4.0"
1603
+ }
1604
+ },
1605
+ "node_modules/v8-compile-cache-lib": {
1606
+ "version": "3.0.1",
1607
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
1608
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
1609
+ "dev": true
1610
+ },
1611
+ "node_modules/vary": {
1612
+ "version": "1.1.2",
1613
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1614
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1615
+ "engines": {
1616
+ "node": ">= 0.8"
1617
+ }
1618
+ },
1619
+ "node_modules/yn": {
1620
+ "version": "3.1.1",
1621
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
1622
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
1623
+ "dev": true,
1624
+ "engines": {
1625
+ "node": ">=6"
1626
+ }
1627
+ }
1628
+ }
1629
+ }
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "oai-reverse-proxy",
3
+ "version": "1.0.0",
4
+ "description": "Reverse proxy for the OpenAI API",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "start:dev": "nodemon --watch src --exec ts-node src/server.ts",
8
+ "start": "node build/server.js"
9
+ },
10
+ "author": "",
11
+ "license": "MIT",
12
+ "dependencies": {
13
+ "cors": "^2.8.5",
14
+ "dotenv": "^16.0.3",
15
+ "express": "^4.18.2",
16
+ "http-proxy-middleware": "^3.0.0-beta.1",
17
+ "pino-http": "^8.3.3",
18
+ "showdown": "^2.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/cors": "^2.8.13",
22
+ "@types/express": "^4.17.17",
23
+ "@types/showdown": "^2.0.0",
24
+ "nodemon": "^2.0.22",
25
+ "ts-node": "^10.9.1",
26
+ "typescript": "^5.0.4"
27
+ }
28
+ }
src/info-page.ts ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import showdown from "showdown";
3
+ import { keys } from "./keys";
4
+
5
+ export const handleInfoPage = (req: Request, res: Response) => {
6
+ // Huggingface puts spaces behind some cloudflare ssl proxy, so `req.protocol` is `http` but the correct URL is actually `https`
7
+ const host = req.get("host");
8
+ const isHuggingface = host?.includes("hf.space");
9
+ const protocol = isHuggingface ? "https" : req.protocol;
10
+ res.send(getInfoPageHtml(protocol + "://" + host));
11
+ };
12
+
13
+ function getInfoPageHtml(host: string) {
14
+ const keylist = keys.list();
15
+ const info = {
16
+ message: "OpenAI Reverse Proxy",
17
+ uptime: process.uptime(),
18
+ timestamp: Date.now(),
19
+ baseUrl: host,
20
+ kobold: host + "/proxy/kobold" + " (not yet implemented)",
21
+ openai: host + "/proxy/openai",
22
+ keys: {
23
+ all: keylist.length,
24
+ active: keylist.filter((k) => !k.isDisabled).length,
25
+ trial: keylist.filter((k) => k.isTrial).length,
26
+ gpt4: keylist.filter((k) => k.isGpt4).length,
27
+ proompts: keylist.reduce((acc, k) => acc + k.promptCount, 0),
28
+ },
29
+ };
30
+
31
+ const readme = require("fs").readFileSync("README.md", "utf8");
32
+ const readmeBody = readme.split("---")[2];
33
+ const converter = new showdown.Converter();
34
+ const html = converter.makeHtml(readmeBody);
35
+
36
+ const pageBody = `<!DOCTYPE html>
37
+ <html lang="en">
38
+ <head>
39
+ <meta charset="utf-8" />
40
+ <title>OpenAI Reverse Proxy</title>
41
+ </head>
42
+ <body style="font-family: sans-serif; background-color: #f0f0f0; padding: 1em;"
43
+ ${html}
44
+ <hr />
45
+ <h2>Service Info</h2>
46
+ <pre>${JSON.stringify(info, null, 2)}</pre>
47
+ </body>
48
+ </html>`;
49
+
50
+ return pageBody;
51
+ }
src/keys.ts ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Manages OpenAI API keys. Tracks usage, disables expired keys, and provides
2
+ round-robin access to keys. Keys are stored in the OPENAI_KEY environment
3
+ variable, either as a single key, or a base64-encoded JSON array of keys.*/
4
+ import { logger } from "./logger";
5
+ import crypto from "crypto";
6
+
7
+ /** Represents a key stored in the OPENAI_KEY environment variable. */
8
+ type KeySchema = {
9
+ /** The OpenAI API key itself. */
10
+ key: string;
11
+ /** Whether this is a free trial key. These are prioritized over paid keys if they can fulfill the request. */
12
+ isTrial?: boolean;
13
+ /** Whether this key has been provisioned for GPT-4. */
14
+ isGpt4?: boolean;
15
+ };
16
+
17
+ /** Runtime information about a key. */
18
+ export type Key = KeySchema & {
19
+ /** Whether this key is currently disabled. We set this if we get a 429 or 401 response from OpenAI. */
20
+ isDisabled?: boolean;
21
+ /** Threshold at which a warning email will be sent by OpenAI. */
22
+ softLimit?: number;
23
+ /** Threshold at which the key will be disabled because it has reached the user-defined limit. */
24
+ hardLimit?: number;
25
+ /** The maximum quota allocated to this key by OpenAI. */
26
+ systemHardLimit?: number;
27
+ /** The current usage of this key. */
28
+ usage?: number;
29
+ /** The number of prompts that have been sent with this key. */
30
+ promptCount: number;
31
+ /** The time at which this key was last used. */
32
+ lastUsed: number;
33
+ /** Key hash for displaying usage in the dashboard. */
34
+ hash: string;
35
+ };
36
+
37
+ const keyPool: Key[] = [];
38
+
39
+ function init() {
40
+ const keyString = process.env.OPENAI_KEY;
41
+ if (!keyString?.trim()) {
42
+ throw new Error("OPENAI_KEY environment variable is not set");
43
+ }
44
+ let keyList: KeySchema[];
45
+ try {
46
+ const decoded = Buffer.from(keyString, "base64").toString();
47
+ keyList = JSON.parse(decoded) as KeySchema[];
48
+ } catch (err) {
49
+ logger.info("OPENAI_KEY is not base64-encoded JSON, assuming bare key");
50
+ // We don't actually know if bare keys are paid/GPT-4 so we assume they are
51
+ keyList = [{ key: keyString, isTrial: false, isGpt4: true }];
52
+ }
53
+ for (const key of keyList) {
54
+ const newKey = {
55
+ ...key,
56
+ isDisabled: false,
57
+ softLimit: 0,
58
+ hardLimit: 0,
59
+ systemHardLimit: 0,
60
+ usage: 0,
61
+ lastUsed: 0,
62
+ promptCount: 0,
63
+ hash: crypto
64
+ .createHash("sha256")
65
+ .update(key.key)
66
+ .digest("hex")
67
+ .slice(0, 6),
68
+ };
69
+ keyPool.push(newKey);
70
+
71
+ logger.info({ key: newKey.hash }, "Key added");
72
+ }
73
+ // TODO: check each key's usage upon startup.
74
+ }
75
+
76
+ function list() {
77
+ return keyPool.map((key) => ({
78
+ ...key,
79
+ key: undefined,
80
+ }));
81
+ }
82
+
83
+ function disable(key: Key) {
84
+ const keyFromPool = keyPool.find((k) => k.key === key.key)!;
85
+ if (keyFromPool.isDisabled) return;
86
+ keyFromPool.isDisabled = true;
87
+ logger.warn({ key: key.hash }, "Key disabled");
88
+ }
89
+
90
+ function anyAvailable() {
91
+ return keyPool.some((key) => !key.isDisabled);
92
+ }
93
+
94
+ function get(model: string) {
95
+ const needsGpt4Key = model.startsWith("gpt-4");
96
+ const availableKeys = keyPool.filter(
97
+ (key) => !key.isDisabled && (!needsGpt4Key || key.isGpt4)
98
+ );
99
+ if (availableKeys.length === 0) {
100
+ let message = "No keys available. Please add more keys.";
101
+ if (needsGpt4Key) {
102
+ message =
103
+ "No GPT-4 keys available. Please add more keys or use a non-GPT-4 model.";
104
+ }
105
+ logger.error(message);
106
+ throw new Error(message);
107
+ }
108
+
109
+ // Prioritize trial keys
110
+ const trialKeys = availableKeys.filter((key) => key.isTrial);
111
+ if (trialKeys.length > 0) {
112
+ logger.info({ key: trialKeys[0].hash }, "Using trial key");
113
+ trialKeys[0].lastUsed = Date.now();
114
+ return trialKeys[0];
115
+ }
116
+
117
+ // Otherwise, return the oldest key
118
+ const oldestKey = availableKeys.sort((a, b) => a.lastUsed - b.lastUsed)[0];
119
+ logger.info({ key: oldestKey.hash }, "Assigning key to request.");
120
+ oldestKey.lastUsed = Date.now();
121
+ return { ...oldestKey };
122
+ }
123
+
124
+ function incrementPrompt(keyHash?: string) {
125
+ if (!keyHash) return;
126
+ const key = keyPool.find((k) => k.hash === keyHash)!;
127
+ key.promptCount++;
128
+ }
129
+
130
+ export const keys = { init, list, get, anyAvailable, disable, incrementPrompt };
src/logger.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import pino from "pino";
2
+
3
+ export const logger = pino();
src/proxy/auth.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Request, Response, NextFunction } from "express";
2
+
3
+ const PROXY_KEY = process.env.PROXY_KEY;
4
+
5
+ export const auth = (req: Request, res: Response, next: NextFunction) => {
6
+ if (!PROXY_KEY) {
7
+ next();
8
+ return;
9
+ }
10
+ if (req.headers.authorization === `Bearer ${PROXY_KEY}`) {
11
+ delete req.headers.authorization;
12
+ next();
13
+ } else {
14
+ res.status(401).json({ error: "Unauthorized" });
15
+ }
16
+ };
src/proxy/common.ts ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import * as http from "http";
3
+ import * as httpProxy from "http-proxy";
4
+ import { logger } from "../logger";
5
+ import { keys } from "../keys";
6
+
7
+ /** Handle and rewrite response to proxied requests to OpenAI */
8
+ export const handleResponse = (
9
+ proxyRes: http.IncomingMessage,
10
+ req: Request,
11
+ res: Response
12
+ ) => {
13
+ const statusCode = proxyRes.statusCode || 500;
14
+ if (statusCode >= 400) {
15
+ let body = "";
16
+ proxyRes.on("data", (chunk) => (body += chunk));
17
+ proxyRes.on("end", () => {
18
+ let errorPayload: any = {
19
+ error: "Proxy couldn't parse error from OpenAI",
20
+ };
21
+ const canTryAgain = keys.anyAvailable()
22
+ ? "You can try again to get a different key."
23
+ : "There are no more keys available.";
24
+ try {
25
+ errorPayload = JSON.parse(body);
26
+ } catch (err) {
27
+ logger.error({ error: err }, errorPayload.error);
28
+ res.json(errorPayload);
29
+ return;
30
+ }
31
+
32
+ if (statusCode === 401) {
33
+ // Key is invalid or was revoked
34
+ logger.warn(
35
+ `OpenAI key is invalid or revoked. Keyhash ${req.key?.hash}`
36
+ );
37
+ keys.disable(req.key!);
38
+ const message = `The OpenAI key is invalid or revoked. ${canTryAgain}`;
39
+ errorPayload.proxy_note = message;
40
+ } else if (statusCode === 429) {
41
+ // Rate limit exceeded
42
+ // Annoyingly they send this for:
43
+ // - Quota exceeded, key is totally dead
44
+ // - Rate limit exceeded, key is still good but backoff needed
45
+ // - Model overloaded, their server is overloaded
46
+ if (errorPayload.error?.type === "insufficient_quota") {
47
+ logger.warn(`OpenAI key is exhausted. Keyhash ${req.key?.hash}`);
48
+ keys.disable(req.key!);
49
+ const message = `The OpenAI key is exhausted. ${canTryAgain}`;
50
+ errorPayload.proxy_note = message;
51
+ } else {
52
+ logger.warn(
53
+ { errorCode: errorPayload.error?.type },
54
+ `OpenAI rate limit exceeded or model overloaded. Keyhash ${req.key?.hash}`
55
+ );
56
+ }
57
+ }
58
+
59
+ res.status(statusCode).json(errorPayload);
60
+ });
61
+ } else {
62
+ // Increment key's usage count
63
+ keys.incrementPrompt(req.key?.hash);
64
+
65
+ Object.keys(proxyRes.headers).forEach((key) => {
66
+ res.setHeader(key, proxyRes.headers[key] as string);
67
+ });
68
+ proxyRes.pipe(res);
69
+ }
70
+ };
71
+
72
+ export const onError: httpProxy.ErrorCallback = (err, _req, res) => {
73
+ logger.error({ error: err }, "Error proxying to OpenAI");
74
+
75
+ (res as http.ServerResponse).writeHead(500, {
76
+ "Content-Type": "application/json",
77
+ });
78
+ res.end(
79
+ JSON.stringify({
80
+ error: {
81
+ type: "proxy_error",
82
+ message: err.message,
83
+ proxy_note:
84
+ "Reverse proxy encountered an error before it could reach OpenAI.",
85
+ },
86
+ })
87
+ );
88
+ };
src/proxy/kobold.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export const kobold = (req: Request, res: Response, next: NextFunction) => {
4
+ // TODO: Implement kobold
5
+ res.status(501).json({ error: "Not implemented" });
6
+ };
src/proxy/openai.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Router } from "express";
2
+ import * as http from "http";
3
+ import { createProxyMiddleware, fixRequestBody } from "http-proxy-middleware";
4
+ import { logger } from "../logger";
5
+ import { Key, keys } from "../keys";
6
+ import { handleResponse, onError } from "./common";
7
+
8
+ /**
9
+ * Modifies the request body to add a randomly selected API key.
10
+ */
11
+ const rewriteRequest = (proxyReq: http.ClientRequest, req: Request) => {
12
+ let key: Key;
13
+
14
+ try {
15
+ key = keys.get(req.body?.model || "gpt-3.5")!;
16
+ } catch (err) {
17
+ proxyReq.destroy(err as any);
18
+ return;
19
+ }
20
+
21
+ req.key = key;
22
+ proxyReq.setHeader("Authorization", `Bearer ${key.key}`);
23
+
24
+ if (req.method === "POST" && req.body) {
25
+ if (req.body?.stream) {
26
+ req.body.stream = false;
27
+ const updatedBody = JSON.stringify(req.body);
28
+ proxyReq.setHeader("Content-Length", Buffer.byteLength(updatedBody));
29
+ (req as any).rawBody = Buffer.from(updatedBody);
30
+ }
31
+
32
+ // body-parser and http-proxy-middleware don't play nice together
33
+ fixRequestBody(proxyReq, req);
34
+ }
35
+ };
36
+
37
+ const openaiProxy = createProxyMiddleware({
38
+ target: "https://api.openai.com",
39
+ changeOrigin: true,
40
+ on: {
41
+ proxyReq: rewriteRequest,
42
+ proxyRes: handleResponse,
43
+ error: onError,
44
+ },
45
+ selfHandleResponse: true,
46
+ logger,
47
+ });
48
+
49
+ const openaiRouter = Router();
50
+ openaiRouter.post("/v1/chat/completions", openaiProxy);
51
+ // openaiRouter.post("/v1/completions", openaiProxy); // TODO: Implement Davinci
52
+ openaiRouter.get("/v1/models", openaiProxy);
53
+ openaiRouter.use((req, res) => {
54
+ logger.warn(`Blocked openai proxy request: ${req.method} ${req.path}`);
55
+ res.status(404).json({ error: "Not found" });
56
+ });
57
+
58
+ export const openai = openaiRouter;
src/proxy/routes.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Accepts incoming requests at either the /kobold or /openai routes and then
2
+ routes them to the appropriate handler to be forwarded to the OpenAI API.
3
+ Incoming OpenAI requests are more or less 1:1 with the OpenAI API, but only a
4
+ subset of the API is supported. Kobold requests must be transformed into
5
+ equivalent OpenAI requests. */
6
+
7
+ import * as express from "express";
8
+ import { auth } from "./auth";
9
+ import { kobold } from "./kobold";
10
+ import { openai } from "./openai";
11
+
12
+ const router = express.Router();
13
+
14
+ router.use(auth);
15
+ router.use("/kobold", kobold);
16
+ router.use("/openai", openai);
17
+
18
+ export { router as proxyRouter };
src/server.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ import express from "express";
4
+ import cors from "cors";
5
+ import pinoHttp from "pino-http";
6
+ import { logger } from "./logger";
7
+ import { keys } from "./keys";
8
+ import { proxyRouter } from "./proxy/routes";
9
+ import { handleInfoPage } from "./info-page";
10
+
11
+ const PORT = process.env.PORT || 7860;
12
+
13
+ const app = express();
14
+ // middleware
15
+ app.use(pinoHttp({ logger }));
16
+ app.use(cors());
17
+ app.use(
18
+ express.json({ limit: "10mb" }),
19
+ express.urlencoded({ extended: true, limit: "10mb" })
20
+ );
21
+ // routes
22
+ app.get("/", handleInfoPage);
23
+ app.use("/proxy", proxyRouter);
24
+ // 500 and 404
25
+ app.use((err: any, _req: unknown, res: express.Response, _next: unknown) => {
26
+ if (err.status) {
27
+ res.status(err.status).json({ error: err.message });
28
+ } else {
29
+ logger.error(err);
30
+ res.status(500).json({ error: "Internal server error" });
31
+ }
32
+ });
33
+ app.use((_req: unknown, res: express.Response) => {
34
+ res.status(404).json({ error: "Not found" });
35
+ });
36
+ // start server and load keys
37
+ app.listen(PORT, () => {
38
+ logger.info(`Server listening on port ${PORT}`);
39
+ keys.init();
40
+ });
src/types/custom.d.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Express } from "express-serve-static-core";
2
+ import { Key } from "../keys";
3
+
4
+ declare global {
5
+ namespace Express {
6
+ interface Request {
7
+ key?: Key;
8
+ }
9
+ }
10
+ }
tsconfig.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ES2020",
5
+ "module": "CommonJS",
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "skipLibCheck": true,
10
+ "skipDefaultLibCheck": true,
11
+ "outDir": "build"
12
+ },
13
+ "include": ["src"],
14
+ "exclude": ["node_modules"],
15
+ "files": ["src/types/custom.d.ts"]
16
+ }