UNPKG

7.44 kBMarkdownView Raw
1# file-server
2
3**A LAN server with auth, upload and multithreaded download**
4
5---
6
7<img src="https://raw.github.com/edfus/file-server/master/img/terminal.gif">
8
9This package is developed as an easy and quick mean to share files across my LAN with some more abilities like authorization / client upload / client range request.
10
11## Features
12
13- User-friendly interactive prompts powered by [prompts](https://github.com/terkelg/prompts#-prompts)
14- Multithreaded download based on [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js)
15- Composing router logic & cascading middlewares in [Koa](https://koajs.com/) style
16- HTTPS & HTTP over the same port
17
18## CMD Usage
19
20The one-line way:
21```bash
22npx @edfus/file-server
23```
24
25Alternatively, you can install this package either globally or locally
26```bash
27npm install @edfus/file-server -g
28```
29
30```bash
31npm install @edfus/file-server
32cd node_modules/@edfus/file-server
33```
34
35```bash
36git clone --depth 1 https://github.com/edfus/file-server
37cd file-server
38npm install
39```
40
41And then run:
42```bash
43# global
44serve
45# local
46npm run serve
47```
48
49Available command-line options:
50
51- `--config [config_path]`: The path to your preferred config location for retriving/creating/updating settings.
52- `--password [passwd]`: The optional password for encrypting and decrypting config file. Password set by the authorization prompt takes priority over this.
53- `--no-prompt`: Skip the prompts, use possible or default settings.
54- `--no-validate`: Do not check validity of pathnames.
55- `--no-fallback`: Exits immediately when any misconfiguration is found.
56- `<folder_name>`: The first unpaired, non-option command line argument will be treated as the `<folder_name>`, if exists. Specifying `<folder_name>` will skip the prompts, serve what you want directly using possible or default settings.
57
58When a encrypted config is encountered, a `To recall your previous configuration, enter the password` prompt will always jump out regardless of the `"will-skip-prompts"` options being set or not. Specify `--password passwd` explicitly in this case.
59
60Examples:
61```bash
62serve .
63
64npx @edfus/file-server /var/www/localhost/ --config /var/www/docker_volume/config
65serve --config /var/www/docker_volume/config --password K3qUrFS+h@G --no-prompt
66npm run serve -- --no-prompt
67```
68
69Alias:
70- `-c`: `--config [config_path]`
71- `-p`: `--password [passwd]`
72- `-h`: `--help`
73- `-n`: `--no-prompt`
74- `-l`, `--loose`: `--no-validate`
75- `-e`, `--set-e`: `--no-fallback`
76
77## Env Settings
78
79See files in folder [./env/](https://github.com/edfus/file-server/tree/master/env) for behaviors that you can customize.
80
81## API
82
83Some quick start snippets:
84
85```js
86import { App, Serve } from "@edfus/file-server";
87
88const app = new App();
89const services = new Serve().mount("./");
90
91for (const service of services)
92 app.use(service);
93
94// simply sugar for http.createServer(app.callback()).listen();
95app.listen(0, "localhost", function () {
96 console.info(`File server is running at http://localhost:${this.address().port}`);
97});
98```
99
100```js
101import { App, Serve } from "@edfus/file-server";
102
103const app = new App();
104
105app.prepend(
106 async (ctx, next) => {
107 await next();
108
109 console.info(
110 new Date().toLocaleString(),
111 ctx.ip,
112 ctx.req.method,
113 ctx.url,
114 ctx.res.statusCode
115 )
116 }
117);
118
119app.use(new Serve().mount("./doc").serveFile).listen(
120 8080, "localhost", function () {
121 console.info(`Server is running at http://localhost:${this.address().port}`);
122 }
123);
124```
125
126This package has two named exports:
127
128### `App`
129
130Class `App` is a minimal implementation of [Koa](https://koajs.com/).
131
132Following properties are available in ctx for middlewares:
133
134```ts
135/**
136 * The prototype from which ctx is created.
137 * You may add additional properties to ctx by editing App#context
138 */
139type BasicContext = {
140 app: App,
141 /* parameter `properties` is not supported */
142 throw (status?: number, message?: string): void;
143 /* parameter `properties` is not supported */
144 assert (shouldBeTruthy: any, status?: number, message?: string): void;
145}
146
147type Context = BasicContext & {
148 req: IncomingMessage,
149 res: ServerResponse,
150 state: {
151 pathname: string,
152 uriObject: URL
153 },
154 url: string,
155 secure: boolean,
156 ip: string
157}
158```
159
160See <https://github.com/edfus/file-server/master/file-server.d.ts> for more details.
161
162### `Serve`
163
164class `Serve` is the core of this package, highly decoupled.
165
166```ts
167class Serve {
168 constructor();
169 implementedMethods: ["GET", "PUT", "HEAD"];
170
171 /**
172 * sugar for
173 * this.implementedMethods.includes(ctx.req.method)
174 *
175 * if (ctx.state.pathname === "/api") {
176 * switch (ctx.state.uriObject.searchParams.get("action")) {
177 * case "list":
178 * case "get-list": return this.getList(ctx);
179 * case "upload": return this.uploadFile(ctx);
180 * }
181 * }
182 *
183 * this.serveFile
184 */
185 [Symbol.iterator] (): IterableIterator<Middleware>;
186
187 _getList (ctx: Context): Promise<void>;
188 _uploadFile (ctx: Context): Promise<void>;
189 _serveFile (ctx: Context): Promise<void>;
190
191 /**
192 * sugar for _getList with correct `this` reference
193 */
194 getList (ctx: Context): Promise<void>;
195
196 /**
197 * sugar for _uploadFile with correct `this` reference
198 */
199 uploadFile (ctx: Context): Promise<void>;
200
201 /**
202 * _serveFile with correct `this` reference
203 * will silence errors with status 404
204 */
205 serveFile (ctx: Context): Promise<void>;
206
207 /**
208 * sugar for
209 * this.pathnameRouter.dir.push(pathname => join(directory, normalize(pathname)));
210 *
211 * this.pathnameRouter.file.push(pathname => join(directory, normalize(pathname)));
212 */
213 mount(directory: string): this
214
215 pathnameRouter: object;
216 fileResHeadersRouter: object;
217 routeThrough<T>(input: T, ...routers: Array<((input: T) => T)>): T;
218
219 etag (stats: Stats): string;
220 listCache: Map<string, object>;
221 mimeCache: Map<string, string>;
222}
223```
224
225Serve#pathnameRouter is where you can customize the routing logic:
226
227```js
228 pathnameRouter = {
229 map: [
230 pathname => pathMap.has(pathname) ? pathMap.get(pathname) : pathname,
231 ],
232 filter: [
233 // hide all files starting with . in their names
234 pathname => basename(pathname).startsWith(".") ? false : pathname
235 ],
236 fs: [
237 pathname => pathname.replace(/<|>|:|"|\||\?|\*/g, "-")
238 ],
239 file: [
240 pathname => pathname.endsWith("/") ? pathname.concat("index.html") : pathname
241 ],
242 dir: [
243 pathname => pathname.endsWith("/") ? pathname : pathname.concat("/")
244 ]
245 };
246```
247
248## Notes
249
250./lib/stream-saver is a modified version of [StreamSaver.js](https://github.com/jimmywarting/StreamSaver.js), only browsers compatible with [Transferable Streams](https://github.com/whatwg/streams/blob/main/transferable-streams-explainer.md) are supported and a valid SSL certificate is required for service worker registration when serving via https (http is ok, though)
251
252Strict [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) rules is applied for $indexHTML. Delete lines in `Serve#fileResHeadersRouter.CSP` in `./bin/cmd.js` if needed.
253
254App#callback trust `proxy set headers` by default (e.g. X-Forwarded-Host, X-Forwarded-For)
255
256HTTP/2 is not supported.
\No newline at end of file