display instance info on the home page

* implements /api/instance
* updates recommended NGINX config
* adds a test on /checkup/
This commit is contained in:
ansuz 2022-05-03 18:20:18 +05:30
parent bbd914b88d
commit 8adeeb21ec
9 changed files with 114 additions and 9 deletions

View file

@ -5,7 +5,8 @@ define([
'/customize/messages.js', '/customize/messages.js',
'jquery', 'jquery',
'/api/config', '/api/config',
], function (h, Language, AppConfig, Msg, $, ApiConfig) { 'optional!/api/instance',
], function (h, Language, AppConfig, Msg, $, ApiConfig, Instance) {
var Pages = {}; var Pages = {};
Pages.setHTML = function (e, html) { Pages.setHTML = function (e, html) {
@ -138,6 +139,21 @@ define([
}); });
var value = AppConfig.hostDescription; var value = AppConfig.hostDescription;
Pages.hostDescription = (value && (value[l] || value.default)) || Msg.home_host; Pages.hostDescription = (value && (value[l] || value.default)) || Msg.home_host;
Pages.Instance = {};
Object.keys(Instance).forEach(function (k) {
var value = Instance[k];
var result = Pages.Instance[k] = value[l] || value.default || undefined;
});
var name;
try {
name = Pages.Instance.name || new URL('/', ApiConfig.httpUnsafeOrigin).host;
} catch (err) {
name = 'CryptPad';
}
Pages.Instance.name = name;
Pages.Instance.description = Pages.Instance.description || Msg.main_catch_phrase;
}()); }());
// used for the about menu // used for the about menu

View file

