xshell 0.0.10 → 0.0.14

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/server.js CHANGED
@@ -1,30 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.server = exports.KNOWN_IPS = exports.PHONE_IP = exports.ROUTER_IP = exports.LOCALHOST_IPS = void 0;
3
+ exports.server = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const http_1 = require("http");
6
6
  const zlib_1 = (0, tslib_1.__importDefault)(require("zlib"));
7
- // --- 3-rd party
7
+ const fs_1 = (0, tslib_1.__importDefault)(require("fs"));
8
+ const util_1 = require("util");
9
+ // --- 3rd party
10
+ const upath_1 = (0, tslib_1.__importDefault)(require("upath"));
8
11
  const invoke_1 = (0, tslib_1.__importDefault)(require("lodash/invoke"));
9
12
  const qs_1 = (0, tslib_1.__importDefault)(require("qs"));
10
- // --- Koa & Koa Middleware
13
+ const resolve_path_1 = (0, tslib_1.__importDefault)(require("resolve-path"));
14
+ // --- koa & koa middleware
11
15
  const koa_1 = (0, tslib_1.__importDefault)(require("koa"));
12
16
  const cors_1 = (0, tslib_1.__importDefault)(require("@koa/cors"));
13
17
  const koa_compress_1 = (0, tslib_1.__importDefault)(require("koa-compress"));
14
18
  const koa_useragent_1 = require("koa-useragent");
15
19
  const utils_1 = require("./utils");
