hfs 0.47.3 → 0.48.0-alpha2
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/admin/assets/index-1c2b890b.css +1 -0
- package/admin/assets/index-344723f8.js +520 -0
- package/{frontend/assets/sha512-108e3b0a.js → admin/assets/sha512-136b5a7c.js} +1 -1
- package/admin/index.html +2 -2
- package/frontend/assets/index-05dfd82f.css +1 -0
- package/frontend/assets/{index-7eec0199.js → index-12411ff6.js} +15 -15
- package/{admin/assets/sha512-0935647a.js → frontend/assets/sha512-549e6c3a.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +20 -13
- package/plugins/vhosting/plugin.js +15 -15
- package/src/adminApis.js +7 -4
- package/src/api.auth.js +0 -3
- package/src/api.file_list.js +8 -7
- package/src/api.lang.js +1 -1
- package/src/api.net.js +136 -0
- package/src/api.plugins.js +31 -36
- package/src/api.vfs.js +1 -1
- package/src/apiMiddleware.js +22 -20
- package/src/config.js +2 -16
- package/src/const.js +4 -2
- package/src/cross.js +221 -0
- package/src/customHtml.js +7 -22
- package/src/debounceAsync.js +12 -9
- package/src/frontEndApis.js +2 -4
- package/src/github.js +86 -40
- package/src/langs/embedded.js +2 -1
- package/src/langs/hfs-lang-de.json +131 -0
- package/src/langs/hfs-lang-it.json +1 -1
- package/src/listen.js +44 -37
- package/src/log.js +8 -5
- package/src/middlewares.js +1 -3
- package/src/misc.js +33 -137
- package/src/plugins.js +46 -26
- package/src/serveFile.js +1 -0
- package/src/update.js +1 -1
- package/src/upload.js +2 -1
- package/src/util-http.js +11 -8
- package/admin/assets/index-62247236.css +0 -1
- package/admin/assets/index-ca5ac85e.js +0 -518
- package/frontend/assets/index-0ea37f5f.css +0 -1
- package/src/util-generators.js +0 -31
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "kumpu",
|
|
3
|
+
"version": 1.0,
|
|
4
|
+
"hfs_version": "0.47.2",
|
|
5
|
+
"translate": {
|
|
6
|
+
"Select": "Auswählen",
|
|
7
|
+
"n_files": "{n,plural,one{# Datei} other{# Dateien}}",
|
|
8
|
+
"n_folders": "{n,plural,one{# Ordner} other{# Ordner}}",
|
|
9
|
+
"filter_count": "{n,plural, one{# gefiltert} other{# gefiltert}}",
|
|
10
|
+
"select_count": "{n,plural, one{# ausgewählt} other{# ausgewählt}}",
|
|
11
|
+
"filter_placeholder": "Filter...",
|
|
12
|
+
"Select some files": "Dateien auswählen",
|
|
13
|
+
"zip_checkboxes": "Nutzen Sie die Kästchen um Dateien auszuwählen, danach können sie die Zip-Funktion wieder benutzen",
|
|
14
|
+
"zip_tooltip_selected": "Ausgewählte Elemente als einzelne Zip-Datei herunterladen",
|
|
15
|
+
"zip_tooltip_whole": "Die ganze Liste (ungefiltert) als einzelne Zip-Datei herunterladen. Wenn Sie einige Elemente auswählen, werden nur diese herunterladen.",
|
|
16
|
+
"zip_confirm_search": "ALLE Ergebnisse dieser Suche als Zip-Datei herunterladen?",
|
|
17
|
+
"zip_confirm_folder": "GANZEN Ordner als Zip-Datei herunterladen?",
|
|
18
|
+
"select_tooltip": "Auswahl wird benutzt von \"Zip\" und \"Löschen\" (wenn verfügbar), aber Sie können die Liste auch filtern",
|
|
19
|
+
"delete_hint": "Clicken sie erst \"Auswählen\", um zu löschen",
|
|
20
|
+
"delete_confirm": "Lösche {n,plural, one{# Element} other{# Elemente}}?",
|
|
21
|
+
"delete_completed": "Löschen: {n} abgeschlossen",
|
|
22
|
+
"delete_failed": ", {n,plural, one{# fehlgeschlagen} other{# fehlgeschlagen}}",
|
|
23
|
+
"delete_select": "Wählen sie Elemente zum Löschen aus",
|
|
24
|
+
"Delete": "Löschen",
|
|
25
|
+
"Options": "Optionen",
|
|
26
|
+
"Search": "Suche",
|
|
27
|
+
"Zip": "Zip",
|
|
28
|
+
"search_msg": "Ordner und Unterordner durchsuchen",
|
|
29
|
+
"Searching": "Suche",
|
|
30
|
+
"Searched": "Gesucht",
|
|
31
|
+
"Clear search": "Ergebnisse löschen",
|
|
32
|
+
"Interrupted": "Abgebrochen",
|
|
33
|
+
"stopped_before": "Gestoppt bevor etwas gefunden wurde",
|
|
34
|
+
"empty_list": "Keine Treffer",
|
|
35
|
+
"filter_none": "Keine Treffer für Filter",
|
|
36
|
+
"Admin-panel": "Admin-Panel",
|
|
37
|
+
"Login": "Login",
|
|
38
|
+
"Username": "Benutzer",
|
|
39
|
+
"Password": "Passwort",
|
|
40
|
+
"login_untrusted": "Login abgebrochen: Serveridentität kann nicht vertraut werden",
|
|
41
|
+
"login_bad_credentials": "Ungültige Anmeldedaten",
|
|
42
|
+
"login_bad_cookies": "Cookies funktionieren nicht - Login gescheitert",
|
|
43
|
+
"User panel": "User-Panel",
|
|
44
|
+
"Change password": "Passwort ändern",
|
|
45
|
+
"enter_pass": "Neues Passwort eingeben",
|
|
46
|
+
"enter_pass2": "Neues Passwort erneut eingeben",
|
|
47
|
+
"pass2_mismatch": "Die Passwörter stimmen nicht überein. Vorgang abgebrochen.",
|
|
48
|
+
"password_changed": "Passwort geändert",
|
|
49
|
+
"Logout": "Logout",
|
|
50
|
+
"connection error": "Verbindungsfehler",
|
|
51
|
+
"Full timestamp:": "Ganzer Zeitstempel:",
|
|
52
|
+
"Search was interrupted": "Suche wurde abgebrochen",
|
|
53
|
+
"Stop list": "Suche abbrechen",
|
|
54
|
+
"upload_starting": "Ihr Download sollte jetzt starten",
|
|
55
|
+
"wrong_account": "Account {u} hat keinen Zugriff, versuchen Sie einen anderen",
|
|
56
|
+
"no_upload_here": "Keine Uploadberechtigung für den momentanen Ordner",
|
|
57
|
+
"Create folder": "Ordner erstellen",
|
|
58
|
+
"Pick files": "Dateien auswählen",
|
|
59
|
+
"Pick folder": "Ordner auswählen",
|
|
60
|
+
"send_files": "Uploade {n,plural,one{# Datei} other{# Dateien}}, {size}",
|
|
61
|
+
"Clear": "Upload abbrechen",
|
|
62
|
+
"failed_upload": "Konnte {name} nicht hochladen",
|
|
63
|
+
"confirm_resume": "Upload fortsetzen?",
|
|
64
|
+
"file too large": "Datei zu groß",
|
|
65
|
+
"Enter folder name": "Ordnername eingeben",
|
|
66
|
+
"Successfully created": "Erstellung erfolgreich",
|
|
67
|
+
"enter_folder": "Ordner öffnen",
|
|
68
|
+
"folder_exists": "Ein Ordner mit diesem Namen existiert bereits",
|
|
69
|
+
"Sort by:": "Sortieren nach: {by}",
|
|
70
|
+
"name": "Name",
|
|
71
|
+
"extension": "Dateityp",
|
|
72
|
+
"size": "Größe",
|
|
73
|
+
"time": "Datum",
|
|
74
|
+
"Invert order": "Umgekehrte Reihenfolge",
|
|
75
|
+
"Folders first": "Ordner zuerst",
|
|
76
|
+
"Numeric names": "Numerische Namen",
|
|
77
|
+
"theme:": "Theme:",
|
|
78
|
+
"auto": "Auto",
|
|
79
|
+
"light": "Hell",
|
|
80
|
+
"dark": "Dunkel",
|
|
81
|
+
"parent folder": "Übergeordneter Ordner",
|
|
82
|
+
"home": "Home",
|
|
83
|
+
"Continue": "Fortfahren",
|
|
84
|
+
"Confirm": "Bestätigen",
|
|
85
|
+
"Don't": "Abbrechen",
|
|
86
|
+
"Warning": "Warnung",
|
|
87
|
+
"Error": "Fehler",
|
|
88
|
+
"Info": "Info",
|
|
89
|
+
"Unauthorized": "Nicht autorisiert",
|
|
90
|
+
"Forbidden": "Verboten",
|
|
91
|
+
"Not found": "Nicht gefunden",
|
|
92
|
+
"Server error": "Server Fehler",
|
|
93
|
+
"Upload": "Upload",
|
|
94
|
+
"upload_concluded": "Upload abgeschlossen:",
|
|
95
|
+
"upload_finished": "{n} abgeschlossen ({size})",
|
|
96
|
+
"upload_errors": "{n} fehlgeschlagen",
|
|
97
|
+
"upload_file_rejected": "Einige Dateien wurden nicht akzeptiert",
|
|
98
|
+
"File menu": "Dateimenü",
|
|
99
|
+
"Folder menu": "Ordnermenü",
|
|
100
|
+
"Name": "Name",
|
|
101
|
+
"file_open": "Öffnen",
|
|
102
|
+
"Download": "Download",
|
|
103
|
+
"Missing permission": "Fehlende Berechtigung",
|
|
104
|
+
"Reload": "Neu laden",
|
|
105
|
+
"Get list": "Liste anzeigen",
|
|
106
|
+
"Skip existing files": "Überspringe existierende Dateien ",
|
|
107
|
+
"Size": "Größe",
|
|
108
|
+
"Timestamp": "Datum",
|
|
109
|
+
"Show": "Anzeigen",
|
|
110
|
+
"Loading failed": "Laden fehlgeschlagen",
|
|
111
|
+
"Rename": "Umbenennen",
|
|
112
|
+
"Tiles mode:": "Kachelmodus:",
|
|
113
|
+
"off": "Aus",
|
|
114
|
+
"Operation successful": "Operation erfolgreich",
|
|
115
|
+
"Uploader": "Uploader",
|
|
116
|
+
"Download counter": "Downloadzähler",
|
|
117
|
+
"Switch zoom mode": "Zoom Modus wechseln",
|
|
118
|
+
"Full screen": "Vollbild",
|
|
119
|
+
"File Show help": "Dateianzeige - Hilfe",
|
|
120
|
+
"showHelpMain": "Tastaturbefehle:",
|
|
121
|
+
"showHelp_←/→": "←/→",
|
|
122
|
+
"showHelp_↑/↓": "↑/↓",
|
|
123
|
+
"showHelp_space": "Leertaste",
|
|
124
|
+
"showHelp_←/→_body": "vorherige/nächste Datei",
|
|
125
|
+
"showHelp_↑/↓_body": "Hohe Bilder scrollen",
|
|
126
|
+
"showHelp_space_body": "Auswählen",
|
|
127
|
+
"showHelp_D_body": "Download",
|
|
128
|
+
"showHelp_Z_body": "Zoom Modus",
|
|
129
|
+
"showHelp_F_body": "Vollbildmodus"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"Searching": "Cercando",
|
|
29
29
|
"Searched": "Risultati ricerca",
|
|
30
30
|
"Clear search": "Annulla ricerca",
|
|
31
|
-
"Interrupted": "
|
|
31
|
+
"Interrupted": "Interrotto",
|
|
32
32
|
"stopped_before": "Ricerca interrotta prima di trovare risultati",
|
|
33
33
|
"empty_list": "Non c'è niente qui",
|
|
34
34
|
"filter_none": "Nessun elemento corrisponde al filtro impostato",
|
package/src/listen.js
CHANGED
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
28
|
};
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.getUrls = exports.getServerStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = exports.getHttpsWorkingPort = void 0;
|
|
30
|
+
exports.getUrls = exports.getIps = exports.getServerStatus = exports.httpsPortCfg = exports.openAdmin = exports.portCfg = exports.getHttpsWorkingPort = void 0;
|
|
31
31
|
const http = __importStar(require("http"));
|
|
32
32
|
const config_1 = require("./config");
|
|
33
33
|
const index_1 = require("./index");
|
|
@@ -41,6 +41,8 @@ const const_1 = require("./const");
|
|
|
41
41
|
const find_process_1 = __importDefault(require("find-process"));
|
|
42
42
|
const adminApis_1 = require("./adminApis");
|
|
43
43
|
const lodash_1 = __importDefault(require("lodash"));
|
|
44
|
+
const crypto_1 = require("crypto");
|
|
45
|
+
const api_net_1 = require("./api.net");
|
|
44
46
|
let httpSrv;
|
|
45
47
|
let httpsSrv;
|
|
46
48
|
const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', !const_1.DEV);
|
|
@@ -59,7 +61,7 @@ exports.portCfg.sub(async (port) => {
|
|
|
59
61
|
if (!port)
|
|
60
62
|
return;
|
|
61
63
|
httpSrv.on('connection', connections_1.newConnection);
|
|
62
|
-
printUrls(
|
|
64
|
+
printUrls(httpSrv.name);
|
|
63
65
|
if (openBrowserAtStart.get() && !const_1.argv.updated)
|
|
64
66
|
openAdmin();
|
|
65
67
|
});
|
|
@@ -69,9 +71,9 @@ function openAdmin() {
|
|
|
69
71
|
if (!a || typeof a === 'string')
|
|
70
72
|
continue;
|
|
71
73
|
const baseUrl = srv.name + '://localhost:' + a.port;
|
|
72
|
-
(0, open_1.default)(baseUrl + const_1.ADMIN_URI, { wait: true }).catch(e => {
|
|
74
|
+
(0, open_1.default)(baseUrl + const_1.ADMIN_URI, { wait: true }).catch(async (e) => {
|
|
73
75
|
console.debug(String(e));
|
|
74
|
-
console.warn("cannot launch browser on this machine >PLEASE< open your browser and reach one of these (you may need a different address)", ...Object.values(getUrls()).flat().map(x => '\n - ' + x + const_1.ADMIN_URI));
|
|
76
|
+
console.warn("cannot launch browser on this machine >PLEASE< open your browser and reach one of these (you may need a different address)", ...Object.values(await getUrls()).flat().map(x => '\n - ' + x + const_1.ADMIN_URI));
|
|
75
77
|
if (!(0, adminApis_1.anyAccountCanLoginAdmin)())
|
|
76
78
|
console.log(`HINT: you can enter command: create-admin YOUR_PASSWORD`);
|
|
77
79
|
});
|
|
@@ -81,19 +83,27 @@ function openAdmin() {
|
|
|
81
83
|
}
|
|
82
84
|
exports.openAdmin = openAdmin;
|
|
83
85
|
const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
84
|
-
var _a, _b;
|
|
86
|
+
var _a, _b, _c;
|
|
85
87
|
stopServer(httpsSrv).then();
|
|
86
88
|
let port = exports.httpsPortCfg.get();
|
|
87
89
|
try {
|
|
88
90
|
while (!index_1.app)
|
|
89
91
|
await (0, misc_1.wait)(100);
|
|
90
|
-
httpsSrv = Object.assign(https.createServer(port
|
|
92
|
+
httpsSrv = Object.assign(https.createServer(port === PORT_DISABLED ? {} : { key: httpsOptions.private_key, cert: httpsOptions.cert }, index_1.app.callback()), { name: 'https' });
|
|
91
93
|
if (port >= 0) {
|
|
94
|
+
const cert = new crypto_1.X509Certificate(httpsOptions.cert);
|
|
95
|
+
const cn = (_a = cert.subject.split('CN=')[1]) === null || _a === void 0 ? void 0 : _a.split('\n')[0];
|
|
96
|
+
if (cn)
|
|
97
|
+
console.log("certificate loaded for", cn);
|
|
98
|
+
const now = new Date();
|
|
99
|
+
httpsSrv.error = new Date(cert.validFrom) > now ? "certificate not valid yet"
|
|
100
|
+
: new Date(cert.validTo) < now ? "certificate expired"
|
|
101
|
+
: undefined;
|
|
92
102
|
const namesForOutput = { cert: 'certificate', private_key: 'private key' };
|
|
93
|
-
const missing = (
|
|
103
|
+
const missing = (_b = httpsNeeds.find(x => !x.get())) === null || _b === void 0 ? void 0 : _b.key();
|
|
94
104
|
if (missing)
|
|
95
105
|
return httpsSrv.error = "missing " + namesForOutput[missing];
|
|
96
|
-
const cantRead = (
|
|
106
|
+
const cantRead = (_c = httpsNeeds.find(x => !httpsOptions[x.key()])) === null || _c === void 0 ? void 0 : _c.key();
|
|
97
107
|
if (cantRead)
|
|
98
108
|
return httpsSrv.error = "cannot read " + namesForOutput[cantRead];
|
|
99
109
|
}
|
|
@@ -107,7 +117,7 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
107
117
|
if (!port)
|
|
108
118
|
return;
|
|
109
119
|
httpsSrv.on('connection', connections_1.newConnection);
|
|
110
|
-
printUrls(
|
|
120
|
+
printUrls(httpsSrv.name);
|
|
111
121
|
});
|
|
112
122
|
const cert = (0, config_1.defineConfig)('cert', '');
|
|
113
123
|
const privateKey = (0, config_1.defineConfig)('private_key', '');
|
|
@@ -130,14 +140,15 @@ for (const cfg of httpsNeeds) {
|
|
|
130
140
|
await considerHttps();
|
|
131
141
|
});
|
|
132
142
|
}
|
|
133
|
-
|
|
143
|
+
const PORT_DISABLED = -1;
|
|
144
|
+
exports.httpsPortCfg = (0, config_1.defineConfig)('https_port', PORT_DISABLED);
|
|
134
145
|
exports.httpsPortCfg.sub(considerHttps);
|
|
135
146
|
function startServer(srv, { port, host }) {
|
|
136
147
|
return new Promise(async (resolve) => {
|
|
137
148
|
if (!srv)
|
|
138
149
|
return 0;
|
|
139
150
|
try {
|
|
140
|
-
if (port
|
|
151
|
+
if (port === PORT_DISABLED || !host && !await testIpV4()) // !host means ipV4+6, and if v4 port alone is busy we won't be notified of the failure, so we'll first test it on its own
|
|
141
152
|
return resolve(0);
|
|
142
153
|
// from a few tests, this seems enough to support the expect-100 http/1.1 mechanism, at least with curl -T, not used by chrome|firefox anyway
|
|
143
154
|
srv.on('checkContinue', (req, res) => srv.emit('request', req, res));
|
|
@@ -216,40 +227,36 @@ async function getServerStatus() {
|
|
|
216
227
|
}
|
|
217
228
|
exports.getServerStatus = getServerStatus;
|
|
218
229
|
const ignore = /^(lo|.*loopback.*|virtualbox.*|.*\(wsl\).*|llw\d|awdl\d|utun\d|anpi\d)$/i; // avoid giving too much information
|
|
219
|
-
function
|
|
230
|
+
async function getIps() {
|
|
231
|
+
const ips = (0, misc_1.onlyTruthy)(Object.entries((0, os_1.networkInterfaces)()).map(([name, nets]) => nets && !ignore.test(name)
|
|
232
|
+
&& v4first((0, misc_1.onlyTruthy)(nets.map(net => !net.internal && net.address)))[0] // for each interface we consider only 1 address
|
|
233
|
+
)).flat();
|
|
234
|
+
const e = await api_net_1.externalIp;
|
|
235
|
+
if (e)
|
|
236
|
+
ips.unshift(e);
|
|
237
|
+
return v4first(ips)
|
|
238
|
+
.filter((x, i, a) => a.length > 1 || !x.startsWith('169.254')); // 169.254 = dhcp failure on the interface, but keep it if it's our only one
|
|
239
|
+
function v4first(a) {
|
|
240
|
+
return lodash_1.default.sortBy(a, x => x.includes(':'));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
exports.getIps = getIps;
|
|
244
|
+
async function getUrls() {
|
|
245
|
+
const ips = (await getIps()).map(ip => ip.includes(':') ? '[' + ip + ']' : ip);
|
|
220
246
|
return Object.fromEntries((0, misc_1.onlyTruthy)([httpSrv, httpsSrv].map(srv => {
|
|
221
247
|
var _a;
|
|
222
248
|
if (!(srv === null || srv === void 0 ? void 0 : srv.listening))
|
|
223
249
|
return false;
|
|
224
250
|
const port = (_a = srv === null || srv === void 0 ? void 0 : srv.address()) === null || _a === void 0 ? void 0 : _a.port;
|
|
225
251
|
const appendPort = port === (srv.name === 'https' ? 443 : 80) ? '' : ':' + port;
|
|
226
|
-
const urls =
|
|
227
|
-
if (net.internal)
|
|
228
|
-
return;
|
|
229
|
-
let { address } = net;
|
|
230
|
-
if (address.includes(':'))
|
|
231
|
-
address = '[' + address + ']';
|
|
232
|
-
return srv.name + '://' + address + appendPort;
|
|
233
|
-
})).flat());
|
|
252
|
+
const urls = ips.map(ip => `${srv.name}://${ip}${appendPort}`);
|
|
234
253
|
return urls.length && [srv.name, urls];
|
|
235
254
|
})));
|
|
236
255
|
}
|
|
237
256
|
exports.getUrls = getUrls;
|
|
238
|
-
function printUrls(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
continue;
|
|
244
|
-
lodash_1.default.remove(nets, 'internal');
|
|
245
|
-
const first = nets[0];
|
|
246
|
-
if (!first)
|
|
247
|
-
continue;
|
|
248
|
-
const best = lodash_1.default.find(nets, { family: 'IPv4' }) || first;
|
|
249
|
-
const appendPort = port === (proto === 'https' ? 443 : 80) ? '' : ':' + port;
|
|
250
|
-
let { address } = best;
|
|
251
|
-
if (address.includes(':'))
|
|
252
|
-
address = '[' + address + ']';
|
|
253
|
-
console.log('network', name, proto + '://' + address + appendPort);
|
|
254
|
-
}
|
|
257
|
+
function printUrls(srvName) {
|
|
258
|
+
getUrls().then(urls => {
|
|
259
|
+
for (const url of urls[srvName])
|
|
260
|
+
console.log('serving on', url);
|
|
261
|
+
});
|
|
255
262
|
}
|
package/src/log.js
CHANGED
|
@@ -89,7 +89,7 @@ const logMw = async (ctx, next) => {
|
|
|
89
89
|
// don't await, as we don't want to hold the middlewares chain
|
|
90
90
|
ctx.state.completed = Promise.race([(0, stream_1.once)(ctx.res, 'finish'), (0, stream_1.once)(ctx.res, 'close')]);
|
|
91
91
|
ctx.state.completed.then(() => {
|
|
92
|
-
var _a, _b, _c, _d
|
|
92
|
+
var _a, _b, _c, _d;
|
|
93
93
|
if (ctx.state.dont_log)
|
|
94
94
|
return;
|
|
95
95
|
if (dontLogNet.compiled()(ctx.ip))
|
|
@@ -120,16 +120,19 @@ const logMw = async (ctx, next) => {
|
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
-
const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s\n'; // Apache's Common Log Format
|
|
123
|
+
const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s %s\n'; // Apache's Common Log Format
|
|
124
124
|
const a = now.toString().split(' ');
|
|
125
125
|
const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
|
|
126
126
|
const user = (0, perm_1.getCurrentUsername)(ctx);
|
|
127
127
|
const length = (_c = ctx.state.length) !== null && _c !== void 0 ? _c : ctx.length;
|
|
128
|
-
const uri =
|
|
129
|
-
|
|
128
|
+
const uri = ctx.originalUrl;
|
|
129
|
+
const extra = ctx.state.includesLastByte && ctx.vfsNode && { dl: 1 }
|
|
130
|
+
|| ctx.state.uploadPath && { ul: ctx.state.uploadPath, size: ctx.state.uploadSize }
|
|
131
|
+
|| undefined;
|
|
132
|
+
events_1.default.emit(logger.name, Object.assign(lodash_1.default.pick(ctx, ['ip', 'method', 'status']), { length, user, ts: now, uri, extra }));
|
|
130
133
|
debounce(() => // once in a while we check if the file is still good (not deleted, etc), or we'll reopen it
|
|
131
134
|
(0, promises_1.stat)(logger.path).catch(() => logger.reopen())); // async = smoother but we may lose some entries
|
|
132
|
-
stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, uri, ctx.req.httpVersion, ctx.status, (
|
|
135
|
+
stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, uri, ctx.req.httpVersion, ctx.status, (_d = length === null || length === void 0 ? void 0 : length.toString()) !== null && _d !== void 0 ? _d : '-', extra ? JSON.stringify(JSON.stringify(extra)) : ''));
|
|
133
136
|
});
|
|
134
137
|
};
|
|
135
138
|
exports.logMw = logMw;
|
package/src/middlewares.js
CHANGED
|
@@ -248,11 +248,9 @@ async function srpCheck(username, password) {
|
|
|
248
248
|
const clientRes2 = await clientRes1.step2(BigInt(salt), BigInt(pubKey));
|
|
249
249
|
return await step1.step2(clientRes2.A, clientRes2.M1).then(() => true, () => false);
|
|
250
250
|
}
|
|
251
|
-
// unify get/post parameters, with JSON decoding to not be limited to strings
|
|
252
251
|
const paramsDecoder = async (ctx, next) => {
|
|
253
252
|
ctx.params = ctx.method === 'POST' && ctx.originalUrl.startsWith(const_1.API_URI)
|
|
254
|
-
|
|
255
|
-
: (0, misc_1.newObj)(ctx.query, x => Array.isArray(x) ? x : (0, misc_1.tryJson)(x));
|
|
253
|
+
&& ((0, misc_1.tryJson)(await (0, misc_1.stream2string)(ctx.req)) || {});
|
|
256
254
|
await next();
|
|
257
255
|
};
|
|
258
256
|
exports.paramsDecoder = paramsDecoder;
|
package/src/misc.js
CHANGED
|
@@ -18,92 +18,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
21
|
+
exports.asyncGeneratorToReadable = exports.stream2string = exports.same = exports.matches = exports.makeMatcher = exports.makeNetMatcher = exports.isLocalHost = exports.onOff = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.debounceAsync = void 0;
|
|
22
22
|
const path_1 = require("path");
|
|
23
23
|
const lodash_1 = __importDefault(require("lodash"));
|
|
24
24
|
const assert_1 = __importDefault(require("assert"));
|
|
25
25
|
__exportStar(require("./util-http"), exports);
|
|
26
|
-
__exportStar(require("./util-generators"), exports);
|
|
27
26
|
__exportStar(require("./util-files"), exports);
|
|
27
|
+
__exportStar(require("./cross"), exports);
|
|
28
|
+
const stream_1 = require("stream");
|
|
29
|
+
const micromatch_1 = require("micromatch");
|
|
30
|
+
const node_net_1 = require("node:net");
|
|
28
31
|
const debounceAsync_1 = __importDefault(require("./debounceAsync"));
|
|
29
32
|
exports.debounceAsync = debounceAsync_1.default;
|
|
30
|
-
const micromatch_1 = require("micromatch");
|
|
31
|
-
const cidr_tools_1 = __importDefault(require("cidr-tools"));
|
|
32
|
-
function enforceFinal(sub, s) {
|
|
33
|
-
return s.endsWith(sub) ? s : s + sub;
|
|
34
|
-
}
|
|
35
|
-
exports.enforceFinal = enforceFinal;
|
|
36
|
-
function removeStarting(sub, s) {
|
|
37
|
-
return s.startsWith(sub) ? s.slice(sub.length) : s;
|
|
38
|
-
}
|
|
39
|
-
exports.removeStarting = removeStarting;
|
|
40
|
-
function prefix(pre, v, post = '') {
|
|
41
|
-
return v ? pre + v + post : '';
|
|
42
|
-
}
|
|
43
|
-
exports.prefix = prefix;
|
|
44
|
-
function setHidden(dest, src) {
|
|
45
|
-
return Object.defineProperties(dest, newObj(src, value => ({
|
|
46
|
-
enumerable: false,
|
|
47
|
-
writable: true,
|
|
48
|
-
value,
|
|
49
|
-
})));
|
|
50
|
-
}
|
|
51
|
-
exports.setHidden = setHidden;
|
|
52
|
-
function newObj(src, returnNewValue, recur = false) {
|
|
53
|
-
if (!src)
|
|
54
|
-
return {};
|
|
55
|
-
const pairs = Object.entries(src).map(([k, v]) => {
|
|
56
|
-
if (typeof k === 'symbol')
|
|
57
|
-
return;
|
|
58
|
-
let _k = k;
|
|
59
|
-
const curDepth = typeof recur === 'number' ? recur : 0;
|
|
60
|
-
let newV = returnNewValue(v, k, (newK) => {
|
|
61
|
-
_k = newK;
|
|
62
|
-
return true; // for convenient expression concatenation
|
|
63
|
-
}, curDepth);
|
|
64
|
-
if ((recur !== false || returnNewValue.length === 4) // if callback is using depth parameter, then it wants recursion
|
|
65
|
-
&& lodash_1.default.isPlainObject(newV)) // is it recurrable?
|
|
66
|
-
newV = newObj(newV, returnNewValue, curDepth + 1);
|
|
67
|
-
return _k !== undefined && [_k, newV];
|
|
68
|
-
});
|
|
69
|
-
return Object.fromEntries(onlyTruthy(pairs));
|
|
70
|
-
}
|
|
71
|
-
exports.newObj = newObj;
|
|
72
|
-
function wait(ms) {
|
|
73
|
-
return new Promise(res => setTimeout(res, ms));
|
|
74
|
-
}
|
|
75
|
-
exports.wait = wait;
|
|
76
|
-
async function waitFor(cb, { interval = 200, timeout = Infinity } = {}) {
|
|
77
|
-
const started = Date.now();
|
|
78
|
-
while (1) {
|
|
79
|
-
const res = await cb();
|
|
80
|
-
if (res)
|
|
81
|
-
return res;
|
|
82
|
-
if (Date.now() - started >= timeout)
|
|
83
|
-
return;
|
|
84
|
-
await wait(interval);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
exports.waitFor = waitFor;
|
|
88
|
-
function wantArray(x) {
|
|
89
|
-
return x == null ? [] : Array.isArray(x) ? x : [x];
|
|
90
|
-
}
|
|
91
|
-
exports.wantArray = wantArray;
|
|
92
|
-
function getOrSet(o, k, creator) {
|
|
93
|
-
return k in o ? o[k]
|
|
94
|
-
: (o[k] = creator());
|
|
95
|
-
}
|
|
96
|
-
exports.getOrSet = getOrSet;
|
|
97
|
-
// 10 chars is 51+bits, 8 is 41+bits
|
|
98
|
-
function randomId(len = 10) {
|
|
99
|
-
if (len > 10)
|
|
100
|
-
return randomId(10) + randomId(len - 10);
|
|
101
|
-
return Math.random()
|
|
102
|
-
.toString(36)
|
|
103
|
-
.substring(2, 2 + len)
|
|
104
|
-
.replace(/l/g, 'L'); // avoid confusion reading l1
|
|
105
|
-
}
|
|
106
|
-
exports.randomId = randomId;
|
|
107
33
|
const cbs = new Set();
|
|
108
34
|
function onProcessExit(cb) {
|
|
109
35
|
cbs.add(cb);
|
|
@@ -127,20 +53,6 @@ function pattern2filter(pattern) {
|
|
|
127
53
|
return (s) => !s || !pattern || re.test((0, path_1.basename)(s));
|
|
128
54
|
}
|
|
129
55
|
exports.pattern2filter = pattern2filter;
|
|
130
|
-
function truthy(value) {
|
|
131
|
-
return Boolean(value);
|
|
132
|
-
}
|
|
133
|
-
exports.truthy = truthy;
|
|
134
|
-
function onlyTruthy(arr) {
|
|
135
|
-
return arr.filter(truthy);
|
|
136
|
-
}
|
|
137
|
-
exports.onlyTruthy = onlyTruthy;
|
|
138
|
-
function pendingPromise() {
|
|
139
|
-
let takeOut;
|
|
140
|
-
const ret = new Promise((resolve, reject) => takeOut = { resolve, reject });
|
|
141
|
-
return Object.assign(ret, takeOut);
|
|
142
|
-
}
|
|
143
|
-
exports.pendingPromise = pendingPromise;
|
|
144
56
|
// install multiple handlers and returns a handy 'uninstall' function which requires no parameter. Pass a map {event:handler}
|
|
145
57
|
function onOff(em, events) {
|
|
146
58
|
events = { ...events }; // avoid later modifications, as we need this later for uninstallation
|
|
@@ -154,30 +66,6 @@ function onOff(em, events) {
|
|
|
154
66
|
};
|
|
155
67
|
}
|
|
156
68
|
exports.onOff = onOff;
|
|
157
|
-
function objRenameKey(o, from, to) {
|
|
158
|
-
if (!o || !o.hasOwnProperty(from) || from === to)
|
|
159
|
-
return;
|
|
160
|
-
o[to] = o[from];
|
|
161
|
-
delete o[from];
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
exports.objRenameKey = objRenameKey;
|
|
165
|
-
function typedKeys(o) {
|
|
166
|
-
return Object.keys(o);
|
|
167
|
-
}
|
|
168
|
-
exports.typedKeys = typedKeys;
|
|
169
|
-
function typedEntries(o) {
|
|
170
|
-
return Object.entries(o);
|
|
171
|
-
}
|
|
172
|
-
exports.typedEntries = typedEntries;
|
|
173
|
-
function hasProp(obj, key) {
|
|
174
|
-
return key in obj;
|
|
175
|
-
}
|
|
176
|
-
exports.hasProp = hasProp;
|
|
177
|
-
function with_(par, cb) {
|
|
178
|
-
return cb(par);
|
|
179
|
-
}
|
|
180
|
-
exports.with_ = with_;
|
|
181
69
|
function isLocalHost(c) {
|
|
182
70
|
const ip = c.socket.remoteAddress; // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
|
|
183
71
|
return ip && (ip === '::1' || ip.endsWith('127.0.0.1'));
|
|
@@ -193,9 +81,27 @@ function makeNetMatcher(mask, emptyMaskReturns = false) {
|
|
|
193
81
|
const neg = ((_a = all[0]) === null || _a === void 0 ? void 0 : _a[0]) === '!';
|
|
194
82
|
if (neg)
|
|
195
83
|
all[0] = all[0].slice(1);
|
|
196
|
-
|
|
84
|
+
const bl = new node_net_1.BlockList();
|
|
85
|
+
for (const x of all) {
|
|
86
|
+
const m = /^([.:\d]+)(?:\/(\d+)|-(.+)|)$/.exec(x);
|
|
87
|
+
if (!m) {
|
|
88
|
+
console.warn("error in network mask", x);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const address = parseAddress(m[1]);
|
|
92
|
+
if (m[2])
|
|
93
|
+
bl.addSubnet(address, Number(m[2]));
|
|
94
|
+
else if (m[3])
|
|
95
|
+
bl.addRange(address, parseAddress(m[2]));
|
|
96
|
+
else
|
|
97
|
+
bl.addAddress(address);
|
|
98
|
+
}
|
|
99
|
+
return (ip) => neg !== bl.check(ip);
|
|
197
100
|
}
|
|
198
101
|
exports.makeNetMatcher = makeNetMatcher;
|
|
102
|
+
function parseAddress(s) {
|
|
103
|
+
return new node_net_1.SocketAddress({ address: s, family: s.includes(':') ? 'ipv6' : 'ipv4' });
|
|
104
|
+
}
|
|
199
105
|
function makeMatcher(mask, emptyMaskReturns = false) {
|
|
200
106
|
return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')') // adding () will allow us to use the pipe at root level
|
|
201
107
|
: () => emptyMaskReturns;
|
|
@@ -215,13 +121,6 @@ function same(a, b) {
|
|
|
215
121
|
}
|
|
216
122
|
}
|
|
217
123
|
exports.same = same;
|
|
218
|
-
function tryJson(s) {
|
|
219
|
-
try {
|
|
220
|
-
return s && JSON.parse(s);
|
|
221
|
-
}
|
|
222
|
-
catch (_a) { }
|
|
223
|
-
}
|
|
224
|
-
exports.tryJson = tryJson;
|
|
225
124
|
async function stream2string(stream) {
|
|
226
125
|
return new Promise((resolve, reject) => {
|
|
227
126
|
let data = '';
|
|
@@ -238,16 +137,13 @@ async function stream2string(stream) {
|
|
|
238
137
|
});
|
|
239
138
|
}
|
|
240
139
|
exports.stream2string = stream2string;
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
exports.try_ = try_;
|
|
250
|
-
function throw_(err) {
|
|
251
|
-
throw err;
|
|
140
|
+
function asyncGeneratorToReadable(generator) {
|
|
141
|
+
const iterator = generator[Symbol.asyncIterator]();
|
|
142
|
+
return new stream_1.Readable({
|
|
143
|
+
objectMode: true,
|
|
144
|
+
read() {
|
|
145
|
+
iterator.next().then(it => this.push(it.done ? null : it.value));
|
|
146
|
+
}
|
|
147
|
+
});
|
|
252
148
|
}
|
|
253
|
-
exports.
|
|
149
|
+
exports.asyncGeneratorToReadable = asyncGeneratorToReadable;
|