@ -11,7 +11,8 @@ define([
'/customize/messages.js', '/customize/messages.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/customize/pages.js' '/customize/pages.js',
//'json!/api/instance',
], function ($, Config, h, Feedback, UI, Hash, Constants, Util, TextFit, Msg, AppConfig, LocalStore, Pages) { ], function ($, Config, h, Feedback, UI, Hash, Constants, Util, TextFit, Msg, AppConfig, LocalStore, Pages) {
var urlArgs = Config.requireConf.urlArgs; var urlArgs = Config.requireConf.urlArgs;
@ -177,6 +178,19 @@ define([
])); ]));
} }
// instance location
var locationBlock;
if (Pages.Instance.location) {
locationBlock = h('div.cp-instance-location', [
h('i.fa.fa-map-pin', {'aria-hidden': 'true'}),
'Encrypted data is hosted in: ', // XXX translate
Pages.Instance.location,
]);
} else {
locationBlock = h('div', h('br')); // XXX
}
return [ return [
h('div#cp-main', [ h('div#cp-main', [
Pages.infopageTopbar(), Pages.infopageTopbar(),
@ -188,12 +202,9 @@ define([
'aria-hidden': 'true', 'aria-hidden': 'true',
alt: '' alt: ''
}), }),
h('h1', 'CryptPad.fr'), // XXX use instance name h('h1', Pages.Instance.name),
UI.setHTML(h('span.tag-line'), Msg.main_catch_phrase), // XXX Use instance description UI.setHTML(h('span.tag-line'), Pages.Instance.description),
h('div.cp-instance-location', [ locationBlock,
h('i.fa.fa-map-pin', {'aria-hidden': 'true'}),
'Encrypted data is hosted in: France (OVH)' // XXX Use instance location
]),
termsLink, termsLink,
privacyLink, privacyLink,
imprintLink imprintLink

View file

@ -183,7 +183,7 @@ server {
# /api/config is loaded once per page load and is used to retrieve # /api/config is loaded once per page load and is used to retrieve
# the caching variable which is applied to every other resource # the caching variable which is applied to every other resource
# which is loaded during that session. # which is loaded during that session.
location ~ ^/api/(config|broadcast).*$ { location ~ ^/api/.*$ {
proxy_pass http://localhost:3000; proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;

View file

@ -261,6 +261,27 @@ var serveBroadcast = makeRouteCache(function (host) {
app.get('/api/config', serveConfig); app.get('/api/config', serveConfig);
app.get('/api/broadcast', serveBroadcast); app.get('/api/broadcast', serveBroadcast);
var define = function (obj) {
return `define(function (){
return ${JSON.stringify(obj, null, '\t')};
});`
};
app.get('/api/instance', function (req, res) { // XXX use caching?
res.setHeader('Content-Type', 'text/javascript');
res.send(define({
name: {
default: Env.instanceName,
},
description: {
default: Env.instanceDescription,
},
location: {
default: Env.instanceJurisdiction,
},
}));
});
var four04_path = Path.resolve(__dirname + '/customize.dist/404.html'); var four04_path = Path.resolve(__dirname + '/customize.dist/404.html');
var fivehundred_path = Path.resolve(__dirname + '/customize.dist/500.html'); var fivehundred_path = Path.resolve(__dirname + '/customize.dist/500.html');
var custom_four04_path = Path.resolve(__dirname + '/customize/404.html'); var custom_four04_path = Path.resolve(__dirname + '/customize/404.html');

View file

@ -12,6 +12,7 @@
var first = true; var first = true;
window.addEventListener('error', function (ev) { window.addEventListener('error', function (ev) {
if (window.CHECKUP_MAIN_LOADED) { return; }
if (!ev) { return; } if (!ev) { return; }
var srcElement = ev.srcElement; var srcElement = ev.srcElement;
if (!srcElement) { return; } if (!srcElement) { return; }

View file

@ -22,6 +22,8 @@ define([
], function ($, ApiConfig, Assertions, h, Messages, DomReady, ], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad, nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig, Pages, Tools, AppConfig) { NetConfig, Pages, Tools, AppConfig) {
window.CHECKUP_MAIN_LOADED = true;
var Assert = Assertions(); var Assert = Assertions();
var trimSlashes = function (s) { var trimSlashes = function (s) {
if (typeof(s) !== 'string') { return s; } if (typeof(s) !== 'string') { return s; }
@ -1364,6 +1366,25 @@ define([
}); });
}); });
assert(function (cb, msg) {
var url = '/api/instance';
msg.appendChild(h('span', [
link(url, url),
" did not load as expected. This is most likely caused by a missing directive in your reverse proxy or an outdated version of the API server.",
]));
require([
`optional!${url}`,
], function (Instance) {
// if the URL fails to load then an empty object will be returned
// this can be interpreted as a failure, even though the rest of the platform should still work
if (!Object.keys(Instance).length) {
return void cb(Instance);
}
cb(true);
});
});
var serverToken; var serverToken;
Tools.common_xhr('/', function (xhr) { Tools.common_xhr('/', function (xhr) {
serverToken = xhr.getResponseHeader('server'); serverToken = xhr.getResponseHeader('server');

View file

@ -8,6 +8,7 @@ define([
// json plugin // json plugin
text: '/bower_components/requirejs-plugins/lib/text', text: '/bower_components/requirejs-plugins/lib/text',
json: '/bower_components/requirejs-plugins/src/json', json: '/bower_components/requirejs-plugins/src/json',
optional: '/lib/optional/optional',
// jquery declares itself as literally "jquery" so it cannot be pulled by path :( // jquery declares itself as literally "jquery" so it cannot be pulled by path :(
"jquery": "/bower_components/jquery/dist/jquery.min", "jquery": "/bower_components/jquery/dist/jquery.min",
"mermaid": "/lib/mermaid/mermaid.min", "mermaid": "/lib/mermaid/mermaid.min",

View file

@ -10,4 +10,5 @@ This file is intended to be used as a log of what third-party source we have ven
* [pdfjs](https://mozilla.github.io/pdf.js/) with some minor modifications to prevent CSP errors * [pdfjs](https://mozilla.github.io/pdf.js/) with some minor modifications to prevent CSP errors
* [mermaid 9.0.0](https://github.com/mermaid-js/mermaid/releases/tag/8.13.4) extends our markdown integration to support a variety of diagram types * [mermaid 9.0.0](https://github.com/mermaid-js/mermaid/releases/tag/8.13.4) extends our markdown integration to support a variety of diagram types
* [Fabricjs 4.6.0](https://github.com/fabricjs/fabric.js) and [Fabric-history](https://github.com/lyzerk/fabric-history) for the whiteboard app * [Fabricjs 4.6.0](https://github.com/fabricjs/fabric.js) and [Fabric-history](https://github.com/lyzerk/fabric-history) for the whiteboard app
* [Requirejs optional module plugin](https://stackoverflow.com/a/27422370)

View file

@ -0,0 +1,33 @@
define("optional", [], {
load : function (moduleName, parentRequire, onload, config){
var onLoadSuccess = function(moduleInstance){
// Module successfully loaded, call the onload callback so that
// requirejs can work its internal magic.
onload(moduleInstance);
}
var onLoadFailure = function(err){
// optional module failed to load.
var failedId = err.requireModules && err.requireModules[0];
console.warn("Could not load optional module: " + failedId);
// Undefine the module to cleanup internal stuff in requireJS
requirejs.undef(failedId);
// Now define the module instance as a simple empty object
// (NOTE: you can return any other value you want here)
define(failedId, [], function(){return {};});
// Now require the module make sure that requireJS thinks
// that is it loaded. Since we've just defined it, requirejs
// will not attempt to download any more script files and
// will just call the onLoadSuccess handler immediately
parentRequire([failedId], onLoadSuccess);
}
parentRequire([moduleName], onLoadSuccess, onLoadFailure, {
accept: 'application/json',
});
}
});