mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
reconcile boot and admin pages further
This adds some remaining parts of the boot page to the admin panel, and then removes the boot page.
This commit is contained in:
@@ -2,8 +2,8 @@ import {buildHomeBanners} from 'app/client/components/Banners';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {localStorageJsonObs} from 'app/client/lib/localStorageObs';
|
||||
import {getTimeFromNow} from 'app/client/lib/timeUtils';
|
||||
import {AdminChecks, probeDetails, ProbeDetails} from 'app/client/models/AdminChecks';
|
||||
import {AppModel, getHomeUrl, reportError} from 'app/client/models/AppModel';
|
||||
import {AdminChecks} from 'app/client/models/AdminChecks';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import {AppHeader} from 'app/client/ui/AppHeader';
|
||||
import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon';
|
||||
@@ -15,7 +15,7 @@ import {basicButton} from 'app/client/ui2018/buttons';
|
||||
import {toggle} from 'app/client/ui2018/checkbox';
|
||||
import {mediaSmall, testId, theme, vars} from 'app/client/ui2018/cssVars';
|
||||
import {cssLink, makeLinks} from 'app/client/ui2018/links';
|
||||
import {SandboxingBootProbeDetails} from 'app/common/BootProbe';
|
||||
import {BootProbeInfo, BootProbeResult, SandboxingBootProbeDetails} from 'app/common/BootProbe';
|
||||
import {commonUrls, getPageTitleSuffix} from 'app/common/gristUrls';
|
||||
import {InstallAPI, InstallAPIImpl, LatestVersion} from 'app/common/InstallAPI';
|
||||
import {naturalCompare} from 'app/common/SortFunc';
|
||||
@@ -41,7 +41,7 @@ export class AdminPanel extends Disposable {
|
||||
constructor(private _appModel: AppModel) {
|
||||
super();
|
||||
document.title = getAdminPanelName() + getPageTitleSuffix(getGristConfig());
|
||||
this._checks = new AdminChecks(this);
|
||||
this._checks = new AdminChecks(this, this._installAPI);
|
||||
}
|
||||
|
||||
public buildDom() {
|
||||
@@ -78,9 +78,38 @@ export class AdminPanel extends Disposable {
|
||||
}
|
||||
|
||||
private _buildMainContent(owner: MultiHolder) {
|
||||
// If probes are available, show the panel as normal.
|
||||
// Otherwise say it is unavailable, and describe a fallback
|
||||
// mechanism for access.
|
||||
return cssPageContainer(
|
||||
dom.cls('clipboard'),
|
||||
{tabIndex: "-1"},
|
||||
dom.maybe(this._checks.probes, probes => {
|
||||
return probes.length > 0
|
||||
? this._buildMainContentForAdmin(owner)
|
||||
: this._buildMainContentForOthers(owner);
|
||||
}),
|
||||
testId('admin-panel'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show something helpful to those without access to the panel,
|
||||
* which could include a legit adminstrator if auth is misconfigured.
|
||||
*/
|
||||
private _buildMainContentForOthers(owner: MultiHolder) {
|
||||
return dom.create(AdminSection, t('Administrator Panel Unavailable'), [
|
||||
dom('p', `You do not have access to the administrator panel.
|
||||
Please log in as an administrator.`),
|
||||
dom('p', `Or, as a fallback, you can set:`),
|
||||
dom('pre', 'GRIST_BOOT_KEY=secret'),
|
||||
dom('p', ` in the environment and visit: `),
|
||||
dom('pre', `/admin?key=secret`)
|
||||
]);
|
||||
}
|
||||
|
||||
private _buildMainContentForAdmin(owner: MultiHolder) {
|
||||
return [
|
||||
dom.create(AdminSection, t('Support Grist'), [
|
||||
dom.create(AdminSectionItem, {
|
||||
id: 'telemetry',
|
||||
@@ -113,7 +142,6 @@ export class AdminPanel extends Disposable {
|
||||
expandedContent: this._buildAuthenticationNotice(owner),
|
||||
})
|
||||
]),
|
||||
|
||||
dom.create(AdminSection, t('Version'), [
|
||||
dom.create(AdminSectionItem, {
|
||||
id: 'version',
|
||||
@@ -123,8 +151,23 @@ export class AdminPanel extends Disposable {
|
||||
}),
|
||||
this._buildUpdates(owner),
|
||||
]),
|
||||
testId('admin-panel'),
|
||||
);
|
||||
dom.create(AdminSection, t('Self Checks'), [
|
||||
this._buildProbeItems(owner, {
|
||||
showRedundant: false,
|
||||
showNovel: true,
|
||||
}),
|
||||
dom.create(AdminSectionItem, {
|
||||
id: 'probe-other',
|
||||
name: 'more...',
|
||||
description: '',
|
||||
value: '',
|
||||
expandedContent: this._buildProbeItems(owner, {
|
||||
showRedundant: true,
|
||||
showNovel: false,
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
private _buildSandboxingDisplay(owner: IDisposableOwner) {
|
||||
@@ -150,11 +193,9 @@ export class AdminPanel extends Disposable {
|
||||
|
||||
private _buildSandboxingNotice() {
|
||||
return [
|
||||
t('Grist allows for very powerful formulas, using Python. \
|
||||
We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor \
|
||||
if your hardware supports it (most will), \
|
||||
to run formulas in each document within a sandbox \
|
||||
isolated from other documents and isolated from the network.'),
|
||||
// Use AdminChecks text for sandboxing, in order not to
|
||||
// duplicate.
|
||||
probeDetails['sandboxing'].info,
|
||||
dom(
|
||||
'div',
|
||||
{style: 'margin-top: 8px'},
|
||||
@@ -412,8 +453,94 @@ isolated from other documents and isolated from the network.'),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the results of various checks. Of the checks, some are considered
|
||||
* "redundant" (already covered elsewhere in the Admin Panel) and the
|
||||
* remainder are "novel".
|
||||
*/
|
||||
private _buildProbeItems(owner: MultiHolder, options: {
|
||||
showRedundant: boolean,
|
||||
showNovel: boolean,
|
||||
}) {
|
||||
return dom.domComputed(
|
||||
use => [
|
||||
...use(this._checks.probes).map(probe => {
|
||||
const isRedundant = probe.id === 'sandboxing';
|
||||
const show = isRedundant ? options.showRedundant : options.showNovel;
|
||||
if (!show) { return null; }
|
||||
const req = this._checks.requestCheck(probe);
|
||||
return this._buildProbeItem(owner, req.probe, use(req.result), req.details);
|
||||
}),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the result of an individual check.
|
||||
*/
|
||||
private _buildProbeItem(owner: MultiHolder,
|
||||
info: BootProbeInfo,
|
||||
result: BootProbeResult,
|
||||
details: ProbeDetails|undefined) {
|
||||
|
||||
const status = (result.success !== undefined) ?
|
||||
(result.success ? '✅' : '❗') : '―';
|
||||
|
||||
return dom.create(AdminSectionItem, {
|
||||
id: `probe-${info.id}`,
|
||||
name: info.id,
|
||||
description: info.name,
|
||||
value: cssStatus(status),
|
||||
expandedContent: [
|
||||
cssCheckHeader(
|
||||
'Results',
|
||||
{ style: 'margin-top: 0px; padding-top: 0px;' },
|
||||
),
|
||||
result.verdict ? dom('pre', result.verdict) : null,
|
||||
(result.success === undefined) ? null :
|
||||
dom('p',
|
||||
result.success ? 'Check succeeded.' : 'Check failed.'),
|
||||
(result.done !== true) ? null :
|
||||
dom('p', 'No fault detected.'),
|
||||
(details?.info === undefined) ? null : [
|
||||
cssCheckHeader('Notes'),
|
||||
details.info,
|
||||
],
|
||||
(result.details === undefined) ? null : [
|
||||
cssCheckHeader('Details'),
|
||||
...Object.entries(result.details).map(([key, val]) => {
|
||||
return dom(
|
||||
'div',
|
||||
cssLabel(key),
|
||||
dom('input', dom.prop(
|
||||
'value',
|
||||
typeof val === 'string' ? val : JSON.stringify(val))));
|
||||
}),
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//function maybeSwitchToggle(value: Observable<boolean|null>): DomContents {
|
||||
// return toggle(value, dom.hide((use) => use(value) === null));
|
||||
//}
|
||||
|
||||
// Ugh I'm not a front end person. h5 small-caps, sure why not.
|
||||
// Hopefully someone with taste will edit someday!
|
||||
const cssCheckHeader = styled('h5', `
|
||||
margin-bottom: 5px;
|
||||
font-variant: small-caps;
|
||||
`);
|
||||
|
||||
const cssStatus = styled('div', `
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
padding: 5px;
|
||||
`);
|
||||
|
||||
const cssPageContainer = styled('div', `
|
||||
overflow: auto;
|
||||
padding: 40px;
|
||||
@@ -491,3 +618,10 @@ export const cssDangerText = styled('div', `
|
||||
const cssHappyText = styled('span', `
|
||||
color: ${theme.controlFg};
|
||||
`);
|
||||
|
||||
export const cssLabel = styled('div', `
|
||||
display: inline-block;
|
||||
min-width: 100px;
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user