@tdio/static-serve 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -0
- package/bin/cli.js +51 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# static-serve
|
|
2
|
+
|
|
3
|
+
A simple, lightweight static file server written in Go, designed with features for modern web application serving.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Static File Serving**: Serves files from a specified root directory.
|
|
8
|
+
- **Dotfile Hiding**: Automatically hides files and directories starting with a dot (`.`) for security.
|
|
9
|
+
- **SPA Mode**: Supports Single Page Application (SPA) routing by redirecting 404s to `index.html` (enabled via `--spa`).
|
|
10
|
+
- **Virtual Mounts**: Mount files or directories to specific paths using `--mount`.
|
|
11
|
+
- **Reverse Proxy (Fallback)**: Proxy specific paths to upstream URLs using `--proxy`. Local files take priority; proxy only triggers when a file is not found locally.
|
|
12
|
+
- **HTTPS Mode**: Serve over TLS with `--tls-cert` and `--tls-key`.
|
|
13
|
+
- **CORS Controls**: Opt-in CORS middleware with origin, credentials, and max-age tuning.
|
|
14
|
+
- **Health Check**: Built-in health check endpoint at `/healthz`.
|
|
15
|
+
- **Request Tracing**: Adds `X-Request-Id` headers to requests for tracing.
|
|
16
|
+
- **Response Caching Hints**: Default `Cache-Control` plus weak ETag generation and 304 handling.
|
|
17
|
+
- **Custom Logging**: Configurable request logging format.
|
|
18
|
+
- **Graceful Shutdown**: Handles OS interrupts for graceful server shutdown.
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
The server processes requests through a middleware pipeline, with the proxy acting as a **fallback** when local files are not found:
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
flowchart TB
|
|
26
|
+
subgraph client [Client]
|
|
27
|
+
REQ[HTTP Request]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
subgraph middleware [Middleware Pipeline]
|
|
31
|
+
TRACE[Tracing]
|
|
32
|
+
LOG[Logging]
|
|
33
|
+
CORS[CORS]
|
|
34
|
+
CACHE[Caching]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph handler [Request Handler]
|
|
38
|
+
ROUTER[Router]
|
|
39
|
+
FS[File Server]
|
|
40
|
+
FALLBACK{File Found?}
|
|
41
|
+
PROXY[Proxy to Upstream]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subgraph response [Response]
|
|
45
|
+
LOCAL[Local File]
|
|
46
|
+
UPSTREAM[Upstream Response]
|
|
47
|
+
ERR404[404 Not Found]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
REQ --> TRACE --> LOG --> CORS --> CACHE --> ROUTER
|
|
51
|
+
ROUTER --> FS
|
|
52
|
+
FS --> FALLBACK
|
|
53
|
+
FALLBACK -->|Yes| LOCAL
|
|
54
|
+
FALLBACK -->|No & Proxy Match| PROXY --> UPSTREAM
|
|
55
|
+
FALLBACK -->|No & No Proxy| ERR404
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Request Flow
|
|
59
|
+
|
|
60
|
+
1. **Tracing**: Assigns a unique `X-Request-Id` to each request
|
|
61
|
+
2. **Logging**: Logs request details in configurable format
|
|
62
|
+
3. **CORS**: Handles preflight requests and sets CORS headers
|
|
63
|
+
4. **Caching**: Adds `Cache-Control` headers and handles `ETag`/`304` responses
|
|
64
|
+
5. **Router**: Routes to mounted paths, health checks, or default file server
|
|
65
|
+
6. **File Server**: Attempts to serve the file from local filesystem
|
|
66
|
+
7. **Fallback Proxy**: If file not found (404) and a proxy rule matches, forwards to upstream
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
You can install `static-serve` globally using npm:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm install -g @tdio/static-serve
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Or run it directly using `npx`:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
npx @tdio/static-serve [OPTIONS] [ROOT_DIR]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
static-serve [OPTIONS] [ROOT_DIR]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If `ROOT_DIR` is not specified, it defaults to the current directory (`./`).
|
|
89
|
+
|
|
90
|
+
### Options
|
|
91
|
+
|
|
92
|
+
| Flag | Description | Default |
|
|
93
|
+
|------|-------------|---------|
|
|
94
|
+
| `--address` | The TCP address to listen on. | `:3000` |
|
|
95
|
+
| `--mount` | Mount specific directory to a virtual path (can be repeated). Format: `<physical_path>:<virtual_path>` or `src=<physical_path>,dst=<virtual_path>` | `""` |
|
|
96
|
+
| `--proxy` | Proxy specific upstream to a virtual path (can be repeated). | `""` |
|
|
97
|
+
| `--spa` | Enable SPA mode (redirects missing files to `/index.html`). | `false` |
|
|
98
|
+
| `--logger-format` | Custom server logger format (Go template syntax). | `{{.Method}} {{.URL.Path}} {{.Status}} {{.RemoteAddr}} {{.UserAgent}}` |
|
|
99
|
+
| `--tls-cert` | Path to TLS certificate file. Enables HTTPS (requires `--tls-key`). | `""` |
|
|
100
|
+
| `--tls-key` | Path to TLS private key file. Enables HTTPS (requires `--tls-cert`). | `""` |
|
|
101
|
+
| `--cors` | Enable CORS middleware for all routes. | `false` |
|
|
102
|
+
| `--cors-allow-origins` | Comma-separated allowed origins (`*` allowed). | `*` |
|
|
103
|
+
| `--cors-allow-credentials` | Whether to include `Access-Control-Allow-Credentials`. | `false` |
|
|
104
|
+
| `--cors-max-age` | Preflight cache max-age (Go duration). | `24h0m0s` |
|
|
105
|
+
| `-v` | Print version and exit. | `false` |
|
|
106
|
+
|
|
107
|
+
### Mount Configuration
|
|
108
|
+
|
|
109
|
+
`static-serve` allows you to mount files or directories to virtual paths. This is useful for serving assets from multiple locations or exposing individual files at specific URLs.
|
|
110
|
+
|
|
111
|
+
* Simple Format: `--mount <physical_path>:<virtual_path>`
|
|
112
|
+
* Advanced Format (Key-Value): `--mount src=<physical_path>,dst=<virtual_path>`
|
|
113
|
+
|
|
114
|
+
Parameters:
|
|
115
|
+
- `src` (or `source`): The physical directory or file on your machine.
|
|
116
|
+
- `dst` (or `path`): The virtual path on the server where the source will be accessible.
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
# Mount a directory (these two forms are functionally identical)
|
|
122
|
+
static-serve --mount /mnt/test/static:/static
|
|
123
|
+
static-serve --mount src=/mnt/test/static,dst=/static
|
|
124
|
+
|
|
125
|
+
# Mount a single file
|
|
126
|
+
static-serve --mount ./config.json:/api/config
|
|
127
|
+
static-serve --mount src=./config.json,dst=/api/config
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Proxy Configuration
|
|
131
|
+
|
|
132
|
+
`static-serve` supports proxying requests to upstream servers using a **fallback pattern**:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Local File Found? ──Yes──► Serve Local File
|
|
136
|
+
│
|
|
137
|
+
No
|
|
138
|
+
│
|
|
139
|
+
▼
|
|
140
|
+
Proxy Rule Matches? ──Yes──► Forward to Upstream
|
|
141
|
+
│
|
|
142
|
+
No
|
|
143
|
+
│
|
|
144
|
+
▼
|
|
145
|
+
Return 404
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This is useful for:
|
|
149
|
+
- **Development**: Serve local static assets while proxying API calls to a backend server
|
|
150
|
+
- **Caching/CDN pattern**: Serve cached files locally, fetch from origin on cache miss
|
|
151
|
+
- **Hybrid deployments**: Override specific upstream resources with local versions
|
|
152
|
+
|
|
153
|
+
#### Simple Format
|
|
154
|
+
|
|
155
|
+
`<url_path>:<upstream_url>`
|
|
156
|
+
|
|
157
|
+
Example: `/api:http://localhost:8080`
|
|
158
|
+
|
|
159
|
+
_Note: This format automatically enables `change_origin` and `strip_prefix`._
|
|
160
|
+
|
|
161
|
+
#### Advanced Format (Key-Value)
|
|
162
|
+
|
|
163
|
+
`path=<url_path>,url=<upstream_url>[,change_origin=bool][,strip_prefix=bool][,methods=GET|POST|...]`
|
|
164
|
+
|
|
165
|
+
Parameters:
|
|
166
|
+
- `path`: The virtual path on the current server to match.
|
|
167
|
+
- `url`: The upstream URL to forward requests to.
|
|
168
|
+
- `change_origin`: Whether to rewrite the `Host` header to match the upstream URL. **Default: `true`**.
|
|
169
|
+
- `strip_prefix`: Whether to strip the matched path prefix before forwarding the request. **Default: `true`**.
|
|
170
|
+
- `method(s)`: pipe-separated list of HTTP methods to proxy. If specified, only these methods will be proxied. Others will pass through to local file serving or return 405. **Default: all methods**.
|
|
171
|
+
|
|
172
|
+
### Examples
|
|
173
|
+
|
|
174
|
+
**Serve current directory on port 8080:**
|
|
175
|
+
```sh
|
|
176
|
+
static-serve --address :8080
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Serve specific directory with SPA mode enabled:**
|
|
180
|
+
```sh
|
|
181
|
+
static-serve --spa ./dist
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Mount files or directories:**
|
|
185
|
+
```sh
|
|
186
|
+
# Mount directories
|
|
187
|
+
static-serve --mount "/var/www/static:/static" --mount "/mnt/media/images:/images" ./public
|
|
188
|
+
|
|
189
|
+
# Mount a single file
|
|
190
|
+
static-serve --mount "./config.json:/api/config"
|
|
191
|
+
|
|
192
|
+
# Using advanced KV format
|
|
193
|
+
static-serve --mount "src=/var/www/static,dst=/static"
|
|
194
|
+
static-serve --mount "src=./config.json,dst=/api/config"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Proxy API requests (fallback behavior):**
|
|
198
|
+
|
|
199
|
+
1. **Basic Proxy**: Forward `/api` to `http://localhost:8080` when files don't exist locally.
|
|
200
|
+
```sh
|
|
201
|
+
static-serve --proxy "/api:http://localhost:8080"
|
|
202
|
+
```
|
|
203
|
+
- If `./api/users.json` exists locally → serves the local file
|
|
204
|
+
- If `./api/users.json` doesn't exist → forwards to `http://localhost:8080/users`
|
|
205
|
+
|
|
206
|
+
2. **Advanced Config**: Forward `/api` but keep the `/api` prefix in the upstream request.
|
|
207
|
+
```sh
|
|
208
|
+
static-serve --proxy "path=/api,url=http://localhost:8080,strip_prefix=false"
|
|
209
|
+
```
|
|
210
|
+
Requests to `http://localhost:3000/api/users` will be forwarded to `http://localhost:8080/api/users` (if no local file exists).
|
|
211
|
+
|
|
212
|
+
3. **Method Filtering**: Only proxy POST and PUT requests to the upstream server.
|
|
213
|
+
```sh
|
|
214
|
+
static-serve --proxy "path=/api,url=http://localhost:8080,methods=POST|PUT"
|
|
215
|
+
```
|
|
216
|
+
- `GET /api/users` → serves local `./api/users` or 404
|
|
217
|
+
- `POST /api/users` → proxies to `http://localhost:8080/users` (if no local file exists)
|
|
218
|
+
|
|
219
|
+
4. **Local Override Pattern**: Override specific upstream assets with local versions.
|
|
220
|
+
```sh
|
|
221
|
+
# Create local override
|
|
222
|
+
mkdir -p ./assets
|
|
223
|
+
echo "local override" > ./assets/config.json
|
|
224
|
+
|
|
225
|
+
# Start server with proxy fallback
|
|
226
|
+
static-serve --proxy "/assets:http://cdn.example.com/assets" ./
|
|
227
|
+
```
|
|
228
|
+
- `GET /assets/config.json` → serves local `./assets/config.json`
|
|
229
|
+
- `GET /assets/other.js` → proxies to `http://cdn.example.com/assets/other.js`
|
|
230
|
+
|
|
231
|
+
**Serve over HTTPS:**
|
|
232
|
+
```sh
|
|
233
|
+
static-serve --tls-cert ./cert.pem --tls-key ./key.pem ./public
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Enable CORS for specific origins:**
|
|
237
|
+
```sh
|
|
238
|
+
static-serve --cors --cors-allow-origins "https://app.example.com,https://admin.example.com" --cors-allow-credentials
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Health Check
|
|
242
|
+
|
|
243
|
+
The server exposes a health check endpoint at `/healthz`.
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
curl http://localhost:3000/healthz
|
|
247
|
+
# Returns 204 No Content if healthy
|
|
248
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const PACKAGE_PREFIX = 'static-serve'; // Must match build-npm.js
|
|
8
|
+
const PACKAGE_SCOPE = '@tdio'; // Must match build-npm.js
|
|
9
|
+
|
|
10
|
+
// Map generic 'x64' to Node's arch if needed, but usually they match.
|
|
11
|
+
// Node: x64, arm64, ia32, etc.
|
|
12
|
+
const platform = process.platform;
|
|
13
|
+
const arch = process.arch;
|
|
14
|
+
|
|
15
|
+
const targetPackage = `${PACKAGE_SCOPE}/${PACKAGE_PREFIX}-${platform}-${arch}`
|
|
16
|
+
|
|
17
|
+
let binaryPath;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Resolve the package.json of the platform-specific package
|
|
21
|
+
// This throws if the package is not installed
|
|
22
|
+
const pkgJsonPath = require.resolve(`${targetPackage}/package.json`);
|
|
23
|
+
const pkgDir = path.dirname(pkgJsonPath);
|
|
24
|
+
|
|
25
|
+
// Binary name is predictable: static-serve on Unix, static-serve.exe on Windows
|
|
26
|
+
const binFile = platform === 'win32' ? `${PACKAGE_PREFIX}.exe` : PACKAGE_PREFIX;
|
|
27
|
+
binaryPath = path.join(pkgDir, binFile);
|
|
28
|
+
|
|
29
|
+
} catch (err) {
|
|
30
|
+
// Fallback: Check if we are running from the source repo/local build
|
|
31
|
+
// Useful for development
|
|
32
|
+
const localBuildPath = path.join(__dirname, '..', 'out', `${PACKAGE_PREFIX}-${platform === 'win32' ? 'windows' : platform}-${arch === 'x64' ? 'amd64' : arch}${platform === 'win32' ? '.exe' : ''}`);
|
|
33
|
+
|
|
34
|
+
if (fs.existsSync(localBuildPath)) {
|
|
35
|
+
binaryPath = localBuildPath;
|
|
36
|
+
} else {
|
|
37
|
+
console.error(`Failed to locate binary for ${platform}-${arch}.`);
|
|
38
|
+
console.error(`Tried package: ${targetPackage}`);
|
|
39
|
+
console.error(`Error: ${err.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
45
|
+
stdio: 'inherit'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
child.on('exit', (code) => {
|
|
49
|
+
process.exit(code);
|
|
50
|
+
});
|
|
51
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tdio/static-serve",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "A simple, lightweight static file server written in Go, designed with features for modern web application serving",
|
|
5
|
+
"bin": {
|
|
6
|
+
"static-serve": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"author": "allex <allex.wxn@gmail.com> (http://iallex.com/)",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/allex/static-serve"
|
|
12
|
+
},
|
|
13
|
+
"os": [
|
|
14
|
+
"darwin",
|
|
15
|
+
"linux",
|
|
16
|
+
"win32"
|
|
17
|
+
],
|
|
18
|
+
"cpu": [
|
|
19
|
+
"x64",
|
|
20
|
+
"arm64"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"bin/cli.js"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"@tdio/static-serve-darwin-x64": "0.0.4",
|
|
28
|
+
"@tdio/static-serve-darwin-arm64": "0.0.4",
|
|
29
|
+
"@tdio/static-serve-linux-x64": "0.0.4",
|
|
30
|
+
"@tdio/static-serve-linux-arm64": "0.0.4",
|
|
31
|
+
"@tdio/static-serve-win32-x64": "0.0.4",
|
|
32
|
+
"@tdio/static-serve-win32-arm64": "0.0.4"
|
|
33
|
+
}
|
|
34
|
+
}
|