Skip to content

Commit

Permalink
reconcile boot and admin pages further (#963)
Browse files Browse the repository at this point in the history
This adds some remaining parts of the boot page to the admin panel, and then removes the boot page.
  • Loading branch information
paulfitz authored May 23, 2024
1 parent 1690b77 commit 5dc4706
Show file tree
Hide file tree
Showing 19 changed files with 506 additions and 296 deletions.
160 changes: 0 additions & 160 deletions app/client/boot.ts

This file was deleted.

65 changes: 42 additions & 23 deletions app/client/models/AdminChecks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { reportError } from 'app/client/models/errors';
import { BootProbeIds, BootProbeInfo, BootProbeResult } from 'app/common/BootProbe';
import { removeTrailingSlash } from 'app/common/gutil';
import { InstallAPI } from 'app/common/InstallAPI';
import { getGristConfig } from 'app/common/urlUtils';
import { Disposable, Observable, UseCBOwner } from 'grainjs';

Expand All @@ -19,7 +20,7 @@ export class AdminChecks {
// Keep track of probe results we have received, by probe ID.
private _results: Map<string, Observable<BootProbeResult>>;

constructor(private _parent: Disposable) {
constructor(private _parent: Disposable, private _installAPI: InstallAPI) {
this.probes = Observable.create(_parent, []);
this._results = new Map();
this._requests = new Map();
Expand All @@ -32,18 +33,16 @@ export class AdminChecks {
const config = getGristConfig();
const errMessage = config.errMessage;
if (!errMessage) {
// Probe tool URLs are relative to the current URL. Don't trust configuration,
// because it may be buggy if the user is here looking at the boot page
// to figure out some problem.
//
// We have been careful to make URLs available with appropriate
// middleware relative to both of the admin panel and the boot page.
const url = new URL(removeTrailingSlash(document.location.href));
url.pathname += '/probe';
const resp = await fetch(url.href);
const _probes = await resp.json();
this.probes.set(_probes.probes);
const _probes = await this._installAPI.getChecks().catch(reportError);
if (!this._parent.isDisposed()) {
// Currently, probes are forbidden if not admin.
// TODO: May want to relax this to allow some probes that help
// diagnose some initial auth problems.
this.probes.set(_probes ? _probes.probes : []);
}
return _probes;
}
return [];
}

/**
Expand All @@ -54,12 +53,12 @@ export class AdminChecks {
const {id} = probe;
let result = this._results.get(id);
if (!result) {
result = Observable.create(this._parent, {});
result = Observable.create(this._parent, {status: 'none'});
this._results.set(id, result);
}
let request = this._requests.get(id);
if (!request) {
request = new AdminCheckRunner(id, this._results, this._parent);
request = new AdminCheckRunner(this._installAPI, id, this._results, this._parent);
this._requests.set(id, request);
}
request.start();
Expand Down Expand Up @@ -93,23 +92,23 @@ export interface AdminCheckRequest {
* Manage a single check.
*/
export class AdminCheckRunner {
constructor(public id: string, public results: Map<string, Observable<BootProbeResult>>,
constructor(private _installAPI: InstallAPI,
public id: string,
public results: Map<string, Observable<BootProbeResult>>,
public parent: Disposable) {
const url = new URL(removeTrailingSlash(document.location.href));
url.pathname = url.pathname + '/probe/' + id;
fetch(url.href).then(async resp => {
const _probes: BootProbeResult = await resp.json();
this._installAPI.runCheck(id).then(result => {
if (parent.isDisposed()) { return; }
const ob = results.get(id);
if (ob) {
ob.set(_probes);
ob.set(result);
}
}).catch(e => console.error(e));
}

public start() {
let result = this.results.get(this.id);
if (!result) {
result = Observable.create(this.parent, {});
result = Observable.create(this.parent, {status: 'none'});
this.results.set(this.id, result);
}
}
Expand All @@ -120,7 +119,7 @@ export class AdminCheckRunner {
* but it can be useful to show extra details and tips in the
* client.
*/
const probeDetails: Record<string, ProbeDetails> = {
export const probeDetails: Record<string, ProbeDetails> = {
'boot-page': {
info: `
This boot page should not be too easy to access. Either turn
Expand All @@ -144,6 +143,17 @@ is set.
`,
},

'sandboxing': {
info: `
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.
`
},

'system-user': {
info: `
It is good practice not to run Grist as the root user.
Expand All @@ -153,6 +163,15 @@ It is good practice not to run Grist as the root user.
'reachable': {
info: `
The main page of Grist should be available.
`
},

'websockets': {
// TODO: add a link to https://support.getgrist.com/self-managed/#how-do-i-run-grist-on-a-server
info: `
Websocket connections need HTTP 1.1 and the ability to pass a few
extra headers in order to work. Sometimes a reverse proxy can
interfere with these requirements.
`
},
};
Expand Down
46 changes: 44 additions & 2 deletions app/client/models/AppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,10 +531,52 @@ export function getOrgNameOrGuest(org: Organization|null, user: FullUser|null) {
return getOrgName(org);
}

export function getHomeUrl(): string {
/**
* If we don't know what the home URL is, the top level of the site
* we are on may work. This should always work for single-server installs
* that don't encode organization information in domains. Even for other
* cases, this should be a good enough home URL for many purposes, it
* just may still have some organization information encoded in it from
* the domain that could influence results that might be supposed to be
* organization-neutral.
*/
export function getFallbackHomeUrl(): string {
const {host, protocol} = window.location;
return `${protocol}//${host}`;
}

/**
* Get the official home URL sent to us from the back end.
*/
export function getConfiguredHomeUrl(): string {
const gristConfig: any = (window as any).gristConfig;
return (gristConfig && gristConfig.homeUrl) || `${protocol}//${host}`;
return (gristConfig && gristConfig.homeUrl) || getFallbackHomeUrl();
}

/**
* Get the home URL, using fallback if on admin page rather
* than trusting back end configuration.
*/
export function getPreferredHomeUrl(): string|undefined {
const gristUrl = urlState().state.get();
if (gristUrl.adminPanel) {
// On the admin panel, we should not trust configuration much,
// since we want the user to be able to access it to diagnose
// problems with configuration. So we access the API via the
// site we happen to be on rather than anything configured on
// the back end. Couldn't we just always do this? Maybe!
// It could require adjustments for calls that are meant
// to be site-neutral if the domain has an org encoded in it.
// But that's a small price to pay. Grist Labs uses a setup
// where api calls go to a dedicated domain distinct from all
// other sites, but there's no particular advantage to it.
return getFallbackHomeUrl();
}
return getConfiguredHomeUrl();
}

export function getHomeUrl(): string {
return getPreferredHomeUrl() || getConfiguredHomeUrl();
}

export function newUserAPIImpl(): UserAPIImpl {
Expand Down
Loading

0 comments on commit 5dc4706

Please sign in to comment.