UNPKG

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