const log = require('./log'); const fs = require('fs'); const resourceDir = require('./resource_dir'); const sql = require('./sql'); const utils = require('./utils'); const optionService = require('./options'); const port = require('./port'); const Option = require('../becca/entities/option.js'); const TaskContext = require('./task_context.js'); const migrationService = require('./migration'); const cls = require('./cls'); const config = require('./config'); const dbReady = utils.deferred(); cls.init(initDbConnection); function schemaExists() { return !!sql.getValue(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'options'`); } function isDbInitialized() { if (!schemaExists()) { return false; } const initialized = sql.getValue("SELECT value FROM options WHERE name = 'initialized'"); return initialized === 'true'; } async function initDbConnection() { if (!isDbInitialized()) { log.info(`DB not initialized, please visit setup page` + (utils.isElectron() ? '' : ` - http://[your-server-host]:${await port} to see instructions on how to initialize Trilium.`)); return; } await migrationService.migrateIfNecessary(); sql.execute('CREATE TEMP TABLE "param_list" (`paramId` TEXT NOT NULL PRIMARY KEY)'); dbReady.resolve(); } async function createInitialDatabase(username, password, theme) { log.info("Creating database schema ..."); if (isDbInitialized()) { throw new Error("DB is already initialized"); } const schema = fs.readFileSync(resourceDir.DB_INIT_DIR + '/schema.sql', 'UTF-8'); const demoFile = fs.readFileSync(resourceDir.DB_INIT_DIR + '/demo.zip'); let rootNote; log.info("Creating root note ..."); sql.transactional(() => { sql.executeScript(schema); require("../becca/becca_loader.js").load(); const Note = require("../becca/entities/note.js"); const Branch = require("../becca/entities/branch.js"); rootNote = new Note({ noteId: 'root', title: 'root', type: 'text', mime: 'text/html' }).save(); rootNote.setContent(''); new Branch({ branchId: 'root', noteId: 'root', parentNoteId: 'none', isExpanded: true, notePosition: 10 }).save(); const optionsInitService = require('./options_init'); optionsInitService.initDocumentOptions(); optionsInitService.initSyncedOptions(username, password); optionsInitService.initNotSyncedOptions(true, { theme }); optionsInitService.initStartupOptions(); }); log.info("Importing demo content ..."); const dummyTaskContext = new TaskContext("initial-demo-import", 'import', false); const zipImportService = require("./import/zip"); await zipImportService.importZip(dummyTaskContext, demoFile, rootNote); sql.transactional(() => { // this needs to happen after ZIP import // previous solution was to move option initialization here but then the important parts of initialization // are not all in one transaction (because ZIP import is async and thus not transactional) const startNoteId = sql.getValue("SELECT noteId FROM branches WHERE parentNoteId = 'root' AND isDeleted = 0 ORDER BY notePosition"); const optionService = require("./options"); optionService.setOption('openTabs', JSON.stringify([ { notePath: startNoteId, active: true } ])); }); log.info("Schema and initial content generated."); initDbConnection(); } function createDatabaseForSync(options, syncServerHost = '', syncProxy = '') { log.info("Creating database for sync"); if (isDbInitialized()) { throw new Error("DB is already initialized"); } const schema = fs.readFileSync(resourceDir.DB_INIT_DIR + '/schema.sql', 'UTF-8'); sql.transactional(() => { sql.executeScript(schema); require('./options_init').initNotSyncedOptions(false, { syncServerHost, syncProxy }); // document options required for sync to kick off for (const opt of options) { new Option(opt).save(); } }); log.info("Schema and not synced options generated."); } function setDbAsInitialized() { if (!isDbInitialized()) { optionService.setOption('initialized', 'true'); initDbConnection(); } } dbReady.then(() => { if (config.General && config.General.noBackup === true) { log.info("Disabling scheduled backups."); return; } setInterval(() => require('./backup').regularBackup(), 4 * 60 * 60 * 1000); // kickoff first backup soon after start up setTimeout(() => require('./backup').regularBackup(), 5 * 60 * 1000); }); log.info("DB size: " + sql.getValue("SELECT page_count * page_size / 1000 as size FROM pragma_page_count(), pragma_page_size()") + " KB"); module.exports = { dbReady, schemaExists, isDbInitialized, initDbConnection, createInitialDatabase, createDatabaseForSync, setDbAsInitialized };