spec-3nweb-server 1.8.11 → 1.9.0
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/.gitlab-ci-linux.yml +13 -0
- package/.gitlab-ci.yml +0 -10
- package/build/3nstorage/routes/owner/param-getter.js +1 -4
- package/build/3nstorage/routes/owner/session-params.js +1 -1
- package/build/asmail/resources/utils.d.ts +4 -0
- package/build/asmail/resources/utils.js +8 -1
- package/build/asmail/routes/delivery/restart-session.js +3 -1
- package/build/asmail/routes/retrieval/get-message-meta.js +4 -1
- package/build/asmail/routes/retrieval/get-message-obj.js +4 -1
- package/build/asmail/routes/retrieval/remove-message.js +4 -1
- package/build/lib.d.ts +1 -1
- package/build/lib.js +2 -1
- package/build/main.js +2 -1
- package/build/services.d.ts +6 -0
- package/build/services.js +11 -2
- package/build/tests/libs-for-tests/mailerid.js +1 -2
- package/build/tests/protocols/asmail/delivery/specs/restart-session.js +1 -1
- package/build/tests/protocols/asmail/delivery/test-utils.js +1 -2
- package/build/tests/shared-checks/check-mid-login.js +1 -1
- package/build/well-known/well-known-route-app.d.ts +3 -0
- package/build/well-known/well-known-route-app.js +53 -0
- package/package.json +1 -1
- package/ts-code/3nstorage/routes/owner/archive-obj-version.ts +1 -0
- package/ts-code/3nstorage/routes/owner/cancel-trans.ts +1 -0
- package/ts-code/3nstorage/routes/owner/delete-obj.ts +1 -0
- package/ts-code/3nstorage/routes/owner/get-archived-obj-ver.ts +1 -0
- package/ts-code/3nstorage/routes/owner/get-current-obj.ts +1 -0
- package/ts-code/3nstorage/routes/owner/get-obj-status.ts +1 -0
- package/ts-code/3nstorage/routes/owner/param-getter.ts +1 -5
- package/ts-code/3nstorage/routes/owner/put-current-obj.ts +1 -0
- package/ts-code/3nstorage/routes/owner/session-params.ts +1 -1
- package/ts-code/asmail/resources/utils.ts +10 -0
- package/ts-code/asmail/routes/delivery/restart-session.ts +6 -1
- package/ts-code/asmail/routes/retrieval/get-message-meta.ts +6 -2
- package/ts-code/asmail/routes/retrieval/get-message-obj.ts +6 -2
- package/ts-code/asmail/routes/retrieval/remove-message.ts +6 -2
- package/ts-code/lib.ts +1 -1
- package/ts-code/main.ts +3 -2
- package/ts-code/services.ts +18 -1
- package/ts-code/tests/libs-for-tests/mailerid.ts +1 -2
- package/ts-code/tests/libs-for-tests/pkl.ts +1 -1
- package/ts-code/tests/protocols/asmail/delivery/specs/restart-session.ts +2 -3
- package/ts-code/tests/protocols/asmail/delivery/test-utils.ts +6 -7
- package/ts-code/tests/shared-checks/check-mid-login.ts +2 -2
- package/ts-code/well-known/well-known-route-app.ts +66 -0
package/.gitlab-ci-linux.yml
CHANGED
|
@@ -48,3 +48,16 @@ pack mock for linux:
|
|
|
48
48
|
# paths:
|
|
49
49
|
# - 3nweb-*.deb
|
|
50
50
|
# expire_in: 3d
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
publish to npm:
|
|
54
|
+
stage: publish
|
|
55
|
+
rules:
|
|
56
|
+
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
57
|
+
when: manual
|
|
58
|
+
needs:
|
|
59
|
+
- build for linux
|
|
60
|
+
- test on linux
|
|
61
|
+
image: node-24
|
|
62
|
+
script:
|
|
63
|
+
- bash publish-task.sh $NPM_PUBLISH_TOKEN
|
package/.gitlab-ci.yml
CHANGED
|
@@ -47,16 +47,6 @@ pack mock:
|
|
|
47
47
|
- npm run clear-for-packing-mock
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
publish to npm:
|
|
51
|
-
stage: publish
|
|
52
|
-
rules:
|
|
53
|
-
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
54
|
-
when: manual
|
|
55
|
-
image: node-24
|
|
56
|
-
script:
|
|
57
|
-
- bash publish-task.sh $NPM_PUBLISH_TOKEN
|
|
58
|
-
|
|
59
|
-
|
|
60
50
|
include:
|
|
61
51
|
- .gitlab-ci-linux.yml
|
|
62
52
|
- .gitlab-ci-windows.yml
|
|
@@ -20,9 +20,6 @@ exports.getParam = void 0;
|
|
|
20
20
|
const users_1 = require("../../resources/users");
|
|
21
21
|
const owner_1 = require("../../../lib-common/service-api/3nstorage/owner");
|
|
22
22
|
function getParam(paramGetter) {
|
|
23
|
-
if ('function' !== typeof paramGetter) {
|
|
24
|
-
throw new TypeError("Given argument 'paramGetter' must be function, but is not.");
|
|
25
|
-
}
|
|
26
23
|
return async (req, res, next) => {
|
|
27
24
|
const session = req.session;
|
|
28
25
|
const userId = session.params.userId;
|
|
@@ -32,7 +29,7 @@ function getParam(paramGetter) {
|
|
|
32
29
|
}
|
|
33
30
|
catch (err) {
|
|
34
31
|
if (err === users_1.SC.USER_UNKNOWN) {
|
|
35
|
-
res.status(owner_1.ERR_SC.server).send("
|
|
32
|
+
res.status(owner_1.ERR_SC.server).send("User disappeared from the system.");
|
|
36
33
|
session.close();
|
|
37
34
|
}
|
|
38
35
|
else {
|
|
@@ -35,7 +35,7 @@ function sessionParams(maxChunk) {
|
|
|
35
35
|
next(err);
|
|
36
36
|
}
|
|
37
37
|
else if (err === users_1.SC.USER_UNKNOWN) {
|
|
38
|
-
res.status(owner_1.ERR_SC.server).send("
|
|
38
|
+
res.status(owner_1.ERR_SC.server).send("User disappeared from the system.");
|
|
39
39
|
req.session.close();
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
@@ -3,4 +3,8 @@ export declare function getObjIdFromParams(req: Request): {
|
|
|
3
3
|
objId: string;
|
|
4
4
|
objIdParseErr?: string;
|
|
5
5
|
};
|
|
6
|
+
export declare function getMsgIdFromParams(req: Request): {
|
|
7
|
+
msgId: string;
|
|
8
|
+
msgIdParseErr?: string;
|
|
9
|
+
};
|
|
6
10
|
export declare function replyWithErr(status: number, msg: string, res: Response, reqToDrainBytesFrom?: Request): void;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.replyWithErr = exports.getObjIdFromParams = void 0;
|
|
19
|
+
exports.replyWithErr = exports.getMsgIdFromParams = exports.getObjIdFromParams = void 0;
|
|
20
20
|
const buffer_utils_1 = require("../../lib-common/buffer-utils");
|
|
21
21
|
const body_parsers_1 = require("../../lib-server/middleware/body-parsers");
|
|
22
22
|
function getObjIdFromParams(req) {
|
|
@@ -26,6 +26,13 @@ function getObjIdFromParams(req) {
|
|
|
26
26
|
{ objId: undefined, objIdParseErr: "Bad object id" });
|
|
27
27
|
}
|
|
28
28
|
exports.getObjIdFromParams = getObjIdFromParams;
|
|
29
|
+
function getMsgIdFromParams(req) {
|
|
30
|
+
const msgId = req.params.msgId;
|
|
31
|
+
return ((!!msgId && buffer_utils_1.base64urlSafe.allCharsFromAlphabet(msgId)) ?
|
|
32
|
+
{ msgId } :
|
|
33
|
+
{ msgId: undefined, msgIdParseErr: "Bad message id" });
|
|
34
|
+
}
|
|
35
|
+
exports.getMsgIdFromParams = getMsgIdFromParams;
|
|
29
36
|
function replyWithErr(status, msg, res, reqToDrainBytesFrom) {
|
|
30
37
|
if (reqToDrainBytesFrom) {
|
|
31
38
|
(0, body_parsers_1.attachByteDrainToRequest)(reqToDrainBytesFrom);
|
|
@@ -22,6 +22,7 @@ const delivery_1 = require("../../../lib-common/service-api/asmail/delivery");
|
|
|
22
22
|
const canonical_address_1 = require("../../../lib-common/canonical-address");
|
|
23
23
|
const confUtil = require("../../../lib-server/conf-util");
|
|
24
24
|
const utils_1 = require("../../resources/utils");
|
|
25
|
+
const buffer_utils_1 = require("../../../lib-common/buffer-utils");
|
|
25
26
|
/**
|
|
26
27
|
* This creates a session restart route handler.
|
|
27
28
|
* @param sessionGenFunc is a promise returning function that generates new session objects.
|
|
@@ -69,7 +70,8 @@ function restartSession(sessionGenFunc, sessionForMsgFunc, incompleteMsgFunc, ma
|
|
|
69
70
|
return (0, utils_1.replyWithErr)(delivery_1.ERR_SC.malformed, "Recipient is either missing in the request, or is malformed", res);
|
|
70
71
|
}
|
|
71
72
|
// check message id
|
|
72
|
-
if (typeof msgId !== 'string') {
|
|
73
|
+
if ((typeof msgId !== 'string') || !msgId || !buffer_utils_1.base64urlSafe.allCharsFromAlphabet(msgId)) {
|
|
74
|
+
console.log(`msgId ->`, msgId, '\n', `!base64urlSafe.allCharsFromAlphabet(msgId) ->`, !buffer_utils_1.base64urlSafe.allCharsFromAlphabet(msgId));
|
|
73
75
|
return (0, utils_1.replyWithErr)(delivery_1.ERR_SC.malformed, "Message id is either missing, or is malformed", res);
|
|
74
76
|
}
|
|
75
77
|
try {
|
|
@@ -23,7 +23,10 @@ const utils_1 = require("../../resources/utils");
|
|
|
23
23
|
function getMsgMeta(getMsgMetaFunc) {
|
|
24
24
|
return async (req, res, next) => {
|
|
25
25
|
const userId = req.session.params.userId;
|
|
26
|
-
const msgId = req
|
|
26
|
+
const { msgId, msgIdParseErr } = (0, utils_1.getMsgIdFromParams)(req);
|
|
27
|
+
if (msgIdParseErr) {
|
|
28
|
+
return (0, utils_1.replyWithErr)(retrieval_1.ERR_SC.malformed, msgIdParseErr, res, req);
|
|
29
|
+
}
|
|
27
30
|
try {
|
|
28
31
|
const meta = await getMsgMetaFunc(userId, msgId);
|
|
29
32
|
res.status(retrieval_1.msgMetadata.SC.ok).json(meta);
|
|
@@ -41,7 +41,10 @@ function extractQueryOptions(req) {
|
|
|
41
41
|
function getMsgObj(getMsgObjFunc) {
|
|
42
42
|
return async (req, res, next) => {
|
|
43
43
|
const userId = req.session.params.userId;
|
|
44
|
-
const msgId = req
|
|
44
|
+
const { msgId, msgIdParseErr } = (0, utils_1.getMsgIdFromParams)(req);
|
|
45
|
+
if (msgIdParseErr) {
|
|
46
|
+
return (0, utils_1.replyWithErr)(retrieval_1.ERR_SC.malformed, msgIdParseErr, res, req);
|
|
47
|
+
}
|
|
45
48
|
const { objId, objIdParseErr } = (0, utils_1.getObjIdFromParams)(req);
|
|
46
49
|
if (objIdParseErr) {
|
|
47
50
|
return (0, utils_1.replyWithErr)(retrieval_1.ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -23,7 +23,10 @@ const utils_1 = require("../../resources/utils");
|
|
|
23
23
|
function deleteMsg(delMsgFunc) {
|
|
24
24
|
return async (req, res, next) => {
|
|
25
25
|
const userId = req.session.params.userId;
|
|
26
|
-
const msgId = req
|
|
26
|
+
const { msgId, msgIdParseErr } = (0, utils_1.getMsgIdFromParams)(req);
|
|
27
|
+
if (msgIdParseErr) {
|
|
28
|
+
return (0, utils_1.replyWithErr)(retrieval_1.ERR_SC.malformed, msgIdParseErr, res, req);
|
|
29
|
+
}
|
|
27
30
|
try {
|
|
28
31
|
await delMsgFunc(userId, msgId);
|
|
29
32
|
res.status(retrieval_1.rmMsg.SC.ok).send();
|
package/build/lib.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AppWithWSs } from './lib-server/web-sockets/app';
|
|
2
|
-
export { Configurations, servicesApp, accountsApp } from './services';
|
|
2
|
+
export { Configurations, servicesApp, accountsApp, wellKnownApp } from './services';
|
|
3
3
|
export { addSingleUserSignup } from './config/signup';
|
|
4
4
|
export import errors = require('./lib-server/middleware/error-handler');
|
|
5
5
|
export * as mock from './mock/run-in-proc';
|
package/build/lib.js
CHANGED
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.mock = exports.addSingleUserSignup = exports.accountsApp = exports.servicesApp = exports.AppWithWSs = void 0;
|
|
19
|
+
exports.mock = exports.addSingleUserSignup = exports.wellKnownApp = exports.accountsApp = exports.servicesApp = exports.AppWithWSs = void 0;
|
|
20
20
|
var app_1 = require("./lib-server/web-sockets/app");
|
|
21
21
|
Object.defineProperty(exports, "AppWithWSs", { enumerable: true, get: function () { return app_1.AppWithWSs; } });
|
|
22
22
|
var services_1 = require("./services");
|
|
23
23
|
Object.defineProperty(exports, "servicesApp", { enumerable: true, get: function () { return services_1.servicesApp; } });
|
|
24
24
|
Object.defineProperty(exports, "accountsApp", { enumerable: true, get: function () { return services_1.accountsApp; } });
|
|
25
|
+
Object.defineProperty(exports, "wellKnownApp", { enumerable: true, get: function () { return services_1.wellKnownApp; } });
|
|
25
26
|
var signup_1 = require("./config/signup");
|
|
26
27
|
Object.defineProperty(exports, "addSingleUserSignup", { enumerable: true, get: function () { return signup_1.addSingleUserSignup; } });
|
|
27
28
|
exports.errors = require("./lib-server/middleware/error-handler");
|
package/build/main.js
CHANGED
|
@@ -36,6 +36,7 @@ async function run(conf) {
|
|
|
36
36
|
const app = new lib_1.AppWithWSs();
|
|
37
37
|
app.use((0, lib_1.servicesApp)(conf, 'console', 'console'));
|
|
38
38
|
app.use((0, lib_1.accountsApp)(conf, 'console', 'console'));
|
|
39
|
+
app.use((0, lib_1.wellKnownApp)(conf));
|
|
39
40
|
return app;
|
|
40
41
|
}
|
|
41
42
|
async function stopProcess() {
|
|
@@ -61,7 +62,7 @@ async function run(conf) {
|
|
|
61
62
|
}
|
|
62
63
|
async function startAppWithoutTLS(port, hostname) {
|
|
63
64
|
await app.start(undefined, port, hostname);
|
|
64
|
-
console.log(`🚀 Started 3NWeb server on port ${port},${hostname ? ` hostname ${hostname},` : ''} without TLS, and requiring TLS reverse proxy infront.`);
|
|
65
|
+
console.log(`🚀 Started 3NWeb server on port ${port},${hostname ? ` hostname ${hostname},` : ''} without TLS, and requiring TLS or Tor-like reverse proxy infront.`);
|
|
65
66
|
}
|
|
66
67
|
try {
|
|
67
68
|
app = createApp(conf);
|
package/build/services.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface Configurations {
|
|
|
27
27
|
noTokenFile?: string;
|
|
28
28
|
serviceUrl?: string;
|
|
29
29
|
};
|
|
30
|
+
wellKnown3NWeb?: {
|
|
31
|
+
mailerid?: string;
|
|
32
|
+
asmail?: string;
|
|
33
|
+
"3nstorage"?: string;
|
|
34
|
+
};
|
|
30
35
|
}
|
|
31
36
|
export declare function servicesApp(conf: Configurations, errLogger?: ErrLogger | 'console', logSetup?: 'console'): AppWithWSs;
|
|
32
37
|
export declare function accountsApp(conf: Configurations, errLogger?: ErrLogger | 'console', logSetup?: 'console'): AppWithWSs;
|
|
38
|
+
export declare function wellKnownApp(conf: Configurations): AppWithWSs;
|
package/build/services.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2015 - 2017, 2020, 2024 -
|
|
3
|
+
Copyright (C) 2015 - 2017, 2020, 2024 - 2026 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.accountsApp = exports.servicesApp = void 0;
|
|
19
|
+
exports.wellKnownApp = exports.accountsApp = exports.servicesApp = void 0;
|
|
20
20
|
const mailerid_authorizer_1 = require("./lib-server/resources/mailerid-authorizer");
|
|
21
21
|
const app_1 = require("./lib-server/web-sockets/app");
|
|
22
22
|
const mailerId_app_1 = require("./mailerId/mailerId-app");
|
|
@@ -25,6 +25,7 @@ const _3nstorage_app_1 = require("./3nstorage/3nstorage-app");
|
|
|
25
25
|
const signup_app_1 = require("./signup/signup-app");
|
|
26
26
|
const error_handler_1 = require("./lib-server/middleware/error-handler");
|
|
27
27
|
const locker_app_1 = require("./locker/locker-app");
|
|
28
|
+
const well_known_route_app_1 = require("./well-known/well-known-route-app");
|
|
28
29
|
function servicesApp(conf, errLogger, logSetup) {
|
|
29
30
|
var _a;
|
|
30
31
|
if (!conf.enabledServices || (typeof conf.enabledServices !== 'object')) {
|
|
@@ -75,4 +76,12 @@ function accountsApp(conf, errLogger, logSetup) {
|
|
|
75
76
|
return app;
|
|
76
77
|
}
|
|
77
78
|
exports.accountsApp = accountsApp;
|
|
79
|
+
function wellKnownApp(conf) {
|
|
80
|
+
const app = new app_1.AppWithWSs();
|
|
81
|
+
if (conf.wellKnown3NWeb) {
|
|
82
|
+
app.http.use('/.well-known', (0, well_known_route_app_1.makeWellKnownApp)(conf.wellKnown3NWeb));
|
|
83
|
+
}
|
|
84
|
+
return app;
|
|
85
|
+
}
|
|
86
|
+
exports.wellKnownApp = wellKnownApp;
|
|
78
87
|
Object.freeze(exports);
|
|
@@ -24,7 +24,6 @@ const provisioning_1 = require("../../lib-common/service-api/mailer-id/provision
|
|
|
24
24
|
const mid_sigs_NaCl_Ed_1 = require("../../lib-common/mid-sigs-NaCl-Ed");
|
|
25
25
|
const pkl_1 = require("./pkl");
|
|
26
26
|
const random_node_1 = require("../../lib-common/random-node");
|
|
27
|
-
const url_1 = require("url");
|
|
28
27
|
async function getMidData(midUrl) {
|
|
29
28
|
let reqOpts = {
|
|
30
29
|
url: midUrl,
|
|
@@ -97,7 +96,7 @@ async function doMailerIdLogin(loginUrl, user) {
|
|
|
97
96
|
responseType: 'json',
|
|
98
97
|
sessionId
|
|
99
98
|
};
|
|
100
|
-
let serviceDomain = (
|
|
99
|
+
let serviceDomain = (new URL(loginUrl)).hostname;
|
|
101
100
|
if (!serviceDomain) {
|
|
102
101
|
throw new Error(`Cannot parse hostname from login url ${loginUrl}`);
|
|
103
102
|
}
|
|
@@ -63,7 +63,7 @@ exports.specs.definition = (setup) => (() => {
|
|
|
63
63
|
(0, async_jasmine_1.itAsync)(`returns error status when message is not known in delivery`, async () => {
|
|
64
64
|
const req = {
|
|
65
65
|
recipient: user2.id,
|
|
66
|
-
msgId: '
|
|
66
|
+
msgId: 'unknown_message'
|
|
67
67
|
};
|
|
68
68
|
const rep = await (0, xhr_utils_1.doJsonRequest)(reqOpts, req);
|
|
69
69
|
expect(rep.status).withContext('status for unknown message').toBe(delivery_1.sessionRestart.SC.unknownMsg);
|
|
@@ -36,8 +36,7 @@ exports.msg = {
|
|
|
36
36
|
msgObjs: [exports.obj1, exports.obj2]
|
|
37
37
|
};
|
|
38
38
|
async function startSessionAndSendMsgObjs(deliveryUrl, recipient, msg) {
|
|
39
|
-
const sessInfo = await (0, asmail_1.startMsgDelivery)(deliveryUrl, { recipient }, { pid: msg.cryptoMeta.pid,
|
|
40
|
-
objIds: [msg.msgObjs[0].objId, msg.msgObjs[1].objId] });
|
|
39
|
+
const sessInfo = await (0, asmail_1.startMsgDelivery)(deliveryUrl, { recipient }, { pid: msg.cryptoMeta.pid, objIds: [msg.msgObjs[0].objId, msg.msgObjs[1].objId] });
|
|
41
40
|
await (0, asmail_1.sendMsgObj)(deliveryUrl, sessInfo.sessionId, msg.msgObjs[0], sessInfo.maxChunkSize);
|
|
42
41
|
await (0, asmail_1.sendMsgObj)(deliveryUrl, sessInfo.sessionId, msg.msgObjs[1], sessInfo.maxChunkSize);
|
|
43
42
|
return sessInfo;
|
|
@@ -71,7 +71,7 @@ function midLoginSpecs(loginUrl, user) {
|
|
|
71
71
|
responseType: 'json',
|
|
72
72
|
sessionId
|
|
73
73
|
};
|
|
74
|
-
const serviceDomain = (
|
|
74
|
+
const serviceDomain = (new URL(loginUrl())).hostname;
|
|
75
75
|
if (!serviceDomain) {
|
|
76
76
|
throw new Error(`Cannot get hostname from ${loginUrl()}`);
|
|
77
77
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2026 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.makeWellKnownApp = void 0;
|
|
20
|
+
const express = require("express");
|
|
21
|
+
function makeWellKnownApp(w3n) {
|
|
22
|
+
const app = express();
|
|
23
|
+
const w3nJson = confTo3NWebJSON(w3n);
|
|
24
|
+
app.get('3nweb.json', (req, res) => {
|
|
25
|
+
// the following implicitly sets content type application/json
|
|
26
|
+
res.status(200).json(w3nJson);
|
|
27
|
+
});
|
|
28
|
+
return app;
|
|
29
|
+
}
|
|
30
|
+
exports.makeWellKnownApp = makeWellKnownApp;
|
|
31
|
+
function confTo3NWebJSON(conf) {
|
|
32
|
+
const json = {};
|
|
33
|
+
const fields = ['3nstorage', 'asmail', 'mailerid'];
|
|
34
|
+
for (const field in fields) {
|
|
35
|
+
const href = conf[field];
|
|
36
|
+
if (href) {
|
|
37
|
+
let u;
|
|
38
|
+
try {
|
|
39
|
+
u = new URL(href);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(` ❌ Configuration field ${field} for well-known needs potocol section`);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
if ((u.protocol !== 'https:') && (u.protocol !== 'http:')) {
|
|
46
|
+
throw new Error(` ❌ Configuration field ${field} for well-known needs potocol section`);
|
|
47
|
+
}
|
|
48
|
+
json[field] = href;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return json;
|
|
52
|
+
}
|
|
53
|
+
Object.freeze(exports);
|
package/package.json
CHANGED
|
@@ -30,6 +30,7 @@ export function archiveCurrentObjVersion(
|
|
|
30
30
|
return async (req: Request, res, next) => {
|
|
31
31
|
|
|
32
32
|
const userId = req.session.params.userId;
|
|
33
|
+
|
|
33
34
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
34
35
|
if (objIdParseErr) {
|
|
35
36
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -28,6 +28,7 @@ export function cancelTransaction(
|
|
|
28
28
|
return async (req: Request, res, next) => {
|
|
29
29
|
|
|
30
30
|
const userId = req.session.params.userId;
|
|
31
|
+
|
|
31
32
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
32
33
|
if (objIdParseErr) {
|
|
33
34
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -33,6 +33,7 @@ function deleteObj(
|
|
|
33
33
|
return async (req: Request, res, next) => {
|
|
34
34
|
|
|
35
35
|
const userId = req.session.params.userId;
|
|
36
|
+
|
|
36
37
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
37
38
|
if (objIdParseErr) {
|
|
38
39
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -56,6 +56,7 @@ export function getArchivedObjVersion(
|
|
|
56
56
|
return async (req: Request, res, next) => {
|
|
57
57
|
|
|
58
58
|
const userId = req.session.params.userId;
|
|
59
|
+
|
|
59
60
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
60
61
|
if (objIdParseErr) {
|
|
61
62
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -63,6 +63,7 @@ export function getCurrentObj(
|
|
|
63
63
|
return async (req: Request, res, next) => {
|
|
64
64
|
|
|
65
65
|
const userId = req.session.params.userId;
|
|
66
|
+
|
|
66
67
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
67
68
|
if (objIdParseErr) {
|
|
68
69
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -28,6 +28,7 @@ export function getObjStatus(
|
|
|
28
28
|
return async (req: Request, res, next) => {
|
|
29
29
|
|
|
30
30
|
const userId = req.session.params.userId;
|
|
31
|
+
|
|
31
32
|
const { objId, objIdParseErr } = getObjIdFromParams(root, req);
|
|
32
33
|
if (objIdParseErr) {
|
|
33
34
|
return replyWithErr(ERR_SC.malformed, objIdParseErr, res);
|
|
@@ -24,9 +24,6 @@ export function getParam<T>(
|
|
|
24
24
|
paramGetter: (userId: string) => Promise<T>
|
|
25
25
|
): RequestHandler {
|
|
26
26
|
|
|
27
|
-
if ('function' !== typeof paramGetter) { throw new TypeError(
|
|
28
|
-
"Given argument 'paramGetter' must be function, but is not."); }
|
|
29
|
-
|
|
30
27
|
return async (req: Request, res, next) => {
|
|
31
28
|
|
|
32
29
|
const session = req.session;
|
|
@@ -37,8 +34,7 @@ export function getParam<T>(
|
|
|
37
34
|
res.status(PARAM_SC.ok).json(value);
|
|
38
35
|
} catch (err) {
|
|
39
36
|
if (err === storeSC.USER_UNKNOWN) {
|
|
40
|
-
res.status(ERR_SC.server).send(
|
|
41
|
-
"Recipient disappeared from the system.");
|
|
37
|
+
res.status(ERR_SC.server).send("User disappeared from the system.");
|
|
42
38
|
session.close();
|
|
43
39
|
} else {
|
|
44
40
|
next(err);
|
|
@@ -37,7 +37,7 @@ export function sessionParams(maxChunk: number|string): RequestHandler {
|
|
|
37
37
|
if ("string" !== typeof err) {
|
|
38
38
|
next(err);
|
|
39
39
|
} else if (err === storeSC.USER_UNKNOWN) {
|
|
40
|
-
res.status(ERR_SC.server).send("
|
|
40
|
+
res.status(ERR_SC.server).send("User disappeared from the system.");
|
|
41
41
|
req.session.close();
|
|
42
42
|
} else {
|
|
43
43
|
next(new Error("Unhandled storage error code: "+err));
|
|
@@ -30,6 +30,16 @@ export function getObjIdFromParams(req: Request): {
|
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function getMsgIdFromParams(req: Request): {
|
|
34
|
+
msgId: string; msgIdParseErr?: string;
|
|
35
|
+
} {
|
|
36
|
+
const msgId = req.params.msgId;
|
|
37
|
+
return ((!!msgId && base64urlSafe.allCharsFromAlphabet(msgId)) ?
|
|
38
|
+
{ msgId } :
|
|
39
|
+
{ msgId: undefined as any, msgIdParseErr: "Bad message id" }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
export function replyWithErr(status: number, msg: string, res: Response, reqToDrainBytesFrom?: Request): void {
|
|
34
44
|
if (reqToDrainBytesFrom) {
|
|
35
45
|
attachByteDrainToRequest(reqToDrainBytesFrom);
|
|
@@ -23,6 +23,7 @@ import { Redirect } from './start-session';
|
|
|
23
23
|
import { checkAndTransformAddress } from '../../../lib-common/canonical-address';
|
|
24
24
|
import * as confUtil from '../../../lib-server/conf-util';
|
|
25
25
|
import { replyWithErr } from '../../resources/utils';
|
|
26
|
+
import { base64urlSafe } from '../../../lib-common/buffer-utils';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* This creates a session restart route handler.
|
|
@@ -84,7 +85,11 @@ export function restartSession(
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// check message id
|
|
87
|
-
if (typeof msgId !== 'string') {
|
|
88
|
+
if ((typeof msgId !== 'string') || !msgId || !base64urlSafe.allCharsFromAlphabet(msgId)) {
|
|
89
|
+
console.log(
|
|
90
|
+
`msgId ->`, msgId, '\n',
|
|
91
|
+
`!base64urlSafe.allCharsFromAlphabet(msgId) ->`, !base64urlSafe.allCharsFromAlphabet(msgId)
|
|
92
|
+
);
|
|
88
93
|
return replyWithErr(ERR_SC.malformed, "Message id is either missing, or is malformed", res);
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -19,14 +19,18 @@ import { RequestHandler } from 'express';
|
|
|
19
19
|
import { SC as recipSC, MsgRetrieval } from '../../resources/recipients';
|
|
20
20
|
import { msgMetadata as api, ERR_SC } from '../../../lib-common/service-api/asmail/retrieval';
|
|
21
21
|
import { Request } from '../../resources/sessions';
|
|
22
|
-
import { replyWithErr } from '../../resources/utils';
|
|
22
|
+
import { getMsgIdFromParams, replyWithErr } from '../../resources/utils';
|
|
23
23
|
|
|
24
24
|
export function getMsgMeta(
|
|
25
25
|
getMsgMetaFunc: MsgRetrieval['getMsgMeta']
|
|
26
26
|
): RequestHandler {
|
|
27
27
|
return async (req: Request, res, next) => {
|
|
28
28
|
const userId = req.session.params.userId;
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
const { msgId, msgIdParseErr } = getMsgIdFromParams(req);
|
|
31
|
+
if (msgIdParseErr) {
|
|
32
|
+
return replyWithErr(ERR_SC.malformed, msgIdParseErr, res, req);
|
|
33
|
+
}
|
|
30
34
|
|
|
31
35
|
try {
|
|
32
36
|
const meta = await getMsgMetaFunc(userId, msgId);
|
|
@@ -21,7 +21,7 @@ import { GetObjQueryOpts, ERR_SC, msgObj as api, HTTP_HEADER } from '../../../li
|
|
|
21
21
|
import { Request } from '../../resources/sessions';
|
|
22
22
|
import { errWithCause } from '../../../lib-common/exceptions/error';
|
|
23
23
|
import { EMPTY_BUFFER } from '../../../lib-common/buffer-utils';
|
|
24
|
-
import { getObjIdFromParams, replyWithErr } from '../../resources/utils';
|
|
24
|
+
import { getMsgIdFromParams, getObjIdFromParams, replyWithErr } from '../../resources/utils';
|
|
25
25
|
|
|
26
26
|
function extractQueryOptions(req: Request): undefined|{
|
|
27
27
|
header: boolean; limit: number|undefined; ofs: number;
|
|
@@ -52,7 +52,11 @@ export function getMsgObj(
|
|
|
52
52
|
return async (req: Request, res, next) => {
|
|
53
53
|
|
|
54
54
|
const userId = req.session.params.userId;
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
const { msgId, msgIdParseErr } = getMsgIdFromParams(req);
|
|
57
|
+
if (msgIdParseErr) {
|
|
58
|
+
return replyWithErr(ERR_SC.malformed, msgIdParseErr, res, req);
|
|
59
|
+
}
|
|
56
60
|
|
|
57
61
|
const { objId, objIdParseErr } = getObjIdFromParams(req);
|
|
58
62
|
if (objIdParseErr) {
|
|
@@ -19,12 +19,16 @@ import { RequestHandler } from 'express';
|
|
|
19
19
|
import { SC as recipSC, MsgRetrieval } from '../../resources/recipients';
|
|
20
20
|
import { rmMsg as api, ERR_SC } from '../../../lib-common/service-api/asmail/retrieval';
|
|
21
21
|
import { Request } from '../../resources/sessions';
|
|
22
|
-
import { replyWithErr } from '../../resources/utils';
|
|
22
|
+
import { getMsgIdFromParams, replyWithErr } from '../../resources/utils';
|
|
23
23
|
|
|
24
24
|
export function deleteMsg(delMsgFunc: MsgRetrieval['deleteMsg']): RequestHandler {
|
|
25
25
|
return async (req: Request, res, next) => {
|
|
26
26
|
const userId = req.session.params.userId;
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
const { msgId, msgIdParseErr } = getMsgIdFromParams(req);
|
|
29
|
+
if (msgIdParseErr) {
|
|
30
|
+
return replyWithErr(ERR_SC.malformed, msgIdParseErr, res, req);
|
|
31
|
+
}
|
|
28
32
|
|
|
29
33
|
try {
|
|
30
34
|
await delMsgFunc(userId, msgId);
|
package/ts-code/lib.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
export { AppWithWSs } from './lib-server/web-sockets/app';
|
|
19
19
|
|
|
20
|
-
export { Configurations, servicesApp, accountsApp } from './services';
|
|
20
|
+
export { Configurations, servicesApp, accountsApp, wellKnownApp } from './services';
|
|
21
21
|
|
|
22
22
|
export { addSingleUserSignup } from './config/signup';
|
|
23
23
|
|
package/ts-code/main.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { ServerOptions } from 'https';
|
|
|
25
25
|
import { CliUsageDisplay, parseProcessArgv } from './cli';
|
|
26
26
|
import { readYamlConfFile } from './config/from-yaml';
|
|
27
27
|
import { sslOptsFromConfig } from './config/letsencrypt';
|
|
28
|
-
import { Configurations, servicesApp, accountsApp, AppWithWSs } from './lib';
|
|
28
|
+
import { Configurations, servicesApp, accountsApp, AppWithWSs, wellKnownApp } from './lib';
|
|
29
29
|
import { addMultiDomainSignup, addSingleUserSignup, readAllSignupTokens, readNoTokensFile, readTokenFile } from './config/signup';
|
|
30
30
|
import { DEFAULT_CONFIG_PATH } from './config/default-confs';
|
|
31
31
|
import { Code } from './lib-common/exceptions/file';
|
|
@@ -45,6 +45,7 @@ async function run(conf: Configurations): Promise<void> {
|
|
|
45
45
|
const app = new AppWithWSs();
|
|
46
46
|
app.use(servicesApp(conf, 'console', 'console'));
|
|
47
47
|
app.use(accountsApp(conf, 'console', 'console'));
|
|
48
|
+
app.use(wellKnownApp(conf));
|
|
48
49
|
return app;
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -73,7 +74,7 @@ async function run(conf: Configurations): Promise<void> {
|
|
|
73
74
|
port: number, hostname: string|undefined
|
|
74
75
|
): Promise<void> {
|
|
75
76
|
await app!.start(undefined, port, hostname);
|
|
76
|
-
console.log(`🚀 Started 3NWeb server on port ${port},${hostname ? ` hostname ${hostname},` : ''} without TLS, and requiring TLS reverse proxy infront.`);
|
|
77
|
+
console.log(`🚀 Started 3NWeb server on port ${port},${hostname ? ` hostname ${hostname},` : ''} without TLS, and requiring TLS or Tor-like reverse proxy infront.`);
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
try {
|
package/ts-code/services.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright (C) 2015 - 2017, 2020, 2024 -
|
|
2
|
+
Copyright (C) 2015 - 2017, 2020, 2024 - 2026 3NSoft Inc.
|
|
3
3
|
|
|
4
4
|
This program is free software: you can redistribute it and/or modify it under
|
|
5
5
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -30,6 +30,7 @@ import { makeSignupApp } from './signup/signup-app';
|
|
|
30
30
|
import { ErrLogger, makeErrLoggerToConsole } from "./lib-server/middleware/error-handler";
|
|
31
31
|
import { makeLockerApp } from "./locker/locker-app";
|
|
32
32
|
import { MidAuthorizer } from "./lib-server/routes/sessions/mid-auth";
|
|
33
|
+
import { makeWellKnownApp } from "./well-known/well-known-route-app";
|
|
33
34
|
|
|
34
35
|
export interface Configurations {
|
|
35
36
|
enabledServices: {
|
|
@@ -56,6 +57,11 @@ export interface Configurations {
|
|
|
56
57
|
noTokenFile?: string;
|
|
57
58
|
serviceUrl?: string;
|
|
58
59
|
};
|
|
60
|
+
wellKnown3NWeb?: {
|
|
61
|
+
mailerid?: string;
|
|
62
|
+
asmail?: string;
|
|
63
|
+
"3nstorage"?: string;
|
|
64
|
+
};
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
export function servicesApp(
|
|
@@ -135,4 +141,15 @@ export function accountsApp(
|
|
|
135
141
|
return app;
|
|
136
142
|
}
|
|
137
143
|
|
|
144
|
+
export function wellKnownApp(conf: Configurations): AppWithWSs {
|
|
145
|
+
|
|
146
|
+
const app = new AppWithWSs();
|
|
147
|
+
|
|
148
|
+
if (conf.wellKnown3NWeb) {
|
|
149
|
+
app.http.use('/.well-known', makeWellKnownApp(conf.wellKnown3NWeb));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return app;
|
|
153
|
+
}
|
|
154
|
+
|
|
138
155
|
Object.freeze(exports);
|
|
@@ -25,7 +25,6 @@ import { certify as certApi }
|
|
|
25
25
|
import { user as midUser } from '../../lib-common/mid-sigs-NaCl-Ed';
|
|
26
26
|
import { doPubKeyLogin, User } from './pkl';
|
|
27
27
|
import { bytesSync as randomBytes } from '../../lib-common/random-node';
|
|
28
|
-
import { parse as parseUrl } from 'url';
|
|
29
28
|
|
|
30
29
|
export { User } from './pkl';
|
|
31
30
|
|
|
@@ -106,7 +105,7 @@ export async function doMailerIdLogin(loginUrl: string, user: User):
|
|
|
106
105
|
responseType: 'json',
|
|
107
106
|
sessionId
|
|
108
107
|
};
|
|
109
|
-
let serviceDomain =
|
|
108
|
+
let serviceDomain = (new URL(loginUrl)).hostname;
|
|
110
109
|
if (!serviceDomain) { throw new Error(
|
|
111
110
|
`Cannot parse hostname from login url ${loginUrl}`); }
|
|
112
111
|
let req: authSession.Request = {
|
|
@@ -23,7 +23,7 @@ import { makeSessionEncryptor, SessionEncryptor }
|
|
|
23
23
|
from '../../lib-common/session-encryptor';
|
|
24
24
|
import { doJsonRequest, RequestOpts, doBinaryRequest }
|
|
25
25
|
from './xhr-utils';
|
|
26
|
-
import {
|
|
26
|
+
import { resolve as resolveUrl } from 'url';
|
|
27
27
|
import { bytesEqual } from './bytes-equal';
|
|
28
28
|
|
|
29
29
|
export interface User {
|
|
@@ -36,8 +36,7 @@ specs.definition = (setup: () => TestSetup) => (() => {
|
|
|
36
36
|
beforeAllAsync(async () => {
|
|
37
37
|
user2 = setup().user2;
|
|
38
38
|
const deliveryUrl = await setup().asmailServer.getDeliveryUrl();
|
|
39
|
-
initialSession = await startSessionAndSendMsgObjs(
|
|
40
|
-
deliveryUrl, user2.id, msg);
|
|
39
|
+
initialSession = await startSessionAndSendMsgObjs(deliveryUrl, user2.id, msg);
|
|
41
40
|
reqOpts = {
|
|
42
41
|
url: resolveUrl(deliveryUrl, api.URL_END),
|
|
43
42
|
method: 'POST',
|
|
@@ -74,7 +73,7 @@ specs.definition = (setup: () => TestSetup) => (() => {
|
|
|
74
73
|
async () => {
|
|
75
74
|
const req: api.Request = {
|
|
76
75
|
recipient: user2.id,
|
|
77
|
-
msgId: '
|
|
76
|
+
msgId: 'unknown_message'
|
|
78
77
|
};
|
|
79
78
|
const rep = await doJsonRequest<api.Reply>(reqOpts, req);
|
|
80
79
|
expect(rep.status).withContext('status for unknown message').toBe(api.SC.unknownMsg);
|
|
@@ -50,14 +50,13 @@ export const msg: Msg = {
|
|
|
50
50
|
|
|
51
51
|
export async function startSessionAndSendMsgObjs(deliveryUrl: string,
|
|
52
52
|
recipient: string, msg: Msg): Promise<DeliveryInfo> {
|
|
53
|
-
const sessInfo = await startMsgDelivery(
|
|
53
|
+
const sessInfo = await startMsgDelivery(
|
|
54
|
+
deliveryUrl,
|
|
54
55
|
{ recipient },
|
|
55
|
-
{ pid: msg.cryptoMeta.pid,
|
|
56
|
-
|
|
57
|
-
await sendMsgObj(deliveryUrl, sessInfo.sessionId, msg.msgObjs[0],
|
|
58
|
-
|
|
59
|
-
await sendMsgObj(deliveryUrl, sessInfo.sessionId, msg.msgObjs[1],
|
|
60
|
-
sessInfo.maxChunkSize);
|
|
56
|
+
{ pid: msg.cryptoMeta.pid, objIds: [ msg.msgObjs[0].objId, msg.msgObjs[1].objId ] }
|
|
57
|
+
);
|
|
58
|
+
await sendMsgObj(deliveryUrl, sessInfo.sessionId, msg.msgObjs[0], sessInfo.maxChunkSize);
|
|
59
|
+
await sendMsgObj(deliveryUrl, sessInfo.sessionId, msg.msgObjs[1], sessInfo.maxChunkSize);
|
|
61
60
|
return sessInfo;
|
|
62
61
|
}
|
|
63
62
|
|
|
@@ -22,7 +22,7 @@ import { startMidSession, provisionMidSigner, User } from '../libs-for-tests/mai
|
|
|
22
22
|
import { expectNonAcceptanceOfBadJsonRequest, expectNonAcceptanceOfBadSessionId } from './requests';
|
|
23
23
|
import * as mid from '../../lib-common/mid-sigs-NaCl-Ed';
|
|
24
24
|
import { bytesSync as randomBytes } from '../../lib-common/random-node';
|
|
25
|
-
import {
|
|
25
|
+
import { resolve as resolveUrl } from 'url';
|
|
26
26
|
|
|
27
27
|
export function midLoginSpecs(
|
|
28
28
|
loginUrl: () => string,
|
|
@@ -86,7 +86,7 @@ export function midLoginSpecs(
|
|
|
86
86
|
sessionId
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
const serviceDomain =
|
|
89
|
+
const serviceDomain = (new URL(loginUrl())).hostname;
|
|
90
90
|
if (!serviceDomain) { throw new Error(
|
|
91
91
|
`Cannot get hostname from ${loginUrl()}`); }
|
|
92
92
|
const req: authSession.Request = {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2026 3NSoft Inc.
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as express from 'express';
|
|
19
|
+
import { Configurations } from '../services';
|
|
20
|
+
|
|
21
|
+
export function makeWellKnownApp(w3n: NonNullable<Configurations['wellKnown3NWeb']>): express.Application {
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
const w3nJson = confTo3NWebJSON(w3n);
|
|
26
|
+
|
|
27
|
+
app.get('3nweb.json', (req, res) => {
|
|
28
|
+
// the following implicitly sets content type application/json
|
|
29
|
+
res.status(200).json(w3nJson);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return app;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface WellKnown3NWeb {
|
|
36
|
+
mailerid?: string;
|
|
37
|
+
asmail?: string;
|
|
38
|
+
'3nstorage'?: string;
|
|
39
|
+
report?: string;
|
|
40
|
+
'w3n-app'?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function confTo3NWebJSON(conf: NonNullable<Configurations['wellKnown3NWeb']>): WellKnown3NWeb {
|
|
44
|
+
const json: WellKnown3NWeb = {};
|
|
45
|
+
const fields: (keyof WellKnown3NWeb)[] = [ '3nstorage', 'asmail', 'mailerid' ];
|
|
46
|
+
for (const field in fields) {
|
|
47
|
+
const href = conf[field];
|
|
48
|
+
if (href) {
|
|
49
|
+
let u: URL;
|
|
50
|
+
try {
|
|
51
|
+
u = new URL(href);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(` ❌ Configuration field ${field} for well-known needs potocol section`);
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
if ((u.protocol !== 'https:') && (u.protocol !== 'http:')) {
|
|
57
|
+
throw new Error(` ❌ Configuration field ${field} for well-known needs potocol section`);
|
|
58
|
+
}
|
|
59
|
+
json[field] = href;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return json;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Object.freeze(exports);
|