hfs 0.1.6 → 0.26.2

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 (91) hide show
  1. package/LICENSE.txt +674 -0
  2. package/README.md +102 -8
  3. package/admin/assets/index.dcc78777.css +1 -0
  4. package/admin/assets/index.f056db34.js +282 -0
  5. package/admin/assets/sha512.3c0e384c.js +8 -0
  6. package/admin/index.html +17 -0
  7. package/admin/logo.svg +36 -0
  8. package/frontend/assets/index.55c710c2.js +85 -0
  9. package/frontend/assets/index.ee805a6c.css +1 -0
  10. package/frontend/assets/sha512.634b743e.js +8 -0
  11. package/frontend/fontello.css +77 -0
  12. package/frontend/fontello.woff2 +0 -0
  13. package/frontend/index.html +18 -0
  14. package/package.json +93 -28
  15. package/plugins/antibrute/plugin.js +38 -0
  16. package/plugins/download-counter/plugin.js +47 -0
  17. package/plugins/download-counter/public/hits.js +5 -0
  18. package/plugins/updater-disabled/plugin.js +44 -0
  19. package/plugins/vhosting/plugin.js +42 -0
  20. package/src/QuickZipStream.js +285 -0
  21. package/src/ThrottledStream.js +93 -0
  22. package/src/adminApis.js +169 -0
  23. package/src/api.accounts.js +59 -0
  24. package/src/api.auth.js +130 -0
  25. package/src/api.file_list.js +103 -0
  26. package/src/api.helpers.js +32 -0
  27. package/src/api.monitor.js +102 -0
  28. package/src/api.plugins.js +125 -0
  29. package/src/api.vfs.js +164 -0
  30. package/src/apiMiddleware.js +136 -0
  31. package/src/block.js +33 -0
  32. package/src/commands.js +105 -0
  33. package/src/config.js +172 -0
  34. package/src/connections.js +57 -0
  35. package/src/const.js +83 -0
  36. package/src/crypt.js +21 -0
  37. package/src/debounceAsync.js +48 -0
  38. package/src/events.js +9 -0
  39. package/src/frontEndApis.js +38 -0
  40. package/src/github.js +102 -0
  41. package/src/index.js +53 -0
  42. package/src/listen.js +226 -0
  43. package/src/log.js +137 -0
  44. package/src/middlewares.js +154 -0
  45. package/src/misc.js +160 -0
  46. package/src/pbkdf2.js +74 -0
  47. package/src/perm.js +176 -0
  48. package/src/plugins.js +338 -0
  49. package/src/serveFile.js +104 -0
  50. package/src/serveGuiFiles.js +113 -0
  51. package/src/sse.js +29 -0
  52. package/src/throttler.js +91 -0
  53. package/src/update.js +69 -0
  54. package/src/util-files.js +141 -0
  55. package/src/util-generators.js +30 -0
  56. package/src/util-http.js +30 -0
  57. package/src/vfs.js +227 -0
  58. package/src/watchLoad.js +73 -0
  59. package/src/zip.js +69 -0
  60. package/.npmignore +0 -19
  61. package/admin-server.js +0 -212
  62. package/cli.js +0 -33
  63. package/file-server.js +0 -100
  64. package/lib/common.js +0 -10
  65. package/lib/extending.js +0 -158
  66. package/lib/mime.js +0 -19
  67. package/lib/misc.js +0 -75
  68. package/lib/serving.js +0 -81
  69. package/lib/vfs.js +0 -403
  70. package/main.js +0 -24
  71. package/note.txt +0 -104
  72. package/speedtest.js +0 -21
  73. package/static/backend.css +0 -14
  74. package/static/backend.html +0 -32
  75. package/static/backend.js +0 -694
  76. package/static/extending.js +0 -187
  77. package/static/frontend.css +0 -29
  78. package/static/frontend.html +0 -23
  79. package/static/frontend.js +0 -230
  80. package/static/icons/files/archive.png +0 -0
  81. package/static/icons/files/audio.png +0 -0
  82. package/static/icons/files/file.png +0 -0
  83. package/static/icons/files/folder.png +0 -0
  84. package/static/icons/files/image.png +0 -0
  85. package/static/icons/files/link.png +0 -0
  86. package/static/icons/files/video.png +0 -0
  87. package/static/jquery.js +0 -4
  88. package/static/jquery.rule-1.0.2.js +0 -273
  89. package/static/misc.js +0 -194
  90. package/static/tpl.js +0 -17
  91. package/todo.txt +0 -25
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.QuickZipStream = void 0;
31
+ const stream_1 = require("stream");
32
+ // @ts-ignore
33
+ const buffer_crc32_1 = require("buffer-crc32");
34
+ const assert_1 = __importDefault(require("assert"));
35
+ const ZIP64_SIZE_LIMIT = 0xffffffff;
36
+ const ZIP64_NUMBER_LIMIT = 0xffff;
37
+ let crc32function;
38
+ Promise.resolve().then(() => __importStar(require('@node-rs/crc32'))).then(lib => crc32function = lib.crc32, () => {
39
+ console.log('using generic lib for crc32');
40
+ crc32function = buffer_crc32_1.unsigned;
41
+ });
42
+ const FLAGS = 0x0808; // bit3 = no crc in local header + bit11 = utf8
43
+ class QuickZipStream extends stream_1.Readable {
44
+ constructor(walker) {
45
+ super({});
46
+ this.walker = walker;
47
+ this.finished = false;
48
+ this.entries = [];
49
+ this.dataWritten = 0;
50
+ this.consumedCalculating = [];
51
+ this.skip = 0;
52
+ }
53
+ earlyClose() {
54
+ this.finished = true;
55
+ this.push(null);
56
+ }
57
+ applyRange(start, end) {
58
+ if (end < start)
59
+ return this.earlyClose();
60
+ this.skip = start;
61
+ this.limit = end - start + 1;
62
+ }
63
+ controlledPush(chunk) {
64
+ if (Array.isArray(chunk))
65
+ chunk = buffer(chunk);
66
+ this.dataWritten += chunk.length;
67
+ if (this.skip) {
68
+ if (this.skip >= chunk.length) {
69
+ this.skip -= chunk.length;
70
+ return true;
71
+ }
72
+ chunk = chunk.subarray(this.skip);
73
+ this.skip = 0;
74
+ }
75
+ const lastBit = this.limit < chunk.length;
76
+ if (lastBit)
77
+ chunk = chunk.subarray(0, this.limit);
78
+ const ret = this.push(chunk);
79
+ if (lastBit)
80
+ this.earlyClose();
81
+ return ret;
82
+ }
83
+ async calculateSize(howLong = 1000) {
84
+ const endBy = Date.now() + howLong;
85
+ while (1) {
86
+ if (Date.now() >= endBy)
87
+ return NaN;
88
+ const { value } = await this.walker.next();
89
+ if (!value)
90
+ break;
91
+ this.consumedCalculating.push(value); // we keep same shape of the generator, so
92
+ }
93
+ // if we reach here, then we were able to consume all entries of the walker (in time)
94
+ let offset = 0;
95
+ let centralDirSize = 0;
96
+ for (const file of this.consumedCalculating) {
97
+ const pathSize = Buffer.from(file.path, 'utf8').length;
98
+ const extraLength = (file.size > ZIP64_SIZE_LIMIT ? 2 : 0) + (offset > ZIP64_SIZE_LIMIT ? 1 : 0);
99
+ const extraDataSize = extraLength && (2 + 2 + extraLength * 8);
100
+ offset += 4 + 2 + 2 + 2 + 4 + 4 + 4 + 4 + 2 + 2 + pathSize + file.size;
101
+ centralDirSize += 4 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + pathSize + extraDataSize;
102
+ }
103
+ const n = this.consumedCalculating.length;
104
+ const centralDirOffset = offset;
105
+ if (n >= ZIP64_NUMBER_LIMIT
106
+ || centralDirOffset >= ZIP64_SIZE_LIMIT
107
+ || centralDirSize >= ZIP64_SIZE_LIMIT)
108
+ centralDirSize += 4 + 8 + 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8 + 4 + 4 + 8 + 4;
109
+ centralDirSize += 4 + 4 + 2 + 2 + 4 + 4 + 2;
110
+ return offset + centralDirSize;
111
+ }
112
+ async _read() {
113
+ if (this.finished || this.destroyed)
114
+ return;
115
+ if (this.workingFile)
116
+ return this.workingFile.resume();
117
+ const file = this.consumedCalculating.shift() || (await this.walker.next()).value;
118
+ if (!file)
119
+ return this.closeArchive();
120
+ let { path, sourcePath, getData, size, ts, mode } = file;
121
+ const pathAsBuffer = Buffer.from(path, 'utf8');
122
+ const offset = this.dataWritten;
123
+ const version = 20;
124
+ this.controlledPush([
125
+ 4, 0x04034b50,
126
+ 2, version,
127
+ 2, FLAGS,
128
+ 2, 0,
129
+ ...ts2buf(ts),
130
+ 4, 0,
131
+ 4, 0,
132
+ 4, 0,
133
+ 2, pathAsBuffer.length,
134
+ 2, 0, // extra length
135
+ ]);
136
+ this.controlledPush(pathAsBuffer);
137
+ if (this.finished)
138
+ return;
139
+ const cache = sourcePath ? crcCache[sourcePath] : undefined;
140
+ const cacheHit = Number(cache === null || cache === void 0 ? void 0 : cache.ts) === Number(ts);
141
+ let crc = cacheHit ? cache.crc : crc32function('');
142
+ const extAttr = !mode ? 0 : (mode | 0x8000) * 0x10000; // it's like <<16 but doesn't overflow so easily
143
+ const entry = { size, crc, pathAsBuffer, ts, offset, version, extAttr };
144
+ if (this.skip >= size && cacheHit) {
145
+ this.skip -= size;
146
+ this.dataWritten += size;
147
+ this.entries.push(entry);
148
+ setTimeout(() => this.push('')); // this "signal" works only after _read() is done
149
+ return;
150
+ }
151
+ const data = getData();
152
+ data.on('error', (err) => console.error(err));
153
+ data.on('end', () => {
154
+ this.workingFile = undefined;
155
+ entry.crc = crc;
156
+ if (sourcePath)
157
+ crcCache[sourcePath] = { ts, crc };
158
+ this.entries.push(entry);
159
+ this.push(''); // continue piping
160
+ });
161
+ this.workingFile = data;
162
+ data.on('data', chunk => {
163
+ if (this.destroyed)
164
+ return data.destroy();
165
+ if (!this.controlledPush(chunk)) // destination buffer full
166
+ data.pause(); // slow down
167
+ if (!cacheHit)
168
+ crc = crc32function(chunk, crc);
169
+ if (this.finished)
170
+ return data.destroy();
171
+ });
172
+ }
173
+ closeArchive() {
174
+ this.finished = true;
175
+ let centralDirOffset = this.dataWritten;
176
+ for (let { size, ts, crc, offset, pathAsBuffer, version, extAttr } of this.entries) {
177
+ const extra = [];
178
+ if (size > ZIP64_SIZE_LIMIT) {
179
+ extra.push(size, size);
180
+ size = ZIP64_SIZE_LIMIT;
181
+ }
182
+ if (offset > ZIP64_SIZE_LIMIT) {
183
+ extra.push(offset);
184
+ offset = ZIP64_SIZE_LIMIT;
185
+ }
186
+ const extraData = buffer(!extra.length ? []
187
+ : [2, 1, 2, 8 * extra.length, ...extra.map(x => [8, x]).flat()]);
188
+ if (extraData.length && version < 45)
189
+ version = 45;
190
+ this.controlledPush([
191
+ 4, 0x02014b50,
192
+ 2, version,
193
+ 2, version,
194
+ 2, FLAGS,
195
+ 2, 0,
196
+ ...ts2buf(ts),
197
+ 4, crc,
198
+ 4, size,
199
+ 4, size,
200
+ 2, pathAsBuffer.length,
201
+ 2, extraData.length,
202
+ 2, 0,
203
+ 2, 0,
204
+ 2, 0,
205
+ 4, extAttr,
206
+ 4, offset,
207
+ ]);
208
+ this.controlledPush(pathAsBuffer);
209
+ this.controlledPush(extraData);
210
+ }
211
+ const after = this.dataWritten;
212
+ let centralDirSize = after - centralDirOffset;
213
+ let n = this.entries.length;
214
+ if (n >= ZIP64_NUMBER_LIMIT
215
+ || centralDirOffset >= ZIP64_SIZE_LIMIT
216
+ || centralDirSize >= ZIP64_SIZE_LIMIT) {
217
+ this.controlledPush([
218
+ 4, 0x06064b50,
219
+ 8, 44,
220
+ 2, 45,
221
+ 2, 45,
222
+ 4, 0,
223
+ 4, 0,
224
+ 8, n,
225
+ 8, n,
226
+ 8, centralDirSize,
227
+ 8, centralDirOffset,
228
+ ]);
229
+ this.controlledPush([
230
+ 4, 0x07064b50,
231
+ 4, 0,
232
+ 8, after,
233
+ 4, 1,
234
+ ]);
235
+ centralDirOffset = ZIP64_SIZE_LIMIT;
236
+ centralDirSize = ZIP64_SIZE_LIMIT;
237
+ n = ZIP64_NUMBER_LIMIT;
238
+ }
239
+ this.controlledPush([
240
+ 4, 0x06054b50,
241
+ 4, 0,
242
+ 2, n,
243
+ 2, n,
244
+ 4, centralDirSize,
245
+ 4, centralDirOffset,
246
+ 2, 0, // comment length
247
+ ]);
248
+ this.push(null); // EOF
249
+ }
250
+ }
251
+ exports.QuickZipStream = QuickZipStream;
252
+ function buffer(pairs) {
253
+ (0, assert_1.default)(pairs.length % 2 === 0);
254
+ let total = 0;
255
+ for (let i = 0; i < pairs.length; i += 2)
256
+ total += pairs[i];
257
+ const ret = Buffer.alloc(total, 0);
258
+ let offset = 0;
259
+ let i = 0;
260
+ while (i < pairs.length) {
261
+ const size = pairs[i++];
262
+ const data = pairs[i++];
263
+ if (size === 1)
264
+ ret.writeUInt8(data, offset);
265
+ else if (size === 2)
266
+ ret.writeUInt16LE(data, offset);
267
+ else if (size === 4)
268
+ ret.writeUInt32LE(data, offset);
269
+ else if (size === 8)
270
+ ret.writeBigUInt64LE(BigInt(data), offset);
271
+ else
272
+ throw 'unsupported';
273
+ offset += size;
274
+ }
275
+ return ret;
276
+ }
277
+ function ts2buf(ts) {
278
+ const date = ((ts.getFullYear() - 1980) & 0x7F) << 9 | (ts.getMonth() + 1) << 5 | ts.getDate();
279
+ const time = ts.getHours() << 11 | ts.getMinutes() << 5 | (ts.getSeconds() / 2) & 0x0F;
280
+ return [
281
+ 2, time,
282
+ 2, date,
283
+ ];
284
+ }
285
+ const crcCache = {};
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ThrottleGroup = exports.ThrottledStream = void 0;
5
+ const stream_1 = require("stream");
6
+ const limiter_1 = require("limiter");
7
+ // throttled stream
8
+ class ThrottledStream extends stream_1.Transform {
9
+ constructor(group, copyStats) {
10
+ super();
11
+ this.group = group;
12
+ this.sent = 0;
13
+ this.lastSpeed = 0;
14
+ this.lastSpeedTime = Date.now();
15
+ this.totalSent = 0; // total sent over connection, since connection can be re-used for multiple requests
16
+ if (!copyStats)
17
+ return;
18
+ this.sent = copyStats.sent;
19
+ this.totalSent = copyStats.totalSent;
20
+ this.lastSpeedTime = copyStats.lastSpeedTime;
21
+ this.lastSpeed = copyStats.lastSpeed;
22
+ }
23
+ async _transform(chunk, encoding, done) {
24
+ let pos = 0;
25
+ while (1) {
26
+ let n = this.group.suggestChunkSize();
27
+ const slice = chunk.slice(pos, pos + n);
28
+ n = slice.length;
29
+ if (!n) // we're done here
30
+ return done();
31
+ try {
32
+ await this.group.consume(n);
33
+ this.push(slice);
34
+ this.sent += n;
35
+ this.totalSent += n;
36
+ pos += n;
37
+ this.emit('sent', n);
38
+ }
39
+ catch (e) {
40
+ done(e);
41
+ return;
42
+ }
43
+ }
44
+ }
45
+ // @return kBs
46
+ getSpeed() {
47
+ const now = Date.now();
48
+ const past = now - this.lastSpeedTime;
49
+ if (past >= 1000) { // recalculate?
50
+ this.lastSpeedTime = now;
51
+ this.lastSpeed = this.sent / past;
52
+ this.sent = 0;
53
+ }
54
+ return this.lastSpeed;
55
+ }
56
+ getBytesSent() {
57
+ return this.totalSent;
58
+ }
59
+ }
60
+ exports.ThrottledStream = ThrottledStream;
61
+ class ThrottleGroup {
62
+ constructor(kBs, parent) {
63
+ this.parent = parent;
64
+ this.bucket = this.updateLimit(kBs); // assignment is redundant and yet the best way I've found to shut up typescript
65
+ }
66
+ // @return kBs
67
+ getLimit() {
68
+ return this.bucket.bucketSize / 1000;
69
+ }
70
+ updateLimit(kBs) {
71
+ if (kBs < 0)
72
+ throw new Error('invalid bytesPerSecond');
73
+ kBs *= 1000;
74
+ return this.bucket = new limiter_1.TokenBucket({
75
+ bucketSize: kBs,
76
+ tokensPerInterval: kBs,
77
+ interval: 'second',
78
+ });
79
+ }
80
+ suggestChunkSize() {
81
+ var _a;
82
+ let b = this.bucket;
83
+ b.parentBucket = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.bucket;
84
+ let min = b.bucketSize;
85
+ while (b = b.parentBucket)
86
+ min = Math.min(min, b.bucketSize);
87
+ return min / 10;
88
+ }
89
+ consume(n) {
90
+ return this.bucket.removeTokens(n);
91
+ }
92
+ }
93
+ exports.ThrottleGroup = ThrottleGroup;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.ctxAdminAccess = exports.localhostAdmin = exports.adminApis = void 0;
31
+ const apiMiddleware_1 = require("./apiMiddleware");
32
+ const config_1 = require("./config");
33
+ const listen_1 = require("./listen");
34
+ const const_1 = require("./const");
35
+ const api_vfs_1 = __importDefault(require("./api.vfs"));
36
+ const api_accounts_1 = __importDefault(require("./api.accounts"));
37
+ const api_plugins_1 = __importDefault(require("./api.plugins"));
38
+ const api_monitor_1 = __importDefault(require("./api.monitor"));
39
+ const connections_1 = require("./connections");
40
+ const misc_1 = require("./misc");
41
+ const lodash_1 = __importDefault(require("lodash"));
42
+ const events_1 = __importDefault(require("./events"));
43
+ const perm_1 = require("./perm");
44
+ const middlewares_1 = require("./middlewares");
45
+ const promises_1 = require("fs/promises");
46
+ const fs_1 = require("fs");
47
+ const readline = __importStar(require("readline"));
48
+ const log_1 = require("./log");
49
+ const child_process_1 = require("child_process");
50
+ const util_1 = require("util");
51
+ exports.adminApis = {
52
+ ...api_vfs_1.default,
53
+ ...api_accounts_1.default,
54
+ ...api_plugins_1.default,
55
+ ...api_monitor_1.default,
56
+ async set_config({ values: v }) {
57
+ var _a, _b;
58
+ if (v) {
59
+ const st = (0, listen_1.getStatus)();
60
+ const noHttp = ((_a = v.port) !== null && _a !== void 0 ? _a : listen_1.portCfg.get()) < 0 || !st.httpSrv.listening;
61
+ const noHttps = ((_b = v.https_port) !== null && _b !== void 0 ? _b : listen_1.httpsPortCfg.get()) < 0 || !st.httpsSrv.listening;
62
+ if (noHttp && noHttps)
63
+ return new apiMiddleware_1.ApiError(const_1.FORBIDDEN, "You cannot switch off both http and https ports");
64
+ await (0, config_1.setConfig)(v);
65
+ }
66
+ return {};
67
+ },
68
+ get_config: config_1.getWholeConfig,
69
+ async get_status() {
70
+ const st = (0, listen_1.getStatus)();
71
+ return {
72
+ started: const_1.HFS_STARTED,
73
+ build: const_1.BUILD_TIMESTAMP,
74
+ version: const_1.VERSION,
75
+ apiVersion: const_1.API_VERSION,
76
+ compatibleApiVersion: const_1.COMPATIBLE_API_VERSION,
77
+ http: await serverStatus(st.httpSrv, listen_1.portCfg.get()),
78
+ https: await serverStatus(st.httpsSrv, listen_1.httpsPortCfg.get()),
79
+ urls: (0, listen_1.getUrls)(),
80
+ proxyDetected: (0, middlewares_1.getProxyDetected)(),
81
+ frpDetected: exports.localhostAdmin.get() && !(0, middlewares_1.getProxyDetected)()
82
+ && (0, connections_1.getConnections)().every(misc_1.isLocalHost)
83
+ && await frpDebounced(),
84
+ };
85
+ async function serverStatus(h, configuredPort) {
86
+ var _a;
87
+ const busy = await h.busy;
88
+ await (0, misc_1.wait)(0); // simple trick to wait for also .error to be updated. If this trickery becomes necessary elsewhere, then we should make also error a Promise.
89
+ return {
90
+ ...lodash_1.default.pick(h, ['listening', 'error']),
91
+ busy,
92
+ port: ((_a = h === null || h === void 0 ? void 0 : h.address()) === null || _a === void 0 ? void 0 : _a.port) || configuredPort,
93
+ };
94
+ }
95
+ },
96
+ async save_pem({ cert, private_key, name = 'self' }) {
97
+ if (!cert || !private_key)
98
+ return new apiMiddleware_1.ApiError(400);
99
+ const files = { cert: name + '.cert', private_key: name + '.key' };
100
+ await (0, promises_1.writeFile)(files.private_key, private_key);
101
+ await (0, promises_1.writeFile)(files.cert, cert);
102
+ return files;
103
+ },
104
+ async get_log({ file = 'log' }, ctx) {
105
+ return new apiMiddleware_1.SendListReadable({
106
+ bufferTime: 10,
107
+ doAtStart(list) {
108
+ const logger = log_1.loggers.find(l => l.name === file);
109
+ if (!logger)
110
+ return list.error(404, true);
111
+ const input = (0, fs_1.createReadStream)(logger.path);
112
+ input.on('error', async (e) => {
113
+ if (e.code === 'ENOENT') // ignore ENOENT, consider it an empty log
114
+ return list.ready();
115
+ list.error(e.code || e.message);
116
+ });
117
+ input.on('end', () => list.ready());
118
+ input.on('ready', () => {
119
+ readline.createInterface({ input }).on('line', line => {
120
+ if (ctx.aborted)
121
+ return input.close();
122
+ const obj = parse(line);
123
+ if (obj)
124
+ list.add(obj);
125
+ }).on('close', () => {
126
+ ctx.res.once('close', (0, misc_1.onOff)(events_1.default, {
127
+ [logger.name](entry) {
128
+ list.add(entry);
129
+ }
130
+ }));
131
+ });
132
+ });
133
+ }
134
+ });
135
+ function parse(line) {
136
+ const m = /^(.+?) (.+?) (.+?) \[(.{11}):(.{14})] "(\w+) ([^"]+) HTTP\/\d.\d" (\d+) (-|\d+)/.exec(line);
137
+ if (!m)
138
+ return;
139
+ const [, ip, , user, date, time, method, uri, status, length] = m;
140
+ return {
141
+ ip,
142
+ user: user === '-' ? undefined : user,
143
+ ts: new Date(date + ' ' + time),
144
+ method,
145
+ uri,
146
+ status: Number(status),
147
+ length: length === '-' ? undefined : Number(length),
148
+ };
149
+ }
150
+ },
151
+ };
152
+ for (const k in exports.adminApis) {
153
+ const was = exports.adminApis[k];
154
+ exports.adminApis[k] = (params, ctx) => ctxAdminAccess(ctx) ? was(params, ctx)
155
+ : new apiMiddleware_1.ApiError(const_1.UNAUTHORIZED);
156
+ }
157
+ exports.localhostAdmin = (0, config_1.defineConfig)('localhost_admin', true);
158
+ function ctxAdminAccess(ctx) {
159
+ return !ctx.state.proxiedFor // we consider localhost_admin only if no proxy is detected
160
+ && exports.localhostAdmin.get() && (0, misc_1.isLocalHost)(ctx)
161
+ || (0, perm_1.getFromAccount)(ctx.state.account, a => a.admin);
162
+ }
163
+ exports.ctxAdminAccess = ctxAdminAccess;
164
+ const frpDebounced = (0, misc_1.debounceAsync)(async () => {
165
+ if (!const_1.IS_WINDOWS)
166
+ return false;
167
+ const { stdout } = await (0, util_1.promisify)(child_process_1.execFile)('tasklist', ['/fi', 'imagename eq frpc.exe', '/nh']);
168
+ return stdout.includes('frpc');
169
+ });
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const api_helpers_1 = require("./api.helpers");
8
+ const apiMiddleware_1 = require("./apiMiddleware");
9
+ const perm_1 = require("./perm");
10
+ const lodash_1 = __importDefault(require("lodash"));
11
+ const const_1 = require("./const");
12
+ function prepareAccount(ac) {
13
+ return ac && {
14
+ ...lodash_1.default.omit(ac, ['password', 'hashed_password', 'srp']),
15
+ username: ac.username,
16
+ hasPassword: (0, perm_1.accountHasPassword)(ac),
17
+ adminActualAccess: (0, perm_1.accountCanLoginAdmin)(ac),
18
+ };
19
+ }
20
+ const apis = {
21
+ get_usernames() {
22
+ return { list: Object.keys((0, perm_1.getAccounts)()) };
23
+ },
24
+ get_account({ username }, ctx) {
25
+ return prepareAccount((0, perm_1.getAccount)(username || (0, perm_1.getCurrentUsername)(ctx)))
26
+ || new apiMiddleware_1.ApiError(404);
27
+ },
28
+ get_accounts() {
29
+ return { list: Object.values((0, perm_1.getAccounts)()).map(prepareAccount) };
30
+ },
31
+ get_admins() {
32
+ return { list: Object.values((0, perm_1.getAccounts)()).map(prepareAccount).filter(ac => ac === null || ac === void 0 ? void 0 : ac.adminActualAccess).map(ac => ac.username) };
33
+ },
34
+ set_account({ username, changes }) {
35
+ const { admin } = changes;
36
+ if (admin === null)
37
+ changes.admin = undefined;
38
+ else if (admin !== undefined && typeof admin !== 'boolean')
39
+ return new apiMiddleware_1.ApiError(400, "invalid admin");
40
+ return (0, perm_1.setAccount)(username, changes) ? {} : new apiMiddleware_1.ApiError(400);
41
+ },
42
+ add_account({ username, ...rest }) {
43
+ if ((0, perm_1.getAccount)(username))
44
+ return new apiMiddleware_1.ApiError(const_1.FORBIDDEN);
45
+ if (!(0, perm_1.addAccount)(username, rest))
46
+ return new apiMiddleware_1.ApiError(400);
47
+ return {};
48
+ },
49
+ del_account({ username }) {
50
+ return (0, perm_1.delAccount)(username) ? {} : new apiMiddleware_1.ApiError(400);
51
+ },
52
+ async change_password_others({ username, newPassword }) {
53
+ return (0, api_helpers_1.changePasswordHelper)((0, perm_1.getAccount)(username), newPassword);
54
+ },
55
+ async change_srp_others({ username, salt, verifier }) {
56
+ return (0, api_helpers_1.changeSrpHelper)((0, perm_1.getAccount)(username), salt, verifier);
57
+ }
58
+ };
59
+ exports.default = apis;