16
- // ------------ CONSTs
17
- exports.LOCALHOST_IPS = new Set(['127.0.0.1', '::ffff:127.0.0.1', '::1']);
18
- exports.ROUTER_IP = '192.168.1.1';
19
- exports.PHONE_IP = '192.168.1.113';
20
- exports.KNOWN_IPS = new Set([...exports.LOCALHOST_IPS, exports.ROUTER_IP, exports.PHONE_IP]);
21
- // ------------ MyServer
20
+ const file_1 = require("./file");
21
+ // ------------ my server
22
22
  exports.server = {
23
23
  app: null,
24
24
  handler: null,
25
25
  server_80: null,
26
26
  /** start http server and listen */
27
27
  async start() {
28
+ // --- init koa app
28
29
  let app = new koa_1.default();
29
30
  app.on('error', (error, ctx) => {
30
31
  console.error(error);
@@ -36,7 +37,7 @@ exports.server = {
36
37
  // https://nodejs.org/api/zlib.html#zlib_class_brotlioptions
37
38
  params: {
38
39
  [zlib_1.default.constants.BROTLI_PARAM_MODE]: zlib_1.default.constants.BROTLI_MODE_TEXT,
39
- [zlib_1.default.constants.BROTLI_PARAM_QUALITY]: 6 // default 11 (maximized compression), may lead to news/get generated 14MB JSON taking 24s
40
+ [zlib_1.default.constants.BROTLI_PARAM_QUALITY]: 6 // default 11 (maximized compression), may lead to news/get generated 14mb json taking 24s
40
41
  },
41
42
  },
42
43
  threshold: 512
@@ -45,76 +46,95 @@ exports.server = {
45
46
  app.use(koa_useragent_1.userAgent);
46
47
  app.use(this.router.bind(this));
47
48
  this.app = app;
48
- await this.listen();
49
- },
50
- async listen() {
51
49
  this.handler = this.app.callback();
52
50
  this.server_80 = (0, http_1.createServer)(this.handler);
53
- await Promise.all([
54
- new Promise(resolve => { this.server_80.listen(8421, resolve); }),
55
- ]);
51
+ await new Promise(resolve => {
52
+ this.server_80.listen(8421, resolve);
53
+ });
56
54
  },
57
55
  stop() {
58
56
  this.server_80.close();
59
57
  },
60
58
  async entry(ctx, next) {
61
- const { req: { tunnel, id }, res } = ctx;
62
- let { req, request } = ctx;
59
+ let { response } = ctx;
60
+ await this.parse(ctx);
61
+ // ------------ next
62
+ try {
63
+ await next();
64
+ }
65
+ catch (error) {
66
+ if (error.status !== 404)
67
+ console.error(error);
68
+ response.status = error.status || 500;
69
+ response.body = (0, utils_1.inspect)(error, { colors: false });
70
+ response.type = 'text/plain';
71
+ }
72
+ },
73
+ /**
74
+ parse req.body to request.body
75
+ process request.ip
76
+ */
77
+ async parse(ctx) {
78
+ const { request, req, req: { tunnel }, } = ctx;
63
79
  if (!tunnel) {
64
80
  const buf = await (0, utils_1.stream_to_buffer)(req);
65
81
  if (buf.length)
66
82
  req.body = buf;
67
83
  }
68
- // ------------ parse req.body to request.body
69
- if (req.body)
70
- if (ctx.is('application/json') || ctx.is('text/plain'))
71
- request.body = JSON.parse(req.body.toString());
72
- else if (ctx.is('application/x-www-form-urlencoded'))
73
- request.body = qs_1.default.parse(req.body.toString());
74
- else if (ctx.is('multipart/form-data')) {
75
- throw new Error('multipart/form-data is not supported');
76
- }
77
- else
78
- request.body = req.body;
79
- // ------------ parse request.ip
84
+ // --- parse request.ip
80
85
  request.ip = (request.headers['x-real-ip'] || request.ip).replace(/^::ffff:/, '');
81
- // ------------ next
82
- await next();
83
- // ------------ post processing
86
+ // --- parse body
87
+ if (!req.body)
88
+ return;
89
+ if (ctx.is('application/json') || ctx.is('text/plain'))
90
+ request.body = JSON.parse(req.body.toString());
91
+ else if (ctx.is('application/x-www-form-urlencoded'))
92
+ request.body = qs_1.default.parse(req.body.toString());
93
+ else if (ctx.is('multipart/form-data')) {
94
+ throw new Error('multipart/form-data is not supported');
95
+ }
96
+ else
97
+ request.body = req.body;
84
98
  },
85
99
  async router(ctx, next) {
86
- let { request, response } = ctx;
87
- request.path;
88
- request._path = decodeURIComponent(request.path);
100
+ var _a;
101
+ let { request } = ctx;
102
+ const _path = request._path = decodeURIComponent(request.path);
89
103
  Object.defineProperty(request, 'path', {
90
- value: request._path,
104
+ value: _path,
91
105
  configurable: true,
92
106
  enumerable: true,
93
107
  writable: true
94
108
  });
95
109
  const { path } = request;
96
- // ------------ RPC
110
+ // ------------ /repl/rpc
97
111
  if (path === '/api/rpc') {
98
112
  await this.rpc(ctx);
99
113
  return;
100
114
  }
101
115
  // ------------ log
102
116
  this.logger(ctx);
117
+ // ------------ repl_router hook
118
+ if (await ((_a = global.repl_router) === null || _a === void 0 ? void 0 : _a.call(global, ctx)))
119
+ return;
103
120
  await (next === null || next === void 0 ? void 0 : next());
104
121
  },
105
- /** args are array http://127.0.0.1/repl/rpc?func=to_json&args=aaa&args=bbb
122
+ /** args are array http://localhost/repl/rpc?func=to_json&args=aaa&args=bbb
106
123
  should use POST when arg is number, otherwise type will be string
107
124
  queries:
108
125
  - func: function name
109
126
  - args?: `[]` args array
110
- - async?: `false` don't wait
111
127
  - ignore?: `false` don't serialize result into response
128
+ - async?: `false` don't wait
112
129
  */
113
130
  async rpc(ctx) {
114
131
  const { request: { query, body }, response } = ctx;
115
132
  let { func, args = [], ignore = false, async: _async = false } = { ...query, ...body };
116
- if (!func)
117
- throw new Error('rpc no func');
133
+ if (!func) {
134
+ let error = new Error('rpc no func');
135
+ error.status = 400;
136
+ throw error;
137
+ }
118
138
  if (!Array.isArray(args))
119
139
  args = [args];
120
140
  // ?async=1 or ?async=0 or ?async=false
@@ -136,84 +156,188 @@ exports.server = {
136
156
  response.body = JSON.stringify(result) || '';
137
157
  }
138
158
  catch (error) {
139
- response.status = 500;
140
- response.body = error;
159
+ error.status = 500;
141
160
  throw error;
142
161
  }
143
162
  },
144
163
  logger(ctx) {
145
164
  const { request } = ctx;
146
- const { query, body, path, method, req: { httpVersion, tunnel }, ip } = request;
147
- const known = exports.KNOWN_IPS.has(ip);
165
+ const { query, body, path, _path, protocol, host, req: { httpVersion: http_version }, ip, } = request;
166
+ let { method } = request;
148
167
  const ua = ctx.userAgent;
149
168
  let s = '';
150
169
  // --- time
151
- s += new Date().to_time_str() + ' ';
152
- let t;
153
- // --- IP 50
154
- if (known)
155
- if (exports.LOCALHOST_IPS.has(ip))
156
- t = '';
157
- else if (ip === exports.ROUTER_IP)
158
- t = 'Router'.grey;
159
- else if (ip === exports.PHONE_IP)
160
- t = 'Phone';
161
- else
162
- t = ip;
163
- else
164
- t = ip;
165
- s += t.pad(50) + ' ';
166
- // --- UA
167
- t = '';
168
- if (!known) {
170
+ s += `${new Date().to_time_str()} `;
171
+ // --- ip
172
+ s += (ip || '').pad(40) + ' ';
173
+ // --- ua
174
+ s += (() => {
175
+ let t = '';
169
176
  if (ua.isMobile)
170
- t += ' Mobile'.magenta;
171
- if (ua.isBot)
172
- t += ' Robot'.blue;
177
+ t += 'mobile';
173
178
  if (ua.isDesktop)
174
- t += ' Desktop';
179
+ t += 'desktop';
180
+ if (ua.isBot)
181
+ t += `${t ? ' ' : ''}${'robot'.blue}`;
175
182
  if (ua.platform !== 'unknown' && !ua.os.startsWith('Windows'))
176
- t += '/' + ua.platform;
183
+ t += '/' + ua.platform.toLowerCase().replace('apple mac', 'mac');
177
184
  if (ua.os !== 'unknown' && ua.platform !== 'Android')
178
- t += '/' + ua.os;
185
+ t += '/' + ua.os.toLowerCase();
179
186
  if (ua.browser !== 'unknown')
180
- t += '/' + ua.browser;
187
+ t += '/' + ua.browser.toLowerCase();
188
+ if (ua.isWechat)
189
+ t += '/weixin';
181
190
  if (ua.version !== 'unknown')
182
- t += '/' + ua.version;
183
- }
184
- s += t.pad(50) + ' ';
185
- // --- Tunnel/HTTP version
186
- s += tunnel ?
187
- ('Tunnel/' + httpVersion).pad(10).cyan
188
- :
189
- ('HTTP/' + httpVersion).pad(10);
190
- s += ' ';
191
- // --- Method 8
192
- if (method === 'GET')
193
- t = method;
194
- else
195
- t = method.red;
196
- s += t.pad(8);
197
- // --- Path 60
198
- if (path.toLowerCase() !== request._path.toLowerCase())
199
- t = request._path.blue + ' → ' + path;
200
- else if (!path.includes('.'))
201
- t = path.yellow;
202
- else
203
- t = path;
204
- s += t.pad(60) + ' ';
205
- // --- Query
206
- if (query && Object.keys(query).length) {
207
- t = ` ${(0, utils_1.inspect)(query, { compact: true }).replace('[Object: null prototype] ', '')}`;
208
- if ((s + t).width > global.WIDTH)
209
- s += '\n';
191
+ t += '/' + ua.version.split('.').slice(0, 2).join('.');
192
+ return t;
193
+ })().pad(40) + ' ';
194
+ // --- https/2.0
195
+ // if (req.tunnel) `tunnel/${http_version}`.pad(10).cyan
196
+ s += `${`${protocol.pad(5)}/${http_version}`.pad(10)} `;
197
+ // --- method
198
+ method = method.toLowerCase();
199
+ s += method === 'get' ? method.pad(10) : method.pad(10).yellow;
200
+ // --- host
201
+ s += `${host.pad(20)} `;
202
+ // --- path
203
+ s += (() => {
204
+ if (path.toLowerCase() !== _path.toLowerCase())
205
+ return `${_path.blue} → ${path}`;
206
+ if (!path.includes('.'))
207
+ return path.yellow;
208
+ return path;
209
+ })();
210
+ // --- query
211
+ if (Object.keys(query).length) {
212
+ let t = (0, utils_1.inspect)(query, { compact: true })
213
+ .replace('[Object: null prototype] ', '');
214
+ if (t.endsWith('\n'))
215
+ t = t.slice(0, -1);
216
+ s += (s + t).width > utils_1.output_width ? '\n' : ' ';
210
217
  s += t;
211
218
  }
212
- // --- Body
219
+ // --- body
213
220
  if (body && Object.keys(body).length)
214
221
  s += '\n' + (0, utils_1.inspect)(body).replace('[Object: null prototype] ', '');
222
+ // --- print log
215
223
  console.log(s);
216
224
  },
225
+ async try_send(ctx, fp, { fs = file_1.ufs, root }) {
226
+ const { request: { _path, path, method }, response, } = ctx;
227
+ if (!(typeof response.body === 'undefined') || response.status !== 404)
228
+ return true;
229
+ if (method !== 'HEAD' && method !== 'GET')
230
+ return false;
231
+ function log_404() {
232
+ let s = `${' '.repeat(13)} ${method.toLowerCase()} 404: ${path}`;
233
+ if (_path !== path)
234
+ s += ` ${_path.bracket()}`;
235
+ console.log(s.red);
236
+ }
237
+ try {
238
+ await this.fsend(ctx, fp, { fs, root });
239
+ return true;
240
+ }
241
+ catch (error) {
242
+ if (error.status !== 404)
243
+ throw error;
244
+ log_404();
245
+ return false;
246
+ }
247
+ },
248
+ /** send file at `path` with the given `options` to the koa `ctx`. */
249
+ async fsend(ctx, path, { fs = fs_1.default, root, absolute } = {}) {
250
+ const { request, response, req } = ctx;
251
+ if (!absolute && !root)
252
+ throw new Error('fsend with `!absolute && !root`');
253
+ if (absolute)
254
+ path = upath_1.default.resolve(path);
255
+ else {
256
+ if (path.startsWith(root))
257
+ path = path.slice(root.length);
258
+ if (path.startsWith('/'))
259
+ path = path.slice(1);
260
+ try {
261
+ path = upath_1.default.normalize((0, resolve_path_1.default)(root, path));
262
+ }
263
+ catch (error) {
264
+ error.message += `, path = ${path}`;
265
+ throw error;
266
+ }
267
+ }
268
+ // stat
269
+ let stats;
270
+ try {
271
+ stats = await (0, util_1.promisify)(fs.stat)(path);
272
+ }
273
+ catch (error) {
274
+ if (['ENOENT', 'ENAMETOOLONG', 'ENOTDIR'].includes(error.code)) {
275
+ error.status = 404;
276
+ throw error;
277
+ }
278
+ error.status = 500;
279
+ error.message = `fs.stat 出错: ${error.message}`;
280
+ throw error;
281
+ }
282
+ if (stats.size >= 100 * 2 ** 20) {
283
+ let error = new Error('body.length >= 100 mb');
284
+ error.status = 500;
285
+ throw error;
286
+ }
287
+ if (!req.tunnel) {
288
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges
289
+ // advertise server support of partial requests
290
+ response.set('accept-ranges', 'bytes');
291
+ }
292
+ if (!response.get('cache-control'))
293
+ response.set('cache-control', 'max-age=0, must-revalidate');
294
+ if (!response.get('last-modified'))
295
+ response.set('last-modified', stats.mtime ? stats.mtime.toUTCString() : new Date().toUTCString());
296
+ const fext = path.fext;
297
+ if (!response.type)
298
+ response.type = fext;
299
+ if (fext === '.pdf')
300
+ response.set('content-disposition', `attachment; filename="${encodeURIComponent(path.fname)}"`);
301
+ if (request.fresh) {
302
+ response.status = 304;
303
+ // 以上会自动设置 response.body = null
304
+ return path;
305
+ }
306
+ if (request.headers.range) {
307
+ if (req.tunnel) {
308
+ response.status = 400;
309
+ response.body = '';
310
+ return;
311
+ }
312
+ try {
313
+ const range_header = request.headers.range;
314
+ const range_value = /=(.*)$/.exec(range_header)[1];
315
+ const range = /^[\w]*?(\d*)-(\d*)$/.exec(range_value);
316
+ let start = range[1] ? parseInt(range[1]) : undefined;
317
+ let end = range[2] ? parseInt(range[2]) : stats.size - 1;
318
+ if (typeof start == 'undefined') {
319
+ start = (stats.size - end);
320
+ end = (stats.size - 1);
321
+ }
322
+ const chunksize = (end - start + 1);
323
+ response.status = 206;
324
+ response.set('content-length', String(chunksize));
325
+ response.set('content-range', `bytes ${start}-${end}/${stats.size}`);
326
+ response.body = fs.createReadStream(path, { start, end });
327
+ }
328
+ catch (err) {
329
+ response.status = 416;
330
+ response.set('content-length', String(stats.size));
331
+ response.set('content-range', `bytes */${stats.size}`);
332
+ response.body = fs.createReadStream(path);
333
+ }
334
+ }
335
+ else {
336
+ response.set('content-length', String(stats.size));
337
+ response.body = fs.createReadStream(path);
338
+ }
339
+ return path;
340
+ }
217
341
  };
218
342
  exports.default = exports.server;
219
343
  //# sourceMappingURL=server.js.map
package/server.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer as create_server } from 'http'\nimport type { Server as HttpServer, IncomingHttpHeaders } from 'http'\n\nimport zlib from 'zlib'\n\n// --- 3-rd party\nimport invoke from 'lodash/invoke'\nimport qs from 'qs'\n\n\n// --- Koa & Koa Middleware\nimport Koa from 'koa'\nimport type { Context, Next } from 'koa'\n\nimport KoaCors from '@koa/cors'\nimport KoaCompress from 'koa-compress'\nimport { userAgent as KoaUserAgent } from 'koa-useragent'\n\n\ndeclare module 'koa' {\n interface Request {\n _path: string\n body: any\n }\n \n interface Context {\n compress: boolean\n }\n}\n\n// --- My Lib\nimport { request as _request } from './net'\nimport { stream_to_buffer, inspect } from './utils'\n\n\ndeclare module 'http' {\n interface IncomingMessage {\n tunnel?: boolean\n id?: string\n body?: Buffer\n }\n \n interface ServerResponse {\n body?: Buffer\n }\n}\n\ninterface Message {\n id: string\n headers: IncomingHttpHeaders\n body: {\n buffer: Buffer\n }\n}\n\n// ------------ CONSTs\nexport const LOCALHOST_IPS = new Set(['127.0.0.1', '::ffff:127.0.0.1', '::1'])\nexport const ROUTER_IP = '192.168.1.1'\nexport const PHONE_IP = '192.168.1.113'\nexport const KNOWN_IPS = new Set([...LOCALHOST_IPS, ROUTER_IP, PHONE_IP])\n\n\n// ------------ MyServer\nexport const server = {\n app: null as Koa,\n \n handler: null as ReturnType<Koa['callback']>,\n \n server_80: null as HttpServer,\n \n \n /** start http server and listen */\n async start () {\n let app = new Koa()\n app.on('error', (error, ctx) => {\n console.error(error)\n console.log(ctx)\n })\n \n app.use(this.entry.bind(this))\n \n app.use(KoaCompress({\n br: {\n // https://nodejs.org/api/zlib.html#zlib_class_brotlioptions\n params: {\n [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,\n [zlib.constants.BROTLI_PARAM_QUALITY]: 6 // default 11 (maximized compression), may lead to news/get generated 14MB JSON taking 24s\n },\n },\n threshold: 512\n }))\n \n app.use(KoaCors({ credentials: true }))\n app.use(KoaUserAgent)\n \n app.use(this.router.bind(this))\n \n this.app = app\n \n await this.listen()\n },\n \n \n async listen () {\n this.handler = this.app.callback()\n this.server_80 = create_server(this.handler)\n \n await Promise.all([\n new Promise<void>( resolve => { this.server_80.listen(8421, resolve) }),\n ])\n },\n \n \n stop () {\n this.server_80.close()\n },\n \n \n async entry (ctx: Context, next: Next) {\n const { req: { tunnel, id }, res } = ctx\n let { req, request } = ctx\n \n if (!tunnel) {\n const buf = await stream_to_buffer(req)\n if (buf.length)\n req.body = buf\n }\n \n // ------------ parse req.body to request.body\n if (req.body)\n if (ctx.is('application/json') || ctx.is('text/plain'))\n request.body = JSON.parse(req.body.toString())\n else if (ctx.is('application/x-www-form-urlencoded'))\n request.body = qs.parse(req.body.toString())\n else if (ctx.is('multipart/form-data')) {\n throw new Error('multipart/form-data is not supported')\n } else\n request.body = req.body\n \n \n // ------------ parse request.ip\n request.ip = (request.headers['x-real-ip'] as string || request.ip).replace(/^::ffff:/, '')\n \n \n // ------------ next\n await next()\n // ------------ post processing\n },\n \n \n \n async router (ctx: Context, next: Next) {\n let { request, response } = ctx\n request.path\n request._path = decodeURIComponent(request.path)\n Object.defineProperty(request, 'path', {\n value: request._path,\n configurable: true,\n enumerable: true,\n writable: true\n })\n \n const { path } = request\n \n // ------------ RPC\n if (path === '/api/rpc') {\n await this.rpc(ctx)\n return\n }\n \n \n // ------------ log\n this.logger(ctx)\n \n await next?.()\n },\n \n \n /** args are array http://127.0.0.1/repl/rpc?func=to_json&args=aaa&args=bbb \n should use POST when arg is number, otherwise type will be string \n queries:\n - func: function name\n - args?: `[]` args array\n - async?: `false` don't wait\n - ignore?: `false` don't serialize result into response\n */\n async rpc (ctx: Context) {\n const { request: { query, body }, response } = ctx\n \n let { func, args = [], ignore = false, async: _async = false }: { func: string, args: any[] | string, ignore: boolean | string, async: boolean | string } = { ...query, ...body }\n \n if (!func) throw new Error('rpc no func')\n \n if (!Array.isArray(args))\n args = [args]\n \n // ?async=1 or ?async=0 or ?async=false\n if (typeof ignore === 'string')\n ignore = ignore.to_bool()\n \n if (typeof _async === 'string')\n _async = _async.to_bool()\n \n try {\n const presult = invoke(global, func, ...args)\n \n if (_async) {\n response.body = ''\n return\n }\n \n const result = await presult\n \n if (ignore) {\n response.body = ''\n return\n }\n \n response.body = JSON.stringify(result) || ''\n } catch (error) {\n response.status = 500\n response.body = error\n throw error\n }\n },\n \n \n logger (ctx: Context) {\n const { request } = ctx\n const { query, body, path, method, req: { httpVersion, tunnel }, ip } = request\n \n const known = KNOWN_IPS.has(ip)\n const ua = ctx.userAgent\n \n let s = ''\n \n // --- time\n s += new Date().to_time_str() + ' '\n \n \n let t: string\n // --- IP 50\n if (known)\n if (LOCALHOST_IPS.has(ip))\n t = ''\n else if (ip === ROUTER_IP)\n t = 'Router'.grey\n else if (ip === PHONE_IP)\n t = 'Phone'\n else\n t = ip\n else\n t = ip\n \n s += t.pad(50) + ' '\n \n \n // --- UA\n t = ''\n if (!known) {\n if (ua.isMobile)\n t += ' Mobile'.magenta\n if (ua.isBot)\n t += ' Robot'.blue\n if (ua.isDesktop)\n t += ' Desktop'\n if (ua.platform !== 'unknown' && !ua.os.startsWith('Windows'))\n t += '/' + ua.platform\n if (ua.os !== 'unknown' && ua.platform !== 'Android')\n t += '/' + ua.os\n if (ua.browser !== 'unknown')\n t += '/' + ua.browser\n if (ua.version !== 'unknown')\n t += '/' + ua.version\n }\n s += t.pad(50) + ' '\n \n \n // --- Tunnel/HTTP version\n s += tunnel ? \n ('Tunnel/' + httpVersion).pad(10).cyan\n :\n ('HTTP/' + httpVersion).pad(10)\n \n s += ' '\n \n \n // --- Method 8\n if (method === 'GET')\n t = method\n else\n t = method.red\n \n s += t.pad(8)\n \n \n // --- Path 60\n if (path.toLowerCase() !== request._path.toLowerCase())\n t = request._path.blue + ' → ' + path\n else\n if (!path.includes('.'))\n t = path.yellow\n else\n t = path\n \n s += t.pad(60) + ' '\n \n \n // --- Query\n if (query && Object.keys(query).length) {\n t = ` ${inspect(query, { compact: true }).replace('[Object: null prototype] ', '')}`\n if ((s + t).width > global.WIDTH)\n s += '\\n'\n s += t\n }\n \n \n // --- Body\n if (body && Object.keys(body).length)\n s += '\\n' + inspect(body).replace('[Object: null prototype] ', '')\n \n \n console.log(s)\n },\n}\n\n\nexport default server\n\nexport type Server = typeof server\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createServer as http_create_server,\n type Server as HttpServer,\n} from 'http'\n\nimport zlib from 'zlib'\nimport { default as nodefs, type Stats } from 'fs'\nimport { promisify } from 'util'\n\n// --- 3rd party\nimport upath from 'upath'\nimport invoke from 'lodash/invoke'\nimport qs from 'qs'\nimport resolve_safely from 'resolve-path'\n\n\n// --- koa & koa middleware\nimport Koa from 'koa'\nimport type { Context, Next } from 'koa'\n\nimport KoaCors from '@koa/cors'\nimport KoaCompress from 'koa-compress'\nimport {\n userAgent as KoaUserAgent,\n type UserAgentContext\n} from 'koa-useragent'\n\n\ndeclare module 'koa' {\n interface Request {\n _path: string\n body: any\n }\n \n interface Context {\n compress: boolean\n userAgent: UserAgentContext['userAgent'] & { isWechat: boolean }\n }\n}\n\n// --- my libs\nimport { request as _request } from './net'\nimport { stream_to_buffer, inspect, output_width } from './utils'\nimport { ufs, type UFS } from './file'\n\n\ndeclare module 'http' {\n interface IncomingMessage {\n tunnel?: boolean\n id?: string\n body?: Buffer\n }\n \n interface ServerResponse {\n body?: Buffer\n }\n}\n\n// ------------ my server\nexport const server = {\n app: null as Koa,\n \n handler: null as ReturnType<Koa['callback']>,\n \n server_80: null as HttpServer,\n \n \n /** start http server and listen */\n async start () {\n // --- init koa app\n let app = new Koa()\n \n app.on('error', (error, ctx) => {\n console.error(error)\n console.log(ctx)\n })\n \n app.use(\n this.entry.bind(this)\n )\n \n app.use(KoaCompress({\n br: {\n // https://nodejs.org/api/zlib.html#zlib_class_brotlioptions\n params: {\n [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,\n [zlib.constants.BROTLI_PARAM_QUALITY]: 6 // default 11 (maximized compression), may lead to news/get generated 14mb json taking 24s\n },\n },\n threshold: 512\n }))\n \n app.use(\n KoaCors({ credentials: true })\n )\n app.use(KoaUserAgent)\n \n app.use(this.router.bind(this))\n \n this.app = app\n \n this.handler = this.app.callback()\n \n this.server_80 = http_create_server(this.handler)\n \n await new Promise<void>(resolve => {\n this.server_80.listen(8421, resolve)\n })\n },\n \n \n stop () {\n this.server_80.close()\n },\n \n \n async entry (ctx: Context, next: Next) {\n let { response } = ctx\n \n await this.parse(ctx)\n \n // ------------ next\n try {\n await next()\n } catch (error) {\n if (error.status !== 404)\n console.error(error)\n response.status = error.status || 500\n response.body = inspect(error, { colors: false })\n response.type = 'text/plain'\n }\n },\n \n \n /** \n parse req.body to request.body \n process request.ip\n */\n async parse (ctx: Context) {\n const {\n request,\n req,\n req: { tunnel },\n } = ctx\n \n if (!tunnel) {\n const buf = await stream_to_buffer(req)\n if (buf.length)\n req.body = buf\n }\n \n // --- parse request.ip\n request.ip = (request.headers['x-real-ip'] as string || request.ip).replace(/^::ffff:/, '')\n \n \n // --- parse body\n if (!req.body) return\n \n if (ctx.is('application/json') || ctx.is('text/plain'))\n request.body = JSON.parse(req.body.toString())\n else if (ctx.is('application/x-www-form-urlencoded'))\n request.body = qs.parse(req.body.toString())\n else if (ctx.is('multipart/form-data')) {\n throw new Error('multipart/form-data is not supported')\n } else\n request.body = req.body\n },\n \n \n async router (ctx: Context, next: Next) {\n let { request } = ctx\n const _path = request._path = decodeURIComponent(request.path)\n Object.defineProperty(request, 'path', {\n value: _path,\n configurable: true,\n enumerable: true,\n writable: true\n })\n \n const { path } = request\n \n // ------------ /repl/rpc\n if (path === '/api/rpc') {\n await this.rpc(ctx)\n return\n }\n \n \n // ------------ log\n this.logger(ctx)\n \n // ------------ repl_router hook\n if (await global.repl_router?.(ctx))\n return\n \n await next?.()\n },\n \n \n /** args are array http://localhost/repl/rpc?func=to_json&args=aaa&args=bbb \n should use POST when arg is number, otherwise type will be string \n queries:\n - func: function name\n - args?: `[]` args array\n - ignore?: `false` don't serialize result into response\n - async?: `false` don't wait\n */\n async rpc (ctx: Context) {\n const { request: { query, body }, response } = ctx\n \n let { func, args = [], ignore = false, async: _async = false }: { func: string, args: any[] | string, ignore: boolean | string, async: boolean | string } = { ...query, ...body }\n \n if (!func) {\n let error = new Error('rpc no func')\n ;(error as any).status = 400\n throw error\n }\n \n if (!Array.isArray(args))\n args = [args]\n \n // ?async=1 or ?async=0 or ?async=false\n if (typeof ignore === 'string')\n ignore = ignore.to_bool()\n \n if (typeof _async === 'string')\n _async = _async.to_bool()\n \n try {\n const presult = invoke(global, func, ...args)\n \n if (_async) {\n response.body = ''\n return\n }\n \n const result = await presult\n \n if (ignore) {\n response.body = ''\n return\n }\n \n response.body = JSON.stringify(result) || ''\n } catch (error) {\n error.status = 500\n throw error\n }\n },\n \n \n logger (ctx: Context) {\n const { request } = ctx\n const {\n query, \n body, \n path, _path, \n protocol,\n host,\n req: { httpVersion: http_version },\n ip,\n } = request\n \n let { method } = request\n \n const ua = ctx.userAgent\n \n \n let s = ''\n \n // --- time\n s += `${new Date().to_time_str()} `\n \n \n // --- ip\n s += (ip || '').pad(40) + ' '\n \n \n // --- ua\n s += (() => {\n let t = ''\n if (ua.isMobile)\n t += 'mobile'\n if (ua.isDesktop)\n t += 'desktop'\n if (ua.isBot)\n t += `${ t ? ' ' : '' }${'robot'.blue}`\n if (ua.platform !== 'unknown' && !ua.os.startsWith('Windows'))\n t += '/' + ua.platform.toLowerCase().replace('apple mac', 'mac')\n if (ua.os !== 'unknown' && ua.platform !== 'Android')\n t += '/' + ua.os.toLowerCase()\n if (ua.browser !== 'unknown')\n t += '/' + ua.browser.toLowerCase()\n if (ua.isWechat)\n t += '/weixin'\n if (ua.version !== 'unknown')\n t += '/' + ua.version.split('.').slice(0, 2).join('.')\n return t\n })().pad(40) + ' '\n \n \n // --- https/2.0\n // if (req.tunnel) `tunnel/${http_version}`.pad(10).cyan\n s += `${`${protocol.pad(5)}/${http_version}`.pad(10)} `\n \n \n // --- method\n method = method.toLowerCase()\n s += method === 'get' ? method.pad(10) : method.pad(10).yellow\n \n \n // --- host\n s += `${host.pad(20)} `\n \n \n // --- path\n s += (() => {\n if (path.toLowerCase() !== _path.toLowerCase())\n return `${_path.blue} → ${path}`\n if (!path.includes('.'))\n return path.yellow\n return path\n })()\n \n \n // --- query\n if (Object.keys(query).length) {\n let t = inspect(query, { compact: true })\n .replace('[Object: null prototype] ', '')\n \n if (t.endsWith('\\n'))\n t = t.slice(0, -1)\n \n s += (s + t).width > output_width ? '\\n' : ' '\n \n s += t\n }\n \n \n // --- body\n if (body && Object.keys(body).length)\n s += '\\n' + inspect(body).replace('[Object: null prototype] ', '')\n \n \n // --- print log\n console.log(s)\n },\n \n \n async try_send (\n ctx: Context, \n fp: string,\n {\n fs = ufs, \n root\n }: {\n fs?: (typeof nodefs) | UFS\n root: string\n }) {\n const {\n request: { _path, path, method },\n response,\n } = ctx\n \n if (!(typeof response.body === 'undefined') || response.status !== 404) return true\n \n if (method !== 'HEAD' && method !== 'GET') return false\n \n function log_404 () {\n let s = `${' '.repeat(13)} ${method.toLowerCase()} 404: ${path}`\n if (_path !== path)\n s += ` ${_path.bracket()}`\n console.log(s.red)\n }\n \n try {\n await this.fsend(ctx, fp, { fs, root })\n return true\n } catch (error) {\n if (error.status !== 404) throw error\n log_404()\n return false\n }\n },\n \n \n /** send file at `path` with the given `options` to the koa `ctx`. */\n async fsend (\n ctx: Context,\n path: string,\n {\n fs = nodefs,\n root,\n absolute\n }: {\n /** `fs` */\n fs?: (typeof nodefs) | UFS\n \n root?: string\n \n /** `false` */\n absolute?: boolean\n \n } = { } as any\n ) {\n const { request, response, req } = ctx\n \n if (!absolute && !root)\n throw new Error('fsend with `!absolute && !root`')\n \n if (absolute)\n path = upath.resolve(path)\n else {\n if (path.startsWith(root))\n path = path.slice(root.length)\n \n if (path.startsWith('/'))\n path = path.slice(1)\n \n try {\n path = upath.normalize(\n resolve_safely(root, path)\n )\n } catch (error) {\n error.message += `, path = ${path}`\n throw error\n }\n }\n \n \n // stat\n let stats: Stats\n try {\n stats = await promisify(fs.stat)(path)\n } catch (error) {\n if (['ENOENT', 'ENAMETOOLONG', 'ENOTDIR'].includes(error.code)) {\n error.status = 404\n throw error\n }\n \n error.status = 500\n error.message = `fs.stat 出错: ${error.message}`\n throw error\n }\n \n \n if (stats.size >= 100 * 2**20) {\n let error = new Error('body.length >= 100 mb')\n ;(error as any).status = 500\n throw error\n }\n \n \n if (!req.tunnel) {\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges\n // advertise server support of partial requests\n response.set('accept-ranges', 'bytes')\n }\n \n if (!response.get('cache-control'))\n response.set('cache-control', 'max-age=0, must-revalidate')\n \n if (!response.get('last-modified'))\n response.set('last-modified', stats.mtime ? stats.mtime.toUTCString() : new Date().toUTCString())\n \n const fext = path.fext\n \n if (!response.type)\n response.type = fext\n \n if (fext === '.pdf')\n response.set('content-disposition', `attachment; filename=\"${encodeURIComponent(path.fname)}\"`)\n \n if (request.fresh) {\n response.status = 304\n // 以上会自动设置 response.body = null\n return path\n }\n \n if (request.headers.range) {\n if (req.tunnel) {\n response.status = 400\n response.body = ''\n return\n }\n \n try {\n const range_header = request.headers.range\n const range_value = /=(.*)$/.exec(range_header)[1]\n const range = /^[\\w]*?(\\d*)-(\\d*)$/.exec(range_value)\n \n let start = range[1] ? parseInt(range[1]) : undefined\n let end = range[2] ? parseInt(range[2]) : stats.size - 1\n \n if (typeof start == 'undefined') {\n start = (stats.size - end)\n end = (stats.size - 1)\n }\n \n const chunksize = (end - start + 1)\n \n response.status = 206\n response.set('content-length', String(chunksize))\n response.set('content-range', `bytes ${start}-${end}/${stats.size}`)\n response.body = fs.createReadStream(path, { start, end })\n } catch (err) {\n response.status = 416\n response.set('content-length', String(stats.size))\n response.set('content-range', `bytes */${stats.size}`)\n response.body = fs.createReadStream(path)\n }\n } else {\n response.set('content-length', String(stats.size))\n response.body = fs.createReadStream(path)\n }\n \n return path\n }\n}\n\n\nexport default server\n\nexport type Server = typeof server\n"]}
package/tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- // --- Module
3
+ // --- module
4
4
  "module": "CommonJS", // none, CommonJS, amd, system, umd, es6, es2015, ESNext
5
5
  "moduleResolution": "Node",
6
6
  "allowSyntheticDefaultImports": true,
@@ -8,7 +8,7 @@
8
8
  "resolveJsonModule": true,
9
9
  "isolatedModules": true,
10
10
 
11
- // --- Build
11
+ // --- build
12
12
  "target": "ES2019",
13
13
  "allowJs": false,
14
14
  "checkJs": false,
@@ -21,18 +21,18 @@
21
21
  "tsBuildInfoFile": "./node_modules/.tsbuildinfo",
22
22
 
23
23
 
24
- // --- Emit
24
+ // --- emit
25
25
  "declaration": true,
26
26
  "emitDeclarationOnly": false,
27
27
  "noEmitOnError": false,
28
28
  "listEmittedFiles": true,
29
29
 
30
- // --- Source Maps
30
+ // --- source maps
31
31
  "sourceMap": true,
32
32
  "inlineSourceMap": false,
33
33
  "inlineSources": true,
34
34
 
35
- // --- Features
35
+ // --- features
36
36
  "experimentalDecorators": true,
37
37
  "emitDecoratorMetadata": true,
38
38
  "preserveSymlinks": true,
@@ -42,7 +42,7 @@
42
42
  "forceConsistentCasingInFileNames": true,
43
43
 
44
44
 
45
- // --- Type Checking
45
+ // --- type checking
46
46
  "strict": false,
47
47
  "alwaysStrict": false,
48
48
  "noImplicitAny": false,
package/ufs.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ import type fs from 'fs';
3
+ import './prototype.js';
4
+ declare type FS = typeof fs;
5
+ export interface UFS extends FS {
6
+ }
7
+ export declare class UFS {
8
+ fss: FS[];
9
+ constructor(fss: any[]);
10
+ use(fs: any): this;
11
+ existsSync(path: string): boolean;
12
+ readdir(...args: any[]): any;
13
+ readdirSync(...args: any[]): unknown[];
14
+ createReadStream(path: string, options?: any): any;
15
+ createWriteStream(path: string, options?: any): any;
16
+ static sync_method_wrapper(this: UFS, method: string, ...args: any[]): any;
17
+ static async_method_wrapper(this: UFS, method: string, ...args: any[]): any;
18
+ }
19
+ export declare let ufs: UFS;
20
+ export declare function set_ufs(_ufs: UFS): void;
21
+ export default UFS;