UNPKG

@electric-sql/cli

Version:

ElectricSQL command line interface (CLI).

180 lines 5.1 kB
import { Command } from "commander"; import { buildDatabaseURL, dedent, parsePgProxyPort, parsePort } from '../util/index.js'; import { addOptionGroupToCommand, getConfig, printConfig } from '../config.js'; import { dockerCompose } from './docker-utils.js'; function makeStartCommand() { const command = new Command("start"); command.description( "Start the ElectricSQL sync service, and an optional PostgreSQL" ); addOptionGroupToCommand(command, "electric"); command.option( "--detach", "Run in the background instead of printing logs to the console" ); command.action(async (opts) => { if (opts.databaseUrl && opts.withPostgres) { console.error("You cannot set --database-url when using --with-postgres."); process.exit(1); } const config = getConfig(opts); if (!config.WITH_POSTGRES && !config.DATABASE_URL) { console.error( "You must set --database-url or the ELECTRIC_DATABASE_URL env var when not using --with-postgres." ); process.exit(1); } const startOptions = { detach: opts.detach, withPostgres: !!config.WITH_POSTGRES, config }; start(startOptions); }); return command; } function start(options) { return new Promise((resolve) => { const exitOnDetached = options.exitOnDetached ?? true; console.log( `Starting ElectricSQL sync service${options.withPostgres ? " with PostgreSQL" : ""}` ); const env = configToEnv(options.config); env.PG_PROXY_PORT_PARSED = parsePgProxyPort( env.PG_PROXY_PORT ).port.toString(); const dockerConfig = { ...env, ...options.withPostgres ? { COMPOSE_PROFILES: "with-postgres", DATABASE_URL: buildDatabaseURL({ user: env.DATABASE_USER, password: env.DATABASE_PASSWORD, host: "postgres", port: parsePort(env.DATABASE_PORT), dbName: env.DATABASE_NAME }), LOGICAL_PUBLISHER_HOST: "electric" } : {} }; console.log("Docker compose config:"); printConfig(dockerConfig); const proc = dockerCompose( "up", [...options.detach ? ["--detach"] : []], options.config.CONTAINER_NAME, dockerConfig ); proc.on("close", async (code) => { if (code === 0) { if (options.detach) { if (options.withPostgres) { await waitForPostgres(options.config.CONTAINER_NAME, dockerConfig); } await waitForElectric(options.config.SERVICE); } if (exitOnDetached) { process.exit(0); } resolve(); } else { console.error( dedent` Failed to start the Electric backend. Check the output from 'docker compose' above. If the error message mentions a port already being allocated or address being already in use, please change the configuration to an alternative port via the ELECTRIC_HTTP_PORT or ELECTRIC_PG_PROXY_PORT environment variables. ` ); process.exit(code ?? 1); } }); }); } function checkPostgres(containerName, env) { return new Promise((resolve, reject) => { try { const proc = dockerCompose( "exec", [ "postgres", "pg_isready", "-U", `${env.DATABASE_USER}`, "-p", `${env.DATABASE_PORT}` ], containerName, env ); proc.on("close", (code) => { resolve(code === 0); }); } catch (e) { reject(e); } }); } async function waitForPostgres(containerName, env) { console.log("Waiting for PostgreSQL to be ready..."); const start2 = Date.now(); const timeout = 10 * 1e3; while (Date.now() - start2 < timeout) { if (await checkPostgres(containerName, env)) { console.log("PostgreSQL is ready"); return; } await new Promise((resolve) => setTimeout(resolve, 500)); } console.error( dedent` Timed out waiting for PostgreSQL to be ready. Check the output from 'docker compose' above. ` ); process.exit(1); } async function waitForElectric(serviceUrl) { console.log("Waiting for Electric to be ready..."); const statusUrl = `${serviceUrl}/api/status`; const start2 = Date.now(); const timeout = 10 * 1e3; while (Date.now() - start2 < timeout) { try { const res = await fetch(statusUrl); if (res.ok) { console.log("Electric is ready"); return; } } catch (e) { } await new Promise((resolve) => setTimeout(resolve, 500)); } console.error( dedent` Timed out waiting for Electric to be ready. Check the output from 'docker compose' above. ` ); process.exit(1); } function configToEnv(config) { const env = {}; for (const [key, val] of Object.entries(config)) { if (val === true) { env[key] = "true"; } else if (val !== void 0) { env[key] = val.toString(); } } return env; } export { makeStartCommand, start }; //# sourceMappingURL=command-start.js.map