Skip to content

Commit 2f09b57

Browse files
authored
feat(wobe): add html hook (#55)
* feat(wobe): add html hook * fix: tests * fix: tests and improve security * fix: lint
1 parent bf0197d commit 2f09b57

9 files changed

Lines changed: 677 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ lib
1414
dist
1515

1616
.DS_Store
17+
test_static_files/

biome.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"noExplicitAny": "off"
2121
},
2222
"style": {
23-
"useTemplate": "off"
23+
"useTemplate": "off",
24+
"useNodejsImportProtocol": "error"
2425
}
2526
}
2627
},
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# html (static files)
2+
3+
`html` serves static files (HTML, JS, CSS, images, etc.) from a directory with guardrails similar to `express.static`: traversal blocking, dotfiles ignored by default, symlink checks, and SPA fallback support.
4+
5+
## Quick usage
6+
7+
```ts
8+
import { Wobe, html } from 'wobe'
9+
import { join } from 'node:path'
10+
11+
const staticRoot = join(__dirname, '../fixtures')
12+
13+
new Wobe()
14+
// Serve under /tata while stripping that prefix for file resolution
15+
.get(
16+
'/tata/*',
17+
html({
18+
rootPath: staticRoot,
19+
fallbackFile: 'index.html', // optional for SPA
20+
stripPrefix: '/tata',
21+
})
22+
)
23+
.listen(3000)
24+
25+
// http://localhost:3000/tata/test.html -> serves fixtures/test.html
26+
// http://localhost:3000/tata/unknown -> serves fixtures/index.html (fallback)
27+
```
28+
29+
## Options
30+
31+
- `rootPath` (string, required): root directory of static files.
32+
- `fallbackFile` (string, optional): file served on 404, useful for SPAs (`text/html`).
33+
- `stripPrefix` (string, optional): prefix to strip from the pathname when mounted under a sub-path (e.g. `/static` or `/tata`).
34+
- `allowDotfiles` (boolean, default `false`): serve dotfiles; otherwise they return 404.
35+
- `allowSymlinks` (boolean, default `false`): allow symlinks pointing outside `rootPath`; otherwise realpath must stay inside root or 403.
36+
37+
## Behavior and safety
38+
39+
- **Methods**: only `GET` and `HEAD` are accepted (`405` otherwise). HEAD returns headers only.
40+
- **Dotfiles**: ignored by default (404).
41+
- **Traversal / symlinks**: path is normalized + realpathed and must stay under `rootPath` unless `allowSymlinks` is true; otherwise 403.
42+
- **Content-Type**: inferred from extension (built-in MIME table); text read via `.text()`, binary via `arrayBuffer()`.
43+
- **SPA fallback**: served only if it resolves inside root (same symlink rule); otherwise 403.
44+
- **Mounted prefixes**: use `stripPrefix` to exclude the mount prefix from file resolution.

packages/wobe-documentation/doc/ecosystem/hooks/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,12 @@ export const authorizationHook = (schema: TSchema): WobeHandler => {
5454
throw new HttpException(
5555
new Response('You are not authorized to access to this route', {
5656
status: 403,
57-
}),
57+
})
5858
)
5959
}
6060
}
6161
```
62+
63+
## Available hooks
64+
65+
- [html](./html.md) — serve static files with SPA fallback, traversal/dotfile guards, GET/HEAD only.

packages/wobe/dev/index.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
process.env.NODE_TEST = 'test'
22
import { join } from 'node:path'
3-
import { Wobe, uploadDirectory } from '../src'
3+
import { Wobe, uploadDirectory, html } from '../src'
4+
5+
const staticRoot = join(__dirname, '../fixtures')
46

57
new Wobe()
6-
.get('/', (ctx) => ctx.res.send('Hi'))
8+
// Sample API route
9+
.get('/api', (ctx) => ctx.res.send('Hi'))
10+
// Example of using the uploadDirectory hook
11+
.get('/bucket/:filename', uploadDirectory({ directory: staticRoot }))
12+
// Serve static assets and fallback to testFile.html for SPA-style routing
713
.get(
8-
'/bucket/:filename',
9-
uploadDirectory({ directory: join(__dirname, '../fixtures') }),
14+
'/tata/*',
15+
html({
16+
rootPath: staticRoot,
17+
fallbackFile: 'testFile.html',
18+
stripPrefix: '/tata',
19+
}),
1020
)
1121
.listen(3000)

packages/wobe/fixtures/test.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
<h1>Hello World</h1>
4+
</body>
5+
</html>

0 commit comments

Comments
 (0)