├── .dockerignore
├── .env.dist
├── .github
├── dependabot.yml
└── workflows
│ ├── codeql.yml
│ ├── release.yml
│ └── temp-release.yml
├── .gitignore
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── data
└── .gitignore
├── docker-compose.gluetun.yml
├── docker-compose.simple.yml
├── package-lock.json
├── package.json
├── src
├── index.js
├── middlewares
│ └── auth.js
├── routers
│ ├── config
│ │ ├── fs.js
│ │ └── index.js
│ └── qbit
│ │ └── index.js
├── routines
│ └── update.js
├── ssl.js
└── utils.js
└── tests
├── config.http
├── ping.http
└── qbit.http
/.dockerignore:
--------------------------------------------------------------------------------
1 | data
2 | node_modules
3 | .env*
4 |
--------------------------------------------------------------------------------
/.env.dist:
--------------------------------------------------------------------------------
1 | CONFIG_PATH=/config
2 | PORT=3000
3 | QBIT_BASE=http://host.docker.internal:8080
4 | RELEASE_TYPE=stable
5 | UPDATE_VT_CRON=0 * * * *
6 | USE_INSECURE_SSL=false
7 | SKIP_X_FORWARD_HEADERS=false
8 | VUETORRENT_PATH=/vuetorrent
9 | GITHUB_AUTH=*****
10 |
11 | # SSL_CERT_PATH=/config/ssl/cert.pem
12 | # SSL_KEY_PATH=/config/ssl/key.pem
13 | # SSL_CERT=*****
14 | # SSL_KEY=*****
15 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # Maintain dependencies for GitHub Actions
4 | - package-ecosystem: 'github-actions'
5 | directory: '/'
6 | schedule:
7 | interval: 'weekly'
8 |
9 | # Maintain dependencies for npm
10 | - package-ecosystem: 'npm'
11 | directory: '/'
12 | schedule:
13 | interval: 'weekly'
14 | groups:
15 | all:
16 | patterns:
17 | - '*'
18 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: 'CodeQL'
2 |
3 | on:
4 | schedule:
5 | # At 00:00 on Sunday
6 | - cron: '0 0 * * 0'
7 | push:
8 | branches:
9 | - main
10 | pull_request:
11 | branches:
12 | - main
13 | paths:
14 | - src
15 |
16 | jobs:
17 | analyze:
18 | name: Analyze
19 | runs-on: ubuntu-latest
20 | timeout-minutes: 360
21 |
22 | permissions:
23 | actions: read
24 | contents: read
25 | security-events: write
26 |
27 | strategy:
28 | fail-fast: false
29 | matrix:
30 | language: ['javascript-typescript']
31 | steps:
32 | - name: Checkout repository
33 | uses: actions/checkout@v4
34 |
35 | - name: Initialize CodeQL
36 | uses: github/codeql-action/init@v3
37 | with:
38 | languages: ${{ matrix.language }}
39 |
40 | - name: Autobuild
41 | uses: github/codeql-action/autobuild@v3
42 |
43 | - name: Perform CodeQL Analysis
44 | uses: github/codeql-action/analyze@v3
45 | with:
46 | category: '/language:${{matrix.language}}'
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | env:
9 | REGISTRY: ghcr.io
10 | IMAGE_NAME: ${{ github.repository }}
11 |
12 | jobs:
13 | setup-release:
14 | name: Run Release Please action
15 | outputs:
16 | release_created: ${{ steps.release.outputs.release_created }}
17 | tag_name: ${{ steps.release.outputs.tag_name }}
18 | runs-on: ubuntu-latest
19 | permissions:
20 | contents: write
21 | pull-requests: write
22 |
23 | steps:
24 | - name: Run release please
25 | uses: googleapis/release-please-action@v4
26 | id: release
27 | with:
28 | release-type: node
29 |
30 | build:
31 | name: Build & push docker image
32 | runs-on: ubuntu-latest
33 | needs: setup-release
34 | if: needs.setup-release.outputs.release_created
35 | permissions:
36 | contents: write
37 | packages: write
38 | steps:
39 | - name: Checkout repository
40 | uses: actions/checkout@v4
41 |
42 | - name: Update latest tag
43 | uses: EndBug/latest-tag@v1
44 | with:
45 | ref: latest-release
46 |
47 | - name: Set up QEMU
48 | uses: docker/setup-qemu-action@v3
49 |
50 | - name: Set up Docker Buildx
51 | uses: docker/setup-buildx-action@v3
52 |
53 | - name: Log in to the Container registry
54 | uses: docker/login-action@v3
55 | with:
56 | registry: ${{ env.REGISTRY }}
57 | username: ${{ github.actor }}
58 | password: ${{ secrets.GITHUB_TOKEN }}
59 |
60 | - name: Extract metadata (tags, labels) for Docker
61 | id: meta
62 | uses: docker/metadata-action@v5
63 | with:
64 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
65 | tags: type=match,pattern=v(.*),group=1,value=${{ needs.setup-release.outputs.tag_name }}
66 |
67 | - name: Build and push Docker image
68 | uses: docker/build-push-action@v6
69 | env:
70 | DOCKER_BUILD_SUMMARY: false
71 | with:
72 | context: .
73 | push: true
74 | platforms: linux/amd64,linux/arm64
75 | tags: ${{ steps.meta.outputs.tags }}
76 | labels: ${{ steps.meta.outputs.labels }}
77 |
--------------------------------------------------------------------------------
/.github/workflows/temp-release.yml:
--------------------------------------------------------------------------------
1 | name: Temporary Release CI
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | name:
7 | description: Docker image tag to apply to the image
8 | required: true
9 |
10 | env:
11 | REGISTRY: ghcr.io
12 | IMAGE_NAME: ${{ github.repository }}
13 |
14 | jobs:
15 | build:
16 | name: Build & push temporary docker image
17 | runs-on: ubuntu-latest
18 | permissions:
19 | contents: read
20 | packages: write
21 | steps:
22 | - name: Checkout repository
23 | uses: actions/checkout@v4
24 |
25 | - name: Set up QEMU
26 | uses: docker/setup-qemu-action@v3
27 |
28 | - name: Set up Docker Buildx
29 | uses: docker/setup-buildx-action@v3
30 |
31 | - name: Log in to the Container registry
32 | uses: docker/login-action@v3
33 | with:
34 | registry: ${{ env.REGISTRY }}
35 | username: ${{ github.actor }}
36 | password: ${{ secrets.GITHUB_TOKEN }}
37 |
38 | - name: Extract metadata (tags, labels) for Docker
39 | id: meta
40 | uses: docker/metadata-action@v5
41 | with:
42 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
43 | tags: ${{ github.event.inputs.name }}
44 |
45 | - name: Build and push Docker image
46 | uses: docker/build-push-action@v6
47 | env:
48 | DOCKER_BUILD_SUMMARY: false
49 | with:
50 | context: .
51 | push: true
52 | platforms: linux/amd64,linux/arm64
53 | tags: ${{ steps.meta.outputs.tags }}
54 | labels: ${{ steps.meta.outputs.labels }}
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | vuetorrent/
4 | .env
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.5.1](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.5.0...v2.5.1) (2025-05-30)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * **ci:** Update token permissions ([c595def](https://github.com/VueTorrent/vuetorrent-backend/commit/c595deff6d9740f9ff98cdc0f0380300149342f1))
9 |
10 | ## [2.5.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.4.0...v2.5.0) (2025-05-30)
11 |
12 |
13 | ### Features
14 |
15 | * **ci:** Add "latest-release" tag on source release ([#95](https://github.com/VueTorrent/vuetorrent-backend/issues/95)) ([f658dbc](https://github.com/VueTorrent/vuetorrent-backend/commit/f658dbc1e4ee954efd3bb1c2cbae2922a7017c80))
16 |
17 | ## [2.4.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.3.0...v2.4.0) (2025-05-25)
18 |
19 |
20 | ### Features
21 |
22 | * Support running server in HTTPS ([#92](https://github.com/VueTorrent/vuetorrent-backend/issues/92)) ([790bbdb](https://github.com/VueTorrent/vuetorrent-backend/commit/790bbdb70b4c2a5b98e654d720901b50d35bc0fd))
23 |
24 | ## [2.3.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.2.0...v2.3.0) (2025-04-26)
25 |
26 |
27 | ### Features
28 |
29 | * Allow version pinning ([#89](https://github.com/VueTorrent/vuetorrent-backend/issues/89)) ([1a56892](https://github.com/VueTorrent/vuetorrent-backend/commit/1a56892c32159633cbca7f2a16551c0fcf93d5f6))
30 | * **GitHub:** Add PAT support for increased rate limit ([#87](https://github.com/VueTorrent/vuetorrent-backend/issues/87)) ([2318ca4](https://github.com/VueTorrent/vuetorrent-backend/commit/2318ca434adb680bd170c2ebd890bdbb42c1e161))
31 |
32 | ## [2.2.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.1.3...v2.2.0) (2024-12-15)
33 |
34 |
35 | ### Features
36 |
37 | * **Docker:** expose a SKIP_X_FORWARD_HEADERS option ([#75](https://github.com/VueTorrent/vuetorrent-backend/issues/75)) ([b3677ab](https://github.com/VueTorrent/vuetorrent-backend/commit/b3677ab3db379aae3bfe55352e270f81e24843f2))
38 |
39 | ## [2.1.3](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.1.2...v2.1.3) (2024-10-06)
40 |
41 |
42 | ### Bug Fixes
43 |
44 | * **auth:** Handle 401 being thrown on login ([5c0e24b](https://github.com/VueTorrent/vuetorrent-backend/commit/5c0e24bde11b135a11c7fbd8e5156a19530542cf))
45 |
46 | ## [2.1.2](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.1.1...v2.1.2) (2024-10-01)
47 |
48 |
49 | ### Bug Fixes
50 |
51 | * **Docker:** Allow container with non-root user ([#54](https://github.com/VueTorrent/vuetorrent-backend/issues/54)) ([bc4bf70](https://github.com/VueTorrent/vuetorrent-backend/commit/bc4bf707c86d9effefeb5b6226d2df30ff79c5fb))
52 |
53 | ## [2.1.1](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.1.0...v2.1.1) (2024-09-26)
54 |
55 |
56 | ### Bug Fixes
57 |
58 | * **update:** Use release branches instead of release asset ([#51](https://github.com/VueTorrent/vuetorrent-backend/issues/51)) ([25b139b](https://github.com/VueTorrent/vuetorrent-backend/commit/25b139b9986a5fc7154b231377d2664e59ff6a9f))
59 |
60 | ## [2.1.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.0.2...v2.1.0) (2024-09-22)
61 |
62 |
63 | ### Features
64 |
65 | * Add auto-update scheduler for WebUI ([16bd320](https://github.com/VueTorrent/vuetorrent-backend/commit/16bd3204615e02bb7b4d089776138345d99c16be))
66 |
67 |
68 | ### Bug Fixes
69 |
70 | * Make dev-only CONFIG_PATH env optional ([503434b](https://github.com/VueTorrent/vuetorrent-backend/commit/503434b512e73c422d5b884535990ff46c1b1242))
71 |
72 | ## [2.0.2](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.0.1...v2.0.2) (2024-09-20)
73 |
74 |
75 | ### Bug Fixes
76 |
77 | * **router:** Remove /backend prefix on /api requests ([#45](https://github.com/VueTorrent/vuetorrent-backend/issues/45)) ([512c619](https://github.com/VueTorrent/vuetorrent-backend/commit/512c61959d28093356482754a395f4349e8fd6b2))
78 |
79 |
80 | ### Performance Improvements
81 |
82 | * Update 404 error message ([#43](https://github.com/VueTorrent/vuetorrent-backend/issues/43)) ([fc5a1dd](https://github.com/VueTorrent/vuetorrent-backend/commit/fc5a1ddecaa13aa42da204a69da51dbd76de68ca))
83 |
84 | ## [2.0.1](https://github.com/VueTorrent/vuetorrent-backend/compare/v2.0.0...v2.0.1) (2024-09-19)
85 |
86 |
87 | ### Bug Fixes
88 |
89 | * Add /backend prefix to all routes ([#40](https://github.com/VueTorrent/vuetorrent-backend/issues/40)) ([4849614](https://github.com/VueTorrent/vuetorrent-backend/commit/4849614fa8fbbde1059ac24ffa437348d79140c5))
90 |
91 | ## [2.0.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.2.3...v2.0.0) (2024-09-18)
92 |
93 |
94 | ### ⚠ BREAKING CHANGES
95 |
96 | * Forward API requests to qBittorrent ([#35](https://github.com/VueTorrent/vuetorrent-backend/issues/35))
97 |
98 | ### Features
99 |
100 | * Add update management routes ([#36](https://github.com/VueTorrent/vuetorrent-backend/issues/36)) ([f26dffe](https://github.com/VueTorrent/vuetorrent-backend/commit/f26dffeb7f6854f431a7cc7b70754de1e4be8024))
101 | * Forward API requests to qBittorrent ([#35](https://github.com/VueTorrent/vuetorrent-backend/issues/35)) ([acd643b](https://github.com/VueTorrent/vuetorrent-backend/commit/acd643b05746dcb65fbadf4509c16cea23d81b03))
102 |
103 | ## [1.2.3](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.2.2...v1.2.3) (2024-06-07)
104 |
105 |
106 | ### Reverts
107 |
108 | * Add ARM architectures support ([#23](https://github.com/VueTorrent/vuetorrent-backend/issues/23)) ([#25](https://github.com/VueTorrent/vuetorrent-backend/issues/25)) ([ed80b5a](https://github.com/VueTorrent/vuetorrent-backend/commit/ed80b5a55080bd1366fc44fdc3a7f56b0a46ee9d))
109 |
110 | ## [1.2.2](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.2.1...v1.2.2) (2024-06-07)
111 |
112 |
113 | ### Bug Fixes
114 |
115 | * **docker:** Add ARM architectures support ([#23](https://github.com/VueTorrent/vuetorrent-backend/issues/23)) ([14fdaf9](https://github.com/VueTorrent/vuetorrent-backend/commit/14fdaf9cac4a825a64d01b23cfa6774fa09f3634))
116 |
117 | ## [1.2.1](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.2.0...v1.2.1) (2024-05-12)
118 |
119 |
120 | ### Bug Fixes
121 |
122 | * **docker:** Enable multi-platform build ([946717b](https://github.com/VueTorrent/vuetorrent-backend/commit/946717b94fb717efc43c4144298b255e8e9069f6))
123 |
124 | ## [1.2.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.1.2...v1.2.0) (2024-05-12)
125 |
126 |
127 | ### Features
128 |
129 | * docker container support arm64 ([#18](https://github.com/VueTorrent/vuetorrent-backend/issues/18)) ([b7666f2](https://github.com/VueTorrent/vuetorrent-backend/commit/b7666f209b7b9e1649d37db46b42c1a1f4061c0d))
130 |
131 | ## [1.1.2](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.1.1...v1.1.2) (2024-05-08)
132 |
133 |
134 | ### Bug Fixes
135 |
136 | * **docker:** Switch to alpine base to reduce image size ([4214e9b](https://github.com/VueTorrent/vuetorrent-backend/commit/4214e9b90d9df90e96925d5665184c7ef5783725))
137 |
138 | ## [1.1.1](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.1.0...v1.1.1) (2024-05-08)
139 |
140 |
141 | ### Bug Fixes
142 |
143 | * **auth:** Add DISABLE_AUTH env to disable auth check ([#15](https://github.com/VueTorrent/vuetorrent-backend/issues/15)) ([f09899d](https://github.com/VueTorrent/vuetorrent-backend/commit/f09899d19321f9b25f1b391635be88f2df656254))
144 |
145 | ## [1.1.0](https://github.com/VueTorrent/vuetorrent-backend/compare/v1.0.0...v1.1.0) (2024-04-01)
146 |
147 |
148 | ### Features
149 |
150 | * Handle authentication through qbit ([#10](https://github.com/VueTorrent/vuetorrent-backend/issues/10)) ([3954e99](https://github.com/VueTorrent/vuetorrent-backend/commit/3954e991f7f79784d854440e1b574ebfce79452e))
151 |
152 | ## 1.0.0 (2024-03-15)
153 |
154 |
155 | ### Features
156 |
157 | * **config:** Create server-side store ([bdd15e4](https://github.com/VueTorrent/vuetorrent-backend/commit/bdd15e404e9efc839ef3023d74ba7011b31fe46d))
158 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:21-alpine
2 | RUN mkdir -p -m 0777 /vuetorrent
3 | WORKDIR /app
4 | COPY package*.json ./
5 | RUN npm ci
6 | COPY src/ src/
7 | EXPOSE 3000
8 | CMD ["node", "src/index.js"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vuetorrent-backend
2 |
3 | This project is a Node.js web service designed to enhance the capabilities of [VueTorrent](https://github.com/VueTorrent/VueTorrent) users.
4 |
5 | > [!TIP]
6 | > Installation guide can be found in the [wiki](https://github.com/VueTorrent/vuetorrent-backend/wiki/Installation)
7 |
8 | Endpoints are accessible using tools like cURL, Postman, or by making HTTP requests from your browser or any programming language/framework.
9 |
10 | You can find a list of every feature in the [Wiki](https://github.com/VueTorrent/vuetorrent-backend/wiki).
11 |
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
--------------------------------------------------------------------------------
/docker-compose.gluetun.yml:
--------------------------------------------------------------------------------
1 | services:
2 | gluetun:
3 | image: ghcr.io/qdm12/gluetun:latest
4 | container_name: gluetun
5 | restart: unless-stopped
6 | cap_add:
7 | - NET_ADMIN
8 | environment:
9 | # Include your gluetun config here
10 | devices:
11 | - "/dev/net/tun:/dev/net/tun"
12 | volumes:
13 | - "./data/gluetun:/gluetun"
14 | ports:
15 | - 8123:8123/tcp # Gluetun API
16 |
17 | # qBittorrent ports, vuetorrent-backend will forward requests to it so no need to expose it
18 | # - "8080:8080"
19 | # NAT port forwarding required for them to work
20 | - "6881:6881"
21 | - "6881:6881/udp"
22 |
23 | # vuetorrent-backend
24 | - "8081:8081"
25 |
26 | qbit:
27 | image: lscr.io/linuxserver/qbittorrent:latest
28 | container_name: qbit
29 | restart: unless-stopped
30 | network_mode: service:gluetun
31 | environment:
32 | - WEBUI_PORT=8080
33 | - PUID=1000
34 | - PGID=1000
35 | volumes:
36 | - "./data/qbittorrent:/config"
37 | - "./data/downloads:/downloads"
38 | - "./data/torrents:/torrents"
39 |
40 | vuetorrent-backend:
41 | # build: .
42 | image: ghcr.io/vuetorrent/vuetorrent-backend:latest
43 | container_name: vuetorrent-backend
44 | restart: unless-stopped
45 | network_mode: service:gluetun
46 | environment:
47 | # Backend port can't match qbit one because of the network_mode
48 | # You'll have to remove "Host header validation" in qbit settings > WebUI
49 | - PORT=8081
50 |
51 | # Here we can use localhost because both qbit and backend container are on the same docker host (network_mode)
52 | - QBIT_BASE=http://localhost:8080
53 | # Use "dev" for nightly releases
54 | - RELEASE_TYPE=stable
55 | # Optional, refer to the node-schedule package for compatible syntax
56 | - UPDATE_VT_CRON=0 * * * *
57 |
58 | # Define this if using self-signed certificates on qBittorrent
59 | # - NODE_EXTRA_CA_CERTS=/config/ssl/cert.pem
60 | # You can also disable SSL verification (not recommended)
61 | # - USE_INSECURE_SSL=true
62 |
63 | # Only enable if backend container is behind a proxy server which already add x-forward headers
64 | # - SKIP_X_FORWARD_HEADERS=true
65 | # Create a Github PAT with your account to increase API rate limit, fine-grained with only public repo access is enough
66 | # - GITHUB_AUTH=${GITHUB_AUTH}
67 |
68 | # If you want HTTPS, define the following variables with respective paths
69 | # - SSL_CERT_PATH=/config/ssl/cert.pem
70 | # - SSL_KEY_PATH=/config/ssl/key.pem
71 | # Use these instead for passing the value directly
72 | # - SSL_CERT=*****
73 | # - SSL_KEY=*****
74 | volumes:
75 | - "./data/config:/config"
76 |
--------------------------------------------------------------------------------
/docker-compose.simple.yml:
--------------------------------------------------------------------------------
1 | services:
2 | qbit:
3 | image: lscr.io/linuxserver/qbittorrent:latest
4 | container_name: qbit
5 | restart: unless-stopped
6 | networks:
7 | - qbit-net
8 | environment:
9 | - WEBUI_PORT=8080
10 | - PUID=1000
11 | - PGID=1000
12 | ports:
13 | # qBittorrent WebUI is exposed by vuetorrent-backend service
14 | # - "8080:8080"
15 |
16 | # qBittorrent ports, NAT port forwarding required for them to work
17 | - "6881:6881"
18 | - "6881:6881/udp"
19 | volumes:
20 | - "./data/qbittorrent:/config"
21 | - "./data/downloads:/downloads"
22 | - "./data/torrents:/torrents"
23 |
24 | vuetorrent-backend:
25 | # build: .
26 | image: ghcr.io/vuetorrent/vuetorrent-backend:latest
27 | container_name: vuetorrent-backend
28 | restart: unless-stopped
29 | networks:
30 | - qbit-net
31 | environment:
32 | # Backend port needs to match qbit one to avoid "Host header validation" issues
33 | - PORT=8080
34 | - QBIT_BASE=http://qbit:8080
35 | # Use "dev" for nightly releases
36 | - RELEASE_TYPE=stable
37 | # Optional, refer to the node-schedule package for compatible syntax
38 | - UPDATE_VT_CRON=0 * * * *
39 |
40 | # Define this if using self-signed certificates on qBittorrent
41 | # - NODE_EXTRA_CA_CERTS=/config/ssl/cert.pem
42 | # You can also disable SSL verification (not recommended)
43 | # - USE_INSECURE_SSL=true
44 |
45 | # Only enable if backend container is behind a proxy server which already add x-forward headers
46 | # - SKIP_X_FORWARD_HEADERS=true
47 | # Create a Github PAT with your account to increase API rate limit
48 | # fine-grained token with public repo access is enough
49 | # https://github.com/settings/personal-access-tokens/new
50 | # - GITHUB_AUTH=${GITHUB_AUTH}
51 |
52 | # If you want HTTPS, define the following variables with respective paths
53 | # - SSL_CERT_PATH=/config/ssl/cert.pem
54 | # - SSL_KEY_PATH=/config/ssl/key.pem
55 | # Use these instead for passing the value directly
56 | # - SSL_CERT=*****
57 | # - SSL_KEY=*****
58 | ports:
59 | - "8080:8080"
60 | volumes:
61 | - "./data/config:/config"
62 |
63 | # This network is only there for the example,
64 | # if you already have an external network (e.g. for use with a reverse proxy),
65 | # you can use the existing one instead.
66 | networks:
67 | qbit-net:
68 | driver: bridge
69 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuetorrent-backend",
3 | "version": "2.5.1",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "vuetorrent-backend",
9 | "version": "2.5.1",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^1.10.0",
13 | "cookie-parser": "^1.4.7",
14 | "cors": "^2.8.5",
15 | "dotenv": "^17.0.0",
16 | "express": "^5.1.0",
17 | "http-proxy": "^1.18.1",
18 | "jszip": "^3.10.1",
19 | "morgan": "^1.10.0",
20 | "node-schedule": "^2.1.1"
21 | },
22 | "devDependencies": {
23 | "nodemon": "^3.1.10"
24 | }
25 | },
26 | "node_modules/abbrev": {
27 | "version": "1.1.1",
28 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
29 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
30 | "dev": true
31 | },
32 | "node_modules/accepts": {
33 | "version": "2.0.0",
34 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
35 | "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
36 | "license": "MIT",
37 | "dependencies": {
38 | "mime-types": "^3.0.0",
39 | "negotiator": "^1.0.0"
40 | },
41 | "engines": {
42 | "node": ">= 0.6"
43 | }
44 | },
45 | "node_modules/accepts/node_modules/mime-db": {
46 | "version": "1.54.0",
47 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
48 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
49 | "license": "MIT",
50 | "engines": {
51 | "node": ">= 0.6"
52 | }
53 | },
54 | "node_modules/accepts/node_modules/mime-types": {
55 | "version": "3.0.1",
56 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
57 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
58 | "license": "MIT",
59 | "dependencies": {
60 | "mime-db": "^1.54.0"
61 | },
62 | "engines": {
63 | "node": ">= 0.6"
64 | }
65 | },
66 | "node_modules/anymatch": {
67 | "version": "3.1.3",
68 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
69 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
70 | "dev": true,
71 | "dependencies": {
72 | "normalize-path": "^3.0.0",
73 | "picomatch": "^2.0.4"
74 | },
75 | "engines": {
76 | "node": ">= 8"
77 | }
78 | },
79 | "node_modules/asynckit": {
80 | "version": "0.4.0",
81 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
82 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
83 | },
84 | "node_modules/axios": {
85 | "version": "1.10.0",
86 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
87 | "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
88 | "license": "MIT",
89 | "dependencies": {
90 | "follow-redirects": "^1.15.6",
91 | "form-data": "^4.0.0",
92 | "proxy-from-env": "^1.1.0"
93 | }
94 | },
95 | "node_modules/balanced-match": {
96 | "version": "1.0.2",
97 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
98 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
99 | "dev": true
100 | },
101 | "node_modules/basic-auth": {
102 | "version": "2.0.1",
103 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
104 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
105 | "dependencies": {
106 | "safe-buffer": "5.1.2"
107 | },
108 | "engines": {
109 | "node": ">= 0.8"
110 | }
111 | },
112 | "node_modules/basic-auth/node_modules/safe-buffer": {
113 | "version": "5.1.2",
114 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
115 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
116 | },
117 | "node_modules/binary-extensions": {
118 | "version": "2.3.0",
119 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
120 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
121 | "dev": true,
122 | "engines": {
123 | "node": ">=8"
124 | },
125 | "funding": {
126 | "url": "https://github.com/sponsors/sindresorhus"
127 | }
128 | },
129 | "node_modules/body-parser": {
130 | "version": "2.2.0",
131 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
132 | "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
133 | "license": "MIT",
134 | "dependencies": {
135 | "bytes": "^3.1.2",
136 | "content-type": "^1.0.5",
137 | "debug": "^4.4.0",
138 | "http-errors": "^2.0.0",
139 | "iconv-lite": "^0.6.3",
140 | "on-finished": "^2.4.1",
141 | "qs": "^6.14.0",
142 | "raw-body": "^3.0.0",
143 | "type-is": "^2.0.0"
144 | },
145 | "engines": {
146 | "node": ">=18"
147 | }
148 | },
149 | "node_modules/body-parser/node_modules/debug": {
150 | "version": "4.4.0",
151 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
152 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
153 | "license": "MIT",
154 | "dependencies": {
155 | "ms": "^2.1.3"
156 | },
157 | "engines": {
158 | "node": ">=6.0"
159 | },
160 | "peerDependenciesMeta": {
161 | "supports-color": {
162 | "optional": true
163 | }
164 | }
165 | },
166 | "node_modules/body-parser/node_modules/ms": {
167 | "version": "2.1.3",
168 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
169 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
170 | "license": "MIT"
171 | },
172 | "node_modules/brace-expansion": {
173 | "version": "1.1.11",
174 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
175 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
176 | "dev": true,
177 | "dependencies": {
178 | "balanced-match": "^1.0.0",
179 | "concat-map": "0.0.1"
180 | }
181 | },
182 | "node_modules/braces": {
183 | "version": "3.0.3",
184 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
185 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
186 | "dev": true,
187 | "dependencies": {
188 | "fill-range": "^7.1.1"
189 | },
190 | "engines": {
191 | "node": ">=8"
192 | }
193 | },
194 | "node_modules/bytes": {
195 | "version": "3.1.2",
196 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
197 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
198 | "license": "MIT",
199 | "engines": {
200 | "node": ">= 0.8"
201 | }
202 | },
203 | "node_modules/call-bind-apply-helpers": {
204 | "version": "1.0.2",
205 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
206 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
207 | "license": "MIT",
208 | "dependencies": {
209 | "es-errors": "^1.3.0",
210 | "function-bind": "^1.1.2"
211 | },
212 | "engines": {
213 | "node": ">= 0.4"
214 | }
215 | },
216 | "node_modules/call-bound": {
217 | "version": "1.0.4",
218 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
219 | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
220 | "license": "MIT",
221 | "dependencies": {
222 | "call-bind-apply-helpers": "^1.0.2",
223 | "get-intrinsic": "^1.3.0"
224 | },
225 | "engines": {
226 | "node": ">= 0.4"
227 | },
228 | "funding": {
229 | "url": "https://github.com/sponsors/ljharb"
230 | }
231 | },
232 | "node_modules/chokidar": {
233 | "version": "3.6.0",
234 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
235 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
236 | "dev": true,
237 | "dependencies": {
238 | "anymatch": "~3.1.2",
239 | "braces": "~3.0.2",
240 | "glob-parent": "~5.1.2",
241 | "is-binary-path": "~2.1.0",
242 | "is-glob": "~4.0.1",
243 | "normalize-path": "~3.0.0",
244 | "readdirp": "~3.6.0"
245 | },
246 | "engines": {
247 | "node": ">= 8.10.0"
248 | },
249 | "funding": {
250 | "url": "https://paulmillr.com/funding/"
251 | },
252 | "optionalDependencies": {
253 | "fsevents": "~2.3.2"
254 | }
255 | },
256 | "node_modules/combined-stream": {
257 | "version": "1.0.8",
258 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
259 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
260 | "dependencies": {
261 | "delayed-stream": "~1.0.0"
262 | },
263 | "engines": {
264 | "node": ">= 0.8"
265 | }
266 | },
267 | "node_modules/concat-map": {
268 | "version": "0.0.1",
269 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
270 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
271 | "dev": true
272 | },
273 | "node_modules/content-disposition": {
274 | "version": "1.0.0",
275 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
276 | "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
277 | "license": "MIT",
278 | "dependencies": {
279 | "safe-buffer": "5.2.1"
280 | },
281 | "engines": {
282 | "node": ">= 0.6"
283 | }
284 | },
285 | "node_modules/content-type": {
286 | "version": "1.0.5",
287 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
288 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
289 | "license": "MIT",
290 | "engines": {
291 | "node": ">= 0.6"
292 | }
293 | },
294 | "node_modules/cookie": {
295 | "version": "0.7.1",
296 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
297 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
298 | "engines": {
299 | "node": ">= 0.6"
300 | }
301 | },
302 | "node_modules/cookie-parser": {
303 | "version": "1.4.7",
304 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
305 | "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
306 | "dependencies": {
307 | "cookie": "0.7.2",
308 | "cookie-signature": "1.0.6"
309 | },
310 | "engines": {
311 | "node": ">= 0.8.0"
312 | }
313 | },
314 | "node_modules/cookie-parser/node_modules/cookie": {
315 | "version": "0.7.2",
316 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
317 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
318 | "engines": {
319 | "node": ">= 0.6"
320 | }
321 | },
322 | "node_modules/cookie-signature": {
323 | "version": "1.0.6",
324 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
325 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
326 | },
327 | "node_modules/core-util-is": {
328 | "version": "1.0.3",
329 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
330 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
331 | "license": "MIT"
332 | },
333 | "node_modules/cors": {
334 | "version": "2.8.5",
335 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
336 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
337 | "dependencies": {
338 | "object-assign": "^4",
339 | "vary": "^1"
340 | },
341 | "engines": {
342 | "node": ">= 0.10"
343 | }
344 | },
345 | "node_modules/cron-parser": {
346 | "version": "4.9.0",
347 | "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
348 | "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
349 | "license": "MIT",
350 | "dependencies": {
351 | "luxon": "^3.2.1"
352 | },
353 | "engines": {
354 | "node": ">=12.0.0"
355 | }
356 | },
357 | "node_modules/debug": {
358 | "version": "2.6.9",
359 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
360 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
361 | "dependencies": {
362 | "ms": "2.0.0"
363 | }
364 | },
365 | "node_modules/delayed-stream": {
366 | "version": "1.0.0",
367 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
368 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
369 | "engines": {
370 | "node": ">=0.4.0"
371 | }
372 | },
373 | "node_modules/depd": {
374 | "version": "2.0.0",
375 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
376 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
377 | "engines": {
378 | "node": ">= 0.8"
379 | }
380 | },
381 | "node_modules/dotenv": {
382 | "version": "17.0.0",
383 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.0.tgz",
384 | "integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==",
385 | "engines": {
386 | "node": ">=12"
387 | },
388 | "funding": {
389 | "url": "https://dotenvx.com"
390 | }
391 | },
392 | "node_modules/dunder-proto": {
393 | "version": "1.0.1",
394 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
395 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
396 | "license": "MIT",
397 | "dependencies": {
398 | "call-bind-apply-helpers": "^1.0.1",
399 | "es-errors": "^1.3.0",
400 | "gopd": "^1.2.0"
401 | },
402 | "engines": {
403 | "node": ">= 0.4"
404 | }
405 | },
406 | "node_modules/ee-first": {
407 | "version": "1.1.1",
408 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
409 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
410 | },
411 | "node_modules/encodeurl": {
412 | "version": "2.0.0",
413 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
414 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
415 | "license": "MIT",
416 | "engines": {
417 | "node": ">= 0.8"
418 | }
419 | },
420 | "node_modules/es-define-property": {
421 | "version": "1.0.1",
422 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
423 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
424 | "license": "MIT",
425 | "engines": {
426 | "node": ">= 0.4"
427 | }
428 | },
429 | "node_modules/es-errors": {
430 | "version": "1.3.0",
431 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
432 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
433 | "license": "MIT",
434 | "engines": {
435 | "node": ">= 0.4"
436 | }
437 | },
438 | "node_modules/es-object-atoms": {
439 | "version": "1.1.1",
440 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
441 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
442 | "license": "MIT",
443 | "dependencies": {
444 | "es-errors": "^1.3.0"
445 | },
446 | "engines": {
447 | "node": ">= 0.4"
448 | }
449 | },
450 | "node_modules/escape-html": {
451 | "version": "1.0.3",
452 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
453 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
454 | "license": "MIT"
455 | },
456 | "node_modules/etag": {
457 | "version": "1.8.1",
458 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
459 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
460 | "license": "MIT",
461 | "engines": {
462 | "node": ">= 0.6"
463 | }
464 | },
465 | "node_modules/eventemitter3": {
466 | "version": "4.0.7",
467 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
468 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
469 | "license": "MIT"
470 | },
471 | "node_modules/express": {
472 | "version": "5.1.0",
473 | "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
474 | "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
475 | "license": "MIT",
476 | "dependencies": {
477 | "accepts": "^2.0.0",
478 | "body-parser": "^2.2.0",
479 | "content-disposition": "^1.0.0",
480 | "content-type": "^1.0.5",
481 | "cookie": "^0.7.1",
482 | "cookie-signature": "^1.2.1",
483 | "debug": "^4.4.0",
484 | "encodeurl": "^2.0.0",
485 | "escape-html": "^1.0.3",
486 | "etag": "^1.8.1",
487 | "finalhandler": "^2.1.0",
488 | "fresh": "^2.0.0",
489 | "http-errors": "^2.0.0",
490 | "merge-descriptors": "^2.0.0",
491 | "mime-types": "^3.0.0",
492 | "on-finished": "^2.4.1",
493 | "once": "^1.4.0",
494 | "parseurl": "^1.3.3",
495 | "proxy-addr": "^2.0.7",
496 | "qs": "^6.14.0",
497 | "range-parser": "^1.2.1",
498 | "router": "^2.2.0",
499 | "send": "^1.1.0",
500 | "serve-static": "^2.2.0",
501 | "statuses": "^2.0.1",
502 | "type-is": "^2.0.1",
503 | "vary": "^1.1.2"
504 | },
505 | "engines": {
506 | "node": ">= 18"
507 | },
508 | "funding": {
509 | "type": "opencollective",
510 | "url": "https://opencollective.com/express"
511 | }
512 | },
513 | "node_modules/express/node_modules/cookie-signature": {
514 | "version": "1.2.2",
515 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
516 | "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
517 | "license": "MIT",
518 | "engines": {
519 | "node": ">=6.6.0"
520 | }
521 | },
522 | "node_modules/express/node_modules/debug": {
523 | "version": "4.4.0",
524 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
525 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
526 | "license": "MIT",
527 | "dependencies": {
528 | "ms": "^2.1.3"
529 | },
530 | "engines": {
531 | "node": ">=6.0"
532 | },
533 | "peerDependenciesMeta": {
534 | "supports-color": {
535 | "optional": true
536 | }
537 | }
538 | },
539 | "node_modules/express/node_modules/mime-db": {
540 | "version": "1.54.0",
541 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
542 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
543 | "license": "MIT",
544 | "engines": {
545 | "node": ">= 0.6"
546 | }
547 | },
548 | "node_modules/express/node_modules/mime-types": {
549 | "version": "3.0.1",
550 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
551 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
552 | "license": "MIT",
553 | "dependencies": {
554 | "mime-db": "^1.54.0"
555 | },
556 | "engines": {
557 | "node": ">= 0.6"
558 | }
559 | },
560 | "node_modules/express/node_modules/ms": {
561 | "version": "2.1.3",
562 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
563 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
564 | "license": "MIT"
565 | },
566 | "node_modules/fill-range": {
567 | "version": "7.1.1",
568 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
569 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
570 | "dev": true,
571 | "dependencies": {
572 | "to-regex-range": "^5.0.1"
573 | },
574 | "engines": {
575 | "node": ">=8"
576 | }
577 | },
578 | "node_modules/finalhandler": {
579 | "version": "2.1.0",
580 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
581 | "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
582 | "license": "MIT",
583 | "dependencies": {
584 | "debug": "^4.4.0",
585 | "encodeurl": "^2.0.0",
586 | "escape-html": "^1.0.3",
587 | "on-finished": "^2.4.1",
588 | "parseurl": "^1.3.3",
589 | "statuses": "^2.0.1"
590 | },
591 | "engines": {
592 | "node": ">= 0.8"
593 | }
594 | },
595 | "node_modules/finalhandler/node_modules/debug": {
596 | "version": "4.4.0",
597 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
598 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
599 | "license": "MIT",
600 | "dependencies": {
601 | "ms": "^2.1.3"
602 | },
603 | "engines": {
604 | "node": ">=6.0"
605 | },
606 | "peerDependenciesMeta": {
607 | "supports-color": {
608 | "optional": true
609 | }
610 | }
611 | },
612 | "node_modules/finalhandler/node_modules/ms": {
613 | "version": "2.1.3",
614 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
615 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
616 | "license": "MIT"
617 | },
618 | "node_modules/follow-redirects": {
619 | "version": "1.15.6",
620 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
621 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
622 | "funding": [
623 | {
624 | "type": "individual",
625 | "url": "https://github.com/sponsors/RubenVerborgh"
626 | }
627 | ],
628 | "engines": {
629 | "node": ">=4.0"
630 | },
631 | "peerDependenciesMeta": {
632 | "debug": {
633 | "optional": true
634 | }
635 | }
636 | },
637 | "node_modules/form-data": {
638 | "version": "4.0.0",
639 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
640 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
641 | "dependencies": {
642 | "asynckit": "^0.4.0",
643 | "combined-stream": "^1.0.8",
644 | "mime-types": "^2.1.12"
645 | },
646 | "engines": {
647 | "node": ">= 6"
648 | }
649 | },
650 | "node_modules/forwarded": {
651 | "version": "0.2.0",
652 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
653 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
654 | "engines": {
655 | "node": ">= 0.6"
656 | }
657 | },
658 | "node_modules/fresh": {
659 | "version": "2.0.0",
660 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
661 | "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
662 | "license": "MIT",
663 | "engines": {
664 | "node": ">= 0.8"
665 | }
666 | },
667 | "node_modules/fsevents": {
668 | "version": "2.3.3",
669 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
670 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
671 | "dev": true,
672 | "hasInstallScript": true,
673 | "optional": true,
674 | "os": [
675 | "darwin"
676 | ],
677 | "engines": {
678 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
679 | }
680 | },
681 | "node_modules/function-bind": {
682 | "version": "1.1.2",
683 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
684 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
685 | "license": "MIT",
686 | "funding": {
687 | "url": "https://github.com/sponsors/ljharb"
688 | }
689 | },
690 | "node_modules/get-intrinsic": {
691 | "version": "1.3.0",
692 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
693 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
694 | "license": "MIT",
695 | "dependencies": {
696 | "call-bind-apply-helpers": "^1.0.2",
697 | "es-define-property": "^1.0.1",
698 | "es-errors": "^1.3.0",
699 | "es-object-atoms": "^1.1.1",
700 | "function-bind": "^1.1.2",
701 | "get-proto": "^1.0.1",
702 | "gopd": "^1.2.0",
703 | "has-symbols": "^1.1.0",
704 | "hasown": "^2.0.2",
705 | "math-intrinsics": "^1.1.0"
706 | },
707 | "engines": {
708 | "node": ">= 0.4"
709 | },
710 | "funding": {
711 | "url": "https://github.com/sponsors/ljharb"
712 | }
713 | },
714 | "node_modules/get-proto": {
715 | "version": "1.0.1",
716 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
717 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
718 | "license": "MIT",
719 | "dependencies": {
720 | "dunder-proto": "^1.0.1",
721 | "es-object-atoms": "^1.0.0"
722 | },
723 | "engines": {
724 | "node": ">= 0.4"
725 | }
726 | },
727 | "node_modules/glob-parent": {
728 | "version": "5.1.2",
729 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
730 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
731 | "dev": true,
732 | "dependencies": {
733 | "is-glob": "^4.0.1"
734 | },
735 | "engines": {
736 | "node": ">= 6"
737 | }
738 | },
739 | "node_modules/gopd": {
740 | "version": "1.2.0",
741 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
742 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
743 | "license": "MIT",
744 | "engines": {
745 | "node": ">= 0.4"
746 | },
747 | "funding": {
748 | "url": "https://github.com/sponsors/ljharb"
749 | }
750 | },
751 | "node_modules/has-flag": {
752 | "version": "3.0.0",
753 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
754 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
755 | "dev": true,
756 | "engines": {
757 | "node": ">=4"
758 | }
759 | },
760 | "node_modules/has-symbols": {
761 | "version": "1.1.0",
762 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
763 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
764 | "license": "MIT",
765 | "engines": {
766 | "node": ">= 0.4"
767 | },
768 | "funding": {
769 | "url": "https://github.com/sponsors/ljharb"
770 | }
771 | },
772 | "node_modules/hasown": {
773 | "version": "2.0.2",
774 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
775 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
776 | "license": "MIT",
777 | "dependencies": {
778 | "function-bind": "^1.1.2"
779 | },
780 | "engines": {
781 | "node": ">= 0.4"
782 | }
783 | },
784 | "node_modules/http-errors": {
785 | "version": "2.0.0",
786 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
787 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
788 | "license": "MIT",
789 | "dependencies": {
790 | "depd": "2.0.0",
791 | "inherits": "2.0.4",
792 | "setprototypeof": "1.2.0",
793 | "statuses": "2.0.1",
794 | "toidentifier": "1.0.1"
795 | },
796 | "engines": {
797 | "node": ">= 0.8"
798 | }
799 | },
800 | "node_modules/http-proxy": {
801 | "version": "1.18.1",
802 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
803 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
804 | "license": "MIT",
805 | "dependencies": {
806 | "eventemitter3": "^4.0.0",
807 | "follow-redirects": "^1.0.0",
808 | "requires-port": "^1.0.0"
809 | },
810 | "engines": {
811 | "node": ">=8.0.0"
812 | }
813 | },
814 | "node_modules/iconv-lite": {
815 | "version": "0.6.3",
816 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
817 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
818 | "license": "MIT",
819 | "dependencies": {
820 | "safer-buffer": ">= 2.1.2 < 3.0.0"
821 | },
822 | "engines": {
823 | "node": ">=0.10.0"
824 | }
825 | },
826 | "node_modules/ignore-by-default": {
827 | "version": "1.0.1",
828 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
829 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
830 | "dev": true
831 | },
832 | "node_modules/immediate": {
833 | "version": "3.0.6",
834 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
835 | "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
836 | "license": "MIT"
837 | },
838 | "node_modules/inherits": {
839 | "version": "2.0.4",
840 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
841 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
842 | },
843 | "node_modules/ipaddr.js": {
844 | "version": "1.9.1",
845 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
846 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
847 | "engines": {
848 | "node": ">= 0.10"
849 | }
850 | },
851 | "node_modules/is-binary-path": {
852 | "version": "2.1.0",
853 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
854 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
855 | "dev": true,
856 | "dependencies": {
857 | "binary-extensions": "^2.0.0"
858 | },
859 | "engines": {
860 | "node": ">=8"
861 | }
862 | },
863 | "node_modules/is-extglob": {
864 | "version": "2.1.1",
865 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
866 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
867 | "dev": true,
868 | "engines": {
869 | "node": ">=0.10.0"
870 | }
871 | },
872 | "node_modules/is-glob": {
873 | "version": "4.0.3",
874 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
875 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
876 | "dev": true,
877 | "dependencies": {
878 | "is-extglob": "^2.1.1"
879 | },
880 | "engines": {
881 | "node": ">=0.10.0"
882 | }
883 | },
884 | "node_modules/is-number": {
885 | "version": "7.0.0",
886 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
887 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
888 | "dev": true,
889 | "engines": {
890 | "node": ">=0.12.0"
891 | }
892 | },
893 | "node_modules/is-promise": {
894 | "version": "4.0.0",
895 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
896 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
897 | "license": "MIT"
898 | },
899 | "node_modules/isarray": {
900 | "version": "1.0.0",
901 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
902 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
903 | "license": "MIT"
904 | },
905 | "node_modules/jszip": {
906 | "version": "3.10.1",
907 | "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
908 | "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
909 | "license": "(MIT OR GPL-3.0-or-later)",
910 | "dependencies": {
911 | "lie": "~3.3.0",
912 | "pako": "~1.0.2",
913 | "readable-stream": "~2.3.6",
914 | "setimmediate": "^1.0.5"
915 | }
916 | },
917 | "node_modules/lie": {
918 | "version": "3.3.0",
919 | "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
920 | "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
921 | "license": "MIT",
922 | "dependencies": {
923 | "immediate": "~3.0.5"
924 | }
925 | },
926 | "node_modules/long-timeout": {
927 | "version": "0.1.1",
928 | "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
929 | "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==",
930 | "license": "MIT"
931 | },
932 | "node_modules/lru-cache": {
933 | "version": "6.0.0",
934 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
935 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
936 | "dev": true,
937 | "dependencies": {
938 | "yallist": "^4.0.0"
939 | },
940 | "engines": {
941 | "node": ">=10"
942 | }
943 | },
944 | "node_modules/luxon": {
945 | "version": "3.5.0",
946 | "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
947 | "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
948 | "license": "MIT",
949 | "engines": {
950 | "node": ">=12"
951 | }
952 | },
953 | "node_modules/math-intrinsics": {
954 | "version": "1.1.0",
955 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
956 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
957 | "license": "MIT",
958 | "engines": {
959 | "node": ">= 0.4"
960 | }
961 | },
962 | "node_modules/media-typer": {
963 | "version": "1.1.0",
964 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
965 | "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
966 | "license": "MIT",
967 | "engines": {
968 | "node": ">= 0.8"
969 | }
970 | },
971 | "node_modules/merge-descriptors": {
972 | "version": "2.0.0",
973 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
974 | "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
975 | "license": "MIT",
976 | "engines": {
977 | "node": ">=18"
978 | },
979 | "funding": {
980 | "url": "https://github.com/sponsors/sindresorhus"
981 | }
982 | },
983 | "node_modules/mime-db": {
984 | "version": "1.52.0",
985 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
986 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
987 | "engines": {
988 | "node": ">= 0.6"
989 | }
990 | },
991 | "node_modules/mime-types": {
992 | "version": "2.1.35",
993 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
994 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
995 | "dependencies": {
996 | "mime-db": "1.52.0"
997 | },
998 | "engines": {
999 | "node": ">= 0.6"
1000 | }
1001 | },
1002 | "node_modules/minimatch": {
1003 | "version": "3.1.2",
1004 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1005 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1006 | "dev": true,
1007 | "dependencies": {
1008 | "brace-expansion": "^1.1.7"
1009 | },
1010 | "engines": {
1011 | "node": "*"
1012 | }
1013 | },
1014 | "node_modules/morgan": {
1015 | "version": "1.10.0",
1016 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
1017 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
1018 | "dependencies": {
1019 | "basic-auth": "~2.0.1",
1020 | "debug": "2.6.9",
1021 | "depd": "~2.0.0",
1022 | "on-finished": "~2.3.0",
1023 | "on-headers": "~1.0.2"
1024 | },
1025 | "engines": {
1026 | "node": ">= 0.8.0"
1027 | }
1028 | },
1029 | "node_modules/morgan/node_modules/on-finished": {
1030 | "version": "2.3.0",
1031 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1032 | "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
1033 | "dependencies": {
1034 | "ee-first": "1.1.1"
1035 | },
1036 | "engines": {
1037 | "node": ">= 0.8"
1038 | }
1039 | },
1040 | "node_modules/ms": {
1041 | "version": "2.0.0",
1042 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1043 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1044 | },
1045 | "node_modules/negotiator": {
1046 | "version": "1.0.0",
1047 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
1048 | "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
1049 | "license": "MIT",
1050 | "engines": {
1051 | "node": ">= 0.6"
1052 | }
1053 | },
1054 | "node_modules/node-schedule": {
1055 | "version": "2.1.1",
1056 | "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz",
1057 | "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==",
1058 | "license": "MIT",
1059 | "dependencies": {
1060 | "cron-parser": "^4.2.0",
1061 | "long-timeout": "0.1.1",
1062 | "sorted-array-functions": "^1.3.0"
1063 | },
1064 | "engines": {
1065 | "node": ">=6"
1066 | }
1067 | },
1068 | "node_modules/nodemon": {
1069 | "version": "3.1.10",
1070 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
1071 | "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
1072 | "dev": true,
1073 | "license": "MIT",
1074 | "dependencies": {
1075 | "chokidar": "^3.5.2",
1076 | "debug": "^4",
1077 | "ignore-by-default": "^1.0.1",
1078 | "minimatch": "^3.1.2",
1079 | "pstree.remy": "^1.1.8",
1080 | "semver": "^7.5.3",
1081 | "simple-update-notifier": "^2.0.0",
1082 | "supports-color": "^5.5.0",
1083 | "touch": "^3.1.0",
1084 | "undefsafe": "^2.0.5"
1085 | },
1086 | "bin": {
1087 | "nodemon": "bin/nodemon.js"
1088 | },
1089 | "engines": {
1090 | "node": ">=10"
1091 | },
1092 | "funding": {
1093 | "type": "opencollective",
1094 | "url": "https://opencollective.com/nodemon"
1095 | }
1096 | },
1097 | "node_modules/nodemon/node_modules/debug": {
1098 | "version": "4.3.4",
1099 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1100 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1101 | "dev": true,
1102 | "dependencies": {
1103 | "ms": "2.1.2"
1104 | },
1105 | "engines": {
1106 | "node": ">=6.0"
1107 | },
1108 | "peerDependenciesMeta": {
1109 | "supports-color": {
1110 | "optional": true
1111 | }
1112 | }
1113 | },
1114 | "node_modules/nodemon/node_modules/ms": {
1115 | "version": "2.1.2",
1116 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1117 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1118 | "dev": true
1119 | },
1120 | "node_modules/nopt": {
1121 | "version": "1.0.10",
1122 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1123 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
1124 | "dev": true,
1125 | "dependencies": {
1126 | "abbrev": "1"
1127 | },
1128 | "bin": {
1129 | "nopt": "bin/nopt.js"
1130 | },
1131 | "engines": {
1132 | "node": "*"
1133 | }
1134 | },
1135 | "node_modules/normalize-path": {
1136 | "version": "3.0.0",
1137 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1138 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1139 | "dev": true,
1140 | "engines": {
1141 | "node": ">=0.10.0"
1142 | }
1143 | },
1144 | "node_modules/object-assign": {
1145 | "version": "4.1.1",
1146 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1147 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1148 | "engines": {
1149 | "node": ">=0.10.0"
1150 | }
1151 | },
1152 | "node_modules/object-inspect": {
1153 | "version": "1.13.4",
1154 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
1155 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
1156 | "license": "MIT",
1157 | "engines": {
1158 | "node": ">= 0.4"
1159 | },
1160 | "funding": {
1161 | "url": "https://github.com/sponsors/ljharb"
1162 | }
1163 | },
1164 | "node_modules/on-finished": {
1165 | "version": "2.4.1",
1166 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1167 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1168 | "license": "MIT",
1169 | "dependencies": {
1170 | "ee-first": "1.1.1"
1171 | },
1172 | "engines": {
1173 | "node": ">= 0.8"
1174 | }
1175 | },
1176 | "node_modules/on-headers": {
1177 | "version": "1.0.2",
1178 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
1179 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
1180 | "engines": {
1181 | "node": ">= 0.8"
1182 | }
1183 | },
1184 | "node_modules/once": {
1185 | "version": "1.4.0",
1186 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1187 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1188 | "license": "ISC",
1189 | "dependencies": {
1190 | "wrappy": "1"
1191 | }
1192 | },
1193 | "node_modules/pako": {
1194 | "version": "1.0.11",
1195 | "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
1196 | "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
1197 | "license": "(MIT AND Zlib)"
1198 | },
1199 | "node_modules/parseurl": {
1200 | "version": "1.3.3",
1201 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1202 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1203 | "license": "MIT",
1204 | "engines": {
1205 | "node": ">= 0.8"
1206 | }
1207 | },
1208 | "node_modules/path-to-regexp": {
1209 | "version": "8.2.0",
1210 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
1211 | "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
1212 | "license": "MIT",
1213 | "engines": {
1214 | "node": ">=16"
1215 | }
1216 | },
1217 | "node_modules/picomatch": {
1218 | "version": "2.3.1",
1219 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1220 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1221 | "dev": true,
1222 | "engines": {
1223 | "node": ">=8.6"
1224 | },
1225 | "funding": {
1226 | "url": "https://github.com/sponsors/jonschlinkert"
1227 | }
1228 | },
1229 | "node_modules/process-nextick-args": {
1230 | "version": "2.0.1",
1231 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
1232 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
1233 | "license": "MIT"
1234 | },
1235 | "node_modules/proxy-addr": {
1236 | "version": "2.0.7",
1237 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1238 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1239 | "dependencies": {
1240 | "forwarded": "0.2.0",
1241 | "ipaddr.js": "1.9.1"
1242 | },
1243 | "engines": {
1244 | "node": ">= 0.10"
1245 | }
1246 | },
1247 | "node_modules/proxy-from-env": {
1248 | "version": "1.1.0",
1249 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
1250 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
1251 | },
1252 | "node_modules/pstree.remy": {
1253 | "version": "1.1.8",
1254 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1255 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1256 | "dev": true
1257 | },
1258 | "node_modules/qs": {
1259 | "version": "6.14.0",
1260 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
1261 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
1262 | "license": "BSD-3-Clause",
1263 | "dependencies": {
1264 | "side-channel": "^1.1.0"
1265 | },
1266 | "engines": {
1267 | "node": ">=0.6"
1268 | },
1269 | "funding": {
1270 | "url": "https://github.com/sponsors/ljharb"
1271 | }
1272 | },
1273 | "node_modules/range-parser": {
1274 | "version": "1.2.1",
1275 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1276 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1277 | "license": "MIT",
1278 | "engines": {
1279 | "node": ">= 0.6"
1280 | }
1281 | },
1282 | "node_modules/raw-body": {
1283 | "version": "3.0.0",
1284 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
1285 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
1286 | "license": "MIT",
1287 | "dependencies": {
1288 | "bytes": "3.1.2",
1289 | "http-errors": "2.0.0",
1290 | "iconv-lite": "0.6.3",
1291 | "unpipe": "1.0.0"
1292 | },
1293 | "engines": {
1294 | "node": ">= 0.8"
1295 | }
1296 | },
1297 | "node_modules/readable-stream": {
1298 | "version": "2.3.8",
1299 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
1300 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
1301 | "license": "MIT",
1302 | "dependencies": {
1303 | "core-util-is": "~1.0.0",
1304 | "inherits": "~2.0.3",
1305 | "isarray": "~1.0.0",
1306 | "process-nextick-args": "~2.0.0",
1307 | "safe-buffer": "~5.1.1",
1308 | "string_decoder": "~1.1.1",
1309 | "util-deprecate": "~1.0.1"
1310 | }
1311 | },
1312 | "node_modules/readable-stream/node_modules/safe-buffer": {
1313 | "version": "5.1.2",
1314 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1315 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
1316 | "license": "MIT"
1317 | },
1318 | "node_modules/readdirp": {
1319 | "version": "3.6.0",
1320 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1321 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1322 | "dev": true,
1323 | "dependencies": {
1324 | "picomatch": "^2.2.1"
1325 | },
1326 | "engines": {
1327 | "node": ">=8.10.0"
1328 | }
1329 | },
1330 | "node_modules/requires-port": {
1331 | "version": "1.0.0",
1332 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
1333 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
1334 | "license": "MIT"
1335 | },
1336 | "node_modules/router": {
1337 | "version": "2.2.0",
1338 | "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
1339 | "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
1340 | "license": "MIT",
1341 | "dependencies": {
1342 | "debug": "^4.4.0",
1343 | "depd": "^2.0.0",
1344 | "is-promise": "^4.0.0",
1345 | "parseurl": "^1.3.3",
1346 | "path-to-regexp": "^8.0.0"
1347 | },
1348 | "engines": {
1349 | "node": ">= 18"
1350 | }
1351 | },
1352 | "node_modules/router/node_modules/debug": {
1353 | "version": "4.4.0",
1354 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
1355 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
1356 | "license": "MIT",
1357 | "dependencies": {
1358 | "ms": "^2.1.3"
1359 | },
1360 | "engines": {
1361 | "node": ">=6.0"
1362 | },
1363 | "peerDependenciesMeta": {
1364 | "supports-color": {
1365 | "optional": true
1366 | }
1367 | }
1368 | },
1369 | "node_modules/router/node_modules/ms": {
1370 | "version": "2.1.3",
1371 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1372 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1373 | "license": "MIT"
1374 | },
1375 | "node_modules/safe-buffer": {
1376 | "version": "5.2.1",
1377 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1378 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1379 | "funding": [
1380 | {
1381 | "type": "github",
1382 | "url": "https://github.com/sponsors/feross"
1383 | },
1384 | {
1385 | "type": "patreon",
1386 | "url": "https://www.patreon.com/feross"
1387 | },
1388 | {
1389 | "type": "consulting",
1390 | "url": "https://feross.org/support"
1391 | }
1392 | ],
1393 | "license": "MIT"
1394 | },
1395 | "node_modules/safer-buffer": {
1396 | "version": "2.1.2",
1397 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1398 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1399 | "license": "MIT"
1400 | },
1401 | "node_modules/semver": {
1402 | "version": "7.6.0",
1403 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
1404 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
1405 | "dev": true,
1406 | "dependencies": {
1407 | "lru-cache": "^6.0.0"
1408 | },
1409 | "bin": {
1410 | "semver": "bin/semver.js"
1411 | },
1412 | "engines": {
1413 | "node": ">=10"
1414 | }
1415 | },
1416 | "node_modules/send": {
1417 | "version": "1.2.0",
1418 | "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
1419 | "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
1420 | "license": "MIT",
1421 | "dependencies": {
1422 | "debug": "^4.3.5",
1423 | "encodeurl": "^2.0.0",
1424 | "escape-html": "^1.0.3",
1425 | "etag": "^1.8.1",
1426 | "fresh": "^2.0.0",
1427 | "http-errors": "^2.0.0",
1428 | "mime-types": "^3.0.1",
1429 | "ms": "^2.1.3",
1430 | "on-finished": "^2.4.1",
1431 | "range-parser": "^1.2.1",
1432 | "statuses": "^2.0.1"
1433 | },
1434 | "engines": {
1435 | "node": ">= 18"
1436 | }
1437 | },
1438 | "node_modules/send/node_modules/debug": {
1439 | "version": "4.4.0",
1440 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
1441 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
1442 | "license": "MIT",
1443 | "dependencies": {
1444 | "ms": "^2.1.3"
1445 | },
1446 | "engines": {
1447 | "node": ">=6.0"
1448 | },
1449 | "peerDependenciesMeta": {
1450 | "supports-color": {
1451 | "optional": true
1452 | }
1453 | }
1454 | },
1455 | "node_modules/send/node_modules/mime-db": {
1456 | "version": "1.54.0",
1457 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
1458 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
1459 | "license": "MIT",
1460 | "engines": {
1461 | "node": ">= 0.6"
1462 | }
1463 | },
1464 | "node_modules/send/node_modules/mime-types": {
1465 | "version": "3.0.1",
1466 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
1467 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
1468 | "license": "MIT",
1469 | "dependencies": {
1470 | "mime-db": "^1.54.0"
1471 | },
1472 | "engines": {
1473 | "node": ">= 0.6"
1474 | }
1475 | },
1476 | "node_modules/send/node_modules/ms": {
1477 | "version": "2.1.3",
1478 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1479 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1480 | "license": "MIT"
1481 | },
1482 | "node_modules/serve-static": {
1483 | "version": "2.2.0",
1484 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
1485 | "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
1486 | "license": "MIT",
1487 | "dependencies": {
1488 | "encodeurl": "^2.0.0",
1489 | "escape-html": "^1.0.3",
1490 | "parseurl": "^1.3.3",
1491 | "send": "^1.2.0"
1492 | },
1493 | "engines": {
1494 | "node": ">= 18"
1495 | }
1496 | },
1497 | "node_modules/setimmediate": {
1498 | "version": "1.0.5",
1499 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
1500 | "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
1501 | "license": "MIT"
1502 | },
1503 | "node_modules/setprototypeof": {
1504 | "version": "1.2.0",
1505 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1506 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1507 | "license": "ISC"
1508 | },
1509 | "node_modules/side-channel": {
1510 | "version": "1.1.0",
1511 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1512 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1513 | "license": "MIT",
1514 | "dependencies": {
1515 | "es-errors": "^1.3.0",
1516 | "object-inspect": "^1.13.3",
1517 | "side-channel-list": "^1.0.0",
1518 | "side-channel-map": "^1.0.1",
1519 | "side-channel-weakmap": "^1.0.2"
1520 | },
1521 | "engines": {
1522 | "node": ">= 0.4"
1523 | },
1524 | "funding": {
1525 | "url": "https://github.com/sponsors/ljharb"
1526 | }
1527 | },
1528 | "node_modules/side-channel-list": {
1529 | "version": "1.0.0",
1530 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1531 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1532 | "license": "MIT",
1533 | "dependencies": {
1534 | "es-errors": "^1.3.0",
1535 | "object-inspect": "^1.13.3"
1536 | },
1537 | "engines": {
1538 | "node": ">= 0.4"
1539 | },
1540 | "funding": {
1541 | "url": "https://github.com/sponsors/ljharb"
1542 | }
1543 | },
1544 | "node_modules/side-channel-map": {
1545 | "version": "1.0.1",
1546 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1547 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1548 | "license": "MIT",
1549 | "dependencies": {
1550 | "call-bound": "^1.0.2",
1551 | "es-errors": "^1.3.0",
1552 | "get-intrinsic": "^1.2.5",
1553 | "object-inspect": "^1.13.3"
1554 | },
1555 | "engines": {
1556 | "node": ">= 0.4"
1557 | },
1558 | "funding": {
1559 | "url": "https://github.com/sponsors/ljharb"
1560 | }
1561 | },
1562 | "node_modules/side-channel-weakmap": {
1563 | "version": "1.0.2",
1564 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1565 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1566 | "license": "MIT",
1567 | "dependencies": {
1568 | "call-bound": "^1.0.2",
1569 | "es-errors": "^1.3.0",
1570 | "get-intrinsic": "^1.2.5",
1571 | "object-inspect": "^1.13.3",
1572 | "side-channel-map": "^1.0.1"
1573 | },
1574 | "engines": {
1575 | "node": ">= 0.4"
1576 | },
1577 | "funding": {
1578 | "url": "https://github.com/sponsors/ljharb"
1579 | }
1580 | },
1581 | "node_modules/simple-update-notifier": {
1582 | "version": "2.0.0",
1583 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1584 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1585 | "dev": true,
1586 | "dependencies": {
1587 | "semver": "^7.5.3"
1588 | },
1589 | "engines": {
1590 | "node": ">=10"
1591 | }
1592 | },
1593 | "node_modules/sorted-array-functions": {
1594 | "version": "1.3.0",
1595 | "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
1596 | "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==",
1597 | "license": "MIT"
1598 | },
1599 | "node_modules/statuses": {
1600 | "version": "2.0.1",
1601 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1602 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1603 | "license": "MIT",
1604 | "engines": {
1605 | "node": ">= 0.8"
1606 | }
1607 | },
1608 | "node_modules/string_decoder": {
1609 | "version": "1.1.1",
1610 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
1611 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
1612 | "license": "MIT",
1613 | "dependencies": {
1614 | "safe-buffer": "~5.1.0"
1615 | }
1616 | },
1617 | "node_modules/string_decoder/node_modules/safe-buffer": {
1618 | "version": "5.1.2",
1619 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1620 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
1621 | "license": "MIT"
1622 | },
1623 | "node_modules/supports-color": {
1624 | "version": "5.5.0",
1625 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1626 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1627 | "dev": true,
1628 | "dependencies": {
1629 | "has-flag": "^3.0.0"
1630 | },
1631 | "engines": {
1632 | "node": ">=4"
1633 | }
1634 | },
1635 | "node_modules/to-regex-range": {
1636 | "version": "5.0.1",
1637 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1638 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1639 | "dev": true,
1640 | "dependencies": {
1641 | "is-number": "^7.0.0"
1642 | },
1643 | "engines": {
1644 | "node": ">=8.0"
1645 | }
1646 | },
1647 | "node_modules/toidentifier": {
1648 | "version": "1.0.1",
1649 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1650 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1651 | "license": "MIT",
1652 | "engines": {
1653 | "node": ">=0.6"
1654 | }
1655 | },
1656 | "node_modules/touch": {
1657 | "version": "3.1.0",
1658 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
1659 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
1660 | "dev": true,
1661 | "dependencies": {
1662 | "nopt": "~1.0.10"
1663 | },
1664 | "bin": {
1665 | "nodetouch": "bin/nodetouch.js"
1666 | }
1667 | },
1668 | "node_modules/type-is": {
1669 | "version": "2.0.1",
1670 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
1671 | "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
1672 | "license": "MIT",
1673 | "dependencies": {
1674 | "content-type": "^1.0.5",
1675 | "media-typer": "^1.1.0",
1676 | "mime-types": "^3.0.0"
1677 | },
1678 | "engines": {
1679 | "node": ">= 0.6"
1680 | }
1681 | },
1682 | "node_modules/type-is/node_modules/mime-db": {
1683 | "version": "1.54.0",
1684 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
1685 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
1686 | "license": "MIT",
1687 | "engines": {
1688 | "node": ">= 0.6"
1689 | }
1690 | },
1691 | "node_modules/type-is/node_modules/mime-types": {
1692 | "version": "3.0.1",
1693 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
1694 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
1695 | "license": "MIT",
1696 | "dependencies": {
1697 | "mime-db": "^1.54.0"
1698 | },
1699 | "engines": {
1700 | "node": ">= 0.6"
1701 | }
1702 | },
1703 | "node_modules/undefsafe": {
1704 | "version": "2.0.5",
1705 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1706 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1707 | "dev": true
1708 | },
1709 | "node_modules/unpipe": {
1710 | "version": "1.0.0",
1711 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1712 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1713 | "license": "MIT",
1714 | "engines": {
1715 | "node": ">= 0.8"
1716 | }
1717 | },
1718 | "node_modules/util-deprecate": {
1719 | "version": "1.0.2",
1720 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1721 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
1722 | "license": "MIT"
1723 | },
1724 | "node_modules/vary": {
1725 | "version": "1.1.2",
1726 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1727 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1728 | "engines": {
1729 | "node": ">= 0.8"
1730 | }
1731 | },
1732 | "node_modules/wrappy": {
1733 | "version": "1.0.2",
1734 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1735 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
1736 | "license": "ISC"
1737 | },
1738 | "node_modules/yallist": {
1739 | "version": "4.0.0",
1740 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1741 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
1742 | "dev": true
1743 | }
1744 | }
1745 | }
1746 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuetorrent-backend",
3 | "version": "2.5.1",
4 | "description": "Node.JS backend for VueTorrent",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "node src/index.js",
8 | "dev": "nodemon -w .env -w src/ src/index.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+ssh://git@github.com/VueTorrent/vuetorrent-backend.git"
14 | },
15 | "author": "Larsluph",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/VueTorrent/vuetorrent-backend/issues"
19 | },
20 | "homepage": "https://github.com/VueTorrent/vuetorrent-backend#readme",
21 | "type": "module",
22 | "dependencies": {
23 | "axios": "^1.10.0",
24 | "cookie-parser": "^1.4.7",
25 | "cors": "^2.8.5",
26 | "dotenv": "^17.0.0",
27 | "express": "^5.1.0",
28 | "http-proxy": "^1.18.1",
29 | "jszip": "^3.10.1",
30 | "morgan": "^1.10.0",
31 | "node-schedule": "^2.1.1"
32 | },
33 | "devDependencies": {
34 | "nodemon": "^3.1.10"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import cookieParser from 'cookie-parser'
2 | import cors from 'cors'
3 | import { config } from 'dotenv'
4 | import express, { Router } from 'express'
5 | import https from 'https'
6 | import morgan from 'morgan'
7 | import scheduler from 'node-schedule'
8 | import authMiddleware from './middlewares/auth.js'
9 | import configRouter from './routers/config/index.js'
10 | import qbitRouter from './routers/qbit/index.js'
11 | import { checkForUpdate } from './routines/update.js'
12 | import { getSSLOptionsFromEnv } from './ssl.js'
13 | import { assert_env } from './utils.js'
14 |
15 | config()
16 | assert_env()
17 |
18 | const app = express()
19 | const router = Router()
20 |
21 | const PORT = process.env.PORT || 3000
22 |
23 | // Middlewares
24 | app.use(morgan('tiny'))
25 | app.use(cors({ origin: true, credentials: true }))
26 | app.use(express.json())
27 | app.use(cookieParser())
28 |
29 | // Routers
30 | app.use('/api', qbitRouter)
31 | router.use('/config', authMiddleware, configRouter)
32 |
33 | // Routes
34 | router.get('/ping', async (req, res) => {
35 | res.send('pong')
36 | })
37 | router.get('/update', authMiddleware, async (req, res) => {
38 | try {
39 | res.send(await checkForUpdate())
40 | } catch (err) {
41 | console.error(err)
42 | res.status(500).send('Failed to update')
43 | }
44 | })
45 |
46 | app.use('/backend', router)
47 |
48 | // WebUI
49 | app.use(express.static(`${process.env.VUETORRENT_PATH || '/vuetorrent'}/vuetorrent/public`))
50 | app.use(async (req, res) => {
51 | res.status(404).send('404 Not Found')
52 | })
53 |
54 | function launchServer(serverInstance, protocol) {
55 | serverInstance.listen(PORT, async () => {
56 | console.log('Checking for updates...')
57 | console.log(await checkForUpdate())
58 | console.log(`${protocol} server is running on port ${PORT}`)
59 | const update_cron = process.env.UPDATE_VT_CRON
60 | if (update_cron && update_cron.length) {
61 | scheduler.scheduleJob(
62 | 'Update VueTorrent',
63 | update_cron,
64 | async () => {
65 | return await checkForUpdate()
66 | }
67 | );
68 | }
69 | })
70 | }
71 |
72 | const sslOptions = getSSLOptionsFromEnv()
73 | let server
74 | if (sslOptions) {
75 | const httpsServer = https.createServer(sslOptions, app)
76 | server = httpsServer
77 | launchServer(httpsServer, 'HTTPS')
78 | } else {
79 | server = app
80 | launchServer(app, 'HTTP')
81 | }
82 |
83 | async function stopServer(signal) {
84 | console.log(`Received ${ signal } signal. Gracefully shutting down...`)
85 | server.close()
86 | await scheduler.gracefulShutdown()
87 | }
88 |
89 | process.on('SIGTERM', stopServer)
90 | process.on('SIGINT', stopServer)
91 |
--------------------------------------------------------------------------------
/src/middlewares/auth.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | export default async function authMiddleware(req, res, next) {
4 | if (req.path === '/ping') {
5 | return next()
6 | }
7 | const { cookies } = req
8 | const { SID } = cookies
9 |
10 | if (await isValidSID(SID)) {
11 | next()
12 | } else {
13 | res.status(401).json({ error: 'Unauthorized' })
14 | }
15 | }
16 |
17 | async function isValidSID(SID) {
18 | const headers = {}
19 | if (SID) {
20 | headers['Cookie'] = `SID=${ SID }`
21 | }
22 |
23 | try {
24 | const r = await axios.get(`${ process.env.QBIT_BASE }/api/v2/app/version`, { headers })
25 | return r.status === 200
26 | } catch (error) {
27 | console.error('Error occurred while fetching version:')
28 | if (error.name === 'AggregateError') {
29 | console.error(error.errors.map(err => err.message).join('\n'))
30 | } else if (error.response) {
31 | console.error(`[${error.response.status}] ${error.response.data}`)
32 | } else {
33 | console.error(error.name, error.message)
34 | }
35 |
36 | return false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/routers/config/fs.js:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs'
2 | import path from 'path'
3 |
4 | const queue = []
5 |
6 | function getDataFilePath() {
7 | return path.join(process.env.CONFIG_PATH || '/config', 'data.json')
8 | }
9 |
10 | async function processQueue() {
11 | if (queue.length === 0) return
12 |
13 | const { data, resolve, reject } = queue[0]
14 |
15 | try {
16 | await fs.writeFile(getDataFilePath(), JSON.stringify(data, null, 2))
17 | resolve()
18 | } catch (err) {
19 | reject(err)
20 | } finally {
21 | queue.shift()
22 | await processQueue()
23 | }
24 | }
25 |
26 | export async function getData() {
27 | const canAccess = await fs.access(getDataFilePath(), fs.constants.F_OK)
28 | .then(() => true, () => false)
29 |
30 | if (!canAccess) {
31 | // File doesn't exists
32 | return {}
33 | }
34 |
35 | const canEdit = await fs.access(getDataFilePath(), fs.constants.R_OK | fs.constants.W_OK)
36 | .then(() => true, () => false)
37 |
38 | if (!canEdit) {
39 | // File not readable / writable
40 | throw new Error('Data file isn\'t readable / writable')
41 | }
42 |
43 | return JSON.parse(await fs.readFile(getDataFilePath(), 'utf8'))
44 | }
45 |
46 | export async function setData(data) {
47 | return new Promise((resolve, reject) => {
48 | queue.push({ data, resolve, reject })
49 | if (queue.length === 1) {
50 | processQueue()
51 | }
52 | })
53 | }
--------------------------------------------------------------------------------
/src/routers/config/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from 'express'
2 | import { getData, setData } from './fs.js'
3 |
4 | const router = Router()
5 |
6 | router.get('/', async (req, res) => {
7 | try {
8 | const config = await getData()
9 | res.json(config)
10 | } catch (err) {
11 | res.status(500).json({ error: err.message })
12 | }
13 | })
14 |
15 | router.get('/:key', async (req, res) => {
16 | const { key } = req.params
17 | try {
18 | const config = await getData()
19 | if (Object.hasOwn(config, key)) {
20 | res.json({ [key]: config[key] })
21 | } else {
22 | res.status(404).json({ error: 'Key not found' })
23 | }
24 | } catch (err) {
25 | res.status(500).json({ error: err.message })
26 | }
27 | })
28 |
29 | router.put('/:key', async (req, res) => {
30 | const { key } = req.params
31 | const { value } = req.body
32 | try {
33 | const config = await getData()
34 | config[key] = value
35 | await setData(config)
36 | res.json({ message: 'Config updated successfully' })
37 | } catch (err) {
38 | res.status(500).json({ error: err.message })
39 | }
40 | })
41 |
42 | router.delete('/:key', async (req, res) => {
43 | const { key } = req.params
44 | try {
45 | const config = await getData()
46 | if (Object.hasOwn(config, key)) {
47 | delete config[key]
48 | await setData(config)
49 | res.json({ message: 'Config deleted successfully' })
50 | } else {
51 | res.status(404).json({ error: 'Key not found' })
52 | }
53 | } catch (err) {
54 | res.status(500).json({ error: err.message })
55 | }
56 | })
57 |
58 | export default router
59 |
--------------------------------------------------------------------------------
/src/routers/qbit/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from 'express'
2 | import httpProxy from 'http-proxy'
3 |
4 | const router = Router()
5 |
6 | router.use((req, res, next) => {
7 | const proxy = httpProxy.createProxyServer({
8 | host: process.env.QBIT_BASE,
9 | xfwd: process.env.SKIP_X_FORWARD_HEADERS !== 'true',
10 | secure: process.env.USE_INSECURE_SSL !== 'true',
11 | })
12 | proxy.web(req, res, { target: `${process.env.QBIT_BASE}/api` }, next)
13 | })
14 |
15 | export default router
16 |
--------------------------------------------------------------------------------
/src/routines/update.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { config } from 'dotenv'
3 | import fs from 'fs'
4 | import JSZip from 'jszip'
5 | import path from 'path'
6 |
7 | config()
8 |
9 |
10 | const BASE_URL_VERSION = '/repos/VueTorrent/VueTorrent/contents/version.txt?ref='
11 | const BASE_URL_ZIPBALL = '/repos/VueTorrent/VueTorrent/zipball/'
12 | const BASE_URL_RELEASE = 'https://github.com/VueTorrent/VueTorrent/releases/download/$TAG/vuetorrent.zip'
13 | const STABLE_BRANCH_NAME = 'latest-release'
14 | const DEV_BRANCH_NAME = 'nightly-release'
15 | const VERSION_PATTERN = /^v?(?[0-9.]+)(-(?\d+)-g(?[0-9a-f]+))?$/
16 |
17 | const BASE_FS_PATH = process.env.VUETORRENT_PATH || '/vuetorrent'
18 | const TEMP_ZIP_PATH = `${ BASE_FS_PATH }/vuetorrent.zip`
19 | const WEBUI_PATH = `${ BASE_FS_PATH }/vuetorrent`
20 | const VERSION_FILE_PATH = `${ WEBUI_PATH }/version.txt`
21 | const WEBUI_OLD_PATH = `${ BASE_FS_PATH }/vuetorrent-old`
22 |
23 | const githubClient = axios.create({
24 | baseURL: 'https://api.github.com',
25 | headers: {
26 | Accept: 'application/vnd.github.v3+json',
27 | Authorization: process.env.GITHUB_AUTH ? `Bearer ${process.env.GITHUB_AUTH}` : undefined,
28 | 'User-Agent': `vuetorrent/vuetorrent-backend`
29 | }
30 | })
31 |
32 | /** @typedef {{ version: string; commits?: string; sha?: string }} VersionType */
33 |
34 | /**
35 | * @param version {string}
36 | * @returns {VersionType | undefined}
37 | */
38 | function extractVersion(version) {
39 | const match = version.match(VERSION_PATTERN)
40 | return match?.groups
41 | }
42 |
43 | function formatVersion(version) {
44 | if (!version) return 'unknown'
45 | if (!version.commits || !version.sha) return `v${ version.version }`
46 | return `v${ version.version }-${ version.commits }-g${ version.sha }`
47 | }
48 |
49 | function getInstalledVersion() {
50 | if (!fs.existsSync(VERSION_FILE_PATH)) return null
51 |
52 | try {
53 | return extractVersion(fs.readFileSync(VERSION_FILE_PATH).toString())
54 | } catch (err) {
55 | console.error(err)
56 | return null
57 | }
58 | }
59 |
60 | async function getLatestVersion(ref) {
61 | /** @type {AxiosResponse>} */
62 | const response = await githubClient.get(BASE_URL_VERSION + ref)
63 |
64 | // Extract from base64
65 | return extractVersion(atob(response.data.content.trim()))
66 | }
67 |
68 | async function downloadFile(url, outputPath) {
69 | const writer = fs.createWriteStream(outputPath)
70 | const response = await githubClient({
71 | url,
72 | method: 'GET',
73 | responseType: 'stream'
74 | })
75 |
76 | response.data.pipe(writer)
77 |
78 | return new Promise((resolve, reject) => {
79 | writer.on('finish', resolve)
80 | writer.on('error', reject)
81 | })
82 | }
83 |
84 | async function unzipFile(zipPath, extractTo) {
85 | const data = fs.readFileSync(zipPath)
86 | const zip = await JSZip.loadAsync(data)
87 | await Promise.all(Object.keys(zip.files).map(async (filename) => {
88 | const file = zip.files[filename]
89 | const newName = filename.slice(filename.indexOf('/'))
90 | const filePath = path.join(extractTo, newName)
91 | if (file.dir) {
92 | fs.mkdirSync(filePath, { recursive: true })
93 | } else {
94 | const content = await file.async('nodebuffer')
95 | fs.writeFileSync(filePath, content)
96 | }
97 | }))
98 | }
99 |
100 | async function downloadUpdate(url) {
101 | if (!fs.existsSync(BASE_FS_PATH)) {
102 | fs.mkdirSync(BASE_FS_PATH, { recursive: true })
103 | }
104 |
105 | // Download zip file
106 | await downloadFile(url, TEMP_ZIP_PATH)
107 |
108 | // Backup current install if it exists
109 | if (fs.existsSync(WEBUI_OLD_PATH)) {
110 | fs.rmSync(WEBUI_OLD_PATH, { recursive: true })
111 | }
112 | if (fs.existsSync(WEBUI_PATH)) {
113 | fs.renameSync(WEBUI_PATH, WEBUI_OLD_PATH)
114 | }
115 |
116 | // Unzip the downloaded file
117 | try {
118 | await unzipFile(TEMP_ZIP_PATH, WEBUI_PATH)
119 | } catch (err) {
120 | console.error(err)
121 | // Restore backup if unzip fails
122 | if (fs.existsSync(WEBUI_OLD_PATH)) {
123 | fs.renameSync(WEBUI_OLD_PATH, WEBUI_PATH)
124 | }
125 | }
126 | }
127 |
128 | export async function checkForUpdate() {
129 | let branchName
130 | switch (process.env.RELEASE_TYPE) {
131 | case undefined:
132 | case 'stable':
133 | case 'latest':
134 | branchName = STABLE_BRANCH_NAME
135 | break
136 | case 'dev':
137 | case 'nightly':
138 | branchName = DEV_BRANCH_NAME
139 | break
140 | }
141 |
142 | const installedVersion = getInstalledVersion()
143 |
144 | /** @type {VersionType | undefined} */
145 | let latestVersion
146 | if (!branchName) {
147 | // If unknown release type is specified, we try to match a specific version
148 | latestVersion = extractVersion(process.env.RELEASE_TYPE)
149 | } else {
150 | latestVersion = await getLatestVersion(branchName)
151 | }
152 |
153 | if (!latestVersion) {
154 | return `Unable to find candidate for release type "${ process.env.RELEASE_TYPE }"`
155 | }
156 |
157 | if (installedVersion?.version !== latestVersion?.version
158 | || installedVersion?.commits !== latestVersion?.commits
159 | || installedVersion?.sha !== latestVersion?.sha) {
160 | if (!branchName) {
161 | await downloadUpdate(BASE_URL_RELEASE.replace('$TAG', `v${latestVersion.version}`))
162 | } else {
163 | await downloadUpdate(BASE_URL_ZIPBALL + branchName)
164 | }
165 | return `Update successful from ${ formatVersion(installedVersion) } to ${ formatVersion(latestVersion) }`
166 | }
167 | return `Instance up-to-date using ref ${process.env.RELEASE_TYPE}`
168 | }
169 |
--------------------------------------------------------------------------------
/src/ssl.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 |
3 | export function getSSLOptionsFromEnv() {
4 | const SSL_CERT = process.env.SSL_CERT
5 | const SSL_KEY = process.env.SSL_KEY
6 | const SSL_CERT_PATH = process.env.SSL_CERT_PATH
7 | const SSL_KEY_PATH = process.env.SSL_KEY_PATH
8 |
9 | let cert, key
10 | if (SSL_CERT && SSL_KEY) {
11 | cert = SSL_CERT
12 | key = SSL_KEY
13 | } else if (
14 | SSL_CERT_PATH && SSL_KEY_PATH &&
15 | fs.existsSync(SSL_CERT_PATH) && fs.existsSync(SSL_KEY_PATH)
16 | ) {
17 | cert = fs.readFileSync(SSL_CERT_PATH)
18 | key = fs.readFileSync(SSL_KEY_PATH)
19 | }
20 |
21 | if (cert && key) {
22 | return { cert, key }
23 | }
24 |
25 | return null
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export function assert_env() {
2 | for (const key of ['QBIT_BASE']) {
3 | if (!process.env[key]) {
4 | throw new Error(`${ key } environment variable is missing`)
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tests/config.http:
--------------------------------------------------------------------------------
1 | # Fetch all config
2 | GET http://localhost:3000/config
3 | ###
4 | # Upsert a key
5 | PUT http://localhost:3000/config/key1
6 | Content-Type: application/json
7 |
8 | {
9 | "value": "value1"
10 | }
11 | ###
12 | # Fetch that key
13 | GET http://localhost:3000/config/key1
14 | ###
15 | # And delete it
16 | DELETE http://localhost:3000/config/key1
17 | ###
--------------------------------------------------------------------------------
/tests/ping.http:
--------------------------------------------------------------------------------
1 | GET http://localhost:3000/ping
--------------------------------------------------------------------------------
/tests/qbit.http:
--------------------------------------------------------------------------------
1 | GET http://localhost:3000/api/v2/auth/login
2 |
3 | username=XXXXX&password=XXXXX
4 | ###
5 | GET http://localhost:3000/api/v2/app/version
6 | ###
7 |
--------------------------------------------------------------------------------