2020-07-21 13:20:51 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Run a home server, doc worker, and static server as a single process for regular
|
|
|
|
* development work.
|
|
|
|
*
|
|
|
|
* PORT -- this sets the main web server port (defaults to 8080)
|
|
|
|
* HOME_PORT -- this sets the main home server port (defaults to 9000)
|
|
|
|
* STATIC_PORT -- port for the static resource server (defaults to 9001)
|
|
|
|
* DOC_PORT -- comma separated ports for doc workers (defaults to 9002)
|
|
|
|
* TEST_CLEAN_DATABASE -- reset the database(s) before starting
|
|
|
|
* GRIST_SINGLE_PORT -- if set, just a single combined server on HOME_PORT
|
|
|
|
* DOC_WORKER_COUNT -- if set, makes sure there are at least this number of
|
|
|
|
* doc workers. Will add ports incrementally after the last
|
|
|
|
* worker added with DOC_PORT.
|
|
|
|
*
|
|
|
|
* If you run more than one doc worker, you'll need to have a redis server running
|
|
|
|
* and REDIS_URL set (e.g. to redis://localhost).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {updateDb} from 'app/server/lib/dbUtils';
|
|
|
|
import {FlexServer} from 'app/server/lib/FlexServer';
|
|
|
|
import * as log from 'app/server/lib/log';
|
|
|
|
import {main as mergedServerMain} from 'app/server/mergedServerMain';
|
|
|
|
import {promisifyAll} from 'bluebird';
|
|
|
|
import * as fse from 'fs-extra';
|
|
|
|
import * as path from 'path';
|
|
|
|
import {createClient, RedisClient} from 'redis';
|
|
|
|
|
|
|
|
promisifyAll(RedisClient.prototype);
|
|
|
|
|
|
|
|
function getPort(envVarName: string, fallbackPort: number): number {
|
|
|
|
const val = process.env[envVarName];
|
|
|
|
return val ? parseInt(val, 10) : fallbackPort;
|
|
|
|
}
|
|
|
|
|
2021-07-13 07:29:38 +00:00
|
|
|
// Checks whether to serve user content on same domain but on different port
|
|
|
|
function checkUserContentPort(): number | null {
|
|
|
|
if (process.env.APP_UNTRUSTED_URL && process.env.APP_HOME_URL) {
|
|
|
|
const homeUrl = new URL(process.env.APP_HOME_URL);
|
|
|
|
const pluginUrl = new URL(process.env.APP_UNTRUSTED_URL);
|
|
|
|
// If the hostname of both home and plugin url are the same,
|
|
|
|
// but the ports are different
|
|
|
|
if (homeUrl.hostname === pluginUrl.hostname &&
|
|
|
|
homeUrl.port !== pluginUrl.port) {
|
|
|
|
const port = parseInt(pluginUrl.port || '80', 10);
|
|
|
|
return port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
export async function main() {
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== devServer");
|
|
|
|
log.info("devServer starting. Please do not set any ports in environment :-)");
|
|
|
|
log.info("Server will be available at http://localhost:8080");
|
|
|
|
|
|
|
|
process.env.GRIST_HOSTED = "true";
|
|
|
|
if (!process.env.GRIST_ADAPT_DOMAIN) {
|
|
|
|
process.env.GRIST_ADAPT_DOMAIN = "true";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Experimental plugins are enabled by default for devs
|
|
|
|
if (!process.env.GRIST_EXPERIMENTAL_PLUGINS) {
|
|
|
|
process.env.GRIST_EXPERIMENTAL_PLUGINS = "1";
|
|
|
|
}
|
|
|
|
|
|
|
|
// For tests, it is useful to start with the database in a known state.
|
|
|
|
// If TEST_CLEAN_DATABASE is set, we reset the database before starting.
|
|
|
|
if (process.env.TEST_CLEAN_DATABASE) {
|
|
|
|
const {createInitialDb} = require('test/gen-server/seed');
|
|
|
|
await createInitialDb();
|
|
|
|
if (process.env.REDIS_URL) {
|
|
|
|
await createClient(process.env.REDIS_URL).flushdbAsync();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
await updateDb();
|
|
|
|
}
|
|
|
|
|
|
|
|
// In V1, we no longer create a config.json file automatically if it is missing.
|
|
|
|
// It is convenient to do that in the dev and test environment.
|
|
|
|
const appRoot = path.dirname(path.dirname(__dirname));
|
|
|
|
const instDir = process.env.GRIST_INST_DIR || appRoot;
|
|
|
|
if (process.env.GRIST_INST_DIR) {
|
|
|
|
const fileName = path.join(instDir, 'config.json');
|
|
|
|
if (!(await fse.pathExists(fileName))) {
|
|
|
|
const config = {
|
|
|
|
untrustedContentOrigin: 'notset',
|
|
|
|
};
|
|
|
|
await fse.writeFile(fileName, JSON.stringify(config, null, 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-13 07:29:38 +00:00
|
|
|
if (!process.env.GOOGLE_CLIENT_ID) {
|
|
|
|
// those key is only for development purposes
|
|
|
|
// and is no secret as it is publicly visible in a plugin page
|
|
|
|
process.env.GOOGLE_CLIENT_ID = '632317221841-ce66sfp00rf92dip4548dn4hf2ga79us.apps.googleusercontent.com';
|
|
|
|
}
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
if (process.env.GRIST_SINGLE_PORT) {
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== mergedServer");
|
|
|
|
const port = getPort("HOME_PORT", 8080);
|
|
|
|
if (!process.env.APP_HOME_URL) {
|
|
|
|
process.env.APP_HOME_URL = `http://localhost:${port}`;
|
|
|
|
}
|
|
|
|
const server = await mergedServerMain(port, ["home", "docs", "static"]);
|
|
|
|
await server.addTestingHooks();
|
2021-07-13 07:29:38 +00:00
|
|
|
// If plugin content is served from same host but on different port,
|
|
|
|
// run webserver on that port
|
|
|
|
const userPort = checkUserContentPort();
|
|
|
|
if (userPort !== null) {
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== userContent");
|
|
|
|
await server.startCopy('pluginServer', userPort);
|
|
|
|
}
|
2020-07-21 13:20:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-27 20:08:18 +00:00
|
|
|
// The home server and web server(s) are effectively identical in Grist deployments
|
|
|
|
// now, but remain distinct in some test setups.
|
2020-07-21 13:20:51 +00:00
|
|
|
const homeServerPort = getPort("HOME_PORT", 9000);
|
2021-05-27 20:08:18 +00:00
|
|
|
const webServerPort = getPort("PORT", 8080);
|
2020-07-21 13:20:51 +00:00
|
|
|
if (!process.env.APP_HOME_URL) {
|
2021-05-27 20:08:18 +00:00
|
|
|
// All servers need to know a "main" URL for Grist. This is generally
|
|
|
|
// that of the web server. In some test setups, the web server port is left
|
|
|
|
// at 0 to be auto-allocated, but for those tests it suffices to use the home
|
|
|
|
// server port.
|
|
|
|
process.env.APP_HOME_URL = `http://localhost:${webServerPort || homeServerPort}`;
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bring up the static resource server
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== staticServer");
|
|
|
|
const staticPort = getPort("STATIC_PORT", 9001);
|
|
|
|
process.env.APP_STATIC_URL = `http://localhost:${staticPort}`;
|
|
|
|
await mergedServerMain(staticPort, ["static"]);
|
|
|
|
|
|
|
|
// Bring up a home server
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== homeServer");
|
|
|
|
const home = await mergedServerMain(homeServerPort, ["home"]);
|
|
|
|
|
|
|
|
// If a distinct webServerPort is specified, we listen also on that port, though serving
|
|
|
|
// exactly the same content. This is handy for testing CORS issues.
|
|
|
|
if (webServerPort !== 0 && webServerPort !== homeServerPort) {
|
|
|
|
await home.startCopy('webServer', webServerPort);
|
|
|
|
}
|
|
|
|
|
2021-07-13 07:29:38 +00:00
|
|
|
// If plugin content is served from same host but on different port,
|
|
|
|
// run webserver on that port
|
|
|
|
const userPort = checkUserContentPort();
|
|
|
|
if (userPort !== null) {
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== userContent");
|
|
|
|
await home.startCopy('pluginServer', userPort);
|
|
|
|
}
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
// Bring up the docWorker(s)
|
|
|
|
log.info("==========================================================================");
|
|
|
|
log.info("== docWorker");
|
|
|
|
const ports = (process.env.DOC_PORT || '9002').split(',').map(port => parseInt(port, 10));
|
|
|
|
if (process.env.DOC_WORKER_COUNT) {
|
|
|
|
const n = parseInt(process.env.DOC_WORKER_COUNT, 10);
|
|
|
|
while (ports.length < n) {
|
|
|
|
ports.push(ports[ports.length - 1] + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.info(`== ports ${ports.join(',')}`);
|
|
|
|
if (ports.length > 1 && !process.env.REDIS_URL) {
|
|
|
|
throw new Error('Need REDIS_URL=redis://localhost or similar for multiple doc workers');
|
|
|
|
}
|
|
|
|
const workers = new Array<FlexServer>();
|
|
|
|
for (const port of ports) {
|
|
|
|
workers.push(await mergedServerMain(port, ["docs"]));
|
|
|
|
}
|
|
|
|
|
|
|
|
await home.addTestingHooks(workers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (require.main === module) {
|
|
|
|
main().catch((e) => {
|
|
|
|
log.error("devServer failed to start %s", e);
|
|
|
|
process.exit(1);
|
|
|
|
});
|
|
|
|
}
|