Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reconcile boot and admin pages further #963

Merged
merged 17 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -505,10 +505,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
Loading