gristlabs_grist-core/app/server/mergedServerMain.ts
Paul Fitzpatrick 68a682f876 (core) add housekeeping endpoints for cleaning doc snapshots+state
Summary:
This adds endpoints that allow the support user to remove unlisted
snapshots for a document, and to remove all action history for
a document.

This does increase what the support user can do, but not in a way
that would be particularly valuable to attack.  It would have some
destructive value, for removing history (removing unlisted
snapshots doesn't impact the user, by contrast).

This would simplify some maintenance operations.

Test Plan: added test for snapshots; tested states manually

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2699
2021-01-05 10:31:14 -05:00

165 lines
4.8 KiB
TypeScript

/**
*
* A version of hosted grist that recombines a home server,
* a doc worker, and a static server on a single port.
*
*/
import {FlexServer, FlexServerOptions} from 'app/server/lib/FlexServer';
import * as log from 'app/server/lib/log';
// Allowed server types. We'll start one or a combination based on the value of GRIST_SERVERS
// environment variable.
export type ServerType = "home" | "docs" | "static" | "app";
const allServerTypes: ServerType[] = ["home", "docs", "static", "app"];
// Parse a comma-separate list of server types into an array, with validation.
function parseServerTypes(serverTypes: string|undefined): ServerType[] {
// Split and filter out empty strings (including the one we get when splitting "").
const types = (serverTypes || "").trim().split(',').filter(part => Boolean(part));
// Check that parts is non-empty and only contains valid options.
if (!types.length) {
throw new Error(`No server types; should be a comma-separated list of ${allServerTypes.join(", ")}`);
}
for (const t of types) {
if (!allServerTypes.includes(t as ServerType)) {
throw new Error(`Invalid server type '${t}'; should be in ${allServerTypes.join(", ")}`);
}
}
return types as ServerType[];
}
interface ServerOptions extends FlexServerOptions {
logToConsole?: boolean; // If set, messages logged to console (default: false)
// (but if options are not given at all in call to main,
// logToConsole is set to true)
s3?: boolean; // If set, documents saved to s3 (default is to check environment
// variables, which get set in various ways in dev/test entry points)
}
/**
* Start a server on the given port, including the functionality specified in serverTypes.
*/
export async function main(port: number, serverTypes: ServerType[],
options: ServerOptions = {logToConsole: true}) {
const includeHome = serverTypes.includes("home");
const includeDocs = serverTypes.includes("docs");
const includeStatic = serverTypes.includes("static");
const includeApp = serverTypes.includes("app");
const server = new FlexServer(port, `server(${serverTypes.join(",")})`, options);
server.addCleanup();
server.setDirectory();
if (process.env.GRIST_TEST_ROUTER) {
// Add a mock api for adding/removing doc workers from load balancer.
server.testAddRouter();
}
if (options.logToConsole) { server.addLogging(); }
if (options.s3 === false) { server.disableS3(); }
await server.loadConfig();
if (includeDocs) {
// It is important that /dw and /v prefixes are accepted (if present) by health check
// in this case, since they are included in the url registered for the doc worker.
server.stripDocWorkerIdPathPrefixIfPresent();
server.addTagChecker();
}
server.addHealthCheck();
if (includeHome || includeStatic || includeApp) {
server.setDirectory();
}
if (includeHome || includeStatic) {
server.addStaticAndBowerDirectories();
}
await server.initHomeDBManager();
server.addHosts();
server.addDocWorkerMap();
if (includeHome || includeStatic) {
await server.addAssetsForPlugins();
}
if (includeHome && !includeApp) {
server.addEarlyWebhooks();
}
if (includeHome || includeDocs || includeApp) {
server.addSessions();
}
server.addAccessMiddleware();
server.addApiMiddleware();
await server.start();
if (includeHome) {
if (!includeApp) {
server.addUsage();
}
if (!includeDocs) {
server.addDocApiForwarder();
}
server.addJsonSupport();
server.addLandingPages();
// todo: add support for home api to standalone app
if (!includeApp) {
server.addHomeApi();
server.addBillingApi();
server.addNotifier();
await server.addHousekeeper();
}
server.addLoginRoutes();
server.addBillingPages();
server.addWelcomePaths();
server.addLogEndpoint();
}
if (includeDocs) {
server.addJsonSupport();
await server.addDoc();
}
server.finalize();
server.summary();
return server;
}
export async function startMain() {
try {
const serverTypes = parseServerTypes(process.env.GRIST_SERVERS);
// No defaults for a port, since this server can serve very different purposes.
if (!process.env.GRIST_PORT) {
throw new Error("GRIST_PORT must be specified");
}
const port = parseInt(process.env.GRIST_PORT, 10);
const server = await main(port, serverTypes);
const opt = process.argv[2];
if (opt === '--testingHooks') {
await server.addTestingHooks();
}
return server;
} catch (e) {
log.error('mergedServer failed to start', e);
process.exit(1);
}
}
if (require.main === module) {
startMain().catch((e) => log.error('mergedServer failed to start', e));
}