@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.
Files changed (3) hide show
  1. package/README.md +248 -0
  2. package/bin/cli.js +51 -0
  3. 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
+ }