Merge e45aa7a973
into 6299db6872
commit
0b1a8581d9
@ -1,160 +0,0 @@
|
|||||||
import { AppModel } from 'app/client/models/AppModel';
|
|
||||||
import { AdminChecks, ProbeDetails } from 'app/client/models/AdminChecks';
|
|
||||||
import { createAppPage } from 'app/client/ui/createAppPage';
|
|
||||||
import { pagePanels } from 'app/client/ui/PagePanels';
|
|
||||||
import { BootProbeInfo, BootProbeResult } from 'app/common/BootProbe';
|
|
||||||
import { getGristConfig } from 'app/common/urlUtils';
|
|
||||||
import { Disposable, dom, Observable, styled, UseCBOwner } from 'grainjs';
|
|
||||||
|
|
||||||
const cssBody = styled('div', `
|
|
||||||
padding: 20px;
|
|
||||||
overflow: auto;
|
|
||||||
`);
|
|
||||||
|
|
||||||
const cssHeader = styled('div', `
|
|
||||||
padding: 20px;
|
|
||||||
`);
|
|
||||||
|
|
||||||
const cssResult = styled('div', `
|
|
||||||
max-width: 500px;
|
|
||||||
`);
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* A "boot" page for inspecting the state of the Grist installation.
|
|
||||||
*
|
|
||||||
* TODO: deferring using any localization machinery so as not
|
|
||||||
* to have to worry about its failure modes yet, but it should be
|
|
||||||
* fine as long as assets served locally are used.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export class Boot extends Disposable {
|
|
||||||
|
|
||||||
private _checks: AdminChecks;
|
|
||||||
|
|
||||||
constructor(_appModel: AppModel) {
|
|
||||||
super();
|
|
||||||
// Setting title in constructor seems to be how we are doing this,
|
|
||||||
// based on other similar pages.
|
|
||||||
document.title = 'Booting Grist';
|
|
||||||
this._checks = new AdminChecks(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the page. Uses the generic Grist layout with an empty
|
|
||||||
* side panel, just for convenience. Could be made a lot prettier.
|
|
||||||
*/
|
|
||||||
public buildDom() {
|
|
||||||
this._checks.fetchAvailableChecks().catch(e => reportError(e));
|
|
||||||
|
|
||||||
const config = getGristConfig();
|
|
||||||
const errMessage = config.errMessage;
|
|
||||||
const rootNode = dom('div',
|
|
||||||
dom.domComputed(
|
|
||||||
use => {
|
|
||||||
return pagePanels({
|
|
||||||
leftPanel: {
|
|
||||||
panelWidth: Observable.create(this, 240),
|
|
||||||
panelOpen: Observable.create(this, false),
|
|
||||||
hideOpener: true,
|
|
||||||
header: null,
|
|
||||||
content: null,
|
|
||||||
},
|
|
||||||
headerMain: cssHeader(dom('h1', 'Grist Boot')),
|
|
||||||
contentMain: this.buildBody(use, {errMessage}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return rootNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The body of the page is very simple right now, basically a
|
|
||||||
* placeholder. Make a section for each probe, and kick them off in
|
|
||||||
* parallel, showing results as they come in.
|
|
||||||
*/
|
|
||||||
public buildBody(use: UseCBOwner, options: {errMessage?: string}) {
|
|
||||||
if (options.errMessage) {
|
|
||||||
return cssBody(cssResult(this.buildError()));
|
|
||||||
}
|
|
||||||
return cssBody([
|
|
||||||
...use(this._checks.probes).map(probe => {
|
|
||||||
const req = this._checks.requestCheck(probe);
|
|
||||||
return cssResult(
|
|
||||||
this.buildResult(req.probe, use(req.result), req.details));
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used when there is an attempt to access the boot page
|
|
||||||
* but something isn't right - either the page isn't enabled, or
|
|
||||||
* the key in the URL is wrong. Give the user some information about
|
|
||||||
* how to set things up.
|
|
||||||
*/
|
|
||||||
public buildError() {
|
|
||||||
return dom(
|
|
||||||
'div',
|
|
||||||
dom('p',
|
|
||||||
'A diagnostics page can be made available at:',
|
|
||||||
dom('blockquote', '/boot/GRIST_BOOT_KEY'),
|
|
||||||
'GRIST_BOOT_KEY is an environment variable ',
|
|
||||||
' set before Grist starts. It should only',
|
|
||||||
' contain characters that are valid in a URL.',
|
|
||||||
' It should be a secret, since no authentication is needed',
|
|
||||||
' to visit the diagnostics page.'),
|
|
||||||
dom('p',
|
|
||||||
'You are seeing this page because either the key is not set,',
|
|
||||||
' or it is not in the URL.'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ugly rendering of information returned by the probe.
|
|
||||||
*/
|
|
||||||
public buildResult(info: BootProbeInfo, result: BootProbeResult,
|
|
||||||
details: ProbeDetails|undefined) {
|
|
||||||
const out: (HTMLElement|string|null)[] = [];
|
|
||||||
out.push(dom('h2', info.name));
|
|
||||||
if (details) {
|
|
||||||
out.push(dom('p', '> ', details.info));
|
|
||||||
}
|
|
||||||
if (result.verdict) {
|
|
||||||
out.push(dom('pre', result.verdict));
|
|
||||||
}
|
|
||||||
if (result.success !== undefined) {
|
|
||||||
out.push(result.success ? '✅' : '❌');
|
|
||||||
}
|
|
||||||
if (result.done === true) {
|
|
||||||
out.push(dom('p', 'no fault detected'));
|
|
||||||
}
|
|
||||||
if (result.details) {
|
|
||||||
for (const [key, val] of Object.entries(result.details)) {
|
|
||||||
out.push(dom(
|
|
||||||
'div',
|
|
||||||
cssLabel(key),
|
|
||||||
dom('input', dom.prop('value', JSON.stringify(val)))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a stripped down page to show boot information.
|
|
||||||
* Make sure the API isn't used since it may well be unreachable
|
|
||||||
* due to a misconfiguration, especially in multi-server setups.
|
|
||||||
*/
|
|
||||||
createAppPage(appModel => {
|
|
||||||
return dom.create(Boot, appModel);
|
|
||||||
}, {
|
|
||||||
useApi: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const cssLabel = styled('div', `
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 100px;
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 5px;
|
|
||||||
`);
|
|
@ -1,15 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf8">
|
|
||||||
<!-- INSERT BASE -->
|
|
||||||
<link rel="icon" type="image/x-icon" href="icons/favicon.png" />
|
|
||||||
<link rel="stylesheet" href="icons/icons.css">
|
|
||||||
<!-- INSERT LOCALE -->
|
|
||||||
<!-- INSERT CONFIG -->
|
|
||||||
<title>Loading...<!-- INSERT TITLE SUFFIX --></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script crossorigin="anonymous" src="boot.bundle.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in new issue