hfs 0.49.0-alpha2 → 0.49.0-alpha3

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.
@@ -1,4 +1,4 @@
1
- import{g as OF,c as UF}from"./index-3a379840.js";function gF(sF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var dF={exports:{}};/*
1
+ import{g as OF,c as UF}from"./index-51e1daa3.js";function gF(sF,hF){for(var eF=0;eF<hF.length;eF++){const tF=hF[eF];if(typeof tF!="string"&&!Array.isArray(tF)){for(const w in tF)if(w!=="default"&&!(w in sF)){const lF=Object.getOwnPropertyDescriptor(tF,w);lF&&Object.defineProperty(sF,w,lF.get?lF:{enumerable:!0,get:()=>tF[w]})}}}return Object.freeze(Object.defineProperty(sF,Symbol.toStringTag,{value:"Module"}))}var dF={exports:{}};/*
2
2
  * [js-sha512]{@link https://github.com/emn178/js-sha512}
3
3
  *
4
4
  * @version 0.8.0
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
6
6
  <link href="/fontello.css" rel="stylesheet" />
7
7
 
8
- <script type="module" crossorigin src="/assets/index-3a379840.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-51e1daa3.js"></script>
9
9
  <link rel="stylesheet" href="/assets/index-db5f0e4b.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "0.49.0-alpha2",
3
+ "version": "0.49.0-alpha3",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
package/src/adminApis.js CHANGED
@@ -53,6 +53,7 @@ const customHtml_1 = require("./customHtml");
53
53
  const lodash_1 = __importDefault(require("lodash"));
54
54
  const update_1 = require("./update");
55
55
  const consoleLog_1 = require("./consoleLog");
56
+ const path_1 = require("path");
56
57
  exports.adminApis = {
57
58
  ...api_vfs_1.default,
58
59
  ...api_accounts_1.default,
@@ -76,9 +77,15 @@ exports.adminApis = {
76
77
  return {};
77
78
  },
78
79
  get_config: config_1.getWholeConfig,
79
- update({ tag }) {
80
- return (0, update_1.update)(tag);
80
+ get_config_text() {
81
+ return {
82
+ path: config_1.configFile.getPath(),
83
+ fullPath: (0, path_1.resolve)(config_1.configFile.getPath()),
84
+ text: config_1.configFile.getText(),
85
+ };
81
86
  },
87
+ set_config_text: ({ text }) => config_1.configFile.save(text, { reparse: true }),
88
+ update: ({ tag }) => (0, update_1.update)(tag),
82
89
  async check_update() {
83
90
  return { options: await (0, update_1.getUpdates)() };
84
91
  },
package/src/api.net.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.makeCert = exports.acme_email = exports.acme_domain = exports.acmeMiddleware = exports.externalIp = void 0;
7
+ exports.makeCert = exports.acme_email = exports.acme_domain = exports.selfCheckMiddleware = exports.acmeMiddleware = exports.externalIp = void 0;
8
8
  const apiMiddleware_1 = require("./apiMiddleware");
9
9
  const nat_upnp_ts_1 = require("nat-upnp-ts");
10
10
  const const_1 = require("./const");
@@ -27,9 +27,13 @@ const originalMethod = upnpClient.getGateway;
27
27
  upnpClient.getGateway = (0, misc_1.debounceAsync)(() => originalMethod.apply(upnpClient), 0, { retain: misc_1.HOUR, retainFailure: 30000 });
28
28
  upnpClient.getGateway().catch(() => { });
29
29
  exports.externalIp = ''; // poll external ip
30
- (0, misc_1.repeat)(10 * misc_1.MINUTE, () => upnpClient.getPublicIp().then(v => exports.externalIp = v));
30
+ (0, misc_1.repeat)(10 * misc_1.MINUTE, () => upnpClient.getPublicIp().then(v => {
31
+ if (v !== exports.externalIp)
32
+ getPublicIps.clearRetain();
33
+ return exports.externalIp = v;
34
+ }));
31
35
  const getNatInfo = (0, misc_1.debounceAsync)(async () => {
32
- var _a, _b;
36
+ var _a, _b, _c, _d;
33
37
  const gettingIps = getPublicIps(); // don't wait, do it in parallel
34
38
  const res = await upnpClient.getGateway().catch(() => null);
35
39
  const status = await (0, listen_1.getServerStatus)();
@@ -48,9 +52,10 @@ const getNatInfo = (0, misc_1.debounceAsync)(async () => {
48
52
  mapped,
49
53
  internalPort,
50
54
  externalPort: mapped === null || mapped === void 0 ? void 0 : mapped.public.port,
55
+ proto: ((_c = status === null || status === void 0 ? void 0 : status.https) === null || _c === void 0 ? void 0 : _c.listening) ? 'https' : ((_d = status === null || status === void 0 ? void 0 : status.http) === null || _d === void 0 ? void 0 : _d.listening) ? 'http' : undefined,
51
56
  };
52
57
  });
53
- async function getPublicIps() {
58
+ const getPublicIps = (0, misc_1.debounceAsync)(async () => {
54
59
  const res = await (0, github_1.getProjectInfo)();
55
60
  const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => { var _a; return (_a = x.v) !== null && _a !== void 0 ? _a : 4; }));
56
61
  const ips = await (0, misc_1.promiseBestEffort)(groupedByVersion.map(singleVersion => Promise.any(singleVersion.map(async (svc) => {
@@ -71,7 +76,7 @@ async function getPublicIps() {
71
76
  return validIps;
72
77
  }))));
73
78
  return lodash_1.default.uniq(ips.flat());
74
- }
79
+ }, 0, { retain: 10 * misc_1.MINUTE });
75
80
  function findGateway() {
76
81
  return new Promise((resolve, reject) => (0, child_process_1.exec)(const_1.IS_WINDOWS || const_1.IS_MAC ? 'netstat -rn' : 'route -n', (err, out) => {
77
82
  var _a;
@@ -99,6 +104,14 @@ const acmeMiddleware = (ctx, next) => {
99
104
  return next();
100
105
  };
101
106
  exports.acmeMiddleware = acmeMiddleware;
107
+ let selfCheckMiddlewareEnabled = false;
108
+ const CHECK_URL = const_1.SPECIAL_URI + 'self-check';
109
+ const selfCheckMiddleware = (ctx, next) => {
110
+ if (!selfCheckMiddlewareEnabled || !ctx.url.startsWith(CHECK_URL))
111
+ return next();
112
+ ctx.body = 'HFS';
113
+ };
114
+ exports.selfCheckMiddleware = selfCheckMiddleware;
102
115
  async function checkDomain(domain) {
103
116
  const resolver = new promises_2.Resolver();
104
117
  const prjInfo = await (0, github_1.getProjectInfo)();
@@ -117,11 +130,10 @@ async function checkDomain(domain) {
117
130
  const domainIpsThisVersion = domainIps.filter(x => (0, net_1.isIPv6)(x) === v6);
118
131
  const ipsThisVersion = publicIps.filter(x => (0, net_1.isIPv6)(x) === v6);
119
132
  if (domainIpsThisVersion.length && ipsThisVersion.length && !lodash_1.default.intersection(domainIpsThisVersion, ipsThisVersion).length)
120
- throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, `configure your domain to point to ${ipsThisVersion} (currently on ${domainIpsThisVersion[0]}) – a change can take hours to be effective`);
133
+ throw new apiMiddleware_1.ApiError(const_1.HTTP_PRECONDITION_FAILED, `configure your domain to point to ${ipsThisVersion} (currently on ${domainIpsThisVersion[0]}) – a change can take hours to be effective`);
121
134
  }
122
135
  }
123
136
  async function generateSSLCert(domain, email) {
124
- await checkDomain(domain);
125
137
  // will answer challenge through our koa app (if on port 80) or must we spawn a dedicated server?
126
138
  const { upnp, externalPort } = await getNatInfo();
127
139
  const { http } = await (0, listen_1.getServerStatus)();
@@ -134,15 +146,16 @@ async function generateSSLCert(domain, email) {
134
146
  acmeMiddlewareEnabled = true;
135
147
  console.debug('acme challenge server ready');
136
148
  try {
137
- let check = await checkPort(domain, 80); // some check services may not consider the domain, but we already verified that
149
+ const checkUrl = `http://${domain}`;
150
+ let check = await selfCheck(checkUrl); // some check services may not consider the domain, but we already verified that
138
151
  if (check && !check.success && upnp && externalPort !== 80) { // consider a short-lived mapping
152
+ console.debug("setting temporary port forward");
139
153
  // @ts-ignore
140
154
  await upnpClient.createMapping({ private: 80, public: { host: '', port: 80 }, description: 'hfs temporary', ttl: 30 }).catch(() => { });
141
- check = await checkPort(domain, 80); // repeat test
155
+ check = await selfCheck(checkUrl); // repeat test
142
156
  }
143
- if (!check)
144
- throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "couldn't test port 80");
145
- if (!check.success)
157
+ //if (!check) throw new ApiError(HTTP_FAILED_DEPENDENCY, "couldn't test port 80")
158
+ if (!(check === null || check === void 0 ? void 0 : check.success))
146
159
  throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "port 80 is not working on the specified domain");
147
160
  const acmeClient = new acme_client_1.default.Client({
148
161
  accountKey: await acme_client_1.default.crypto.createPrivateKey(),
@@ -172,29 +185,48 @@ async function generateSSLCert(domain, email) {
172
185
  console.debug('acme terminated');
173
186
  }
174
187
  }
175
- async function checkPort(ip, port) {
188
+ async function checkService(url, serviceKey) {
189
+ var _a;
176
190
  const prjInfo = await (0, github_1.getProjectInfo)();
177
- console.log(`checking server ${ip}:${port}`);
178
- for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo.checkServerServices), 2)) {
179
- try {
180
- return Promise.any(services.map(async ({ url, body, selector, regexpSuccess, regexpFailure, ...rest }) => {
181
- const service = new URL(url).hostname;
182
- console.log('trying service', service);
183
- const res = await (0, util_http_1.httpString)(applySymbols(url), { family: (0, net_1.isIPv6)(ip) ? 6 : 4, body: applySymbols(body), ...rest });
184
- const success = new RegExp(regexpSuccess).test(res);
185
- const failure = new RegExp(regexpFailure).test(res);
186
- if (success === failure)
187
- throw console.debug('inconsistent:' + service); // this result cannot be trusted
188
- console.debug(service, 'responded', success);
189
- return { success, service, ip, port };
190
- }));
191
+ console.log(`checking server ${url}`);
192
+ selfCheckMiddlewareEnabled = true;
193
+ try {
194
+ const parsed = new URL(url);
195
+ const family = !(0, net_1.isIP)(parsed.hostname) ? undefined : (0, net_1.isIPv6)(parsed.hostname) ? 6 : 4;
196
+ for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo[serviceKey]), 2)) {
197
+ try {
198
+ return await Promise.any(services.map(async (svc) => {
199
+ if (!svc.url || svc.type)
200
+ throw 'unsupported ' + svc.type; // only default type supported for now
201
+ let { url: serviceUrl, body, regexpSuccess, regexpFailure, ...rest } = svc;
202
+ const service = new URL(serviceUrl).hostname;
203
+ console.log('trying external service', service);
204
+ body = applySymbols(body);
205
+ serviceUrl = applySymbols(serviceUrl);
206
+ const res = await (0, misc_1.haveTimeout)(10000, (0, util_http_1.httpString)(serviceUrl, { family, ...rest, body }));
207
+ const success = new RegExp(regexpSuccess).test(res);
208
+ const failure = new RegExp(regexpFailure).test(res);
209
+ if (success === failure)
210
+ throw 'inconsistent: ' + service + ': ' + res; // this result cannot be trusted
211
+ console.debug(service, 'responded', success);
212
+ return { success, service, url };
213
+ }));
214
+ }
215
+ catch (e) {
216
+ console.debug(((_a = e === null || e === void 0 ? void 0 : e.errors) === null || _a === void 0 ? void 0 : _a.map(String)) || (e === null || e === void 0 ? void 0 : e.cause) || String(e));
217
+ }
218
+ }
219
+ function applySymbols(s) {
220
+ return s === null || s === void 0 ? void 0 : s.replace('$IP', parsed.hostname).replace('$PORT', parsed.port || (parsed.protocol === 'https:' ? '443' : '80')).replace('$URL', url.replace(/\/$/, '') + CHECK_URL);
191
221
  }
192
- catch (_a) { }
193
222
  }
194
- function applySymbols(s) {
195
- return s === null || s === void 0 ? void 0 : s.replace('$IP', ip).replace('$PORT', String(port));
223
+ finally {
224
+ selfCheckMiddlewareEnabled = false;
196
225
  }
197
226
  }
227
+ function selfCheck(url) {
228
+ return checkService(url, 'selfCheckServices');
229
+ }
198
230
  const apis = {
199
231
  get_nat: getNatInfo,
200
232
  check_domain({ domain }) {
@@ -218,15 +250,20 @@ const apis = {
218
250
  await upnpClient.createMapping({ private: internal || internalPort, public: { host: '', port: external }, description: 'hfs', ttl: 0 });
219
251
  return {};
220
252
  },
221
- async check_server({ port }) {
222
- const { publicIps, internalPort, externalPort } = await getNatInfo();
223
- if (!publicIps.length)
253
+ async self_check({ url }) {
254
+ if (url)
255
+ return await selfCheck(url)
256
+ || new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
257
+ const nat = await getNatInfo();
258
+ if (!nat.publicIps.length)
224
259
  return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'cannot detect public ip');
225
- if (!internalPort)
260
+ if (!nat.internalPort)
226
261
  return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, 'no internal port');
227
- port || (port = externalPort || internalPort);
228
- const res = await (0, misc_1.promiseBestEffort)(publicIps.map(ip => checkPort(ip, port)));
229
- return res.length ? res : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
262
+ const finalPort = nat.externalPort || nat.internalPort;
263
+ const proto = nat.proto || ((0, listen_1.getCertObject)() ? 'https' : 'http');
264
+ const defPort = proto === 'https' ? 443 : 80;
265
+ const results = (0, misc_1.onlyTruthy)(await (0, misc_1.promiseBestEffort)(nat.publicIps.map(ip => selfCheck(`${proto}://${ip}${finalPort === defPort ? '' : ':' + finalPort}`))));
266
+ return results.length ? results : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
230
267
  },
231
268
  async make_cert({ domain, email }) {
232
269
  await (0, exports.makeCert)(domain, email);
package/src/commands.js CHANGED
@@ -57,14 +57,7 @@ const commands = {
57
57
  },
58
58
  'create-admin': {
59
59
  params: '<password> [<username>=admin]',
60
- async cb(password, username = 'admin') {
61
- if ((0, perm_1.getAccount)(username))
62
- throw `user ${username} already exists`;
63
- const acc = (0, perm_1.addAccount)(username, { admin: true });
64
- await (0, perm_1.updateAccount)(acc, acc => {
65
- acc.password = password;
66
- });
67
- }
60
+ cb: perm_1.createAdmin
68
61
  },
69
62
  'change-password': {
70
63
  params: '<user> <password>',
@@ -105,8 +98,8 @@ const commands = {
105
98
  }
106
99
  },
107
100
  update: {
108
- params: '',
109
- cb: () => (0, update_1.update)()
101
+ params: '[<version>=latest]',
102
+ cb: update_1.update
110
103
  },
111
104
  'check-update': {
112
105
  params: '',
package/src/config.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.saveConfigAsap = exports.setConfig = exports.getWholeConfig = exports.getConfig = exports.configKeyExists = exports.defineConfig = exports.currentVersion = exports.versionToScalar = void 0;
7
+ exports.configFile = exports.saveConfigAsap = exports.setConfig = exports.getWholeConfig = exports.getConfig = exports.configKeyExists = exports.defineConfig = exports.currentVersion = exports.versionToScalar = void 0;
8
8
  const events_1 = __importDefault(require("events"));
9
9
  const const_1 = require("./const");
10
10
  const watchLoad_1 = require("./watchLoad");
@@ -70,12 +70,12 @@ function defineConfig(k, defaultValue, compiler) {
70
70
  cb(getConfig(k), { k, was: defaultValue, defaultValue, version: configVersion.compiled() });
71
71
  const eventName = CONFIG_CHANGE_EVENT_PREFIX + k;
72
72
  return (0, misc_1.onOff)(cfgEvents, {
73
- [eventName]() {
73
+ [eventName](v, was, version) {
74
74
  if (stack.includes(cb))
75
75
  return; // avoid infinite loop in case a subscriber changes the value
76
- stack.push(cb); // @ts-ignore arguments
76
+ stack.push(cb);
77
77
  try {
78
- return cb.apply(this, arguments);
78
+ return cb(v, { k, was, version, defaultValue });
79
79
  }
80
80
  finally {
81
81
  stack.pop();
@@ -177,13 +177,16 @@ const saveDebounced = (0, misc_1.debounceAsync)(async () => {
177
177
  const aWeekAgo = Date.now() - misc_1.DAY * 7;
178
178
  if (await (0, promises_1.stat)(bak).then(x => aWeekAgo > Number(x.mtime || x.ctime), () => true))
179
179
  await (0, promises_1.copyFile)(filePath, bak).catch(() => { }); // ignore errors
180
- await save(yaml_1.default.stringify({ ...state, version: const_1.VERSION }, { lineWidth: 1000 }))
180
+ await exports.configFile.save(stringify({ ...state, version: const_1.VERSION }))
181
181
  .catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
182
182
  });
183
183
  const saveConfigAsap = () => void (saveDebounced());
184
184
  exports.saveConfigAsap = saveConfigAsap;
185
+ function stringify(obj) {
186
+ return yaml_1.default.stringify(obj, { lineWidth: 1000 });
187
+ }
185
188
  console.log("config", filePath);
186
- const { save } = (0, watchLoad_1.watchLoad)(filePath, text => setConfig(yaml_1.default.parse(text) || {}, false), {
189
+ exports.configFile = (0, watchLoad_1.watchLoad)(filePath, text => setConfig(yaml_1.default.parse(text, { uniqueKeys: false }) || {}, false), {
187
190
  failedOnFirstAttempt() {
188
191
  console.log("No config file, using defaults");
189
192
  setConfig({}, false);
package/src/const.js CHANGED
@@ -23,53 +23,30 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  __setModuleDefault(result, mod);
24
24
  return result;
25
25
  };
26
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
28
+ };
26
29
  var __importDefault = (this && this.__importDefault) || function (mod) {
27
30
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
31
  };
29
32
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.HFS_REPO_BRANCH = exports.HFS_REPO = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
33
+ exports.APP_PATH = exports.IS_BINARY = exports.IS_MAC = exports.IS_WINDOWS = exports.HFS_REPO_BRANCH = exports.RUNNING_BETA = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
31
34
  const minimist_1 = __importDefault(require("minimist"));
32
35
  const fs = __importStar(require("fs"));
33
36
  const os_1 = require("os");
34
37
  const fs_1 = require("fs");
35
38
  const path_1 = require("path");
39
+ __exportStar(require("./cross-const"), exports);
36
40
  exports.argv = (0, minimist_1.default)(process.argv.slice(2));
37
41
  exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
38
42
  exports.ORIGINAL_CWD = process.cwd();
39
43
  exports.HFS_STARTED = new Date();
40
44
  const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
41
- exports.BUILD_TIMESTAMP = "2023-10-04T19:41:32.601Z";
45
+ exports.BUILD_TIMESTAMP = "2023-10-12T14:44:47.246Z";
42
46
  const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
43
47
  exports.VERSION = pkg.version;
44
48
  exports.RUNNING_BETA = exports.VERSION.includes('-');
45
- exports.API_VERSION = 8.4;
46
- exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
47
- exports.HFS_REPO = 'rejetto/hfs';
48
49
  exports.HFS_REPO_BRANCH = exports.RUNNING_BETA ? 'next' : 'main';
49
- exports.SPECIAL_URI = '/~/';
50
- exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
51
- exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
52
- exports.API_URI = exports.SPECIAL_URI + 'api/';
53
- exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
54
- exports.HTTP_OK = 200;
55
- exports.HTTP_NO_CONTENT = 204;
56
- exports.HTTP_PARTIAL_CONTENT = 206;
57
- exports.HTTP_MOVED_PERMANENTLY = 301;
58
- exports.HTTP_TEMPORARY_REDIRECT = 302;
59
- exports.HTTP_NOT_MODIFIED = 304;
60
- exports.HTTP_BAD_REQUEST = 400;
61
- exports.HTTP_UNAUTHORIZED = 401;
62
- exports.HTTP_FORBIDDEN = 403;
63
- exports.HTTP_NOT_FOUND = 404;
64
- exports.HTTP_METHOD_NOT_ALLOWED = 405;
65
- exports.HTTP_NOT_ACCEPTABLE = 406;
66
- exports.HTTP_CONFLICT = 409;
67
- exports.HTTP_PAYLOAD_TOO_LARGE = 413;
68
- exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
69
- exports.HTTP_FOOL = 418;
70
- exports.HTTP_FAILED_DEPENDENCY = 424;
71
- exports.HTTP_SERVER_ERROR = 500;
72
- exports.HTTP_SERVICE_UNAVAILABLE = 503;
73
50
  exports.IS_WINDOWS = process.platform === 'win32';
74
51
  exports.IS_MAC = process.platform === 'darwin';
75
52
  exports.IS_BINARY = !(0, path_1.basename)(process.execPath).includes('node'); // this won't be node if pkg was used
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.HFS_REPO = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = void 0;
4
+ exports.API_VERSION = 8.4;
5
+ exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
6
+ exports.HFS_REPO = 'rejetto/hfs';
7
+ exports.SPECIAL_URI = '/~/';
8
+ exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
9
+ exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
10
+ exports.API_URI = exports.SPECIAL_URI + 'api/';
11
+ exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
12
+ exports.HTTP_OK = 200;
13
+ exports.HTTP_NO_CONTENT = 204;
14
+ exports.HTTP_PARTIAL_CONTENT = 206;
15
+ exports.HTTP_MOVED_PERMANENTLY = 301;
16
+ exports.HTTP_TEMPORARY_REDIRECT = 302;
17
+ exports.HTTP_NOT_MODIFIED = 304;
18
+ exports.HTTP_BAD_REQUEST = 400;
19
+ exports.HTTP_UNAUTHORIZED = 401;
20
+ exports.HTTP_FORBIDDEN = 403;
21
+ exports.HTTP_NOT_FOUND = 404;
22
+ exports.HTTP_METHOD_NOT_ALLOWED = 405;
23
+ exports.HTTP_NOT_ACCEPTABLE = 406;
24
+ exports.HTTP_CONFLICT = 409;
25
+ exports.HTTP_PRECONDITION_FAILED = 412;
26
+ exports.HTTP_PAYLOAD_TOO_LARGE = 413;
27
+ exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
28
+ exports.HTTP_FOOL = 418;
29
+ exports.HTTP_FAILED_DEPENDENCY = 424;
30
+ exports.HTTP_SERVER_ERROR = 500;
31
+ exports.HTTP_SERVICE_UNAVAILABLE = 503;
package/src/cross.js CHANGED
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
18
  };
@@ -8,6 +22,7 @@ exports.promiseBestEffort = exports.escapeHTML = exports.ipForUrl = exports.ipLo
8
22
  // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
9
23
  // all content here is shared between client and server
10
24
  const lodash_1 = __importDefault(require("lodash"));
25
+ __exportStar(require("./cross-const"), exports);
11
26
  exports.REPO_URL = 'https://github.com/rejetto/hfs/';
12
27
  exports.WIKI_URL = exports.REPO_URL + 'wiki/';
13
28
  exports.MINUTE = 60000;
@@ -50,7 +65,7 @@ function wait(ms, val) {
50
65
  }
51
66
  exports.wait = wait;
52
67
  function haveTimeout(ms, job, error) {
53
- return Promise.race([job, wait(ms).then(() => { throw error; })]);
68
+ return Promise.race([job, wait(ms).then(() => { throw error || Error('timeout'); })]);
54
69
  }
55
70
  exports.haveTimeout = haveTimeout;
56
71
  function objSameKeys(src, newValue) {
package/src/index.js CHANGED
@@ -31,6 +31,7 @@ process.title = 'HFS ' + const_1.VERSION;
31
31
  const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(',')) || [(0, misc_1.randomId)(30)];
32
32
  exports.app = new koa_1.default({ keys });
33
33
  exports.app.use(middlewares_1.someSecurity)
34
+ .use(api_net_1.selfCheckMiddleware)
34
35
  .use(api_net_1.acmeMiddleware)
35
36
  .use((0, koa_session_1.default)({ key: 'hfs_$id', signed: true, rolling: true, sameSite: 'lax' }, exports.app))
36
37
  .use(middlewares_1.prepareState)
@@ -56,7 +57,7 @@ function errorHandler(err) {
56
57
  }
57
58
  process.on('uncaughtException', err => {
58
59
  if (err.syscall !== 'watch')
59
- console.error(err);
60
+ console.error("uncaught:", err);
60
61
  });
61
62
  (0, config_1.defineConfig)('proxies', 0).sub(n => {
62
63
  exports.app.proxy = n > 0;
package/src/listen.js CHANGED
@@ -212,7 +212,7 @@ function startServer(srv, { port, host }) {
212
212
  }
213
213
  console.error(srv.name, srv.error);
214
214
  const k = (srv === httpSrv ? exports.portCfg : exports.httpsPortCfg).key();
215
- console.log(` >> try specifying a different port, enter this command: config ${k} 8011`);
215
+ console.log(` >> try specifying a different port, enter this command: config ${k} 1080`);
216
216
  resolve(0);
217
217
  }
218
218
  });
package/src/misc.js CHANGED
@@ -106,7 +106,7 @@ function parseAddress(s) {
106
106
  return new node_net_1.SocketAddress({ address: s, family: (0, net_1.isIPv6)(s) ? 'ipv6' : 'ipv4' });
107
107
  }
108
108
  function makeMatcher(mask, emptyMaskReturns = false) {
109
- return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')') // adding () will allow us to use the pipe at root level
109
+ return mask ? (0, micromatch_1.matcher)(mask.replace(/^(!)?/, '$1(') + ')', { nocase: true }) // adding () will allow us to use the pipe at root level
110
110
  : () => emptyMaskReturns;
111
111
  }
112
112
  exports.makeMatcher = makeMatcher;
package/src/perm.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = exports.getCurrentUsername = void 0;
7
+ exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.createAdmin = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.expandUsername = exports.getCurrentUsername = void 0;
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
9
  const crypt_1 = require("./crypt");
10
10
  const misc_1 = require("./misc");
@@ -43,6 +43,21 @@ function saveSrpInfo(account, salt, verifier) {
43
43
  }
44
44
  exports.saveSrpInfo = saveSrpInfo;
45
45
  exports.allowClearTextLogin = (0, config_1.defineConfig)('allow_clear_text_login', false);
46
+ const createAdminConfig = (0, config_1.defineConfig)('create-admin', '');
47
+ createAdminConfig.sub(v => {
48
+ if (!v)
49
+ return;
50
+ createAdmin(v);
51
+ createAdminConfig.set('');
52
+ });
53
+ function createAdmin(pass, username = 'admin') {
54
+ const acc = addAccount(username, { admin: true });
55
+ if (!acc)
56
+ return console.log("cannot create, already exists");
57
+ updateAccount(acc, acc => { acc.password = pass; });
58
+ console.log("account admin created");
59
+ }
60
+ exports.createAdmin = createAdmin;
46
61
  const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
47
62
  async function updateAccount(account, changer) {
48
63
  const was = JSON.stringify(account);
package/src/plugins.js CHANGED
@@ -35,6 +35,7 @@ const const_1 = require("./const");
35
35
  const Const = __importStar(require("./const"));
36
36
  const misc_1 = require("./misc");
37
37
  const config_1 = require("./config");
38
+ const vfs_1 = require("./vfs");
38
39
  const serveFile_1 = require("./serveFile");
39
40
  const events_1 = __importDefault(require("./events"));
40
41
  const promises_1 = require("fs/promises");
@@ -153,7 +154,7 @@ const pluginsMiddleware = async (ctx, next) => {
153
154
  const a = path.substring(const_1.PLUGINS_PUB_URI.length).split('/');
154
155
  const name = a.shift();
155
156
  if (plugins.hasOwnProperty(name)) // do it only if the plugin is loaded
156
- await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'), 'auto');
157
+ await (0, serveFile_1.serveFile)(ctx, plugins[name].folder + '/public/' + a.join('/'), vfs_1.MIME_AUTO);
157
158
  return;
158
159
  }
159
160
  await next();
package/src/serveFile.js CHANGED
@@ -38,7 +38,7 @@ function serveFileNode(ctx, node) {
38
38
  }
39
39
  }
40
40
  exports.serveFileNode = serveFileNode;
41
- const mimeCfg = (0, config_1.defineConfig)('mime', { '*': 'auto' });
41
+ const mimeCfg = (0, config_1.defineConfig)('mime', { '*': vfs_1.MIME_AUTO });
42
42
  async function serveFile(ctx, source, mime, content) {
43
43
  if (!source)
44
44
  return;
@@ -42,6 +42,7 @@ const customHtml_1 = require("./customHtml");
42
42
  const lodash_1 = __importDefault(require("lodash"));
43
43
  const config_1 = require("./config");
44
44
  const lang_1 = require("./lang");
45
+ const vfs_1 = require("./vfs");
45
46
  const logGui = (0, config_1.defineConfig)('log_gui', false);
46
47
  // in case of dev env we have our static files within the 'dist' folder'
47
48
  const DEV_STATIC = process.env.DEV ? 'dist/' : '';
@@ -69,7 +70,7 @@ function serveStatic(uri) {
69
70
  if (content === null)
70
71
  return ctx.status = const_1.HTTP_NOT_FOUND;
71
72
  if (!serveApp)
72
- return (0, serveFile_1.serveFile)(ctx, fullPath, 'auto', content);
73
+ return (0, serveFile_1.serveFile)(ctx, fullPath, vfs_1.MIME_AUTO, content);
73
74
  // we don't cache the index as it's small and may prevent plugins change to apply
74
75
  ctx.body = await treatIndex(ctx, uri, String(content));
75
76
  };
package/src/update.js CHANGED
@@ -63,15 +63,22 @@ function updateSupported() {
63
63
  return const_1.IS_BINARY;
64
64
  }
65
65
  exports.updateSupported = updateSupported;
66
- async function update(tag) {
66
+ async function update(tag = '') {
67
67
  if (!updateSupported())
68
- throw "only binary versions are supported for now";
68
+ throw "only binary versions supports automatic update for now";
69
69
  let updateSource = await localUpdateAvailable() && (0, fs_1.createReadStream)(LOCAL_UPDATE);
70
70
  if (!updateSource) {
71
+ if (/^\d/.test(tag)) // work even if the tag is passed without the initial 'v' (useful for console commands)
72
+ tag = 'v' + tag;
71
73
  const update = !tag ? (await getUpdates())[0]
72
- : await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/tags/' + tag);
74
+ : await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/tags/' + tag).catch(e => {
75
+ if (e.message === '404')
76
+ console.error("version not found");
77
+ else
78
+ throw e;
79
+ });
73
80
  if (!update)
74
- throw "no update found";
81
+ throw "update not found";
75
82
  const assetSearch = { win32: 'windows', darwin: 'mac', linux: 'linux' }[process.platform];
76
83
  if (!assetSearch)
77
84
  throw "this feature doesn't support your platform: " + process.platform;
@@ -109,7 +116,8 @@ async function update(tag) {
109
116
  console.log('quitting');
110
117
  setTimeout(() => process.exit()); // give time to return (and caller to complete, eg: rest api to reply)
111
118
  }
112
- catch (_a) {
119
+ catch (e) {
120
+ console.error((e === null || e === void 0 ? void 0 : e.message) || String(e));
113
121
  plugins_1.pluginsWatcher.unpause();
114
122
  }
115
123
  }
package/src/util-files.js CHANGED
@@ -138,8 +138,9 @@ async function* dirStream(path, deep = 0) {
138
138
  exports.dirStream = dirStream;
139
139
  async function unzip(stream, cb) {
140
140
  let pending = Promise.resolve();
141
- return new Promise(resolve => stream.pipe(unzip_stream_1.default.Parse())
141
+ return new Promise((resolve, reject) => stream.pipe(unzip_stream_1.default.Parse())
142
142
  .on('end', () => pending.then(resolve))
143
+ .on('error', reject)
143
144
  .on('entry', (entry) => pending = pending.then(async () => {
144
145
  const { path, type } = entry;
145
146
  const dest = await (0, misc_1.try_)(() => cb(path), e => {
@@ -150,7 +151,7 @@ async function unzip(stream, cb) {
150
151
  return entry.autodrain();
151
152
  console.debug('unzip', dest);
152
153
  await prepareFolder(dest);
153
- const thisFile = entry.pipe((0, fs_1.createWriteStream)(dest));
154
+ const thisFile = entry.pipe((0, fs_1.createWriteStream)(dest).on('error', reject));
154
155
  await (0, stream_1.once)(thisFile, 'finish');
155
156
  })));
156
157
  }
package/src/util-http.js CHANGED
@@ -36,9 +36,7 @@ function httpStream(url, { body, ...options } = {}) {
36
36
  }).on('error', e => {
37
37
  reject(req.res || e);
38
38
  });
39
- if (body)
40
- req.write(body);
41
- req.end();
39
+ req.end(body);
42
40
  });
43
41
  }
44
42
  exports.httpStream = httpStream;