hfs 3.0.7 → 3.1.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/README.md +1 -1
- package/admin/assets/index-CDiYkO8j.css +1 -0
- package/admin/assets/index-Dm5BN1iZ.js +1 -0
- package/admin/assets/index-P5i8LyWl.js +822 -0
- package/admin/assets/{sha512-gDcjPgJS.js → sha512-DG-ElrDe.js} +1 -1
- package/admin/flags/freakflags.css +284 -0
- package/admin/flags/sprite.png +0 -0
- package/admin/index.html +3 -2
- package/frontend/assets/index-legacy-CG_og8xL.js +1 -0
- package/frontend/assets/index-legacy-DDLKWGcm.js +9 -0
- package/frontend/assets/{sha512-legacy-BBwwadDl.js → sha512-legacy-D1zEZzDe.js} +1 -1
- package/frontend/fontello.css +3 -1
- package/frontend/fontello.woff2 +0 -0
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +16409 -0
- package/package.json +12 -11
- package/plugins/antibrute/plugin.js +14 -3
- package/src/AsapStream.js +49 -0
- package/src/ThrottledStream.js +1 -1
- package/src/adminApis.js +16 -5
- package/src/api.accounts.js +1 -6
- package/src/api.auth.js +103 -100
- package/src/api.get_file_list.js +7 -0
- package/src/api.lang.js +1 -0
- package/src/api.log.js +5 -3
- package/src/api.monitor.js +5 -0
- package/src/api.net.js +5 -0
- package/src/api.plugins.js +13 -2
- package/src/api.vfs.js +41 -28
- package/src/apiMiddleware.js +5 -4
- package/src/auth.js +39 -6
- package/src/commands.js +16 -27
- package/src/comments.js +4 -4
- package/src/config.js +5 -2
- package/src/const.js +2 -16
- package/src/cross-const.js +5 -2
- package/src/cross.js +21 -15
- package/src/expiringCache.js +16 -5
- package/src/fileAttr.js +1 -1
- package/src/first.js +7 -4
- package/src/frontEndApis.js +88 -102
- package/src/github.js +32 -21
- package/src/ips.js +1 -1
- package/src/log.js +5 -3
- package/src/middlewares.js +2 -1
- package/src/misc.js +2 -20
- package/src/perm.js +27 -12
- package/src/persistence.js +1 -1
- package/src/plugins.js +12 -6
- package/src/serveFile.js +3 -3
- package/src/serveGuiAndSharedFiles.js +3 -0
- package/src/serveGuiFiles.js +2 -1
- package/src/srp.js +5 -6
- package/src/stat.js +4 -1
- package/src/update.js +18 -25
- package/src/upload.js +4 -3
- package/src/util-files.js +2 -1
- package/src/util-http.js +3 -3
- package/src/vfs.js +4 -2
- package/src/webdav.js +297 -0
- package/admin/assets/index-Bj9Dk3JN.js +0 -822
- package/admin/assets/index-Byv1_NxP.css +0 -1
- package/admin/flags/ad.png +0 -0
- package/admin/flags/ae.png +0 -0
- package/admin/flags/af.png +0 -0
- package/admin/flags/ag.png +0 -0
- package/admin/flags/ai.png +0 -0
- package/admin/flags/al.png +0 -0
- package/admin/flags/am.png +0 -0
- package/admin/flags/ao.png +0 -0
- package/admin/flags/aq.png +0 -0
- package/admin/flags/ar.png +0 -0
- package/admin/flags/as.png +0 -0
- package/admin/flags/at.png +0 -0
- package/admin/flags/au.png +0 -0
- package/admin/flags/aw.png +0 -0
- package/admin/flags/ax.png +0 -0
- package/admin/flags/az.png +0 -0
- package/admin/flags/ba.png +0 -0
- package/admin/flags/bb.png +0 -0
- package/admin/flags/bd.png +0 -0
- package/admin/flags/be.png +0 -0
- package/admin/flags/bf.png +0 -0
- package/admin/flags/bg.png +0 -0
- package/admin/flags/bh.png +0 -0
- package/admin/flags/bi.png +0 -0
- package/admin/flags/bj.png +0 -0
- package/admin/flags/bl.png +0 -0
- package/admin/flags/bm.png +0 -0
- package/admin/flags/bn.png +0 -0
- package/admin/flags/bo.png +0 -0
- package/admin/flags/bq.png +0 -0
- package/admin/flags/br.png +0 -0
- package/admin/flags/bs.png +0 -0
- package/admin/flags/bt.png +0 -0
- package/admin/flags/bv.png +0 -0
- package/admin/flags/bw.png +0 -0
- package/admin/flags/by.png +0 -0
- package/admin/flags/bz.png +0 -0
- package/admin/flags/ca.png +0 -0
- package/admin/flags/cc.png +0 -0
- package/admin/flags/cd.png +0 -0
- package/admin/flags/cf.png +0 -0
- package/admin/flags/cg.png +0 -0
- package/admin/flags/ch.png +0 -0
- package/admin/flags/ci.png +0 -0
- package/admin/flags/ck.png +0 -0
- package/admin/flags/cl.png +0 -0
- package/admin/flags/cm.png +0 -0
- package/admin/flags/cn.png +0 -0
- package/admin/flags/co.png +0 -0
- package/admin/flags/cr.png +0 -0
- package/admin/flags/cu.png +0 -0
- package/admin/flags/cv.png +0 -0
- package/admin/flags/cw.png +0 -0
- package/admin/flags/cx.png +0 -0
- package/admin/flags/cy.png +0 -0
- package/admin/flags/cz.png +0 -0
- package/admin/flags/de.png +0 -0
- package/admin/flags/dj.png +0 -0
- package/admin/flags/dk.png +0 -0
- package/admin/flags/dm.png +0 -0
- package/admin/flags/do.png +0 -0
- package/admin/flags/dz.png +0 -0
- package/admin/flags/ec.png +0 -0
- package/admin/flags/ee.png +0 -0
- package/admin/flags/eg.png +0 -0
- package/admin/flags/eh.png +0 -0
- package/admin/flags/er.png +0 -0
- package/admin/flags/es.png +0 -0
- package/admin/flags/et.png +0 -0
- package/admin/flags/fi.png +0 -0
- package/admin/flags/fj.png +0 -0
- package/admin/flags/fk.png +0 -0
- package/admin/flags/fm.png +0 -0
- package/admin/flags/fo.png +0 -0
- package/admin/flags/fr.png +0 -0
- package/admin/flags/ga.png +0 -0
- package/admin/flags/gb-eng.png +0 -0
- package/admin/flags/gb-nir.png +0 -0
- package/admin/flags/gb-sct.png +0 -0
- package/admin/flags/gb-wls.png +0 -0
- package/admin/flags/gb.png +0 -0
- package/admin/flags/gd.png +0 -0
- package/admin/flags/ge.png +0 -0
- package/admin/flags/gf.png +0 -0
- package/admin/flags/gg.png +0 -0
- package/admin/flags/gh.png +0 -0
- package/admin/flags/gi.png +0 -0
- package/admin/flags/gl.png +0 -0
- package/admin/flags/gm.png +0 -0
- package/admin/flags/gn.png +0 -0
- package/admin/flags/gp.png +0 -0
- package/admin/flags/gq.png +0 -0
- package/admin/flags/gr.png +0 -0
- package/admin/flags/gs.png +0 -0
- package/admin/flags/gt.png +0 -0
- package/admin/flags/gu.png +0 -0
- package/admin/flags/gw.png +0 -0
- package/admin/flags/gy.png +0 -0
- package/admin/flags/hk.png +0 -0
- package/admin/flags/hm.png +0 -0
- package/admin/flags/hn.png +0 -0
- package/admin/flags/hr.png +0 -0
- package/admin/flags/ht.png +0 -0
- package/admin/flags/hu.png +0 -0
- package/admin/flags/id.png +0 -0
- package/admin/flags/ie.png +0 -0
- package/admin/flags/il.png +0 -0
- package/admin/flags/im.png +0 -0
- package/admin/flags/in.png +0 -0
- package/admin/flags/io.png +0 -0
- package/admin/flags/iq.png +0 -0
- package/admin/flags/ir.png +0 -0
- package/admin/flags/is.png +0 -0
- package/admin/flags/it.png +0 -0
- package/admin/flags/je.png +0 -0
- package/admin/flags/jm.png +0 -0
- package/admin/flags/jo.png +0 -0
- package/admin/flags/jp.png +0 -0
- package/admin/flags/ke.png +0 -0
- package/admin/flags/kg.png +0 -0
- package/admin/flags/kh.png +0 -0
- package/admin/flags/ki.png +0 -0
- package/admin/flags/km.png +0 -0
- package/admin/flags/kn.png +0 -0
- package/admin/flags/kp.png +0 -0
- package/admin/flags/kr.png +0 -0
- package/admin/flags/kw.png +0 -0
- package/admin/flags/ky.png +0 -0
- package/admin/flags/kz.png +0 -0
- package/admin/flags/la.png +0 -0
- package/admin/flags/lb.png +0 -0
- package/admin/flags/lc.png +0 -0
- package/admin/flags/li.png +0 -0
- package/admin/flags/lk.png +0 -0
- package/admin/flags/lr.png +0 -0
- package/admin/flags/ls.png +0 -0
- package/admin/flags/lt.png +0 -0
- package/admin/flags/lu.png +0 -0
- package/admin/flags/lv.png +0 -0
- package/admin/flags/ly.png +0 -0
- package/admin/flags/ma.png +0 -0
- package/admin/flags/mc.png +0 -0
- package/admin/flags/md.png +0 -0
- package/admin/flags/me.png +0 -0
- package/admin/flags/mf.png +0 -0
- package/admin/flags/mg.png +0 -0
- package/admin/flags/mh.png +0 -0
- package/admin/flags/mk.png +0 -0
- package/admin/flags/ml.png +0 -0
- package/admin/flags/mm.png +0 -0
- package/admin/flags/mn.png +0 -0
- package/admin/flags/mo.png +0 -0
- package/admin/flags/mp.png +0 -0
- package/admin/flags/mq.png +0 -0
- package/admin/flags/mr.png +0 -0
- package/admin/flags/ms.png +0 -0
- package/admin/flags/mt.png +0 -0
- package/admin/flags/mu.png +0 -0
- package/admin/flags/mv.png +0 -0
- package/admin/flags/mw.png +0 -0
- package/admin/flags/mx.png +0 -0
- package/admin/flags/my.png +0 -0
- package/admin/flags/mz.png +0 -0
- package/admin/flags/na.png +0 -0
- package/admin/flags/nc.png +0 -0
- package/admin/flags/ne.png +0 -0
- package/admin/flags/nf.png +0 -0
- package/admin/flags/ng.png +0 -0
- package/admin/flags/ni.png +0 -0
- package/admin/flags/nl.png +0 -0
- package/admin/flags/no.png +0 -0
- package/admin/flags/np.png +0 -0
- package/admin/flags/nr.png +0 -0
- package/admin/flags/nu.png +0 -0
- package/admin/flags/nz.png +0 -0
- package/admin/flags/om.png +0 -0
- package/admin/flags/pa.png +0 -0
- package/admin/flags/pe.png +0 -0
- package/admin/flags/pf.png +0 -0
- package/admin/flags/pg.png +0 -0
- package/admin/flags/ph.png +0 -0
- package/admin/flags/pk.png +0 -0
- package/admin/flags/pl.png +0 -0
- package/admin/flags/pm.png +0 -0
- package/admin/flags/pn.png +0 -0
- package/admin/flags/pr.png +0 -0
- package/admin/flags/ps.png +0 -0
- package/admin/flags/pt.png +0 -0
- package/admin/flags/pw.png +0 -0
- package/admin/flags/py.png +0 -0
- package/admin/flags/qa.png +0 -0
- package/admin/flags/re.png +0 -0
- package/admin/flags/ro.png +0 -0
- package/admin/flags/rs.png +0 -0
- package/admin/flags/ru.png +0 -0
- package/admin/flags/rw.png +0 -0
- package/admin/flags/sa.png +0 -0
- package/admin/flags/sb.png +0 -0
- package/admin/flags/sc.png +0 -0
- package/admin/flags/sd.png +0 -0
- package/admin/flags/se.png +0 -0
- package/admin/flags/sg.png +0 -0
- package/admin/flags/sh.png +0 -0
- package/admin/flags/si.png +0 -0
- package/admin/flags/sj.png +0 -0
- package/admin/flags/sk.png +0 -0
- package/admin/flags/sl.png +0 -0
- package/admin/flags/sm.png +0 -0
- package/admin/flags/sn.png +0 -0
- package/admin/flags/so.png +0 -0
- package/admin/flags/sr.png +0 -0
- package/admin/flags/ss.png +0 -0
- package/admin/flags/st.png +0 -0
- package/admin/flags/sv.png +0 -0
- package/admin/flags/sx.png +0 -0
- package/admin/flags/sy.png +0 -0
- package/admin/flags/sz.png +0 -0
- package/admin/flags/tc.png +0 -0
- package/admin/flags/td.png +0 -0
- package/admin/flags/tf.png +0 -0
- package/admin/flags/tg.png +0 -0
- package/admin/flags/th.png +0 -0
- package/admin/flags/tj.png +0 -0
- package/admin/flags/tk.png +0 -0
- package/admin/flags/tl.png +0 -0
- package/admin/flags/tm.png +0 -0
- package/admin/flags/tn.png +0 -0
- package/admin/flags/to.png +0 -0
- package/admin/flags/tr.png +0 -0
- package/admin/flags/tt.png +0 -0
- package/admin/flags/tv.png +0 -0
- package/admin/flags/tw.png +0 -0
- package/admin/flags/tz.png +0 -0
- package/admin/flags/ua.png +0 -0
- package/admin/flags/ug.png +0 -0
- package/admin/flags/um.png +0 -0
- package/admin/flags/us.png +0 -0
- package/admin/flags/uy.png +0 -0
- package/admin/flags/uz.png +0 -0
- package/admin/flags/va.png +0 -0
- package/admin/flags/vc.png +0 -0
- package/admin/flags/ve.png +0 -0
- package/admin/flags/vg.png +0 -0
- package/admin/flags/vi.png +0 -0
- package/admin/flags/vn.png +0 -0
- package/admin/flags/vu.png +0 -0
- package/admin/flags/wf.png +0 -0
- package/admin/flags/ws.png +0 -0
- package/admin/flags/xk.png +0 -0
- package/admin/flags/ye.png +0 -0
- package/admin/flags/yt.png +0 -0
- package/admin/flags/za.png +0 -0
- package/admin/flags/zm.png +0 -0
- package/admin/flags/zw.png +0 -0
- package/frontend/assets/index-legacy-COBT1YmS.js +0 -50
package/src/expiringCache.js
CHANGED
|
@@ -2,15 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.expiringCache = expiringCache;
|
|
4
4
|
function expiringCache(ttlMs) {
|
|
5
|
+
if (!ttlMs)
|
|
6
|
+
throw Error('invalid TTL');
|
|
5
7
|
const o = new Map();
|
|
6
8
|
return Object.assign(o, {
|
|
9
|
+
// creator can return undefined if the value should not be cached. `invalidate` is useful in case you have some custom logic for it, other than ttl.
|
|
7
10
|
try(k, creator) {
|
|
8
11
|
let ret = o.get(k);
|
|
9
|
-
if (ret === undefined) {
|
|
10
|
-
ret = creator();
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (ret === undefined) { // undefined = missing, as we don't accept this value in our cache
|
|
13
|
+
ret = creator(invalidate);
|
|
14
|
+
if (ret !== undefined) {
|
|
15
|
+
o.set(k, ret);
|
|
16
|
+
Promise.resolve(ret).then(v => {
|
|
17
|
+
if (v === undefined) // even in a promise, we'll consider undefined as a request to cancel the caching
|
|
18
|
+
invalidate();
|
|
19
|
+
}, () => { }) // avoid js warning
|
|
20
|
+
.finally(() => setTimeout(invalidate, ttlMs)); // wait for async (in case) before starting the timer
|
|
21
|
+
}
|
|
22
|
+
function invalidate() {
|
|
23
|
+
o.delete(k);
|
|
24
|
+
}
|
|
14
25
|
}
|
|
15
26
|
return ret;
|
|
16
27
|
},
|
package/src/fileAttr.js
CHANGED
|
@@ -16,7 +16,7 @@ const fsx = (0, cross_1.try_)(() => {
|
|
|
16
16
|
return { set: (0, util_1.promisify)(lib.set), get: (0, util_1.promisify)(lib.get) };
|
|
17
17
|
}, () => console.warn('fs-x-attributes not available'));
|
|
18
18
|
const fileAttrDb = new kvstorage_1.KvStorage({ defaultPutDelay: 1000, maxPutDelay: 5000 });
|
|
19
|
-
(0, first_1.onProcessExit)(() => fileAttrDb.
|
|
19
|
+
(0, first_1.onProcessExit)(() => fileAttrDb.close());
|
|
20
20
|
const FN = 'file-attr.kv';
|
|
21
21
|
(0, promises_1.access)(FN).then(() => fileAttrDb.open(FN).catch(e => console.error(String(e))), () => { });
|
|
22
22
|
const FILE_ATTR_PREFIX = 'user.hfs.'; // user. prefix to be linux compatible
|
package/src/first.js
CHANGED
|
@@ -9,12 +9,15 @@ function onProcessExit(cb) {
|
|
|
9
9
|
return () => cbsOnExit.delete(cb);
|
|
10
10
|
}
|
|
11
11
|
exports.quitting = false;
|
|
12
|
-
onProcessExit(() => exports.quitting = true);
|
|
13
12
|
// 'exit' event is handled as the last resort, but it's not compatible with async callbacks
|
|
14
|
-
onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal =>
|
|
13
|
+
onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal => {
|
|
14
|
+
exports.quitting = true;
|
|
15
15
|
console.log('quitting', signal || '');
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
return Promise.allSettled(Array.from(cbsOnExit).map(cb => cb(signal))).then(() => {
|
|
17
|
+
console.debug('process exit');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
18
21
|
// keep calling cb in a sync fashion – returning a promise instead would break the code for argv.updating (update.ts)
|
|
19
22
|
function onFirstEvent(emitter, events, cb) {
|
|
20
23
|
let already = false;
|
package/src/frontEndApis.js
CHANGED
|
@@ -1,47 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// This file is part of HFS - Copyright 2021-2023, 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 () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
5
|
};
|
|
39
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
7
|
exports.frontEndApis = void 0;
|
|
41
8
|
exports.notifyClient = notifyClient;
|
|
9
|
+
exports.moveFiles = moveFiles;
|
|
10
|
+
exports.requestedRename = requestedRename;
|
|
42
11
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
43
12
|
const api_get_file_list_1 = require("./api.get_file_list");
|
|
44
|
-
const
|
|
13
|
+
const api_auth_1 = require("./api.auth");
|
|
45
14
|
const events_1 = __importDefault(require("./events"));
|
|
46
15
|
const util_files_1 = require("./util-files");
|
|
47
16
|
const const_1 = require("./const");
|
|
@@ -58,7 +27,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
58
27
|
const partialFolderSize = {};
|
|
59
28
|
exports.frontEndApis = {
|
|
60
29
|
get_file_list: api_get_file_list_1.get_file_list,
|
|
61
|
-
...
|
|
30
|
+
...api_auth_1.authApis,
|
|
62
31
|
get_notifications({ channel }, ctx) {
|
|
63
32
|
(0, misc_1.apiAssertTypes)({ string: { channel } });
|
|
64
33
|
const list = new SendList_1.SendListReadable();
|
|
@@ -70,7 +39,7 @@ exports.frontEndApis = {
|
|
|
70
39
|
});
|
|
71
40
|
},
|
|
72
41
|
async get_file_details({ uris }, ctx) {
|
|
73
|
-
if (typeof uris
|
|
42
|
+
if (!Array.isArray(uris) || typeof uris[0] !== 'string')
|
|
74
43
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad uris');
|
|
75
44
|
const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx);
|
|
76
45
|
return {
|
|
@@ -97,7 +66,7 @@ exports.frontEndApis = {
|
|
|
97
66
|
catch {
|
|
98
67
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
99
68
|
}
|
|
100
|
-
if (!(0, util_files_1.isValidFileName)(name))
|
|
69
|
+
if (!name || !(0, util_files_1.isValidFileName)(name))
|
|
101
70
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
|
|
102
71
|
const parentNode = await (0, vfs_1.urlToNode)(uri, ctx);
|
|
103
72
|
if (!parentNode)
|
|
@@ -124,76 +93,19 @@ exports.frontEndApis = {
|
|
|
124
93
|
}
|
|
125
94
|
const node = await (0, vfs_1.urlToNode)(uri, ctx);
|
|
126
95
|
if (!node)
|
|
127
|
-
|
|
96
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
128
97
|
if ((0, vfs_1.isRoot)(node) || !(0, util_files_1.isValidFileName)(dest))
|
|
129
98
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FORBIDDEN);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!node.source)
|
|
133
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY);
|
|
134
|
-
const destNode = await (0, vfs_1.urlToNode)((0, misc_1.pathEncode)(dest), ctx, node.parent);
|
|
135
|
-
if (destNode && (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_delete', ctx)) // if destination exists, you need delete permission
|
|
136
|
-
return new apiMiddleware_1.ApiError(ctx.status);
|
|
137
|
-
try {
|
|
138
|
-
const destSource = (0, path_1.join)((0, path_1.dirname)(node.source), dest);
|
|
139
|
-
await (0, promises_1.rename)(node.source, destSource);
|
|
140
|
-
(0, comments_1.getCommentFor)(node.source).then(c => {
|
|
141
|
-
if (!c)
|
|
142
|
-
return;
|
|
143
|
-
void (0, comments_1.setCommentFor)(node.source, '');
|
|
144
|
-
void (0, comments_1.setCommentFor)(destSource, c);
|
|
145
|
-
});
|
|
146
|
-
return {};
|
|
147
|
-
}
|
|
148
|
-
catch (e) {
|
|
149
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e);
|
|
150
|
-
}
|
|
99
|
+
await requestedRename(node, dest, ctx);
|
|
100
|
+
return {};
|
|
151
101
|
},
|
|
152
|
-
async move_files({ uri_from, uri_to }, ctx
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
ctx.logExtra(null, { target: uri_from.map(misc_1.pathDecode), destination: (0, misc_1.pathDecode)(uri_to) });
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
159
|
-
}
|
|
160
|
-
const destNode = await (0, vfs_1.urlToNode)(uri_to, ctx);
|
|
161
|
-
const err = !destNode ? const_1.HTTP_NOT_FOUND
|
|
162
|
-
: !(0, vfs_1.nodeIsFolder)(destNode) ? const_1.HTTP_METHOD_NOT_ALLOWED
|
|
163
|
-
: (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_upload', ctx);
|
|
164
|
-
if (err)
|
|
165
|
-
return new apiMiddleware_1.ApiError(err);
|
|
166
|
-
return {
|
|
167
|
-
errors: await Promise.all(uri_from.map(async (from1) => {
|
|
168
|
-
if (typeof from1 !== 'string')
|
|
169
|
-
return const_1.HTTP_BAD_REQUEST;
|
|
170
|
-
const srcNode = await (0, vfs_1.urlToNode)(from1, ctx);
|
|
171
|
-
const src = srcNode?.source;
|
|
172
|
-
if (!src)
|
|
173
|
-
return const_1.HTTP_NOT_FOUND;
|
|
174
|
-
const destName = (0, path_1.basename)(src);
|
|
175
|
-
const destChild = await (0, vfs_1.urlToNode)(destName, ctx, destNode);
|
|
176
|
-
if (destChild && (0, vfs_1.statusCodeForMissingPerm)(destChild, 'can_delete', ctx))
|
|
177
|
-
return ctx.status;
|
|
178
|
-
const dest = (0, path_1.join)(destNode.source, destName);
|
|
179
|
-
if (lodash_1.default.isFunction(override))
|
|
180
|
-
return override?.(srcNode, dest);
|
|
181
|
-
return (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_delete', ctx)
|
|
182
|
-
|| (0, promises_1.rename)(src, dest).catch(async (e) => {
|
|
183
|
-
if (e.code !== 'EXDEV')
|
|
184
|
-
throw e; // exdev = different drive
|
|
185
|
-
await (0, promises_1.copyFile)(src, dest);
|
|
186
|
-
await (0, promises_1.unlink)(src);
|
|
187
|
-
}).catch(e => e.code || String(e));
|
|
188
|
-
}))
|
|
189
|
-
};
|
|
102
|
+
async move_files({ uri_from, uri_to }, ctx) {
|
|
103
|
+
return moveFiles(uri_from, uri_to, ctx);
|
|
190
104
|
},
|
|
191
|
-
async copy_files(
|
|
192
|
-
return
|
|
193
|
-
(srcNode, dest) => // but override behavior
|
|
105
|
+
async copy_files({ uri_from, uri_to }, ctx) {
|
|
106
|
+
return moveFiles(uri_from, uri_to, ctx, (srcNode, dest) => // override behavior
|
|
194
107
|
(0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_read', ctx)
|
|
195
|
-
// .source is checked by
|
|
196
|
-
|| (0, promises_1.copyFile)(srcNode.source, dest, fs_1.default.constants.COPYFILE_EXCL | fs_1.default.constants.COPYFILE_FICLONE)
|
|
108
|
+
|| (0, promises_1.copyFile)(srcNode.source, dest, fs_1.default.constants.COPYFILE_EXCL | fs_1.default.constants.COPYFILE_FICLONE) // .source is checked by moveFiles
|
|
197
109
|
.catch(e => e.code || String(e)));
|
|
198
110
|
},
|
|
199
111
|
async comment({ uri, comment }, ctx) {
|
|
@@ -243,3 +155,77 @@ function notifyClient(channel, name, data) {
|
|
|
243
155
|
events_1.default.emit(NOTIFICATION_PREFIX + channel, name, data);
|
|
244
156
|
}
|
|
245
157
|
const NOTIFICATION_PREFIX = 'notificationChannel:';
|
|
158
|
+
async function moveFiles(uri_from, uri_to, ctx, override) {
|
|
159
|
+
(0, misc_1.apiAssertTypes)({ array: { uri_from }, string: { uri_to } });
|
|
160
|
+
try {
|
|
161
|
+
ctx.logExtra(null, { target: uri_from.map(misc_1.pathDecode), destination: (0, misc_1.pathDecode)(uri_to) });
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
165
|
+
}
|
|
166
|
+
const destNode = await (0, vfs_1.urlToNode)(uri_to, ctx);
|
|
167
|
+
const err = !destNode ? const_1.HTTP_NOT_FOUND
|
|
168
|
+
: !(0, vfs_1.nodeIsFolder)(destNode) ? const_1.HTTP_METHOD_NOT_ALLOWED
|
|
169
|
+
: (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_upload', ctx);
|
|
170
|
+
if (err)
|
|
171
|
+
return new apiMiddleware_1.ApiError(err);
|
|
172
|
+
return {
|
|
173
|
+
errors: await Promise.all(uri_from.map(async (from1) => {
|
|
174
|
+
if (typeof from1 !== 'string')
|
|
175
|
+
return const_1.HTTP_BAD_REQUEST;
|
|
176
|
+
const srcNode = await (0, vfs_1.urlToNode)(from1, ctx);
|
|
177
|
+
const src = srcNode?.source;
|
|
178
|
+
if (!src)
|
|
179
|
+
return const_1.HTTP_NOT_FOUND;
|
|
180
|
+
const destName = (0, path_1.basename)(src);
|
|
181
|
+
const destChild = await (0, vfs_1.urlToNode)(destName, ctx, destNode);
|
|
182
|
+
if (destChild && (0, vfs_1.statusCodeForMissingPerm)(destChild, 'can_delete', ctx))
|
|
183
|
+
return ctx.status;
|
|
184
|
+
const dest = (0, path_1.join)(destNode.source, destName);
|
|
185
|
+
if (lodash_1.default.isFunction(override))
|
|
186
|
+
return override?.(srcNode, dest);
|
|
187
|
+
return (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_delete', ctx)
|
|
188
|
+
|| (0, promises_1.rename)(src, dest).catch(async (e) => {
|
|
189
|
+
if (e.code !== 'EXDEV')
|
|
190
|
+
throw e; // exdev = different drive
|
|
191
|
+
await (0, promises_1.copyFile)(src, dest);
|
|
192
|
+
await (0, promises_1.unlink)(src);
|
|
193
|
+
}).catch(e => e.code || String(e));
|
|
194
|
+
}))
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function requestedRename(node, newName, ctx) {
|
|
198
|
+
if (!node)
|
|
199
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
200
|
+
if ((0, vfs_1.statusCodeForMissingPerm)(node, 'can_delete', ctx))
|
|
201
|
+
return new apiMiddleware_1.ApiError(ctx.status);
|
|
202
|
+
try {
|
|
203
|
+
if (node.name) // virtual name = virtual rename
|
|
204
|
+
node.name = newName;
|
|
205
|
+
else {
|
|
206
|
+
if (!node.source)
|
|
207
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY);
|
|
208
|
+
const destNode = await (0, vfs_1.urlToNode)((0, misc_1.pathEncode)(newName), ctx, node.parent);
|
|
209
|
+
if (destNode && (0, vfs_1.statusCodeForMissingPerm)(destNode, 'can_delete', ctx)) // if destination exists, you need delete permission
|
|
210
|
+
return new apiMiddleware_1.ApiError(ctx.status);
|
|
211
|
+
try {
|
|
212
|
+
const destSource = (0, path_1.join)((0, path_1.dirname)(node.source), newName);
|
|
213
|
+
await (0, promises_1.rename)(node.source, destSource);
|
|
214
|
+
(0, comments_1.getCommentFor)(node.source).then(c => {
|
|
215
|
+
if (!c)
|
|
216
|
+
return;
|
|
217
|
+
void (0, comments_1.setCommentFor)(node.source, '');
|
|
218
|
+
void (0, comments_1.setCommentFor)(destSource, c);
|
|
219
|
+
});
|
|
220
|
+
return {};
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e);
|
|
230
|
+
}
|
|
231
|
+
}
|
package/src/github.js
CHANGED
|
@@ -235,27 +235,38 @@ async function isPluginBlacklisted(repo) {
|
|
|
235
235
|
return (0, exports.getProjectInfo)().then(x => x?.repo_blacklist?.[repo]?.message || '', () => undefined);
|
|
236
236
|
}
|
|
237
237
|
async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
238
|
+
const seen = new Set();
|
|
239
|
+
return new misc_1.AsapStream(pluginPromises());
|
|
240
|
+
async function* pluginPromises() {
|
|
241
|
+
// github doesn't allow complex search, so we have to do it multiple times and merge the results
|
|
242
|
+
const searches = [
|
|
243
|
+
...text.split(' ').filter(Boolean).slice(0, 2).map(x => 'user:' + encodeURI(x)), // first 2 words can be the author of the plugin
|
|
244
|
+
encodeURI(text), // search elsewhere, and results after the author search
|
|
245
|
+
];
|
|
246
|
+
for (const term of searches) {
|
|
247
|
+
for await (const it of apiGithubPaginated(`search/repositories?q=topic:hfs-plugin+${term}`)) {
|
|
248
|
+
const repo = it.full_name;
|
|
249
|
+
if (!repo || seen.has(repo)) // avoid duplicates, as we search multiple times
|
|
250
|
+
continue;
|
|
251
|
+
seen.add(repo);
|
|
252
|
+
if (skipRepos.includes(repo))
|
|
253
|
+
continue;
|
|
254
|
+
yield (async () => {
|
|
255
|
+
if (await isPluginBlacklisted(repo))
|
|
256
|
+
return;
|
|
257
|
+
const pl = await readOnlineCompatiblePlugin(repo, it.default_branch).catch(() => undefined);
|
|
258
|
+
if (!pl)
|
|
259
|
+
return;
|
|
260
|
+
Object.assign(pl, {
|
|
261
|
+
repo,
|
|
262
|
+
downloading: exports.downloading[repo],
|
|
263
|
+
license: it.license?.spdx_id,
|
|
264
|
+
}, lodash_1.default.pick(it, ['pushed_at', 'stargazers_count', 'default_branch']));
|
|
265
|
+
return pl;
|
|
266
|
+
})();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
259
270
|
}
|
|
260
271
|
exports.alerts = persistence_1.storedMap.singleSync('alerts', []);
|
|
261
272
|
const cachedCentralInfo = persistence_1.storedMap.singleSync('cachedCentralInfo', ''); // persisting it could also be useful for no-internet instances, so that you can provide a fresher copy
|
package/src/ips.js
CHANGED
|
@@ -12,7 +12,7 @@ exports.ips = new kvstorage_1.KvStorage({
|
|
|
12
12
|
maxPutDelay: 10 * misc_1.MINUTE,
|
|
13
13
|
maxPutDelayCreate: 0,
|
|
14
14
|
});
|
|
15
|
-
(0, first_1.onProcessExit)(() => exports.ips.
|
|
15
|
+
(0, first_1.onProcessExit)(() => exports.ips.close());
|
|
16
16
|
const trackIpsMw = async (ctx, next) => {
|
|
17
17
|
if (exports.ips.isOpen() && !(0, misc_1.isLocalHost)(ctx))
|
|
18
18
|
exports.ips.put(ctx.ip, { ts: new Date, country: ctx.state.connection.country });
|
package/src/log.js
CHANGED
|
@@ -106,10 +106,12 @@ const logMw = async (ctx, next) => {
|
|
|
106
106
|
// do it now so it's available for returning plugins
|
|
107
107
|
ctx.state.completed = Promise.race([(0, events_1.once)(ctx.res, 'finish'), (0, events_1.once)(ctx.res, 'close')]);
|
|
108
108
|
await next();
|
|
109
|
-
|
|
109
|
+
if (!ctx.state.dontLog) // with Finder's webdav spam, it's best to not report even in console
|
|
110
|
+
console.debug(ctx.status, ctx.method, ctx.originalUrl, ctx.isAborted() ? '(aborted)' : '');
|
|
110
111
|
if (!logSpam.get()
|
|
111
|
-
&& (ctx.querystring.includes('{.exec|')
|
|
112
|
-
|
|
112
|
+
&& (ctx.querystring.includes('{.exec|') // v2's bug
|
|
113
|
+
// requests generated by security scanners, other bots, and macos' finder
|
|
114
|
+
|| ctx.status === misc_1.HTTP_NOT_FOUND && /wlwmanifest.xml$|\.(php)$|cgi|robots.txt$/.test(ctx.path))) {
|
|
113
115
|
events_2.default.emit('spam', ctx);
|
|
114
116
|
return;
|
|
115
117
|
}
|
package/src/middlewares.js
CHANGED
|
@@ -27,8 +27,9 @@ const allowAuthorizationHeader = (0, config_1.defineConfig)('authorization_heade
|
|
|
27
27
|
exports.sessionDuration = (0, config_1.defineConfig)('session_duration', Number(process.env.SESSION_DURATION) || misc_1.DAY / 1000, v => v * 1000);
|
|
28
28
|
exports.gzipper = (0, koa_compress_1.default)({
|
|
29
29
|
threshold: 2048,
|
|
30
|
-
gzip: { flush: zlib_1.constants.Z_SYNC_FLUSH },
|
|
30
|
+
gzip: { flush: zlib_1.constants.Z_SYNC_FLUSH }, // flush is necessary for SSE, at least in Chrome145
|
|
31
31
|
deflate: { flush: zlib_1.constants.Z_SYNC_FLUSH },
|
|
32
|
+
zstd: { flush: zlib_1.constants.Z_SYNC_FLUSH },
|
|
32
33
|
br: false, // disable brotli
|
|
33
34
|
filter(type) {
|
|
34
35
|
return /text|javascript|style/i.test(type);
|
package/src/misc.js
CHANGED
|
@@ -18,7 +18,6 @@ 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.AsapStream = void 0;
|
|
22
21
|
exports.pattern2filter = pattern2filter;
|
|
23
22
|
exports.isLocalHost = isLocalHost;
|
|
24
23
|
exports.netMatches = netMatches;
|
|
@@ -33,6 +32,7 @@ __exportStar(require("./util-files"), exports);
|
|
|
33
32
|
__exportStar(require("./fileAttr"), exports);
|
|
34
33
|
__exportStar(require("./cross"), exports);
|
|
35
34
|
__exportStar(require("./debounceAsync"), exports);
|
|
35
|
+
__exportStar(require("./AsapStream"), exports);
|
|
36
36
|
const stream_1 = require("stream");
|
|
37
37
|
const node_net_1 = require("node:net");
|
|
38
38
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
@@ -131,30 +131,12 @@ function asyncGeneratorToReadable(generator) {
|
|
|
131
131
|
}
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
|
-
// produces as promises resolve, not sequentially
|
|
135
|
-
class AsapStream extends stream_1.Readable {
|
|
136
|
-
promises;
|
|
137
|
-
finished = false;
|
|
138
|
-
constructor(promises) {
|
|
139
|
-
super({ objectMode: true });
|
|
140
|
-
this.promises = promises;
|
|
141
|
-
}
|
|
142
|
-
_read() {
|
|
143
|
-
if (this.finished)
|
|
144
|
-
return;
|
|
145
|
-
this.finished = true;
|
|
146
|
-
for (const p of this.promises)
|
|
147
|
-
p.then(x => x !== undefined && this.push(x), e => this.emit('error', e));
|
|
148
|
-
Promise.allSettled(this.promises).then(() => this.push(null));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
exports.AsapStream = AsapStream;
|
|
152
134
|
function apiAssertTypes(paramsByType) {
|
|
153
135
|
for (const [types, params] of Object.entries(paramsByType)) {
|
|
154
136
|
if (!lodash_1.default.isPlainObject(params))
|
|
155
137
|
throw "invalid apiAssertTypes call";
|
|
156
138
|
for (const [name, val] of Object.entries(params))
|
|
157
|
-
if (!types.split('_').some(
|
|
139
|
+
if (!types.split('_').some(t => t === 'array' ? Array.isArray(val) : t === 'object' ? lodash_1.default.isPlainObject(val) : typeof val === t))
|
|
158
140
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad ' + name);
|
|
159
141
|
}
|
|
160
142
|
}
|
package/src/perm.js
CHANGED
|
@@ -22,23 +22,21 @@ exports.accountCanLogin = accountCanLogin;
|
|
|
22
22
|
exports.accountIsDisabled = accountIsDisabled;
|
|
23
23
|
exports.accountCanLoginAdmin = accountCanLoginAdmin;
|
|
24
24
|
exports.accountCanChangePassword = accountCanChangePassword;
|
|
25
|
-
exports.changeSrpHelper = changeSrpHelper;
|
|
26
25
|
const lodash_1 = __importDefault(require("lodash"));
|
|
27
26
|
const misc_1 = require("./misc");
|
|
28
27
|
const config_1 = require("./config");
|
|
29
28
|
const tssrp6a_1 = require("tssrp6a");
|
|
30
29
|
const events_1 = __importDefault(require("./events"));
|
|
31
|
-
const apiMiddleware_1 = require("./apiMiddleware");
|
|
32
30
|
const auth_1 = require("./auth");
|
|
33
31
|
// provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
|
|
34
32
|
function expandUsername(who) {
|
|
35
|
-
const ret =
|
|
33
|
+
const ret = new Set();
|
|
36
34
|
const q = [who];
|
|
37
35
|
for (const u of q) {
|
|
38
36
|
const a = getAccount(u);
|
|
39
37
|
if (!a || a.disabled)
|
|
40
38
|
continue;
|
|
41
|
-
ret.
|
|
39
|
+
ret.add(u);
|
|
42
40
|
if (a.belongs)
|
|
43
41
|
q.push(...a.belongs);
|
|
44
42
|
}
|
|
@@ -46,8 +44,8 @@ function expandUsername(who) {
|
|
|
46
44
|
}
|
|
47
45
|
// check if current username or any ancestor match the provided usernames
|
|
48
46
|
function ctxBelongsTo(ctx, usernames) {
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
const s = ctx.state.usernames ||= expandUsername((0, auth_1.getCurrentUsername)(ctx));
|
|
48
|
+
return usernames.some(u => s.has(u)); // cache ancestors' usernames inside context state
|
|
51
49
|
}
|
|
52
50
|
function getUsernames() {
|
|
53
51
|
return Object.keys(exports.accounts.get());
|
|
@@ -125,7 +123,30 @@ exports.accounts.sub(lodash_1.default.debounce(obj => {
|
|
|
125
123
|
(0, misc_1.setHidden)(rec, { username: norm });
|
|
126
124
|
}
|
|
127
125
|
void updateAccount(rec, {}); // work fields
|
|
126
|
+
removeLoops(norm);
|
|
128
127
|
});
|
|
128
|
+
function removeLoops(normalizedUsername, visiting = new Set()) {
|
|
129
|
+
if (visiting.has(normalizedUsername))
|
|
130
|
+
return;
|
|
131
|
+
visiting.add(normalizedUsername);
|
|
132
|
+
const account = obj[normalizedUsername];
|
|
133
|
+
const removed = lodash_1.default.remove(account.belongs, parent => {
|
|
134
|
+
const k = normalizeUsername(parent);
|
|
135
|
+
return obj[k] && visiting.has(k);
|
|
136
|
+
});
|
|
137
|
+
if (removed.length)
|
|
138
|
+
saveAccountsAsap();
|
|
139
|
+
if (account?.belongs?.length) {
|
|
140
|
+
for (const parent of account.belongs) {
|
|
141
|
+
const k = normalizeUsername(parent);
|
|
142
|
+
if (obj[k])
|
|
143
|
+
removeLoops(k, visiting);
|
|
144
|
+
}
|
|
145
|
+
if (!account.belongs.length)
|
|
146
|
+
delete account.belongs;
|
|
147
|
+
}
|
|
148
|
+
visiting.delete(normalizedUsername);
|
|
149
|
+
}
|
|
129
150
|
})); // don't trigger in the middle of a series of deletion, as we may have an inconsistent state
|
|
130
151
|
function normalizeUsername(username) {
|
|
131
152
|
return username.toLocaleLowerCase();
|
|
@@ -202,9 +223,3 @@ function accountCanLoginAdmin(account) {
|
|
|
202
223
|
function accountCanChangePassword(account) {
|
|
203
224
|
return account && !getFromAccount(account, a => a.disable_password_change);
|
|
204
225
|
}
|
|
205
|
-
async function changeSrpHelper(account, salt, verifier) {
|
|
206
|
-
if (!salt || !verifier)
|
|
207
|
-
return new apiMiddleware_1.ApiError(misc_1.HTTP_BAD_REQUEST, 'missing parameters');
|
|
208
|
-
await updateAccount(account, account => saveSrpInfo(account, salt, verifier));
|
|
209
|
-
return {};
|
|
210
|
-
}
|
package/src/persistence.js
CHANGED
|
@@ -22,4 +22,4 @@ exports.storedMap.open('data.kv').catch(e => {
|
|
|
22
22
|
for (const x of await (0, fast_glob_1.default)('*.kv.lock')) // legacy pre 3.0.5
|
|
23
23
|
(0, promises_1.unlink)(x).catch(() => { });
|
|
24
24
|
});
|
|
25
|
-
(0, first_1.onProcessExit)(() => exports.storedMap.
|
|
25
|
+
(0, first_1.onProcessExit)(() => exports.storedMap.close());
|
package/src/plugins.js
CHANGED
|
@@ -219,11 +219,7 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
219
219
|
await Promise.all(mapPlugins(async (pl, id) => {
|
|
220
220
|
try {
|
|
221
221
|
const res = await pl.middleware?.(ctx);
|
|
222
|
-
|
|
223
|
-
console.debug("plugin changed response", id);
|
|
224
|
-
lastStatus = ctx.status;
|
|
225
|
-
lastBody = ctx.body;
|
|
226
|
-
}
|
|
222
|
+
printChange(id);
|
|
227
223
|
if (ctx.isAborted())
|
|
228
224
|
ctx.stop();
|
|
229
225
|
// don't just check ctx.isStopped, as the async plugin that called ctx.stop will reach here after sync ones
|
|
@@ -236,7 +232,7 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
236
232
|
printError(id, e);
|
|
237
233
|
}
|
|
238
234
|
}));
|
|
239
|
-
// expose public plugins' files
|
|
235
|
+
// expose public plugins' files
|
|
240
236
|
if (!ctx.isStopped) {
|
|
241
237
|
const { path } = ctx;
|
|
242
238
|
if (path.startsWith(const_1.PLUGINS_PUB_URI)) {
|
|
@@ -252,13 +248,23 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
252
248
|
if (ctx.body === undefined && ctx.status === const_1.HTTP_NOT_FOUND) // no response was provided by plugins, so we'll do
|
|
253
249
|
await next();
|
|
254
250
|
}
|
|
251
|
+
lastStatus = ctx.status;
|
|
252
|
+
lastBody = ctx.body;
|
|
255
253
|
for (const [id, f] of Object.entries(after))
|
|
256
254
|
try {
|
|
257
255
|
await f();
|
|
256
|
+
printChange(id);
|
|
258
257
|
}
|
|
259
258
|
catch (e) {
|
|
260
259
|
printError(id, e);
|
|
261
260
|
}
|
|
261
|
+
function printChange(id) {
|
|
262
|
+
if (id === exports.SERVER_CODE_ID || (lastStatus === ctx.status && lastBody === ctx.body))
|
|
263
|
+
return;
|
|
264
|
+
console.debug("plugin changed response:", id);
|
|
265
|
+
lastStatus = ctx.status;
|
|
266
|
+
lastBody = ctx.body;
|
|
267
|
+
}
|
|
262
268
|
function printError(id, e) {
|
|
263
269
|
console.log(`error middleware plugin ${id}: ${e?.message || e}`);
|
|
264
270
|
console.debug(e);
|
package/src/serveFile.js
CHANGED
|
@@ -36,7 +36,7 @@ function forceDownload(ctx, name) {
|
|
|
36
36
|
disposition(ctx, name, true);
|
|
37
37
|
}
|
|
38
38
|
function disposition(ctx, name, forceDownload = false) {
|
|
39
|
-
// ctx.attachment is not working well on Windows. Eg: for file "èÖ.txt" it is producing `Content-Disposition: attachment; filename="??.txt"`. Koa uses module content-disposition, that actually produces a better result anyway: ``
|
|
39
|
+
// ctx.attachment is not working well on Windows. Eg: for the file "èÖ.txt" it is producing `Content-Disposition: attachment; filename="??.txt"`. Koa uses module content-disposition, that actually produces a better result anyway: ``
|
|
40
40
|
ctx.set('Content-Disposition', (forceDownload ? 'attachment; ' : '')
|
|
41
41
|
+ `filename="${toAsciiEquivalent(name)}"; filename*=UTF-8''${encodeURIComponent(name)}`);
|
|
42
42
|
}
|
|
@@ -44,10 +44,10 @@ async function serveFileNode(ctx, node) {
|
|
|
44
44
|
const { source, mime } = node;
|
|
45
45
|
const name = (0, vfs_1.getNodeName)(node);
|
|
46
46
|
const mimeString = typeof mime === 'string' ? mime
|
|
47
|
-
: lodash_1.default.find(mime, (
|
|
47
|
+
: lodash_1.default.find(mime, (_val, mask) => (0, misc_1.matches)(name, mask));
|
|
48
48
|
if (allowedReferer.get()) {
|
|
49
49
|
const ref = (0, misc_1.try_)(() => new URL(ctx.get('referer') || '').host);
|
|
50
|
-
if (ref && ref !== ctx.host // automatically accept if referer is basically the hosting domain
|
|
50
|
+
if (ref && ref !== ctx.host // automatically accept if the referer is basically the hosting domain
|
|
51
51
|
&& !(0, misc_1.matches)(ref, allowedReferer.get()))
|
|
52
52
|
return ctx.status = const_1.HTTP_FORBIDDEN;
|
|
53
53
|
}
|
|
@@ -28,6 +28,7 @@ const comments_1 = require("./comments");
|
|
|
28
28
|
const basicWeb_1 = require("./basicWeb");
|
|
29
29
|
const icons_1 = require("./icons");
|
|
30
30
|
const plugins_1 = require("./plugins");
|
|
31
|
+
const webdav_1 = require("./webdav");
|
|
31
32
|
const serveFrontendFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.FRONTEND_PROXY, cross_const_1.FRONTEND_URI);
|
|
32
33
|
const serveFrontendPrefixed = (0, koa_mount_1.default)(cross_const_1.FRONTEND_URI.slice(0, -1), serveFrontendFiles);
|
|
33
34
|
const serveAdminFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.ADMIN_PROXY, cross_const_1.ADMIN_URI);
|
|
@@ -58,6 +59,8 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
|
|
|
58
59
|
ctx.state.considerAsGui = true;
|
|
59
60
|
return (0, serveFile_1.serveFile)(ctx, (0, path_1.join)(plugin?.folder || '', icons_1.ICONS_FOLDER, file), const_1.MIME_AUTO);
|
|
60
61
|
}
|
|
62
|
+
if (await (0, webdav_1.handledWebdav)(ctx))
|
|
63
|
+
return;
|
|
61
64
|
const { get } = ctx.query;
|
|
62
65
|
const getUploadTempHash = get === cross_const_1.UPLOAD_TEMP_HASH;
|
|
63
66
|
if (ctx.method === 'PUT' || getUploadTempHash) { // PUT is what you get with `curl -T file url/`
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -62,7 +62,7 @@ const getFaviconTimestamp = (0, misc_1.debounceAsync)(async () => {
|
|
|
62
62
|
return !f ? 0 : (0, misc_1.statWithTimeout)(f).then(x => x?.mtimeMs || 0, () => 0);
|
|
63
63
|
}, { retain: 5_000 });
|
|
64
64
|
async function treatIndex(ctx, filesUri, body) {
|
|
65
|
-
const session = await
|
|
65
|
+
const session = await api_auth_1.authApis.refresh_session({}, ctx);
|
|
66
66
|
ctx.set('etag', '');
|
|
67
67
|
ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
68
68
|
ctx.type = 'html';
|
|
@@ -105,6 +105,7 @@ async function treatIndex(ctx, filesUri, body) {
|
|
|
105
105
|
lang
|
|
106
106
|
}, null, 4).replace(/<(\/script)/g, '<"+"$1') /*avoid breaking our script container*/}
|
|
107
107
|
document.documentElement.setAttribute('ver', HFS.VERSION.split('-')[0])
|
|
108
|
+
document.documentElement.setAttribute('browser', ${JSON.stringify((0, misc_1.shortenAgent)(ctx.get('user-agent')))})
|
|
108
109
|
</script>
|
|
109
110
|
${isFrontend && `
|
|
110
111
|
<title>${adminApis_1.title.get()}</title>
|