diff --git a/packages/cli/src/commands/init/initHandler.js b/packages/cli/src/commands/init/initHandler.js index 3fe4134447..eadc74f815 100644 --- a/packages/cli/src/commands/init/initHandler.js +++ b/packages/cli/src/commands/init/initHandler.js @@ -1,88 +1,7 @@ -const inquirer = require("inquirer") -const { exists, readFile, writeFile, ensureDir } = require("fs-extra") -const chalk = require("chalk") -const { serverFileName, xPlatHomeDir } = require("../../common") -const { join } = require("path") -const Sqrl = require("squirrelly") -const uuid = require("uuid") +const { xPlatHomeDir } = require("../../common") +const initialiseBudibase = require("@budibase/server/src/utilities/initialiseBudibase") module.exports = opts => { - return run(opts) -} - -const run = async opts => { - try { - await ensureAppDir(opts) - await setEnvironmentVariables(opts) - await createClientDatabase(opts) - await createDevEnvFile(opts) - console.log(chalk.green("Budibase successfully initialised.")) - } catch (error) { - console.error(`Error initialising Budibase: ${error.message}`) - } -} - -const setEnvironmentVariables = async opts => { - if (opts.couchDbUrl) { - process.env.COUCH_DB_URL = opts.couchDbUrl - } else { - const dataDir = join(opts.dir, ".data") - await ensureDir(dataDir) - process.env.COUCH_DB_URL = - dataDir + (dataDir.endsWith("/") || dataDir.endsWith("\\") ? "" : "/") - } -} - -const ensureAppDir = async opts => { opts.dir = xPlatHomeDir(opts.dir) - await ensureDir(opts.dir) -} - -const createClientDatabase = async opts => { - // cannot be a top level require as it - // will cause environment module to be loaded prematurely - const clientDb = require("@budibase/server/src/db/clientDb") - - if (opts.clientId === "new") { - // cannot be a top level require as it - // will cause environment module to be loaded prematurely - const CouchDB = require("@budibase/server/src/db/client") - const existing = await CouchDB.allDbs() - - let i = 0 - let isExisting = true - while (isExisting) { - i += 1 - opts.clientId = i.toString() - isExisting = existing.includes(clientDb.name(opts.clientId)) - } - } - - await clientDb.create(opts.clientId) -} - -const createDevEnvFile = async opts => { - const destConfigFile = join(opts.dir, "./.env") - let createConfig = !(await exists(destConfigFile)) || opts.quiet - if (!createConfig) { - const answers = await inquirer.prompt([ - { - type: "input", - name: "overwrite", - message: ".env already exists - overwrite? (N/y)", - }, - ]) - createConfig = ["Y", "y", "yes"].includes(answers.overwrite) - } - - if (createConfig) { - const template = await readFile(serverFileName(".env.template"), { - encoding: "utf8", - }) - opts.adminSecret = uuid.v4() - opts.cookieKey1 = uuid.v4() - opts.cookieKey2 = uuid.v4() - const config = Sqrl.Render(template, opts) - await writeFile(destConfigFile, config, { flag: "w+" }) - } + return initialiseBudibase(opts) } diff --git a/packages/server/package.json b/packages/server/package.json index f725f65abf..e57691209e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -26,7 +26,7 @@ "test": "jest routes --runInBand", "test:integration": "jest workflow --runInBand", "test:watch": "jest --watch", - "initialise": "node ../cli/bin/budi init -b local -q", + "initialise": "node ../cli/bin/budi init -q", "run:docker": "node src/index", "budi": "node ../cli/bin/budi", "dev:builder": "nodemon ../cli/bin/budi run", diff --git a/packages/server/src/electron.js b/packages/server/src/electron.js index d496c276cc..1d6687a145 100644 --- a/packages/server/src/electron.js +++ b/packages/server/src/electron.js @@ -1,55 +1,73 @@ const { app, BrowserWindow, shell } = require("electron") const { join } = require("path") -const { homedir } = require("os") const isDev = require("electron-is-dev") const { autoUpdater } = require("electron-updater") const unhandled = require("electron-unhandled") +const { existsSync } = require("fs-extra") +const initialiseBudibase = require("./utilities/initialiseBudibase") +const { budibaseAppsDir } = require("./utilities/budibaseDir") -require("dotenv").config({ path: join(homedir(), ".budibase", ".env") }) +const budibaseDir = budibaseAppsDir() +const envFile = join(budibaseDir, ".env") -if (isDev) { - unhandled({ - showDialog: true, +if (!existsSync(envFile)) { + // assume not initialised + initialiseBudibase({ dir: budibaseDir }).then(() => { + startApp() }) +} else { + startApp() } -const APP_URL = "http://localhost:4001/_builder" -const APP_TITLE = "Budibase Builder" +function startApp() { + // evict environment from cache, so it reloads when next asked + delete require.cache[require.resolve("./environment")] + require("dotenv").config({ path: envFile }) -let win - -function handleRedirect(e, url) { - e.preventDefault() - shell.openExternal(url) -} - -async function createWindow() { - app.server = await require("./app")() - win = new BrowserWindow({ width: 1920, height: 1080 }) - win.setTitle(APP_TITLE) - win.loadURL(APP_URL) if (isDev) { - win.webContents.openDevTools() - } else { - autoUpdater.checkForUpdatesAndNotify() + unhandled({ + showDialog: true, + }) } - // open _blank in default browser - win.webContents.on("new-window", handleRedirect) - win.webContents.on("will-navigate", handleRedirect) + const APP_URL = "http://localhost:4001/_builder" + const APP_TITLE = "Budibase Builder" + + let win + + function handleRedirect(e, url) { + e.preventDefault() + shell.openExternal(url) + } + + async function createWindow() { + app.server = await require("./app")() + win = new BrowserWindow({ width: 1920, height: 1080 }) + win.setTitle(APP_TITLE) + win.loadURL(APP_URL) + if (isDev) { + win.webContents.openDevTools() + } else { + autoUpdater.checkForUpdatesAndNotify() + } + + // open _blank in default browser + win.webContents.on("new-window", handleRedirect) + win.webContents.on("will-navigate", handleRedirect) + } + + app.whenReady().then(createWindow) + + // Quit when all windows are closed. + app.on("window-all-closed", () => { + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== "darwin") app.quit() + }) + + app.on("activate", () => { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (win === null) createWindow() + }) } - -app.whenReady().then(createWindow) - -// Quit when all windows are closed. -app.on("window-all-closed", () => { - // On macOS it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== "darwin") app.quit() -}) - -app.on("activate", () => { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (win === null) createWindow() -}) diff --git a/packages/server/src/utilities/initialiseBudibase.js b/packages/server/src/utilities/initialiseBudibase.js new file mode 100644 index 0000000000..87fedb434f --- /dev/null +++ b/packages/server/src/utilities/initialiseBudibase.js @@ -0,0 +1,66 @@ +const { exists, readFile, writeFile, ensureDir } = require("fs-extra") +const { join, resolve } = require("path") +const Sqrl = require("squirrelly") +const uuid = require("uuid") + +module.exports = async opts => { + await ensureDir(opts.dir) + await setCouchDbUrl(opts) + + // need an env file to create the client database + await createDevEnvFile(opts) + await createClientDatabase(opts) + + // need to recreate the env file, as we only now have a client id + // quiet flag will force overwrite of config + opts.quiet = true + await createDevEnvFile(opts) +} + +const setCouchDbUrl = async opts => { + if (!opts.couchDbUrl) { + const dataDir = join(opts.dir, ".data") + await ensureDir(dataDir) + opts.couchDbUrl = + dataDir + (dataDir.endsWith("/") || dataDir.endsWith("\\") ? "" : "/") + } +} + +const createDevEnvFile = async opts => { + const destConfigFile = join(opts.dir, "./.env") + let createConfig = !(await exists(destConfigFile)) || opts.quiet + if (createConfig) { + const template = await readFile( + resolve(__dirname, "..", "..", ".env.template"), + { + encoding: "utf8", + } + ) + opts.cookieKey1 = opts.cookieKey1 || uuid.v4() + const config = Sqrl.Render(template, opts) + await writeFile(destConfigFile, config, { flag: "w+" }) + } +} + +const createClientDatabase = async opts => { + // cannot be a top level require as it + // will cause environment module to be loaded prematurely + const clientDb = require("../db/clientDb") + + if (!opts.clientId || opts.clientId === "new") { + // cannot be a top level require as it + // will cause environment module to be loaded prematurely + const CouchDB = require("../db/client") + const existing = await CouchDB.allDbs() + + let i = 0 + let isExisting = true + while (isExisting) { + i += 1 + opts.clientId = i.toString() + isExisting = existing.includes(clientDb.name(opts.clientId)) + } + } + + await clientDb.create(opts.clientId) +}