#12: Remove Electron-specific code in db initialization

This commit is contained in:
Elian Doran 2024-05-12 11:29:44 +03:00
parent 780f2577c8
commit 80151c2b2f
No known key found for this signature in database
12 changed files with 109 additions and 85 deletions

View File

@ -81,6 +81,10 @@ const app = startTrilium({
setupCompleteCallback: () => {
windowService.createMainWindow(electron.app);
windowService.closeSetupWindow();
},
getInitialTheme() {
return electron.nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
});
electronRouting(app);

View File

@ -6,9 +6,9 @@ import helmet = require('helmet');
import compression = require('compression');
import sessionParser = require('./routes/session_parser');
import utils = require('./services/utils');
import { RouteConfig } from './routes/types';
import { AppConfig } from './types';
function buildApp(routeConfig: RouteConfig) {
function buildApp(appConfig: AppConfig) {
require('./services/handlers');
require('./becca/becca_loader');
@ -40,7 +40,7 @@ function buildApp(routeConfig: RouteConfig) {
app.use(favicon(`${__dirname}/../../common/images/app-icons/win/icon.ico`));
require('./routes/assets').register(app);
require('./routes/routes').register(app, routeConfig);
require('./routes/routes').register(app, appConfig);
require('./routes/custom').register(app);
require('./routes/error_handlers').register(app);

View File

@ -3,5 +3,10 @@ import startTrilium = require("./www");
startTrilium({
setupCompleteCallback: (res) => {
res.redirect('.');
},
getInitialTheme() {
// default based on the poll in https://github.com/zadam/trilium/issues/2516
return "dark";
}
});

View File

@ -5,56 +5,61 @@ import setupService = require('../../services/setup');
import log = require('../../services/log');
import appInfo = require('../../services/app_info');
import { Request } from 'express';
import { InitDbOptions } from '../../types';
function getStatus() {
return {
isInitialized: sqlInit.isDbInitialized(),
schemaExists: sqlInit.schemaExists(),
syncVersion: appInfo.syncVersion
};
}
async function setupNewDocument() {
await sqlInit.createInitialDatabase();
}
function setupSyncFromServer(req: Request) {
const { syncServerHost, syncProxy, password } = req.body;
return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password);
}
function saveSyncSeed(req: Request) {
const { options, syncVersion } = req.body;
if (appInfo.syncVersion !== syncVersion) {
const message = `Could not setup sync since local sync protocol version is ${appInfo.syncVersion} while remote is ${syncVersion}. To fix this issue, use same Trilium version on all instances.`;
log.error(message);
return [400, {
error: message
}]
function buildRoutes(initOptions: InitDbOptions) {
function getStatus() {
return {
isInitialized: sqlInit.isDbInitialized(),
schemaExists: sqlInit.schemaExists(),
syncVersion: appInfo.syncVersion
};
}
log.info("Saved sync seed.");
sqlInit.createDatabaseForSync(options);
}
function getSyncSeed() {
log.info("Serving sync seed.");
async function setupNewDocument() {
await sqlInit.createInitialDatabase(initOptions);
}
function setupSyncFromServer(req: Request) {
const { syncServerHost, syncProxy, password } = req.body;
return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password, initOptions);
}
function saveSyncSeed(req: Request) {
const { options, syncVersion } = req.body;
if (appInfo.syncVersion !== syncVersion) {
const message = `Could not setup sync since local sync protocol version is ${appInfo.syncVersion} while remote is ${syncVersion}. To fix this issue, use same Trilium version on all instances.`;
log.error(message);
return [400, {
error: message
}]
}
log.info("Saved sync seed.");
sqlInit.createDatabaseForSync(options, initOptions);
}
function getSyncSeed() {
log.info("Serving sync seed.");
return {
options: setupService.getSyncSeedOptions(),
syncVersion: appInfo.syncVersion
};
}
return {
options: setupService.getSyncSeedOptions(),
syncVersion: appInfo.syncVersion
getStatus,
setupNewDocument,
setupSyncFromServer,
getSyncSeed,
saveSyncSeed
};
}
export = {
getStatus,
setupNewDocument,
setupSyncFromServer,
getSyncSeed,
saveSyncSeed
};
export = buildRoutes;

View File

@ -38,7 +38,7 @@ import recentNotesRoute = require('./api/recent_notes');
import appInfoRoute = require('./api/app_info');
import exportRoute = require('./api/export');
import importRoute = require('./api/import');
import setupApiRoute = require('./api/setup');
import buildSetupRoute = require('./api/setup');
import sqlRoute = require('./api/sql');
import databaseRoute = require('./api/database');
import imageRoute = require('./api/image');
@ -71,7 +71,7 @@ import etapiSpecialNoteRoutes = require('../etapi/special_notes');
import etapiSpecRoute = require('../etapi/spec');
import etapiBackupRoute = require('../etapi/backup');
import { AppRequest, AppRequestHandler } from './route-interface';
import { RouteConfig } from './types';
import { AppConfig } from '../types';
const csrfMiddleware = csurf({
cookie: {
@ -102,7 +102,7 @@ const uploadMiddlewareWithErrorHandling = function (req: express.Request, res: e
});
};
function register(app: express.Application, config: RouteConfig) {
function register(app: express.Application, appConfig: AppConfig) {
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage);
route(GET, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPasswordPage);
@ -116,7 +116,7 @@ function register(app: express.Application, config: RouteConfig) {
route(PST, '/login', [loginRateLimiter], loginRoute.login);
route(PST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout);
route(PST, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPassword);
route(GET, '/setup', [], setupRoute.buildSetupRoute(config.setupCompleteCallback));
route(GET, '/setup', [], setupRoute.buildSetupRoute(appConfig.setupCompleteCallback));
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
apiRoute(PST, '/api/tree/load', treeApiRoute.load);
@ -241,6 +241,7 @@ function register(app: express.Application, config: RouteConfig) {
route(GET, '/api/health-check', [], () => ({ "status": "ok" }), apiResultHandler);
// group of the services below are meant to be executed from the outside
const setupApiRoute = buildSetupRoute(appConfig);
route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler);
route(PST, '/api/setup/new-document', [auth.checkAppNotInitialized], setupApiRoute.setupNewDocument, apiResultHandler, false);
route(PST, '/api/setup/sync-from-server', [auth.checkAppNotInitialized], setupApiRoute.setupSyncFromServer, apiResultHandler, false);

View File

@ -5,7 +5,7 @@ import setupService = require('../services/setup');
import assetPath = require('../services/asset_path');
import appPath = require('../services/app_path');
import { Request, Response } from 'express';
import { SetupCompleteCallback } from './types';
import { SetupCompleteCallback } from '../types';
function buildSetupRoute(setupCompleteCallback: SetupCompleteCallback) {
return (req: Request, res: Response) => {

View File

@ -1,8 +0,0 @@
import { Response } from "express";
export type SetupCompleteCallback = (res: Response) => void;
export interface RouteConfig {
/** Callback to be invoked when first setup is complete. */
setupCompleteCallback: SetupCompleteCallback;
}

View File

@ -5,6 +5,7 @@ import log = require('./log');
import dateUtils = require('./date_utils');
import keyboardActions = require('./keyboard_actions');
import { KeyboardShortcutWithRequiredActionName } from './keyboard_actions_interface';
import { InitDbOptions } from '../types';
function initDocumentOptions() {
optionService.createOption('documentId', utils.randomSecureToken(16), false);
@ -16,7 +17,7 @@ interface NotSyncedOpts {
syncProxy?: string;
}
function initNotSyncedOptions(initialized: boolean, opts: NotSyncedOpts = {}) {
function initNotSyncedOptions(initialized: boolean, opts: NotSyncedOpts = {}, initOptions: InitDbOptions) {
optionService.createOption('openNoteContexts', JSON.stringify([
{
notePath: 'root',
@ -32,17 +33,9 @@ function initNotSyncedOptions(initialized: boolean, opts: NotSyncedOpts = {}) {
optionService.createOption('initialized', initialized ? 'true' : 'false', false);
optionService.createOption('lastSyncedPull', '0', false);
optionService.createOption('lastSyncedPush', '0', false);
optionService.createOption('lastSyncedPush', '0', false);
let theme = 'dark'; // default based on the poll in https://github.com/zadam/trilium/issues/2516
if (utils.isElectron()) {
const {nativeTheme} = require('electron');
theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
optionService.createOption('theme', theme, false);
optionService.createOption('theme', initOptions.getInitialTheme(), false);
optionService.createOption('syncServerHost', opts.syncServerHost || '', false);
optionService.createOption('syncServerTimeout', '120000', false);

View File

@ -8,6 +8,7 @@ import appInfo = require('./app_info');
import utils = require('./utils');
import becca = require('../becca/becca');
import { SetupStatusResponse, SetupSyncSeedResponse } from './api-interface';
import { InitDbOptions } from '../types';
async function hasSyncServerSchemaAndSeed() {
const response = await requestToSyncServer<SetupStatusResponse>('GET', '/api/setup/status');
@ -56,7 +57,7 @@ async function requestToSyncServer<T>(method: string, path: string, body?: strin
}), timeout) as T;
}
async function setupSyncFromSyncServer(syncServerHost: string, syncProxy: string, password: string) {
async function setupSyncFromSyncServer(syncServerHost: string, syncProxy: string, password: string, initOptions: InitDbOptions) {
if (sqlInit.isDbInitialized()) {
return {
result: 'failure',
@ -87,7 +88,7 @@ async function setupSyncFromSyncServer(syncServerHost: string, syncProxy: string
}
}
sqlInit.createDatabaseForSync(resp.options, syncServerHost, syncProxy);
sqlInit.createDatabaseForSync(resp.options, initOptions, syncServerHost, syncProxy);
triggerSync();

View File

@ -11,6 +11,8 @@ import migrationService = require('./migration');
import cls = require('./cls');
import config = require('./config');
import { OptionRow } from '../becca/entities/rows';
import optionsInitService = require("./options_init");
import { InitDbOptions } from '../types';
const dbReady = utils.deferred<void>();
@ -46,7 +48,7 @@ async function initDbConnection() {
dbReady.resolve();
}
async function createInitialDatabase() {
async function createInitialDatabase(initOptions: InitDbOptions) {
if (isDbInitialized()) {
throw new Error("DB is already initialized");
}
@ -84,10 +86,8 @@ async function createInitialDatabase() {
notePosition: 10
}).save();
const optionsInitService = require('./options_init');
optionsInitService.initDocumentOptions();
optionsInitService.initNotSyncedOptions(true, {});
optionsInitService.initNotSyncedOptions(true, {}, initOptions);
optionsInitService.initStartupOptions();
require('./encryption/password').resetPassword();
});
@ -120,7 +120,7 @@ async function createInitialDatabase() {
initDbConnection();
}
function createDatabaseForSync(options: OptionRow[], syncServerHost = '', syncProxy = '') {
function createDatabaseForSync(options: OptionRow[], initOptions: InitDbOptions, syncServerHost = '', syncProxy = '') {
log.info("Creating database for sync");
if (isDbInitialized()) {
@ -132,7 +132,7 @@ function createDatabaseForSync(options: OptionRow[], syncServerHost = '', syncPr
sql.transactional(() => {
sql.executeScript(schema);
require('./options_init').initNotSyncedOptions(false, { syncServerHost, syncProxy });
optionsInitService.initNotSyncedOptions(false, { syncServerHost, syncProxy }, initOptions);
// document options required for sync to kick off
for (const opt of options) {

23
server/src/types.ts Normal file
View File

@ -0,0 +1,23 @@
import { Response } from "express";
export type InitialThemeCallback = () => "dark" | "light";
export type SetupCompleteCallback = (res: Response) => void;
/**
* Options needed when the database is first initialized, such as methods to obtain default values.
*/
export interface InitDbOptions {
/**
* Called during first setup, in order to determine which theme to set for the user.
*/
getInitialTheme: InitialThemeCallback;
}
/**
* Handles differences between clients, for example allowing different behaviour when running from the web server versus the desktop application.
*/
export interface AppConfig extends InitDbOptions {
/** Callback to be invoked when first setup is complete. */
setupCompleteCallback: SetupCompleteCallback;
}

View File

@ -31,9 +31,9 @@ import port = require('./services/port');
import host = require('./services/host');
import semver = require('semver');
import type { Express } from "express";
import { RouteConfig } from './routes/types';
import { AppConfig } from './types';
function startTrilium(routeConfig: RouteConfig) {
function startTrilium(appConfig: AppConfig) {
if (!semver.satisfies(process.version, ">=10.5.0")) {
console.error("Trilium only supports node.js 10.5 and later");
process.exit(1);
@ -41,7 +41,7 @@ function startTrilium(routeConfig: RouteConfig) {
log.info(JSON.stringify(appInfo, null, 2));
const app = buildApp(routeConfig);
const app = buildApp(appConfig);
const cpuInfos = require('os').cpus();
if (cpuInfos && cpuInfos[0] !== undefined) { // https://github.com/zadam/trilium/pull/3957