Projektstart
This commit is contained in:
31
backend/node_modules/@fastify/static/.eslintrc.json
generated
vendored
Normal file
31
backend/node_modules/@fastify/static/.eslintrc.json
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["eslint:recommended", "standard"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["types/*.test-d.ts", "types/*.d.ts"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": ["./tsconfig.eslint.json"]
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
||||
],
|
||||
"rules": {
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-argument": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-misused-promises": ["error", {
|
||||
"checksVoidReturn": false
|
||||
}]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
5
backend/node_modules/@fastify/static/.gitattributes
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set
|
||||
* text=auto
|
||||
|
||||
# Require Unix line endings
|
||||
* text eol=lf
|
||||
13
backend/node_modules/@fastify/static/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/static/.github/dependabot.yml
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
21
backend/node_modules/@fastify/static/.github/stale.yml
generated
vendored
Normal file
21
backend/node_modules/@fastify/static/.github/stale.yml
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 15
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "discussion"
|
||||
- "feature request"
|
||||
- "bug"
|
||||
- "help wanted"
|
||||
- "plugin suggestion"
|
||||
- "good first issue"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
23
backend/node_modules/@fastify/static/.github/workflows/ci.yml
generated
vendored
Normal file
23
backend/node_modules/@fastify/static/.github/workflows/ci.yml
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- next
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
|
||||
with:
|
||||
license-check: true
|
||||
lint: true
|
||||
2
backend/node_modules/@fastify/static/.taprc
generated
vendored
Normal file
2
backend/node_modules/@fastify/static/.taprc
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
files:
|
||||
- test/**/*.test.js
|
||||
21
backend/node_modules/@fastify/static/LICENSE
generated
vendored
Normal file
21
backend/node_modules/@fastify/static/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2023 Fastify
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
493
backend/node_modules/@fastify/static/README.md
generated
vendored
Normal file
493
backend/node_modules/@fastify/static/README.md
generated
vendored
Normal file
@@ -0,0 +1,493 @@
|
||||
# @fastify/static
|
||||
|
||||

|
||||
[](https://www.npmjs.com/package/@fastify/static)
|
||||
[](https://standardjs.com/)
|
||||
|
||||
Plugin for serving static files as fast as possible. Supports Fastify version `4.x`.
|
||||
|
||||
| Fastify version | branch |
|
||||
| --------------- | -------------------------------------------------------------------- |
|
||||
| `^4.x` | This branch |
|
||||
| `^3.x` | [`v5.x`](https://github.com/fastify/fastify-static/tree/v5.x) |
|
||||
| `^2.x` | [`2.x`](https://github.com/fastify/fastify-static/tree/2.x) |
|
||||
| `^1.11.x` | [`1.x`](https://github.com/fastify/fastify-static/tree/1.x) |
|
||||
|
||||
## Install
|
||||
|
||||
`npm i @fastify/static`
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const fastify = require('fastify')({logger: true})
|
||||
const path = require('node:path')
|
||||
|
||||
fastify.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, 'public'),
|
||||
prefix: '/public/', // optional: default '/'
|
||||
constraints: { host: 'example.com' } // optional: default {}
|
||||
})
|
||||
|
||||
fastify.get('/another/path', function (req, reply) {
|
||||
reply.sendFile('myHtml.html') // serving path.join(__dirname, 'public', 'myHtml.html') directly
|
||||
})
|
||||
|
||||
fastify.get('/another/patch-async', async function (req, reply) {
|
||||
return reply.sendFile('myHtml.html')
|
||||
})
|
||||
|
||||
fastify.get('/path/with/different/root', function (req, reply) {
|
||||
reply.sendFile('myHtml.html', path.join(__dirname, 'build')) // serving a file from a different root location
|
||||
})
|
||||
|
||||
fastify.get('/another/path', function (req, reply) {
|
||||
reply.sendFile('myHtml.html', { cacheControl: false }) // overriding the options disabling cache-control headers
|
||||
})
|
||||
|
||||
// Run the server!
|
||||
fastify.listen({ port: 3000 }, (err, address) => {
|
||||
if (err) throw err
|
||||
// Server is now listening on ${address}
|
||||
})
|
||||
```
|
||||
|
||||
### Multiple prefixed roots
|
||||
|
||||
```js
|
||||
const fastify = require('fastify')()
|
||||
const fastifyStatic = require('@fastify/static')
|
||||
const path = require('node:path')
|
||||
// first plugin
|
||||
fastify.register(fastifyStatic, {
|
||||
root: path.join(__dirname, 'public')
|
||||
})
|
||||
|
||||
// second plugin
|
||||
fastify.register(fastifyStatic, {
|
||||
root: path.join(__dirname, 'node_modules'),
|
||||
prefix: '/node_modules/',
|
||||
decorateReply: false // the reply decorator has been added by the first plugin registration
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Sending a file with `content-disposition` header
|
||||
|
||||
```js
|
||||
const fastify = require('fastify')()
|
||||
const path = require('node:path')
|
||||
|
||||
fastify.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, 'public'),
|
||||
prefix: '/public/', // optional: default '/'
|
||||
})
|
||||
|
||||
fastify.get('/another/path', function (req, reply) {
|
||||
reply.download('myHtml.html', 'custom-filename.html') // sending path.join(__dirname, 'public', 'myHtml.html') directly with custom filename
|
||||
})
|
||||
|
||||
fastify.get('another/patch-async', async function (req, reply) {
|
||||
// an async handler must always return the reply object
|
||||
return reply.download('myHtml.html', 'custom-filename.html')
|
||||
})
|
||||
|
||||
fastify.get('/path/without/cache/control', function (req, reply) {
|
||||
reply.download('myHtml.html', { cacheControl: false }) // serving a file disabling cache-control headers
|
||||
})
|
||||
|
||||
fastify.get('/path/without/cache/control', function (req, reply) {
|
||||
reply.download('myHtml.html', 'custom-filename.html', { cacheControl: false })
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
#### `root` (required)
|
||||
|
||||
The absolute path of the directory that contains the files to serve.
|
||||
The file to serve will be determined by combining `req.url` with the
|
||||
provided root directory.
|
||||
|
||||
You can also provide an array of directories containing files to serve.
|
||||
This is useful for serving multiple static directories under a single prefix. Files are served in a "first found, first served" manner, so the order in which you list the directories is important. For best performance, you should always list your main asset directory first. Duplicate paths will raise an error.
|
||||
|
||||
#### `prefix`
|
||||
|
||||
Default: `'/'`
|
||||
|
||||
A URL path prefix used to create a virtual mount path for the static directory.
|
||||
|
||||
#### `constraints`
|
||||
|
||||
Default: `{}`
|
||||
|
||||
Constraints that will be added to registered routes. See Fastify's documentation for
|
||||
[route constraints](https://fastify.dev/docs/latest/Reference/Routes/#constraints).
|
||||
|
||||
#### `prefixAvoidTrailingSlash`
|
||||
|
||||
Default: `false`
|
||||
|
||||
If set to false prefix will get trailing "/" at the end. If set to true, prefix will not append "/" to prefix.
|
||||
|
||||
#### `schemaHide`
|
||||
|
||||
Default: `true`
|
||||
|
||||
A flag that define if the fastify route hide-schema attribute is hidden or not
|
||||
|
||||
#### `setHeaders`
|
||||
|
||||
Default: `undefined`
|
||||
|
||||
A function to set custom headers on the response. Alterations to the headers
|
||||
must be done synchronously. The function is called as `fn(res, path, stat)`,
|
||||
where the arguments are:
|
||||
|
||||
- `res` The response object.
|
||||
- `path` The path of the file that is being sent.
|
||||
- `stat` The stat object of the file that is being sent.
|
||||
|
||||
#### `send` Options
|
||||
|
||||
The following options are also supported and will be passed directly to the
|
||||
[`send`](https://www.npmjs.com/package/send) module:
|
||||
|
||||
- [`acceptRanges`](https://www.npmjs.com/package/send#acceptranges)
|
||||
- [`cacheControl`](https://www.npmjs.com/package/send#cachecontrol)
|
||||
- [`dotfiles`](https://www.npmjs.com/package/send#dotfiles)
|
||||
- [`etag`](https://www.npmjs.com/package/send#etag)
|
||||
- [`extensions`](https://www.npmjs.com/package/send#extensions)
|
||||
- [`immutable`](https://www.npmjs.com/package/send#immutable)
|
||||
- [`index`](https://www.npmjs.com/package/send#index)
|
||||
- [`lastModified`](https://www.npmjs.com/package/send#lastmodified)
|
||||
- [`maxAge`](https://www.npmjs.com/package/send#maxage)
|
||||
|
||||
You're able to alter this options when calling `reply.download('filename.html', options)` or `reply.download('filename.html', 'otherfilename.html', options)` on each response to a request.
|
||||
|
||||
#### `redirect`
|
||||
|
||||
Default: `false`
|
||||
|
||||
If set to `true`, `@fastify/static` redirects to the directory with a trailing slash.
|
||||
|
||||
This option cannot be set to `true` with `wildcard` set to `false` on a server
|
||||
with `ignoreTrailingSlash` set to `true`.
|
||||
|
||||
If this option is set to `false`, then requesting directories without trailing
|
||||
slash will trigger your app's 404 handler using `reply.callNotFound()`.
|
||||
|
||||
#### `wildcard`
|
||||
|
||||
Default: `true`
|
||||
|
||||
If set to `true`, `@fastify/static` adds a wildcard route to serve files.
|
||||
If set to `false`, `@fastify/static` globs the filesystem for all defined
|
||||
files in the served folder (`${root}/**/**`), and just creates the routes needed for
|
||||
those and it will not serve the newly added file on the filesystem.
|
||||
|
||||
The default options of https://www.npmjs.com/package/glob are applied
|
||||
for getting the file list.
|
||||
|
||||
This option cannot be set to `false` with `redirect` set to `true` on a server
|
||||
with `ignoreTrailingSlash` set to `true`.
|
||||
|
||||
#### `allowedPath`
|
||||
|
||||
Default: `(pathName, root, request) => true`
|
||||
|
||||
This function allows filtering the served files. Also, with the help of the request object a more complex path authentication is possible.
|
||||
If the function returns `true`, the file will be served.
|
||||
If the function returns `false`, Fastify's 404 handler will be called.
|
||||
|
||||
#### `index`
|
||||
|
||||
Default: `undefined`
|
||||
|
||||
Under the hood we use [send](https://github.com/pillarjs/send#index) lib that by default supports "index.html" files.
|
||||
To disable this set false or to supply a new index pass a string or an array in preferred order.
|
||||
|
||||
#### `serveDotFiles`
|
||||
|
||||
Default: `false`
|
||||
|
||||
When `true`, files in hidden directories (e.g. `.foo`) will be served.
|
||||
|
||||
#### `list`
|
||||
|
||||
Default: `undefined`
|
||||
|
||||
If set, it provides the directory list calling the directory path.
|
||||
|
||||
Default response is json.
|
||||
|
||||
Note:
|
||||
- Multi-root is not supported within the `list` option.
|
||||
- If `dotfiles` option value is `deny` or `ignore`, dotfiles will be excluded.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
fastify.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, 'public'),
|
||||
prefix: '/public/',
|
||||
index: false
|
||||
list: true
|
||||
})
|
||||
```
|
||||
|
||||
Request
|
||||
|
||||
```bash
|
||||
GET .../public
|
||||
```
|
||||
|
||||
Response
|
||||
|
||||
```json
|
||||
{ "dirs": ["dir1", "dir2"], "files": ["file1.png", "file2.txt"] }
|
||||
```
|
||||
|
||||
#### `list.format`
|
||||
|
||||
Default: `json`
|
||||
|
||||
Options: `html`, `json`
|
||||
|
||||
Directory list can be also in `html` format; in that case, `list.render` function is required.
|
||||
|
||||
You can override the option with URL parameter `format`. Options are `html` and `json`.
|
||||
|
||||
```bash
|
||||
GET .../public/assets?format=json
|
||||
```
|
||||
|
||||
will return the response as json independent of `list.format`.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
fastify.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, 'public'),
|
||||
prefix: '/public/',
|
||||
list: {
|
||||
format: 'html',
|
||||
render: (dirs, files) => {
|
||||
return `
|
||||
<html><body>
|
||||
<ul>
|
||||
${dirs.map(dir => `<li><a href="${dir.href}">${dir.name}</a></li>`).join('\n ')}
|
||||
</ul>
|
||||
<ul>
|
||||
${files.map(file => `<li><a href="${file.href}" target="_blank">${file.name}</a></li>`).join('\n ')}
|
||||
</ul>
|
||||
</body></html>
|
||||
`
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Request
|
||||
|
||||
```bash
|
||||
GET .../public
|
||||
```
|
||||
|
||||
Response
|
||||
|
||||
```html
|
||||
<html><body>
|
||||
<ul>
|
||||
<li><a href="/dir1">dir1</a></li>
|
||||
<li><a href="/dir1">dir2</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/foo.html" target="_blank">foo.html</a></li>
|
||||
<li><a href="/foobar.html" target="_blank">foobar.html</a></li>
|
||||
<li><a href="/index.css" target="_blank">index.css</a></li>
|
||||
<li><a href="/index.html" target="_blank">index.html</a></li>
|
||||
</ul>
|
||||
</body></html>
|
||||
```
|
||||
|
||||
#### `list.names`
|
||||
|
||||
Default: `['']`
|
||||
|
||||
Directory list can respond to different routes, declared in `list.names` options.
|
||||
|
||||
Note: if a file with the same name exists, the actual file is sent.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
fastify.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
prefixAvoidTrailingSlash: true,
|
||||
list: {
|
||||
format: 'json',
|
||||
names: ['index', 'index.json', '/']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Dir list respond with the same content to
|
||||
|
||||
```bash
|
||||
GET .../public
|
||||
GET .../public/
|
||||
GET .../public/index
|
||||
GET .../public/index.json
|
||||
```
|
||||
|
||||
#### `list.extendedFolderInfo`
|
||||
|
||||
Default: `undefined`
|
||||
|
||||
If `true` some extended information for folders will be accessible in `list.render` and in the json response.
|
||||
|
||||
```js
|
||||
render(dirs, files) {
|
||||
const dir = dirs[0];
|
||||
dir.fileCount // number of files in this folder
|
||||
dir.totalFileCount // number of files in this folder (recursive)
|
||||
dir.folderCount // number of folders in this folder
|
||||
dir.totalFolderCount // number of folders in this folder (recursive)
|
||||
dir.totalSize // size of all files in this folder (recursive)
|
||||
dir.lastModified // most recent last modified timestamp of all files in this folder (recursive)
|
||||
}
|
||||
```
|
||||
|
||||
Warning: This will slightly decrease the performance, especially for deeply nested file structures.
|
||||
|
||||
#### `list.jsonFormat`
|
||||
|
||||
Default: `names`
|
||||
|
||||
Options: `names`, `extended`
|
||||
|
||||
This option determines the output format when `json` is selected.
|
||||
|
||||
`names`:
|
||||
```json
|
||||
{
|
||||
"dirs": [
|
||||
"dir1",
|
||||
"dir2"
|
||||
],
|
||||
"files": [
|
||||
"file1.txt",
|
||||
"file2.txt"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`extended`:
|
||||
```json
|
||||
{
|
||||
"dirs": [
|
||||
{
|
||||
"name": "dir1",
|
||||
"stats": {
|
||||
"dev": 2100,
|
||||
"size": 4096,
|
||||
...
|
||||
},
|
||||
"extendedInfo": {
|
||||
"fileCount": 4,
|
||||
"totalSize": 51233,
|
||||
...
|
||||
}
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"name": "file1.txt",
|
||||
"stats": {
|
||||
"dev": 2200,
|
||||
"size": 554,
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `preCompressed`
|
||||
|
||||
Default: `false`
|
||||
|
||||
Try to send the brotli encoded asset first (when supported within the `Accept-Encoding` headers), retry for gzip, then the fall back to the original `pathname`. You may choose to skip compression for smaller files that don't benefit from it.
|
||||
|
||||
Assume this structure with the compressed asset as a sibling of the un-compressed counterpart:
|
||||
|
||||
```
|
||||
./public
|
||||
├── main.js
|
||||
├── main.js.br
|
||||
├── main.js.gz
|
||||
├── crit.css
|
||||
├── crit.css.gz
|
||||
└── index.html
|
||||
```
|
||||
|
||||
#### Disable serving
|
||||
|
||||
If you would just like to use the reply decorator and not serve whole directories automatically, you can simply pass the option `{ serve: false }`. This will prevent the plugin from serving everything under `root`.
|
||||
|
||||
#### Disabling reply decorator
|
||||
|
||||
The reply object is decorated with a `sendFile` function by default. If you want to
|
||||
disable this, pass the option `{ decorateReply: false }`. If @fastify/static is
|
||||
registered to multiple prefixes in the same route only one can initialize reply
|
||||
decorators.
|
||||
|
||||
#### Handling 404s
|
||||
|
||||
If a request matches the URL `prefix` but a file cannot be found for the
|
||||
request, Fastify's 404 handler will be called. You can set a custom 404
|
||||
handler with [`fastify.setNotFoundHandler()`](https://fastify.dev/docs/latest/Reference/Server/#setnotfoundhandler).
|
||||
|
||||
When registering `@fastify/static` within an encapsulated context, the `wildcard` option may need to be set to `false` in order to support index resolution and nested not-found-handler:
|
||||
|
||||
```js
|
||||
const app = require('fastify')();
|
||||
|
||||
app.register((childContext, _, done) => {
|
||||
childContext.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, 'docs'), // docs is a folder that contains `index.html` and `404.html`
|
||||
wildcard: false
|
||||
});
|
||||
childContext.setNotFoundHandler((_, reply) => {
|
||||
return reply.code(404).type('text/html').sendFile('404.html');
|
||||
});
|
||||
done();
|
||||
}, { prefix: 'docs' });
|
||||
```
|
||||
|
||||
This code will send the `index.html` for the paths `docs`, `docs/`, and `docs/index.html`. For all other `docs/<undefined-routes>` it will reply with `404.html`.
|
||||
|
||||
### Handling Errors
|
||||
|
||||
If an error occurs while trying to send a file, the error will be passed
|
||||
to Fastify's error handler. You can set a custom error handler with
|
||||
[`fastify.setErrorHandler()`](https://fastify.dev/docs/latest/Reference/Server/#seterrorhandler).
|
||||
|
||||
### Payload `stream.filename`
|
||||
|
||||
If you need to access the filename inside the `onSend` hook, you can use `payload.filename`.
|
||||
|
||||
```js
|
||||
fastify.addHook('onSend', function (req, reply, payload, next) {
|
||||
console.log(payload.filename)
|
||||
next()
|
||||
})
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE)
|
||||
1
backend/node_modules/@fastify/static/example/public/.hidden/sample.json
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/example/public/.hidden/sample.json
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"hello": "world"}
|
||||
BIN
backend/node_modules/@fastify/static/example/public/images/sample.jpg
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/example/public/images/sample.jpg
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
9
backend/node_modules/@fastify/static/example/public/index.css
generated
vendored
Normal file
9
backend/node_modules/@fastify/static/example/public/index.css
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
#my-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
height: 28px;
|
||||
margin-top: -14px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
11
backend/node_modules/@fastify/static/example/public/index.html
generated
vendored
Normal file
11
backend/node_modules/@fastify/static/example/public/index.html
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="index.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="index.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
<button id="my-button">
|
||||
The button
|
||||
</button>
|
||||
</body>
|
||||
</html>
|
||||
8
backend/node_modules/@fastify/static/example/public/index.js
generated
vendored
Normal file
8
backend/node_modules/@fastify/static/example/public/index.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
window.onload = function () {
|
||||
const b = document.getElementById('my-button')
|
||||
b.onclick = function () {
|
||||
window.alert('foo')
|
||||
}
|
||||
}
|
||||
4
backend/node_modules/@fastify/static/example/public2/test.css
generated
vendored
Normal file
4
backend/node_modules/@fastify/static/example/public2/test.css
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
8
backend/node_modules/@fastify/static/example/public2/test.html
generated
vendored
Normal file
8
backend/node_modules/@fastify/static/example/public2/test.html
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="test.css"></link>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test 2</h1>
|
||||
</body>
|
||||
</html>
|
||||
15
backend/node_modules/@fastify/static/example/server-compress.js
generated
vendored
Normal file
15
backend/node_modules/@fastify/static/example/server-compress.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const fastify = require('fastify')({ logger: { level: 'trace' } })
|
||||
|
||||
fastify
|
||||
// Compress everything.
|
||||
.register(require('@fastify/compress'), { threshold: 0 })
|
||||
.register(require('../'), {
|
||||
// An absolute path containing static files to serve.
|
||||
root: path.join(__dirname, '/public')
|
||||
})
|
||||
.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
49
backend/node_modules/@fastify/static/example/server-dir-list.js
generated
vendored
Normal file
49
backend/node_modules/@fastify/static/example/server-dir-list.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const Handlebars = require('handlebars')
|
||||
|
||||
const fastify = require('fastify')({ logger: { level: 'trace' } })
|
||||
|
||||
// Handlebar template for listing files and directories.
|
||||
const template = `
|
||||
<html>
|
||||
<body>
|
||||
dirs
|
||||
<ul>
|
||||
{{#dirs}}
|
||||
<li><a href="{{href}}">{{name}}</a></li>
|
||||
{{/dirs}}
|
||||
</ul>
|
||||
|
||||
list
|
||||
|
||||
<ul>
|
||||
{{#files}}
|
||||
<li><a href="{{href}}" target="_blank">{{name}}</a></li>
|
||||
{{/files}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
const handlebarTemplate = Handlebars.compile(template)
|
||||
|
||||
fastify
|
||||
.register(require('..'), {
|
||||
// An absolute path containing static files to serve.
|
||||
root: path.join(__dirname, '/public'),
|
||||
// Do not append a trailing slash to prefixes.
|
||||
prefixAvoidTrailingSlash: true,
|
||||
// Return a directory listing with a handlebar template.
|
||||
list: {
|
||||
// html or json response? html requires a render method.
|
||||
format: 'html',
|
||||
// A list of filenames that trigger a directory list response.
|
||||
names: ['index', 'index.html', 'index.htm', '/'],
|
||||
// You can provide your own render method as needed.
|
||||
render: (dirs, files) => handlebarTemplate({ dirs, files })
|
||||
}
|
||||
})
|
||||
.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
15
backend/node_modules/@fastify/static/example/server-hidden-file.js
generated
vendored
Normal file
15
backend/node_modules/@fastify/static/example/server-hidden-file.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const fastify = require('fastify')({ logger: { level: 'trace' } })
|
||||
|
||||
fastify
|
||||
.register(require('../'), {
|
||||
// An absolute path containing static files to serve.
|
||||
root: path.join(__dirname, '/public'),
|
||||
wildcard: false,
|
||||
serveDotFiles: true
|
||||
})
|
||||
.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
13
backend/node_modules/@fastify/static/example/server.js
generated
vendored
Normal file
13
backend/node_modules/@fastify/static/example/server.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const fastify = require('fastify')({ logger: { level: 'trace' } })
|
||||
|
||||
fastify
|
||||
.register(require('../'), {
|
||||
// An absolute path containing static files to serve.
|
||||
root: path.join(__dirname, '/public')
|
||||
})
|
||||
.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
560
backend/node_modules/@fastify/static/index.js
generated
vendored
Normal file
560
backend/node_modules/@fastify/static/index.js
generated
vendored
Normal file
@@ -0,0 +1,560 @@
|
||||
'use strict'
|
||||
|
||||
const { PassThrough } = require('node:stream')
|
||||
const path = require('node:path')
|
||||
const { fileURLToPath } = require('node:url')
|
||||
const { statSync } = require('node:fs')
|
||||
const { glob } = require('glob')
|
||||
const fp = require('fastify-plugin')
|
||||
const send = require('@fastify/send')
|
||||
const encodingNegotiator = require('@fastify/accept-negotiator')
|
||||
const contentDisposition = require('content-disposition')
|
||||
|
||||
const dirList = require('./lib/dirList')
|
||||
|
||||
const endForwardSlashRegex = /\/$/u
|
||||
const asteriskRegex = /\*/gu
|
||||
|
||||
const supportedEncodings = ['br', 'gzip', 'deflate']
|
||||
send.mime.default_type = 'application/octet-stream'
|
||||
|
||||
async function fastifyStatic (fastify, opts) {
|
||||
opts.root = normalizeRoot(opts.root)
|
||||
checkRootPathForErrors(fastify, opts.root)
|
||||
|
||||
const setHeaders = opts.setHeaders
|
||||
if (setHeaders !== undefined && typeof setHeaders !== 'function') {
|
||||
throw new TypeError('The `setHeaders` option must be a function')
|
||||
}
|
||||
|
||||
const invalidDirListOpts = dirList.validateOptions(opts)
|
||||
if (invalidDirListOpts) {
|
||||
throw invalidDirListOpts
|
||||
}
|
||||
|
||||
if (opts.dotfiles === undefined) {
|
||||
opts.dotfiles = 'allow'
|
||||
}
|
||||
|
||||
const sendOptions = {
|
||||
root: opts.root,
|
||||
acceptRanges: opts.acceptRanges,
|
||||
cacheControl: opts.cacheControl,
|
||||
dotfiles: opts.dotfiles,
|
||||
etag: opts.etag,
|
||||
extensions: opts.extensions,
|
||||
immutable: opts.immutable,
|
||||
index: opts.index,
|
||||
lastModified: opts.lastModified,
|
||||
maxAge: opts.maxAge
|
||||
}
|
||||
|
||||
let prefix = opts.prefix ?? (opts.prefix = '/')
|
||||
|
||||
if (!opts.prefixAvoidTrailingSlash) {
|
||||
prefix =
|
||||
prefix[prefix.length - 1] === '/'
|
||||
? prefix
|
||||
: prefix + '/'
|
||||
}
|
||||
|
||||
// Set the schema hide property if defined in opts or true by default
|
||||
const routeOpts = {
|
||||
constraints: opts.constraints,
|
||||
schema: {
|
||||
hide: opts.schemaHide !== undefined ? opts.schemaHide : true
|
||||
},
|
||||
errorHandler (error, request, reply) {
|
||||
if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
||||
reply.request.raw.destroy()
|
||||
return
|
||||
}
|
||||
|
||||
fastify.errorHandler(error, request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.decorateReply !== false) {
|
||||
fastify.decorateReply('sendFile', function (filePath, rootPath, options) {
|
||||
const opts = typeof rootPath === 'object' ? rootPath : options
|
||||
const root = typeof rootPath === 'string' ? rootPath : opts && opts.root
|
||||
pumpSendToReply(
|
||||
this.request,
|
||||
this,
|
||||
filePath,
|
||||
root || sendOptions.root,
|
||||
0,
|
||||
opts
|
||||
)
|
||||
return this
|
||||
})
|
||||
|
||||
fastify.decorateReply(
|
||||
'download',
|
||||
function (filePath, fileName, options = {}) {
|
||||
const { root, ...opts } =
|
||||
typeof fileName === 'object' ? fileName : options
|
||||
fileName = typeof fileName === 'string' ? fileName : filePath
|
||||
|
||||
// Set content disposition header
|
||||
this.header('content-disposition', contentDisposition(fileName))
|
||||
|
||||
pumpSendToReply(this.request, this, filePath, root, 0, opts)
|
||||
|
||||
return this
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (opts.serve !== false) {
|
||||
if (opts.wildcard && typeof opts.wildcard !== 'boolean') {
|
||||
throw new Error('"wildcard" option must be a boolean')
|
||||
}
|
||||
if (opts.wildcard === undefined || opts.wildcard === true) {
|
||||
fastify.route({
|
||||
...routeOpts,
|
||||
method: ['HEAD', 'GET'],
|
||||
path: prefix + '*',
|
||||
handler (req, reply) {
|
||||
pumpSendToReply(req, reply, '/' + req.params['*'], sendOptions.root)
|
||||
}
|
||||
})
|
||||
if (opts.redirect === true && prefix !== opts.prefix) {
|
||||
fastify.get(opts.prefix, routeOpts, (req, reply) => {
|
||||
reply.redirect(301, getRedirectUrl(req.raw.url))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const indexes = opts.index === undefined ? ['index.html'] : [].concat(opts.index)
|
||||
const indexDirs = new Map()
|
||||
const routes = new Set()
|
||||
|
||||
const roots = Array.isArray(sendOptions.root) ? sendOptions.root : [sendOptions.root]
|
||||
for (let rootPath of roots) {
|
||||
rootPath = rootPath.split(path.win32.sep).join(path.posix.sep)
|
||||
!rootPath.endsWith('/') && (rootPath += '/')
|
||||
const files = await glob('**/**', {
|
||||
cwd: rootPath, absolute: false, follow: true, nodir: true, dot: opts.serveDotFiles
|
||||
})
|
||||
|
||||
for (let file of files) {
|
||||
file = file.split(path.win32.sep).join(path.posix.sep)
|
||||
const route = prefix + file
|
||||
|
||||
if (routes.has(route)) {
|
||||
continue
|
||||
}
|
||||
|
||||
routes.add(route)
|
||||
|
||||
setUpHeadAndGet(routeOpts, route, `/${file}`, rootPath)
|
||||
|
||||
const key = path.posix.basename(route)
|
||||
if (indexes.includes(key) && !indexDirs.has(key)) {
|
||||
indexDirs.set(path.posix.dirname(route), rootPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [dirname, rootPath] of indexDirs.entries()) {
|
||||
const pathname = dirname + (dirname.endsWith('/') ? '' : '/')
|
||||
const file = '/' + pathname.replace(prefix, '')
|
||||
setUpHeadAndGet(routeOpts, pathname, file, rootPath)
|
||||
|
||||
if (opts.redirect === true) {
|
||||
setUpHeadAndGet(routeOpts, pathname.replace(endForwardSlashRegex, ''), file.replace(endForwardSlashRegex, ''), rootPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedPath = opts.allowedPath
|
||||
|
||||
function pumpSendToReply (
|
||||
request,
|
||||
reply,
|
||||
pathname,
|
||||
rootPath,
|
||||
rootPathOffset = 0,
|
||||
pumpOptions,
|
||||
checkedEncodings
|
||||
) {
|
||||
const pathnameOrig = pathname
|
||||
const options = Object.assign({}, sendOptions, pumpOptions)
|
||||
|
||||
if (rootPath) {
|
||||
if (Array.isArray(rootPath)) {
|
||||
options.root = rootPath[rootPathOffset]
|
||||
} else {
|
||||
options.root = rootPath
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedPath && !allowedPath(pathname, options.root, request)) {
|
||||
return reply.callNotFound()
|
||||
}
|
||||
|
||||
let encoding
|
||||
let pathnameForSend = pathname
|
||||
|
||||
if (opts.preCompressed) {
|
||||
/**
|
||||
* We conditionally create this structure to track our attempts
|
||||
* at sending pre-compressed assets
|
||||
*/
|
||||
if (!checkedEncodings) {
|
||||
checkedEncodings = new Set()
|
||||
}
|
||||
|
||||
encoding = getEncodingHeader(request.headers, checkedEncodings)
|
||||
|
||||
if (encoding) {
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = findIndexFile(pathname, options.root, options.index)
|
||||
if (!pathname) {
|
||||
return reply.callNotFound()
|
||||
}
|
||||
pathnameForSend = pathnameForSend + pathname + '.' + getEncodingExtension(encoding)
|
||||
} else {
|
||||
pathnameForSend = pathname + '.' + getEncodingExtension(encoding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `send(..., path, ...)` will URI-decode path so we pass an encoded path here
|
||||
const stream = send(request.raw, encodeURI(pathnameForSend), options)
|
||||
let resolvedFilename
|
||||
stream.on('file', function (file) {
|
||||
resolvedFilename = file
|
||||
})
|
||||
|
||||
const wrap = new PassThrough({
|
||||
flush (cb) {
|
||||
this.finished = true
|
||||
if (reply.raw.statusCode === 304) {
|
||||
reply.send('')
|
||||
}
|
||||
cb()
|
||||
}
|
||||
})
|
||||
|
||||
wrap.getHeader = reply.getHeader.bind(reply)
|
||||
wrap.setHeader = reply.header.bind(reply)
|
||||
wrap.removeHeader = () => {}
|
||||
wrap.finished = false
|
||||
|
||||
Object.defineProperty(wrap, 'filename', {
|
||||
get () {
|
||||
return resolvedFilename
|
||||
}
|
||||
})
|
||||
Object.defineProperty(wrap, 'statusCode', {
|
||||
get () {
|
||||
return reply.raw.statusCode
|
||||
},
|
||||
set (code) {
|
||||
reply.code(code)
|
||||
}
|
||||
})
|
||||
|
||||
if (request.method === 'HEAD') {
|
||||
wrap.on('finish', reply.send.bind(reply))
|
||||
} else {
|
||||
wrap.on('pipe', function () {
|
||||
if (encoding) {
|
||||
reply.header('content-type', getContentType(pathname))
|
||||
reply.header('content-encoding', encoding)
|
||||
}
|
||||
reply.send(wrap)
|
||||
})
|
||||
}
|
||||
|
||||
if (setHeaders !== undefined) {
|
||||
stream.on('headers', setHeaders)
|
||||
}
|
||||
|
||||
stream.on('directory', function (_, path) {
|
||||
if (opts.list) {
|
||||
dirList.send({
|
||||
reply,
|
||||
dir: path,
|
||||
options: opts.list,
|
||||
route: pathname,
|
||||
prefix,
|
||||
dotfiles: opts.dotfiles
|
||||
}).catch((err) => reply.send(err))
|
||||
return
|
||||
}
|
||||
|
||||
if (opts.redirect === true) {
|
||||
try {
|
||||
reply.redirect(301, getRedirectUrl(request.raw.url))
|
||||
} catch (error) {
|
||||
// the try-catch here is actually unreachable, but we keep it for safety and prevent DoS attack
|
||||
/* istanbul ignore next */
|
||||
reply.send(error)
|
||||
}
|
||||
} else {
|
||||
// if is a directory path without a trailing slash, and has an index file, reply as if it has a trailing slash
|
||||
if (!pathname.endsWith('/') && findIndexFile(pathname, options.root, options.index)) {
|
||||
return pumpSendToReply(
|
||||
request,
|
||||
reply,
|
||||
pathname + '/',
|
||||
rootPath,
|
||||
undefined,
|
||||
undefined,
|
||||
checkedEncodings
|
||||
)
|
||||
}
|
||||
|
||||
reply.callNotFound()
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('error', function (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
// when preCompress is enabled and the path is a directory without a trailing slash
|
||||
if (opts.preCompressed && encoding) {
|
||||
const indexPathname = findIndexFile(pathname, options.root, options.index)
|
||||
if (indexPathname) {
|
||||
return pumpSendToReply(
|
||||
request,
|
||||
reply,
|
||||
pathname + '/',
|
||||
rootPath,
|
||||
undefined,
|
||||
undefined,
|
||||
checkedEncodings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// if file exists, send real file, otherwise send dir list if name match
|
||||
if (opts.list && dirList.handle(pathname, opts.list)) {
|
||||
dirList.send({
|
||||
reply,
|
||||
dir: dirList.path(opts.root, pathname),
|
||||
options: opts.list,
|
||||
route: pathname,
|
||||
prefix,
|
||||
dotfiles: opts.dotfiles
|
||||
}).catch((err) => reply.send(err))
|
||||
return
|
||||
}
|
||||
|
||||
// root paths left to try?
|
||||
if (Array.isArray(rootPath) && rootPathOffset < (rootPath.length - 1)) {
|
||||
return pumpSendToReply(request, reply, pathname, rootPath, rootPathOffset + 1)
|
||||
}
|
||||
|
||||
if (opts.preCompressed && !checkedEncodings.has(encoding)) {
|
||||
checkedEncodings.add(encoding)
|
||||
return pumpSendToReply(
|
||||
request,
|
||||
reply,
|
||||
pathnameOrig,
|
||||
rootPath,
|
||||
rootPathOffset,
|
||||
undefined,
|
||||
checkedEncodings
|
||||
)
|
||||
}
|
||||
|
||||
return reply.callNotFound()
|
||||
}
|
||||
|
||||
// The `send` library terminates the request with a 404 if the requested
|
||||
// path contains a dotfile and `send` is initialized with `{dotfiles:
|
||||
// 'ignore'}`. `send` aborts the request before getting far enough to
|
||||
// check if the file exists (hence, a 404 `NotFoundError` instead of
|
||||
// `ENOENT`).
|
||||
// https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L582
|
||||
if (err.status === 404) {
|
||||
return reply.callNotFound()
|
||||
}
|
||||
|
||||
reply.send(err)
|
||||
})
|
||||
|
||||
// we cannot use pump, because send error
|
||||
// handling is not compatible
|
||||
stream.pipe(wrap)
|
||||
}
|
||||
|
||||
function setUpHeadAndGet (routeOpts, route, file, rootPath) {
|
||||
const toSetUp = Object.assign({}, routeOpts, {
|
||||
method: ['HEAD', 'GET'],
|
||||
url: route,
|
||||
handler: serveFileHandler
|
||||
})
|
||||
toSetUp.config = toSetUp.config || {}
|
||||
toSetUp.config.file = file
|
||||
toSetUp.config.rootPath = rootPath
|
||||
fastify.route(toSetUp)
|
||||
}
|
||||
|
||||
function serveFileHandler (req, reply) {
|
||||
// TODO: remove the fallback branch when bump major
|
||||
/* istanbul ignore next */
|
||||
const routeConfig = req.routeOptions?.config || req.routeConfig
|
||||
pumpSendToReply(req, reply, routeConfig.file, routeConfig.rootPath)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRoot (root) {
|
||||
if (root === undefined) {
|
||||
return root
|
||||
}
|
||||
if (root instanceof URL && root.protocol === 'file:') {
|
||||
return fileURLToPath(root)
|
||||
}
|
||||
if (Array.isArray(root)) {
|
||||
const result = []
|
||||
for (let i = 0, il = root.length; i < il; ++i) {
|
||||
if (root[i] instanceof URL && root[i].protocol === 'file:') {
|
||||
result.push(fileURLToPath(root[i]))
|
||||
} else {
|
||||
result.push(root[i])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
function checkRootPathForErrors (fastify, rootPath) {
|
||||
if (rootPath === undefined) {
|
||||
throw new Error('"root" option is required')
|
||||
}
|
||||
|
||||
if (Array.isArray(rootPath)) {
|
||||
if (!rootPath.length) {
|
||||
throw new Error('"root" option array requires one or more paths')
|
||||
}
|
||||
|
||||
if (new Set(rootPath).size !== rootPath.length) {
|
||||
throw new Error(
|
||||
'"root" option array contains one or more duplicate paths'
|
||||
)
|
||||
}
|
||||
|
||||
// check each path and fail at first invalid
|
||||
rootPath.map((path) => checkPath(fastify, path))
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof rootPath === 'string') {
|
||||
return checkPath(fastify, rootPath)
|
||||
}
|
||||
|
||||
throw new Error('"root" option must be a string or array of strings')
|
||||
}
|
||||
|
||||
function checkPath (fastify, rootPath) {
|
||||
if (typeof rootPath !== 'string') {
|
||||
throw new Error('"root" option must be a string')
|
||||
}
|
||||
if (path.isAbsolute(rootPath) === false) {
|
||||
throw new Error('"root" option must be an absolute path')
|
||||
}
|
||||
|
||||
let pathStat
|
||||
|
||||
try {
|
||||
pathStat = statSync(rootPath)
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
fastify.log.warn(`"root" path "${rootPath}" must exist`)
|
||||
return
|
||||
}
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
if (pathStat.isDirectory() === false) {
|
||||
throw new Error('"root" option must point to a directory')
|
||||
}
|
||||
}
|
||||
|
||||
function getContentType (path) {
|
||||
const type = send.mime.getType(path) || send.mime.default_type
|
||||
|
||||
if (!send.isUtf8MimeType(type)) {
|
||||
return type
|
||||
}
|
||||
return `${type}; charset=UTF-8`
|
||||
}
|
||||
|
||||
function findIndexFile (pathname, root, indexFiles = ['index.html']) {
|
||||
// TODO remove istanbul ignore
|
||||
/* istanbul ignore else */
|
||||
if (Array.isArray(indexFiles)) {
|
||||
return indexFiles.find(filename => {
|
||||
const p = path.join(root, pathname, filename)
|
||||
try {
|
||||
const stats = statSync(p)
|
||||
return !stats.isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return false
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/fastify/fastify-compress/blob/665e132fa63d3bf05ad37df3c20346660b71a857/index.js#L451
|
||||
function getEncodingHeader (headers, checked) {
|
||||
if (!('accept-encoding' in headers)) return
|
||||
|
||||
// consider the no-preference token as gzip for downstream compat
|
||||
const header = headers['accept-encoding'].toLowerCase().replace(asteriskRegex, 'gzip')
|
||||
|
||||
return encodingNegotiator.negotiate(
|
||||
header,
|
||||
supportedEncodings.filter((enc) => !checked.has(enc))
|
||||
)
|
||||
}
|
||||
|
||||
function getEncodingExtension (encoding) {
|
||||
switch (encoding) {
|
||||
case 'br':
|
||||
return 'br'
|
||||
|
||||
case 'gzip':
|
||||
return 'gz'
|
||||
}
|
||||
}
|
||||
|
||||
function getRedirectUrl (url) {
|
||||
let i = 0
|
||||
// we detect how many slash before a valid path
|
||||
for (; i < url.length; ++i) {
|
||||
if (url[i] !== '/' && url[i] !== '\\') break
|
||||
}
|
||||
// turns all leading / or \ into a single /
|
||||
url = '/' + url.substr(i)
|
||||
try {
|
||||
const parsed = new URL(url, 'http://localhost.com/')
|
||||
const parsedPathname = parsed.pathname
|
||||
return parsedPathname + (parsedPathname[parsedPathname.length - 1] !== '/' ? '/' : '') + (parsed.search || '')
|
||||
} catch {
|
||||
// the try-catch here is actually unreachable, but we keep it for safety and prevent DoS attack
|
||||
/* istanbul ignore next */
|
||||
const err = new Error(`Invalid redirect URL: ${url}`)
|
||||
/* istanbul ignore next */
|
||||
err.statusCode = 400
|
||||
/* istanbul ignore next */
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = fp(fastifyStatic, {
|
||||
fastify: '4.x',
|
||||
name: '@fastify/static'
|
||||
})
|
||||
module.exports.default = fastifyStatic
|
||||
module.exports.fastifyStatic = fastifyStatic
|
||||
211
backend/node_modules/@fastify/static/lib/dirList.js
generated
vendored
Normal file
211
backend/node_modules/@fastify/static/lib/dirList.js
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
'use strict'
|
||||
|
||||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
const fs = require('node:fs/promises')
|
||||
const fastq = require('fastq')
|
||||
const fastqConcurrency = Math.max(1, os.cpus().length - 1)
|
||||
|
||||
const dirList = {
|
||||
_getExtendedInfo: async function (dir, info) {
|
||||
const depth = dir.split(path.sep).length
|
||||
const files = await fs.readdir(dir)
|
||||
|
||||
const worker = async (filename) => {
|
||||
const filePath = path.join(dir, filename)
|
||||
let stats
|
||||
try {
|
||||
stats = await fs.stat(filePath)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
info.totalFolderCount++
|
||||
filePath.split(path.sep).length === depth + 1 && info.folderCount++
|
||||
await dirList._getExtendedInfo(filePath, info)
|
||||
} else {
|
||||
info.totalSize += stats.size
|
||||
info.totalFileCount++
|
||||
filePath.split(path.sep).length === depth + 1 && info.fileCount++
|
||||
info.lastModified = Math.max(info.lastModified, stats.mtimeMs)
|
||||
}
|
||||
}
|
||||
const queue = fastq.promise(worker, fastqConcurrency)
|
||||
await Promise.all(files.map(filename => queue.push(filename)))
|
||||
},
|
||||
|
||||
/**
|
||||
* get extended info about a folder
|
||||
* @param {string} folderPath full path fs dir
|
||||
* @return {Promise<ExtendedInfo>}
|
||||
*/
|
||||
getExtendedInfo: async function (folderPath) {
|
||||
const info = {
|
||||
totalSize: 0,
|
||||
fileCount: 0,
|
||||
totalFileCount: 0,
|
||||
folderCount: 0,
|
||||
totalFolderCount: 0,
|
||||
lastModified: 0
|
||||
}
|
||||
|
||||
await dirList._getExtendedInfo(folderPath, info)
|
||||
|
||||
return info
|
||||
},
|
||||
|
||||
/**
|
||||
* get files and dirs from dir, or error
|
||||
* @param {string} dir full path fs dir
|
||||
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
||||
* @param {string} dotfiles
|
||||
* note: can't use glob because don't get error on non existing dir
|
||||
*/
|
||||
list: async function (dir, options, dotfiles) {
|
||||
const entries = { dirs: [], files: [] }
|
||||
let files = await fs.readdir(dir)
|
||||
if (dotfiles === 'deny' || dotfiles === 'ignore') {
|
||||
files = files.filter(file => file.charAt(0) !== '.')
|
||||
}
|
||||
if (files.length < 1) {
|
||||
return entries
|
||||
}
|
||||
|
||||
const worker = async (filename) => {
|
||||
let stats
|
||||
try {
|
||||
stats = await fs.stat(path.join(dir, filename))
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const entry = { name: filename, stats }
|
||||
if (stats.isDirectory()) {
|
||||
if (options.extendedFolderInfo) {
|
||||
entry.extendedInfo = await dirList.getExtendedInfo(path.join(dir, filename))
|
||||
}
|
||||
entries.dirs.push(entry)
|
||||
} else {
|
||||
entries.files.push(entry)
|
||||
}
|
||||
}
|
||||
const queue = fastq.promise(worker, fastqConcurrency)
|
||||
await Promise.all(files.map(filename => queue.push(filename)))
|
||||
|
||||
entries.dirs.sort((a, b) => a.name.localeCompare(b.name))
|
||||
entries.files.sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
return entries
|
||||
},
|
||||
|
||||
/**
|
||||
* send dir list content, or 404 on error
|
||||
* @param {Fastify.Reply} reply
|
||||
* @param {string} dir full path fs dir
|
||||
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
||||
* @param {string} route request route
|
||||
* @param {string} dotfiles
|
||||
*/
|
||||
send: async function ({ reply, dir, options, route, prefix, dotfiles }) {
|
||||
if (reply.request.query.format === 'html' && typeof options.render !== 'function') {
|
||||
throw new Error('The `list.render` option must be a function and is required with the URL parameter `format=html`')
|
||||
}
|
||||
|
||||
let entries
|
||||
try {
|
||||
entries = await dirList.list(dir, options, dotfiles)
|
||||
} catch {
|
||||
return reply.callNotFound()
|
||||
}
|
||||
|
||||
const format = reply.request.query.format || options.format
|
||||
if (format !== 'html') {
|
||||
if (options.jsonFormat !== 'extended') {
|
||||
const nameEntries = { dirs: [], files: [] }
|
||||
entries.dirs.forEach(entry => nameEntries.dirs.push(entry.name))
|
||||
entries.files.forEach(entry => nameEntries.files.push(entry.name))
|
||||
|
||||
reply.send(nameEntries)
|
||||
} else {
|
||||
reply.send(entries)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const html = options.render(
|
||||
entries.dirs.map(entry => dirList.htmlInfo(entry, route, prefix, options)),
|
||||
entries.files.map(entry => dirList.htmlInfo(entry, route, prefix, options)))
|
||||
reply.type('text/html').send(html)
|
||||
},
|
||||
|
||||
/**
|
||||
* provide the html information about entry and route, to get name and full route
|
||||
* @param entry file or dir name and stats
|
||||
* @param {string} route request route
|
||||
* @return {ListFile}
|
||||
*/
|
||||
htmlInfo: function (entry, route, prefix, options) {
|
||||
if (options.names?.includes(path.basename(route))) {
|
||||
route = path.normalize(path.join(route, '..'))
|
||||
}
|
||||
return {
|
||||
href: encodeURI(path.join(prefix, route, entry.name).replace(/\\/gu, '/')),
|
||||
name: entry.name,
|
||||
stats: entry.stats,
|
||||
extendedInfo: entry.extendedInfo
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* say if the route can be handled by dir list or not
|
||||
* @param {string} route request route
|
||||
* @param {(boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat)} options
|
||||
* @return {boolean}
|
||||
*/
|
||||
handle: function (route, options) {
|
||||
return options.names?.includes(path.basename(route)) ||
|
||||
// match trailing slash
|
||||
((options.names?.includes('/') && route[route.length - 1] === '/') ?? false)
|
||||
},
|
||||
|
||||
/**
|
||||
* get path from route and fs root paths, considering trailing slash
|
||||
* @param {string} root fs root path
|
||||
* @param {string} route request route
|
||||
*/
|
||||
path: function (root, route) {
|
||||
const _route = route[route.length - 1] === '/'
|
||||
? route + 'none'
|
||||
: route
|
||||
return path.dirname(path.join(root, _route))
|
||||
},
|
||||
|
||||
/**
|
||||
* validate options
|
||||
* @return {Error}
|
||||
*/
|
||||
validateOptions: function (options) {
|
||||
if (!options.list) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(options.root)) {
|
||||
return new TypeError('multi-root with list option is not supported')
|
||||
}
|
||||
|
||||
if (options.list.format && options.list.format !== 'json' && options.list.format !== 'html') {
|
||||
return new TypeError('The `list.format` option must be json or html')
|
||||
}
|
||||
if (options.list.names && !Array.isArray(options.list.names)) {
|
||||
return new TypeError('The `list.names` option must be an array')
|
||||
}
|
||||
if (options.list.jsonFormat != null && options.list.jsonFormat !== 'names' && options.list.jsonFormat !== 'extended') {
|
||||
return new TypeError('The `list.jsonFormat` option must be name or extended')
|
||||
}
|
||||
if (options.list.format === 'html' && typeof options.list.render !== 'function') {
|
||||
return new TypeError('The `list.render` option must be a function and is required with html format')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = dirList
|
||||
73
backend/node_modules/@fastify/static/package.json
generated
vendored
Normal file
73
backend/node_modules/@fastify/static/package.json
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "@fastify/static",
|
||||
"version": "7.0.4",
|
||||
"description": "Plugin for serving static files as fast as possible.",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"coverage": "npm run test:unit -- --coverage-report=html",
|
||||
"lint": "npm run lint:javascript && npm run lint:typescript",
|
||||
"lint:javascript": "standard | snazzy",
|
||||
"lint:fix": "standard --fix && npm run lint:typescript -- --fix",
|
||||
"lint:typescript": "eslint -c .eslintrc.json types/**/*.d.ts types/**/*.test-d.ts",
|
||||
"test": "npm run test:unit && npm run test:typescript",
|
||||
"test:typescript": "tsd",
|
||||
"test:unit": "tap",
|
||||
"example": "node example/server.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/fastify-static.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fastify",
|
||||
"static"
|
||||
],
|
||||
"author": "Tommaso Allevi - @allevo",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fastify/fastify-static/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fastify/fastify-static",
|
||||
"dependencies": {
|
||||
"@fastify/accept-negotiator": "^1.0.0",
|
||||
"@fastify/send": "^2.0.0",
|
||||
"content-disposition": "^0.5.3",
|
||||
"fastify-plugin": "^4.0.0",
|
||||
"fastq": "^1.17.0",
|
||||
"glob": "^10.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fastify/compress": "^7.0.0",
|
||||
"@fastify/pre-commit": "^2.0.2",
|
||||
"@types/node": "^20.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"coveralls": "^3.0.4",
|
||||
"eslint-plugin-typescript": "^0.14.0",
|
||||
"fastify": "^4.0.0-rc.2",
|
||||
"handlebars": "^4.7.6",
|
||||
"pino": "^8.4.2",
|
||||
"proxyquire": "^2.1.0",
|
||||
"simple-get": "^4.0.0",
|
||||
"snazzy": "^9.0.0",
|
||||
"standard": "^17.0.0",
|
||||
"tap": "^16.0.0",
|
||||
"tsd": "^0.31.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "test/types"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
174
backend/node_modules/@fastify/static/test/content-type.test.js
generated
vendored
Normal file
174
backend/node_modules/@fastify/static/test/content-type.test.js
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint n/no-deprecated-api: "off" */
|
||||
|
||||
const path = require('node:path')
|
||||
const { test } = require('tap')
|
||||
const simple = require('simple-get')
|
||||
const Fastify = require('fastify')
|
||||
|
||||
const fastifyStatic = require('../')
|
||||
|
||||
test('register /content-type', t => {
|
||||
t.plan(6)
|
||||
|
||||
const pluginOptions = {
|
||||
root: path.join(__dirname, '/content-type'),
|
||||
prefix: '/content-type'
|
||||
}
|
||||
const fastify = Fastify()
|
||||
fastify.register(fastifyStatic, pluginOptions)
|
||||
|
||||
t.teardown(fastify.close.bind(fastify))
|
||||
|
||||
fastify.listen({ port: 0 }, (err) => {
|
||||
t.error(err)
|
||||
|
||||
fastify.server.unref()
|
||||
|
||||
t.test('/content-type/index.html', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/index.html'
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/html; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/index.css', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/index.css'
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/css; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/sample.jpg', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/sample.jpg'
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'image/jpeg')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/test.txt', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/test.txt'
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/plain; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/binary', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/binary'
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'application/octet-stream')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('register /content-type preCompressed', t => {
|
||||
t.plan(6)
|
||||
|
||||
const pluginOptions = {
|
||||
root: path.join(__dirname, '/content-type'),
|
||||
prefix: '/content-type',
|
||||
preCompressed: true
|
||||
}
|
||||
const fastify = Fastify()
|
||||
fastify.register(fastifyStatic, pluginOptions)
|
||||
|
||||
t.teardown(fastify.close.bind(fastify))
|
||||
|
||||
fastify.listen({ port: 0 }, (err) => {
|
||||
t.error(err)
|
||||
|
||||
fastify.server.unref()
|
||||
|
||||
t.test('/content-type/index.html', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/index.html',
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, deflate, br'
|
||||
}
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/html; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/index.css', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/index.css',
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, deflate, br'
|
||||
}
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/css; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/sample.jpg', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/sample.jpg',
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, deflate, br'
|
||||
}
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'image/jpeg')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/test.txt', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/test.txt',
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, deflate, br'
|
||||
}
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'text/plain; charset=UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('/content-type/binary', (t) => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:' + fastify.server.address().port + '/content-type/binary',
|
||||
headers: {
|
||||
'accept-encoding': 'gzip, deflate, br'
|
||||
}
|
||||
}, (err, response) => {
|
||||
t.error(err)
|
||||
t.equal(response.headers['content-type'], 'application/octet-stream')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
0
backend/node_modules/@fastify/static/test/content-type/binary
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/content-type/binary
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/binary.br
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/binary.br
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<EFBFBD>
|
||||
0
backend/node_modules/@fastify/static/test/content-type/index.css
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/content-type/index.css
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/index.css.br
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/index.css.br
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<EFBFBD>
|
||||
5
backend/node_modules/@fastify/static/test/content-type/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/content-type/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
the body
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/content-type/index.html.br
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/content-type/index.html.br
generated
vendored
Normal file
Binary file not shown.
BIN
backend/node_modules/@fastify/static/test/content-type/sample.jpg
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/content-type/sample.jpg
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
0
backend/node_modules/@fastify/static/test/content-type/test.txt
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/content-type/test.txt
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/test.txt.br
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/content-type/test.txt.br
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<EFBFBD>
|
||||
866
backend/node_modules/@fastify/static/test/dir-list.test.js
generated
vendored
Normal file
866
backend/node_modules/@fastify/static/test/dir-list.test.js
generated
vendored
Normal file
@@ -0,0 +1,866 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint n/no-deprecated-api: "off" */
|
||||
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const t = require('tap')
|
||||
const simple = require('simple-get')
|
||||
const Fastify = require('fastify')
|
||||
|
||||
const fastifyStatic = require('..')
|
||||
const dirList = require('../lib/dirList')
|
||||
|
||||
const helper = {
|
||||
arrange: function (t, options, f) {
|
||||
return helper.arrangeModule(t, options, fastifyStatic, f)
|
||||
},
|
||||
arrangeModule: function (t, options, mock, f) {
|
||||
const fastify = Fastify()
|
||||
fastify.register(mock, options)
|
||||
t.teardown(fastify.close.bind(fastify))
|
||||
fastify.listen({ port: 0 }, err => {
|
||||
t.error(err)
|
||||
fastify.server.unref()
|
||||
f('http://localhost:' + fastify.server.address().port)
|
||||
})
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(path.join(__dirname, 'static/shallow/empty'))
|
||||
} catch (error) {}
|
||||
|
||||
t.test('throws when `root` is an array', t => {
|
||||
t.plan(2)
|
||||
|
||||
const err = dirList.validateOptions({ root: ['hello', 'world'], list: true })
|
||||
t.type(err, TypeError)
|
||||
t.equal(err.message, 'multi-root with list option is not supported')
|
||||
})
|
||||
|
||||
t.test('throws when `list.format` option is invalid', t => {
|
||||
t.plan(2)
|
||||
|
||||
const err = dirList.validateOptions({ list: { format: 'hello' } })
|
||||
t.type(err, TypeError)
|
||||
t.equal(err.message, 'The `list.format` option must be json or html')
|
||||
})
|
||||
|
||||
t.test('throws when `list.names option` is not an array', t => {
|
||||
t.plan(2)
|
||||
|
||||
const err = dirList.validateOptions({ list: { names: 'hello' } })
|
||||
t.type(err, TypeError)
|
||||
t.equal(err.message, 'The `list.names` option must be an array')
|
||||
})
|
||||
|
||||
t.test('throws when `list.jsonFormat` option is invalid', t => {
|
||||
t.plan(2)
|
||||
|
||||
const err = dirList.validateOptions({ list: { jsonFormat: 'hello' } })
|
||||
t.type(err, TypeError)
|
||||
t.equal(err.message, 'The `list.jsonFormat` option must be name or extended')
|
||||
})
|
||||
|
||||
t.test('throws when `list.format` is html and `list render` is not a function', t => {
|
||||
t.plan(2)
|
||||
|
||||
const err = dirList.validateOptions({ list: { format: 'html', render: 'hello' } })
|
||||
t.type(err, TypeError)
|
||||
t.equal(err.message, 'The `list.render` option must be a function and is required with html format')
|
||||
})
|
||||
|
||||
t.test('dir list wrong options', t => {
|
||||
t.plan(3)
|
||||
|
||||
const cases = [
|
||||
{
|
||||
options: {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
list: {
|
||||
format: 'no-json,no-html'
|
||||
}
|
||||
},
|
||||
error: new TypeError('The `list.format` option must be json or html')
|
||||
},
|
||||
{
|
||||
options: {
|
||||
root: path.join(__dirname, '/static'),
|
||||
list: {
|
||||
format: 'html'
|
||||
// no render function
|
||||
}
|
||||
},
|
||||
error: new TypeError('The `list.render` option must be a function and is required with html format')
|
||||
},
|
||||
{
|
||||
options: {
|
||||
root: path.join(__dirname, '/static'),
|
||||
list: {
|
||||
names: 'not-an-array'
|
||||
}
|
||||
},
|
||||
error: new TypeError('The `list.names` option must be an array')
|
||||
}
|
||||
]
|
||||
|
||||
for (const case_ of cases) {
|
||||
const fastify = Fastify()
|
||||
fastify.register(fastifyStatic, case_.options)
|
||||
fastify.listen({ port: 0 }, err => {
|
||||
t.equal(err.message, case_.error.message)
|
||||
fastify.server.unref()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.test('dir list default options', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
list: true
|
||||
}
|
||||
const route = '/public/shallow'
|
||||
const content = { dirs: ['empty'], files: ['sample.jpg'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list, custom options', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: true
|
||||
}
|
||||
|
||||
const route = '/public/'
|
||||
const content = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list html format', t => {
|
||||
t.plan(6)
|
||||
|
||||
// render html in 2 ways: one with handlebars and one with template string
|
||||
|
||||
const Handlebars = require('handlebars')
|
||||
const source = `
|
||||
<html><body>
|
||||
<ul>
|
||||
{{#dirs}}
|
||||
<li><a href="{{href}}">{{name}}</a></li>
|
||||
{{/dirs}}
|
||||
</ul>
|
||||
<ul>
|
||||
{{#files}}
|
||||
<li><a href="{{href}}" target="_blank">{{name}}</a></li>
|
||||
{{/files}}
|
||||
</ul>
|
||||
</body></html>
|
||||
`
|
||||
const handlebarTemplate = Handlebars.compile(source)
|
||||
const templates = [
|
||||
{
|
||||
render: (dirs, files) => {
|
||||
return handlebarTemplate({ dirs, files })
|
||||
},
|
||||
output: `
|
||||
<html><body>
|
||||
<ul>
|
||||
<li><a href="/public/deep">deep</a></li>
|
||||
<li><a href="/public/shallow">shallow</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/public/.example" target="_blank">.example</a></li>
|
||||
<li><a href="/public/100%25.txt" target="_blank">100%.txt</a></li>
|
||||
<li><a href="/public/a%20.md" target="_blank">a .md</a></li>
|
||||
<li><a href="/public/foo.html" target="_blank">foo.html</a></li>
|
||||
<li><a href="/public/foobar.html" target="_blank">foobar.html</a></li>
|
||||
<li><a href="/public/index.css" target="_blank">index.css</a></li>
|
||||
<li><a href="/public/index.html" target="_blank">index.html</a></li>
|
||||
</ul>
|
||||
</body></html>
|
||||
`
|
||||
},
|
||||
|
||||
{
|
||||
render: (dirs, files) => {
|
||||
return `
|
||||
<html><body>
|
||||
<ul>
|
||||
${dirs.map(dir => `<li><a href="${dir.href}">${dir.name}</a></li>`).join('\n ')}
|
||||
</ul>
|
||||
<ul>
|
||||
${files.map(file => `<li><a href="${file.href}" target="_blank">${file.name}</a></li>`).join('\n ')}
|
||||
</ul>
|
||||
</body></html>
|
||||
`
|
||||
},
|
||||
output: `
|
||||
<html><body>
|
||||
<ul>
|
||||
<li><a href="/public/deep">deep</a></li>
|
||||
<li><a href="/public/shallow">shallow</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/public/.example" target="_blank">.example</a></li>
|
||||
<li><a href="/public/100%25.txt" target="_blank">100%.txt</a></li>
|
||||
<li><a href="/public/a%20.md" target="_blank">a .md</a></li>
|
||||
<li><a href="/public/foo.html" target="_blank">foo.html</a></li>
|
||||
<li><a href="/public/foobar.html" target="_blank">foobar.html</a></li>
|
||||
<li><a href="/public/index.css" target="_blank">index.css</a></li>
|
||||
<li><a href="/public/index.html" target="_blank">index.html</a></li>
|
||||
</ul>
|
||||
</body></html>
|
||||
`
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
for (const template of templates) {
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
names: ['index', 'index.htm'],
|
||||
render: template.render
|
||||
}
|
||||
}
|
||||
const routes = ['/public/index.htm', '/public/index']
|
||||
|
||||
// check all routes by names
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
for (const route of routes) {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), template.output)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.test('dir list href nested structure', t => {
|
||||
t.plan(6)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
names: ['index', 'index.htm'],
|
||||
render (dirs, files) {
|
||||
return dirs[0].href
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const routes = [
|
||||
{ path: '/public/', response: '/public/deep' },
|
||||
{ path: '/public/index', response: '/public/deep' },
|
||||
{ path: '/public/deep/', response: '/public/deep/path' },
|
||||
{ path: '/public/deep/index.htm', response: '/public/deep/path' },
|
||||
{ path: '/public/deep/path/', response: '/public/deep/path/for' }
|
||||
]
|
||||
helper.arrange(t, options, (url) => {
|
||||
for (const route of routes) {
|
||||
t.test(route.path, t => {
|
||||
t.plan(5)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route.path
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), route.response)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + body.toString()
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list html format - stats', t => {
|
||||
t.plan(7)
|
||||
|
||||
const options1 = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
render (dirs, files) {
|
||||
t.ok(dirs.length > 0)
|
||||
t.ok(files.length > 0)
|
||||
|
||||
t.ok(dirs.every(every))
|
||||
t.ok(files.every(every))
|
||||
|
||||
function every (value) {
|
||||
return value.stats &&
|
||||
value.stats.atime &&
|
||||
!value.extendedInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const route = '/public/'
|
||||
|
||||
helper.arrange(t, options1, (url) => {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list html format - extended info', t => {
|
||||
t.plan(4)
|
||||
|
||||
const route = '/public/'
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
extendedFolderInfo: true,
|
||||
render (dirs, files) {
|
||||
t.test('dirs', t => {
|
||||
t.plan(dirs.length * 7)
|
||||
|
||||
for (const value of dirs) {
|
||||
t.ok(value.extendedInfo)
|
||||
|
||||
t.equal(typeof value.extendedInfo.fileCount, 'number')
|
||||
t.equal(typeof value.extendedInfo.totalFileCount, 'number')
|
||||
t.equal(typeof value.extendedInfo.folderCount, 'number')
|
||||
t.equal(typeof value.extendedInfo.totalFolderCount, 'number')
|
||||
t.equal(typeof value.extendedInfo.totalSize, 'number')
|
||||
t.equal(typeof value.extendedInfo.lastModified, 'number')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list json format', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
prefixAvoidTrailingSlash: true,
|
||||
list: {
|
||||
format: 'json',
|
||||
names: ['index', 'index.json', '/']
|
||||
}
|
||||
}
|
||||
const routes = ['/public/shallow/']
|
||||
const content = { dirs: ['empty'], files: ['sample.jpg'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
for (const route of routes) {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list json format - extended info', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
prefixAvoidTrailingSlash: true,
|
||||
list: {
|
||||
format: 'json',
|
||||
names: ['index', 'index.json', '/'],
|
||||
extendedFolderInfo: true,
|
||||
jsonFormat: 'extended'
|
||||
|
||||
}
|
||||
}
|
||||
const routes = ['/public/shallow/']
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
for (const route of routes) {
|
||||
t.test(route, t => {
|
||||
t.plan(5)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
const bodyObject = JSON.parse(body.toString())
|
||||
t.equal(bodyObject.dirs[0].name, 'empty')
|
||||
t.equal(typeof bodyObject.dirs[0].stats.atime, 'string')
|
||||
t.equal(typeof bodyObject.dirs[0].extendedInfo.totalSize, 'number')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.test('json format with url parameter format', t => {
|
||||
t.plan(13)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'json',
|
||||
render (dirs, files) {
|
||||
return 'html'
|
||||
}
|
||||
}
|
||||
}
|
||||
const route = '/public/'
|
||||
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(jsonContent))
|
||||
t.ok(response.headers['content-type'].includes('application/json'))
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=html'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), 'html')
|
||||
t.ok(response.headers['content-type'].includes('text/html'))
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=json'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(jsonContent))
|
||||
t.ok(response.headers['content-type'].includes('application/json'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('json format with url parameter format and without render option', t => {
|
||||
t.plan(12)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'json'
|
||||
}
|
||||
}
|
||||
const route = '/public/'
|
||||
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(jsonContent))
|
||||
t.ok(response.headers['content-type'].includes('application/json'))
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=html'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 500)
|
||||
t.equal(JSON.parse(body.toString()).message, 'The `list.render` option must be a function and is required with the URL parameter `format=html`')
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=json'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(jsonContent))
|
||||
t.ok(response.headers['content-type'].includes('application/json'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('html format with url parameter format', t => {
|
||||
t.plan(13)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
render (dirs, files) {
|
||||
return 'html'
|
||||
}
|
||||
}
|
||||
}
|
||||
const route = '/public/'
|
||||
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), 'html')
|
||||
t.ok(response.headers['content-type'].includes('text/html'))
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=html'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), 'html')
|
||||
t.ok(response.headers['content-type'].includes('text/html'))
|
||||
})
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route + '?format=json'
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(jsonContent))
|
||||
t.ok(response.headers['content-type'].includes('application/json'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list on empty dir', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
list: true
|
||||
}
|
||||
const route = '/public/shallow/empty'
|
||||
const content = { dirs: [], files: [] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list serve index.html on index option', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
names: ['index', 'index.html'],
|
||||
render: () => 'dir list index'
|
||||
}
|
||||
}
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test('serve index.html from fs', t => {
|
||||
t.plan(6)
|
||||
|
||||
let route = '/public/index.html'
|
||||
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), '<html>\n <body>\n the body\n </body>\n</html>\n')
|
||||
})
|
||||
|
||||
route = '/public/index'
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), 'dir list index')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('serve a non existent dir and get error', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: '/none',
|
||||
prefix: '/public',
|
||||
list: true
|
||||
}
|
||||
const route = '/public/'
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 404)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('serve a non existent dir and get error', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
list: {
|
||||
names: ['index']
|
||||
}
|
||||
}
|
||||
const route = '/public/none/index'
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(2)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 404)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list with dotfiles allow option', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static-dotfiles'),
|
||||
prefix: '/public',
|
||||
dotfiles: 'allow',
|
||||
index: false,
|
||||
list: true
|
||||
}
|
||||
const route = '/public/'
|
||||
const content = { dirs: ['dir'], files: ['.aaa', 'test.txt'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list with dotfiles deny option', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static-dotfiles'),
|
||||
prefix: '/public',
|
||||
dotfiles: 'deny',
|
||||
index: false,
|
||||
list: true
|
||||
}
|
||||
const route = '/public/'
|
||||
const content = { dirs: ['dir'], files: ['test.txt'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list with dotfiles ignore option', t => {
|
||||
t.plan(2)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static-dotfiles'),
|
||||
prefix: '/public',
|
||||
dotfiles: 'ignore',
|
||||
index: false,
|
||||
list: true
|
||||
}
|
||||
const route = '/public/'
|
||||
const content = { dirs: ['dir'], files: ['test.txt'] }
|
||||
|
||||
helper.arrange(t, options, (url) => {
|
||||
t.test(route, t => {
|
||||
t.plan(3)
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(response.statusCode, 200)
|
||||
t.equal(body.toString(), JSON.stringify(content))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('dir list error', t => {
|
||||
t.plan(7)
|
||||
|
||||
const options = {
|
||||
root: path.join(__dirname, '/static'),
|
||||
prefix: '/public',
|
||||
prefixAvoidTrailingSlash: true,
|
||||
index: false,
|
||||
list: {
|
||||
format: 'html',
|
||||
names: ['index', 'index.htm'],
|
||||
render: () => ''
|
||||
}
|
||||
}
|
||||
|
||||
const errorMessage = 'mocking send'
|
||||
dirList.send = async () => { throw new Error(errorMessage) }
|
||||
|
||||
const mock = t.mock('..', {
|
||||
'../lib/dirList.js': dirList
|
||||
})
|
||||
|
||||
const routes = ['/public/', '/public/index.htm']
|
||||
|
||||
helper.arrangeModule(t, options, mock, (url) => {
|
||||
for (const route of routes) {
|
||||
simple.concat({
|
||||
method: 'GET',
|
||||
url: url + route
|
||||
}, (err, response, body) => {
|
||||
t.error(err)
|
||||
t.equal(JSON.parse(body.toString()).message, errorMessage)
|
||||
t.equal(response.statusCode, 500)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
0
backend/node_modules/@fastify/static/test/static-dotfiles/.aaa
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-dotfiles/.aaa
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-dotfiles/dir/index.html
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-dotfiles/dir/index.html
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-dotfiles/test.txt
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-dotfiles/test.txt
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static-encode/[...]/a .md
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static-encode/[...]/a .md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
example
|
||||
1
backend/node_modules/@fastify/static/test/static-hidden/.hidden/sample.json
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static-hidden/.hidden/sample.json
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"hello": "world"}
|
||||
5
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
the body
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html.br
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html.br
generated
vendored
Normal file
Binary file not shown.
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html.gz
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/all-three.html.gz
generated
vendored
Normal file
Binary file not shown.
5
backend/node_modules/@fastify/static/test/static-pre-compressed/dir-gz/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static-pre-compressed/dir-gz/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
dir-gz index
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/dir-gz/index.html.gz
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/dir-gz/index.html.gz
generated
vendored
Normal file
Binary file not shown.
5
backend/node_modules/@fastify/static/test/static-pre-compressed/dir/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static-pre-compressed/dir/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
dir index
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/dir/index.html.br
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/dir/index.html.br
generated
vendored
Normal file
Binary file not shown.
0
backend/node_modules/@fastify/static/test/static-pre-compressed/empty/.gitkeep
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static-pre-compressed/empty/.gitkeep
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static-pre-compressed/gzip-only.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static-pre-compressed/gzip-only.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>foo</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/gzip-only.html.gz
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/gzip-only.html.gz
generated
vendored
Normal file
Binary file not shown.
5
backend/node_modules/@fastify/static/test/static-pre-compressed/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static-pre-compressed/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
index
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/index.html.br
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/index.html.br
generated
vendored
Normal file
Binary file not shown.
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/sample.jpg
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/sample.jpg
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/sample.jpg.br
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static-pre-compressed/sample.jpg.br
generated
vendored
Normal file
Binary file not shown.
3
backend/node_modules/@fastify/static/test/static-pre-compressed/uncompressed.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static-pre-compressed/uncompressed.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>foobar</body>
|
||||
</html>
|
||||
3
backend/node_modules/@fastify/static/test/static-symbolic-link/origin/subdir/subdir/index.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static-symbolic-link/origin/subdir/subdir/index.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>index</body>
|
||||
</html>
|
||||
4025
backend/node_modules/@fastify/static/test/static.test.js
generated
vendored
Normal file
4025
backend/node_modules/@fastify/static/test/static.test.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
backend/node_modules/@fastify/static/test/static/.example
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static/.example
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
contents of a dotfile
|
||||
1
backend/node_modules/@fastify/static/test/static/100%.txt
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static/100%.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
100%
|
||||
1
backend/node_modules/@fastify/static/test/static/a .md
generated
vendored
Normal file
1
backend/node_modules/@fastify/static/test/static/a .md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
example
|
||||
5
backend/node_modules/@fastify/static/test/static/deep/path/for/test/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static/deep/path/for/test/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
inner index.html
|
||||
</body>
|
||||
</html>
|
||||
5
backend/node_modules/@fastify/static/test/static/deep/path/for/test/purpose/foo.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static/deep/path/for/test/purpose/foo.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
the deep path for test purpose body
|
||||
</body>
|
||||
</html>
|
||||
3
backend/node_modules/@fastify/static/test/static/foo.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static/foo.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>foo</body>
|
||||
</html>
|
||||
3
backend/node_modules/@fastify/static/test/static/foobar.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static/foobar.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>foobar</body>
|
||||
</html>
|
||||
0
backend/node_modules/@fastify/static/test/static/index.css
generated
vendored
Normal file
0
backend/node_modules/@fastify/static/test/static/index.css
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static/index.html
generated
vendored
Normal file
5
backend/node_modules/@fastify/static/test/static/index.html
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
the body
|
||||
</body>
|
||||
</html>
|
||||
BIN
backend/node_modules/@fastify/static/test/static/shallow/sample.jpg
generated
vendored
Normal file
BIN
backend/node_modules/@fastify/static/test/static/shallow/sample.jpg
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
3
backend/node_modules/@fastify/static/test/static2/bar.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static2/bar.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>bar</body>
|
||||
</html>
|
||||
3
backend/node_modules/@fastify/static/test/static2/index.html
generated
vendored
Normal file
3
backend/node_modules/@fastify/static/test/static2/index.html
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>
|
||||
<body>index2</body>
|
||||
</html>
|
||||
10
backend/node_modules/@fastify/static/tsconfig.eslint.json
generated
vendored
Normal file
10
backend/node_modules/@fastify/static/tsconfig.eslint.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["ES2018"],
|
||||
"module": "commonjs",
|
||||
"noEmit": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["types/*.test-d.ts", "types/*.d.ts"]
|
||||
}
|
||||
124
backend/node_modules/@fastify/static/types/index.d.ts
generated
vendored
Normal file
124
backend/node_modules/@fastify/static/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Definitions by: Jannik <https://github.com/jannikkeye>
|
||||
// Leo <https://github.com/leomelzer>
|
||||
/// <reference types="node" />
|
||||
|
||||
import { FastifyPluginAsync, FastifyReply, FastifyRequest, RouteOptions } from 'fastify'
|
||||
import { Stats } from 'fs'
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyReply {
|
||||
sendFile(filename: string, rootPath?: string): FastifyReply;
|
||||
sendFile(filename: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
||||
sendFile(filename: string, rootPath?: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
||||
download(filepath: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
||||
download(filepath: string, filename?: string): FastifyReply;
|
||||
download(filepath: string, filename?: string, options?: fastifyStatic.SendOptions): FastifyReply;
|
||||
}
|
||||
}
|
||||
|
||||
type FastifyStaticPlugin = FastifyPluginAsync<NonNullable<fastifyStatic.FastifyStaticOptions>>;
|
||||
|
||||
declare namespace fastifyStatic {
|
||||
export interface SetHeadersResponse {
|
||||
getHeader: FastifyReply['getHeader'];
|
||||
setHeader: FastifyReply['header'];
|
||||
readonly filename: string;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export interface ExtendedInformation {
|
||||
fileCount: number;
|
||||
totalFileCount: number;
|
||||
folderCount: number;
|
||||
totalFolderCount: number;
|
||||
totalSize: number;
|
||||
lastModified: number;
|
||||
}
|
||||
|
||||
export interface ListDir {
|
||||
href: string;
|
||||
name: string;
|
||||
stats: Stats;
|
||||
extendedInfo?: ExtendedInformation;
|
||||
}
|
||||
|
||||
export interface ListFile {
|
||||
href: string;
|
||||
name: string;
|
||||
stats: Stats;
|
||||
}
|
||||
|
||||
export interface ListRender {
|
||||
(dirs: ListDir[], files: ListFile[]): string;
|
||||
}
|
||||
|
||||
export interface ListOptions {
|
||||
names?: string[];
|
||||
extendedFolderInfo?: boolean;
|
||||
jsonFormat?: 'names' | 'extended';
|
||||
}
|
||||
|
||||
export interface ListOptionsJsonFormat extends ListOptions {
|
||||
format: 'json';
|
||||
// Required when the URL parameter `format=html` exists
|
||||
render?: ListRender;
|
||||
}
|
||||
|
||||
export interface ListOptionsHtmlFormat extends ListOptions {
|
||||
format: 'html';
|
||||
render: ListRender;
|
||||
}
|
||||
|
||||
// Passed on to `send`
|
||||
export interface SendOptions {
|
||||
acceptRanges?: boolean;
|
||||
cacheControl?: boolean;
|
||||
dotfiles?: 'allow' | 'deny' | 'ignore';
|
||||
etag?: boolean;
|
||||
extensions?: string[];
|
||||
immutable?: boolean;
|
||||
index?: string[] | string | false;
|
||||
lastModified?: boolean;
|
||||
maxAge?: string | number;
|
||||
serveDotFiles?: boolean;
|
||||
}
|
||||
|
||||
export interface FastifyStaticOptions extends SendOptions {
|
||||
root: string | string[] | URL | URL[];
|
||||
prefix?: string;
|
||||
prefixAvoidTrailingSlash?: boolean;
|
||||
serve?: boolean;
|
||||
decorateReply?: boolean;
|
||||
schemaHide?: boolean;
|
||||
setHeaders?: (res: SetHeadersResponse, path: string, stat: Stats) => void;
|
||||
redirect?: boolean;
|
||||
wildcard?: boolean;
|
||||
list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;
|
||||
allowedPath?: (pathName: string, root: string, request: FastifyRequest) => boolean;
|
||||
/**
|
||||
* @description
|
||||
* Opt-in to looking for pre-compressed files
|
||||
*/
|
||||
preCompressed?: boolean;
|
||||
|
||||
// Passed on to `send`
|
||||
acceptRanges?: boolean;
|
||||
cacheControl?: boolean;
|
||||
dotfiles?: 'allow' | 'deny' | 'ignore';
|
||||
etag?: boolean;
|
||||
extensions?: string[];
|
||||
immutable?: boolean;
|
||||
index?: string[] | string | false;
|
||||
lastModified?: boolean;
|
||||
maxAge?: string | number;
|
||||
constraints?: RouteOptions['constraints'];
|
||||
}
|
||||
|
||||
export const fastifyStatic: FastifyStaticPlugin
|
||||
|
||||
export { fastifyStatic as default }
|
||||
}
|
||||
|
||||
declare function fastifyStatic(...params: Parameters<FastifyStaticPlugin>): ReturnType<FastifyStaticPlugin>;
|
||||
|
||||
export = fastifyStatic;
|
||||
212
backend/node_modules/@fastify/static/types/index.test-d.ts
generated
vendored
Normal file
212
backend/node_modules/@fastify/static/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
import fastify, { FastifyInstance, FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify'
|
||||
import { Server } from 'http'
|
||||
import { Stats } from 'fs'
|
||||
import { expectAssignable, expectError, expectType } from 'tsd'
|
||||
import * as fastifyStaticStar from '..'
|
||||
import fastifyStatic, {
|
||||
FastifyStaticOptions,
|
||||
fastifyStatic as fastifyStaticNamed
|
||||
} from '..'
|
||||
|
||||
import fastifyStaticCjsImport = require('..');
|
||||
const fastifyStaticCjs = require('..')
|
||||
|
||||
const app: FastifyInstance = fastify()
|
||||
|
||||
app.register(fastifyStatic)
|
||||
app.register(fastifyStaticNamed)
|
||||
app.register(fastifyStaticCjs)
|
||||
app.register(fastifyStaticCjsImport.default)
|
||||
app.register(fastifyStaticCjsImport.fastifyStatic)
|
||||
app.register(fastifyStaticStar.default)
|
||||
app.register(fastifyStaticStar.fastifyStatic)
|
||||
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(fastifyStatic)
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(fastifyStaticNamed)
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(fastifyStaticCjsImport.default)
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(fastifyStaticCjsImport.fastifyStatic)
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(fastifyStaticStar.default)
|
||||
expectType<FastifyPluginAsync<FastifyStaticOptions, Server>>(
|
||||
fastifyStaticStar.fastifyStatic
|
||||
)
|
||||
expectType<any>(fastifyStaticCjs)
|
||||
|
||||
const appWithImplicitHttp = fastify()
|
||||
const options: FastifyStaticOptions = {
|
||||
acceptRanges: true,
|
||||
cacheControl: true,
|
||||
decorateReply: true,
|
||||
dotfiles: 'allow',
|
||||
etag: true,
|
||||
extensions: ['.js'],
|
||||
immutable: true,
|
||||
index: ['1'],
|
||||
lastModified: true,
|
||||
maxAge: '',
|
||||
prefix: '',
|
||||
prefixAvoidTrailingSlash: false,
|
||||
root: '',
|
||||
schemaHide: true,
|
||||
serve: true,
|
||||
wildcard: true,
|
||||
list: false,
|
||||
setHeaders: (res, path, stat) => {
|
||||
expectType<string>(res.filename)
|
||||
expectType<number>(res.statusCode)
|
||||
expectType<ReturnType<FastifyReply['getHeader']>>(res.getHeader('X-Test'))
|
||||
res.setHeader('X-Test', 'string')
|
||||
|
||||
expectType<string>(path)
|
||||
|
||||
expectType<Stats>(stat)
|
||||
},
|
||||
preCompressed: false,
|
||||
allowedPath: (pathName: string, root: string, request: FastifyRequest) => {
|
||||
return true
|
||||
},
|
||||
constraints: {
|
||||
host: /.*\.example\.com/,
|
||||
version: '1.0.2'
|
||||
}
|
||||
}
|
||||
|
||||
expectError<FastifyStaticOptions>({
|
||||
root: '',
|
||||
wildcard: '**/**'
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: '',
|
||||
list: {
|
||||
format: 'json'
|
||||
}
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: '',
|
||||
list: {
|
||||
format: 'json',
|
||||
render: () => ''
|
||||
}
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: '',
|
||||
list: {
|
||||
format: 'html',
|
||||
render: () => ''
|
||||
}
|
||||
})
|
||||
|
||||
expectError<FastifyStaticOptions>({
|
||||
root: '',
|
||||
list: {
|
||||
format: 'html'
|
||||
}
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: ['']
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: new URL('')
|
||||
})
|
||||
|
||||
expectAssignable<FastifyStaticOptions>({
|
||||
root: [new URL('')]
|
||||
})
|
||||
|
||||
appWithImplicitHttp
|
||||
.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
appWithImplicitHttp.get('/', (request, reply) => {
|
||||
reply.sendFile('some-file-name')
|
||||
})
|
||||
})
|
||||
|
||||
const appWithHttp2 = fastify({ http2: true })
|
||||
|
||||
appWithHttp2
|
||||
.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
appWithHttp2.get('/', (request, reply) => {
|
||||
reply.sendFile('some-file-name')
|
||||
})
|
||||
|
||||
appWithHttp2.get('/download', (request, reply) => {
|
||||
reply.download('some-file-name')
|
||||
})
|
||||
|
||||
appWithHttp2.get('/download/1', (request, reply) => {
|
||||
reply.download('some-file-name', { maxAge: '2 days' })
|
||||
})
|
||||
|
||||
appWithHttp2.get('/download/2', (request, reply) => {
|
||||
reply.download('some-file-name', 'some-filename', { cacheControl: false, acceptRanges: true })
|
||||
})
|
||||
})
|
||||
|
||||
const multiRootAppWithImplicitHttp = fastify()
|
||||
options.root = ['']
|
||||
|
||||
multiRootAppWithImplicitHttp
|
||||
.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
multiRootAppWithImplicitHttp.get('/', (request, reply) => {
|
||||
reply.sendFile('some-file-name')
|
||||
})
|
||||
|
||||
multiRootAppWithImplicitHttp.get('/', (request, reply) => {
|
||||
reply.sendFile('some-file-name', { cacheControl: false, acceptRanges: true })
|
||||
})
|
||||
|
||||
multiRootAppWithImplicitHttp.get('/', (request, reply) => {
|
||||
reply.sendFile('some-file-name', 'some-root-name', { cacheControl: false, acceptRanges: true })
|
||||
})
|
||||
|
||||
multiRootAppWithImplicitHttp.get('/download', (request, reply) => {
|
||||
reply.download('some-file-name')
|
||||
})
|
||||
|
||||
multiRootAppWithImplicitHttp.get('/download/1', (request, reply) => {
|
||||
reply.download('some-file-name', { maxAge: '2 days' })
|
||||
})
|
||||
|
||||
multiRootAppWithImplicitHttp.get('/download/2', (request, reply) => {
|
||||
reply.download('some-file-name', 'some-filename', { cacheControl: false, acceptRanges: true })
|
||||
})
|
||||
})
|
||||
|
||||
const noIndexApp = fastify()
|
||||
options.root = ''
|
||||
options.index = false
|
||||
|
||||
noIndexApp
|
||||
.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
noIndexApp.get('/', (request, reply) => {
|
||||
reply.send('<h1>fastify-static</h1>')
|
||||
})
|
||||
})
|
||||
|
||||
options.root = new URL('')
|
||||
|
||||
const URLRootApp = fastify()
|
||||
URLRootApp.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
URLRootApp.get('/', (request, reply) => {
|
||||
reply.send('<h1>fastify-static</h1>')
|
||||
})
|
||||
})
|
||||
|
||||
const defaultIndexApp = fastify()
|
||||
options.index = 'index.html'
|
||||
|
||||
defaultIndexApp
|
||||
.register(fastifyStatic, options)
|
||||
.after(() => {
|
||||
defaultIndexApp.get('/', (request, reply) => {
|
||||
reply.send('<h1>fastify-static</h1>')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user