mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	refactor(services): use named imports from utils
This commit is contained in:
		
							parent
							
								
									3814621e1c
								
							
						
					
					
						commit
						7fe23c7ac2
					
				@ -6,7 +6,7 @@ import log from "./log.js";
 | 
				
			|||||||
import os from "os";
 | 
					import os from "os";
 | 
				
			||||||
import fs from "fs";
 | 
					import fs from "fs";
 | 
				
			||||||
import config from "./config.js";
 | 
					import config from "./config.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron } from "./utils.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const template = `[Desktop Entry]
 | 
					const template = `[Desktop Entry]
 | 
				
			||||||
Type=Application
 | 
					Type=Application
 | 
				
			||||||
@ -23,7 +23,7 @@ Terminal=false
 | 
				
			|||||||
 * We overwrite this file during every run as it might have been updated.
 | 
					 * We overwrite this file during every run as it might have been updated.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function installLocalAppIcon() {
 | 
					function installLocalAppIcon() {
 | 
				
			||||||
    if (!utils.isElectron()
 | 
					    if (!isElectron()
 | 
				
			||||||
        || ["win32", "darwin"].includes(os.platform())
 | 
					        || ["win32", "darwin"].includes(os.platform())
 | 
				
			||||||
        || (config.General && config.General.noDesktopIcon)) {
 | 
					        || (config.General && config.General.noDesktopIcon)) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
import etapiTokenService from "./etapi_tokens.js";
 | 
					import etapiTokenService from "./etapi_tokens.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import sqlInit from "./sql_init.js";
 | 
					import sqlInit from "./sql_init.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron } from "./utils.js";
 | 
				
			||||||
import passwordEncryptionService from "./encryption/password_encryption.js";
 | 
					import passwordEncryptionService from "./encryption/password_encryption.js";
 | 
				
			||||||
import config from "./config.js";
 | 
					import config from "./config.js";
 | 
				
			||||||
import passwordService from "./encryption/password.js";
 | 
					import passwordService from "./encryption/password.js";
 | 
				
			||||||
@ -15,7 +15,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) {
 | 
				
			|||||||
    if (!sqlInit.isDbInitialized()) {
 | 
					    if (!sqlInit.isDbInitialized()) {
 | 
				
			||||||
        res.redirect("setup");
 | 
					        res.redirect("setup");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
 | 
					    else if (!req.session.loggedIn && !isElectron() && !noAuthentication) {
 | 
				
			||||||
        res.redirect("login");
 | 
					        res.redirect("login");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
@ -26,7 +26,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) {
 | 
				
			|||||||
// for electron things which need network stuff
 | 
					// for electron things which need network stuff
 | 
				
			||||||
//  currently, we're doing that for file upload because handling form data seems to be difficult
 | 
					//  currently, we're doing that for file upload because handling form data seems to be difficult
 | 
				
			||||||
function checkApiAuthOrElectron(req: Request, res: Response, next: NextFunction) {
 | 
					function checkApiAuthOrElectron(req: Request, res: Response, next: NextFunction) {
 | 
				
			||||||
    if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
 | 
					    if (!req.session.loggedIn && !isElectron() && !noAuthentication) {
 | 
				
			||||||
        reject(req, res, "Logged in session not found");
 | 
					        reject(req, res, "Logged in session not found");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
@ -53,7 +53,7 @@ function checkAppInitialized(req: Request, res: Response, next: NextFunction) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkPasswordSet(req: Request, res: Response, next: NextFunction) {
 | 
					function checkPasswordSet(req: Request, res: Response, next: NextFunction) {
 | 
				
			||||||
    if (!utils.isElectron() && !passwordService.isPasswordSet()) {
 | 
					    if (!isElectron() && !passwordService.isPasswordSet()) {
 | 
				
			||||||
        res.redirect("set-password");
 | 
					        res.redirect("set-password");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        next();
 | 
					        next();
 | 
				
			||||||
@ -61,7 +61,7 @@ function checkPasswordSet(req: Request, res: Response, next: NextFunction) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkPasswordNotSet(req: Request, res: Response, next: NextFunction) {
 | 
					function checkPasswordNotSet(req: Request, res: Response, next: NextFunction) {
 | 
				
			||||||
    if (!utils.isElectron() && passwordService.isPasswordSet()) {
 | 
					    if (!isElectron() && passwordService.isPasswordSet()) {
 | 
				
			||||||
        res.redirect("login");
 | 
					        res.redirect("login");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        next();
 | 
					        next();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import noteService from "./notes.js";
 | 
					import noteService from "./notes.js";
 | 
				
			||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { randomString, escapeHtml, unescapeHtml } from "./utils.js";
 | 
				
			||||||
import attributeService from "./attributes.js";
 | 
					import attributeService from "./attributes.js";
 | 
				
			||||||
import dateNoteService from "./date_notes.js";
 | 
					import dateNoteService from "./date_notes.js";
 | 
				
			||||||
import treeService from "./tree.js";
 | 
					import treeService from "./tree.js";
 | 
				
			||||||
@ -549,9 +549,9 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.setNoteToParent = treeService.setNoteToParent;
 | 
					    this.setNoteToParent = treeService.setNoteToParent;
 | 
				
			||||||
    this.transactional = sql.transactional;
 | 
					    this.transactional = sql.transactional;
 | 
				
			||||||
    this.randomString = utils.randomString;
 | 
					    this.randomString = randomString;
 | 
				
			||||||
    this.escapeHtml = utils.escapeHtml;
 | 
					    this.escapeHtml = escapeHtml;
 | 
				
			||||||
    this.unescapeHtml = utils.unescapeHtml;
 | 
					    this.unescapeHtml = unescapeHtml;
 | 
				
			||||||
    this.sql = sql;
 | 
					    this.sql = sql;
 | 
				
			||||||
    this.getAppInfo = () => appInfo;
 | 
					    this.getAppInfo = () => appInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import becca from "../becca/becca.js";
 | 
					import becca from "../becca/becca.js";
 | 
				
			||||||
import NotFoundError from "../errors/not_found_error.js";
 | 
					import NotFoundError from "../errors/not_found_error.js";
 | 
				
			||||||
import protectedSessionService from "./protected_session.js";
 | 
					import protectedSessionService from "./protected_session.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { hash } from "./utils.js";
 | 
				
			||||||
import type { Blob } from "./blob-interface.js";
 | 
					import type { Blob } from "./blob-interface.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getBlobPojo(entityName: string, entityId: string, opts?: { preview: boolean }) {
 | 
					function getBlobPojo(entityName: string, entityId: string, opts?: { preview: boolean }) {
 | 
				
			||||||
@ -51,7 +51,7 @@ function processContent(content: Buffer | string | null, isProtected: boolean, i
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function calculateContentHash({blobId, content}: Blob) {
 | 
					function calculateContentHash({blobId, content}: Blob) {
 | 
				
			||||||
    return utils.hash(`${blobId}|${content.toString()}`);
 | 
					    return hash(`${blobId}|${content.toString()}`);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import log from "./log.js";
 | 
				
			|||||||
import becca from "../becca/becca.js";
 | 
					import becca from "../becca/becca.js";
 | 
				
			||||||
import cloningService from "./cloning.js";
 | 
					import cloningService from "./cloning.js";
 | 
				
			||||||
import branchService from "./branches.js";
 | 
					import branchService from "./branches.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { randomString } from "./utils.js";
 | 
				
			||||||
import eraseService from "./erase.js";
 | 
					import eraseService from "./erase.js";
 | 
				
			||||||
import BNote from "../becca/entities/bnote.js";
 | 
					import BNote from "../becca/entities/bnote.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,7 +32,7 @@ const ACTION_HANDLERS: Record<string, ActionHandler> = {
 | 
				
			|||||||
        note.addRelation(action.relationName, action.targetNoteId);
 | 
					        note.addRelation(action.relationName, action.targetNoteId);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    deleteNote: (action, note) => {
 | 
					    deleteNote: (action, note) => {
 | 
				
			||||||
        const deleteId = `searchbulkaction-${utils.randomString(10)}`;
 | 
					        const deleteId = `searchbulkaction-${randomString(10)}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        note.deleteNote(deleteId);
 | 
					        note.deleteNote(deleteId);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import fs from "fs";
 | 
				
			|||||||
import themeNames from "./code_block_theme_names.json" with { type: "json" }
 | 
					import themeNames from "./code_block_theme_names.json" with { type: "json" }
 | 
				
			||||||
import { t } from "i18next";
 | 
					import { t } from "i18next";
 | 
				
			||||||
import { join } from "path";
 | 
					import { join } from "path";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron, getResourceDir } from "./utils.js";
 | 
				
			||||||
import env from "./env.js";
 | 
					import env from "./env.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -31,7 +31,7 @@ interface ColorTheme {
 | 
				
			|||||||
 * @returns the supported themes, grouped.
 | 
					 * @returns the supported themes, grouped.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function listSyntaxHighlightingThemes() {
 | 
					export function listSyntaxHighlightingThemes() {
 | 
				
			||||||
    const path = join(utils.getResourceDir(), getStylesDirectory());
 | 
					    const path = join(getResourceDir(), getStylesDirectory());
 | 
				
			||||||
    const systemThemes = readThemesFromFileSystem(path);
 | 
					    const systemThemes = readThemesFromFileSystem(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@ -46,7 +46,7 @@ export function listSyntaxHighlightingThemes() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getStylesDirectory() {
 | 
					function getStylesDirectory() {
 | 
				
			||||||
    if (utils.isElectron() && !env.isDev()) {
 | 
					    if (isElectron() && !env.isDev()) {
 | 
				
			||||||
        return "styles";
 | 
					        return "styles";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { hash } from "./utils.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import eraseService from "./erase.js";
 | 
					import eraseService from "./erase.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,7 +43,7 @@ function getEntityHashes() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    for (const entityHashMap of Object.values(hashMap)) {
 | 
					    for (const entityHashMap of Object.values(hashMap)) {
 | 
				
			||||||
        for (const key in entityHashMap) {
 | 
					        for (const key in entityHashMap) {
 | 
				
			||||||
            entityHashMap[key] = utils.hash(entityHashMap[key]);
 | 
					            entityHashMap[key] = hash(entityHashMap[key]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
import sql from "../sql.js";
 | 
					import sql from "../sql.js";
 | 
				
			||||||
import optionService from "../options.js";
 | 
					import optionService from "../options.js";
 | 
				
			||||||
import myScryptService from "./my_scrypt.js";
 | 
					import myScryptService from "./my_scrypt.js";
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { randomSecureToken, toBase64 } from "../utils.js";
 | 
				
			||||||
import passwordEncryptionService from "./password_encryption.js";
 | 
					import passwordEncryptionService from "./password_encryption.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isPasswordSet() {
 | 
					function isPasswordSet() {
 | 
				
			||||||
@ -25,10 +25,10 @@ function changePassword(currentPassword: string, newPassword: string) {
 | 
				
			|||||||
    sql.transactional(() => {
 | 
					    sql.transactional(() => {
 | 
				
			||||||
        const decryptedDataKey = passwordEncryptionService.getDataKey(currentPassword);
 | 
					        const decryptedDataKey = passwordEncryptionService.getDataKey(currentPassword);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32));
 | 
					        optionService.setOption('passwordVerificationSalt', randomSecureToken(32));
 | 
				
			||||||
        optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32));
 | 
					        optionService.setOption('passwordDerivedKeySalt', randomSecureToken(32));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const newPasswordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(newPassword));
 | 
					        const newPasswordVerificationKey = toBase64(myScryptService.getVerificationHash(newPassword));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (decryptedDataKey) {
 | 
					        if (decryptedDataKey) {
 | 
				
			||||||
            // TODO: what should happen if the decrypted data key is null?
 | 
					            // TODO: what should happen if the decrypted data key is null?
 | 
				
			||||||
@ -48,16 +48,16 @@ function setPassword(password: string) {
 | 
				
			|||||||
        throw new Error("Password is set already. Either change it or perform 'reset password' first.");
 | 
					        throw new Error("Password is set already. Either change it or perform 'reset password' first.");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    optionService.createOption('passwordVerificationSalt', utils.randomSecureToken(32), true);
 | 
					    optionService.createOption('passwordVerificationSalt', randomSecureToken(32), true);
 | 
				
			||||||
    optionService.createOption('passwordDerivedKeySalt', utils.randomSecureToken(32), true);
 | 
					    optionService.createOption('passwordDerivedKeySalt', randomSecureToken(32), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const passwordVerificationKey = utils.toBase64(myScryptService.getVerificationHash(password));
 | 
					    const passwordVerificationKey = toBase64(myScryptService.getVerificationHash(password));
 | 
				
			||||||
    optionService.createOption('passwordVerificationHash', passwordVerificationKey, true);
 | 
					    optionService.createOption('passwordVerificationHash', passwordVerificationKey, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // passwordEncryptionService expects these options to already exist
 | 
					    // passwordEncryptionService expects these options to already exist
 | 
				
			||||||
    optionService.createOption('encryptedDataKey', '', true);
 | 
					    optionService.createOption('encryptedDataKey', '', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
 | 
					    passwordEncryptionService.setDataKey(password, randomSecureToken(16));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        success: true
 | 
					        success: true
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
import optionService from "../options.js";
 | 
					import optionService from "../options.js";
 | 
				
			||||||
import myScryptService from "./my_scrypt.js";
 | 
					import myScryptService from "./my_scrypt.js";
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { toBase64 } from "../utils.js";
 | 
				
			||||||
import dataEncryptionService from "./data_encryption.js";
 | 
					import dataEncryptionService from "./data_encryption.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function verifyPassword(password: string) {
 | 
					function verifyPassword(password: string) {
 | 
				
			||||||
    const givenPasswordHash = utils.toBase64(myScryptService.getVerificationHash(password));
 | 
					    const givenPasswordHash = toBase64(myScryptService.getVerificationHash(password));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dbPasswordHash = optionService.getOptionOrNull('passwordVerificationHash');
 | 
					    const dbPasswordHash = optionService.getOptionOrNull('passwordVerificationHash');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import sql from "./sql.js";
 | 
				
			|||||||
import dateUtils from "./date_utils.js";
 | 
					import dateUtils from "./date_utils.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import cls from "./cls.js";
 | 
					import cls from "./cls.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { randomString } from "./utils.js";
 | 
				
			||||||
import instanceId from "./instance_id.js";
 | 
					import instanceId from "./instance_id.js";
 | 
				
			||||||
import becca from "../becca/becca.js";
 | 
					import becca from "../becca/becca.js";
 | 
				
			||||||
import blobService from "../services/blob.js";
 | 
					import blobService from "../services/blob.js";
 | 
				
			||||||
@ -30,7 +30,7 @@ function putEntityChange(origEntityChange: EntityChange) {
 | 
				
			|||||||
    delete ec.id;
 | 
					    delete ec.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!ec.changeId) {
 | 
					    if (!ec.changeId) {
 | 
				
			||||||
        ec.changeId = utils.randomString(12);
 | 
					        ec.changeId = randomString(12);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ec.componentId = ec.componentId || cls.getComponentId() || "NA"; // NA = not available
 | 
					    ec.componentId = ec.componentId || cls.getComponentId() || "NA"; // NA = not available
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import becca from "../becca/becca.js";
 | 
					import becca from "../becca/becca.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { fromBase64, randomSecureToken } from "./utils.js";
 | 
				
			||||||
import BEtapiToken from "../becca/entities/betapi_token.js";
 | 
					import BEtapiToken from "../becca/entities/betapi_token.js";
 | 
				
			||||||
import crypto from "crypto";
 | 
					import crypto from "crypto";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,7 +12,7 @@ function getTokenHash(token: crypto.BinaryLike) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createToken(tokenName: string) {
 | 
					function createToken(tokenName: string) {
 | 
				
			||||||
    const token = utils.randomSecureToken(32);
 | 
					    const token = randomSecureToken(32);
 | 
				
			||||||
    const tokenHash = getTokenHash(token);
 | 
					    const tokenHash = getTokenHash(token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const etapiToken = new BEtapiToken({
 | 
					    const etapiToken = new BEtapiToken({
 | 
				
			||||||
@ -34,7 +34,7 @@ function parseAuthToken(auth: string | undefined) {
 | 
				
			|||||||
        // allow also basic auth format for systems which allow this type of authentication
 | 
					        // allow also basic auth format for systems which allow this type of authentication
 | 
				
			||||||
        // expect ETAPI token in the password field, require "etapi" username
 | 
					        // expect ETAPI token in the password field, require "etapi" username
 | 
				
			||||||
        // https://github.com/zadam/trilium/issues/3181
 | 
					        // https://github.com/zadam/trilium/issues/3181
 | 
				
			||||||
        const basicAuthStr = utils.fromBase64(auth.substring(6)).toString("utf-8");
 | 
					        const basicAuthStr = fromBase64(auth.substring(6)).toString("utf-8");
 | 
				
			||||||
        const basicAuthChunks = basicAuthStr.split(":");
 | 
					        const basicAuthChunks = basicAuthStr.split(":");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (basicAuthChunks.length !== 2) {
 | 
					        if (basicAuthChunks.length !== 2) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { getContentDisposition, stripTags } from "../utils.js";
 | 
				
			||||||
import becca from "../../becca/becca.js";
 | 
					import becca from "../../becca/becca.js";
 | 
				
			||||||
import TaskContext from "../task_context.js";
 | 
					import TaskContext from "../task_context.js";
 | 
				
			||||||
import BBranch from "../../becca/entities/bbranch.js";
 | 
					import BBranch from "../../becca/entities/bbranch.js";
 | 
				
			||||||
@ -58,7 +58,7 @@ function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const filename = `${branch.prefix ? (`${branch.prefix} - `) : ''}${note.title}.opml`;
 | 
					    const filename = `${branch.prefix ? (`${branch.prefix} - `) : ''}${note.title}.opml`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
 | 
					    res.setHeader('Content-Disposition', getContentDisposition(filename));
 | 
				
			||||||
    res.setHeader('Content-Type', 'text/x-opml');
 | 
					    res.setHeader('Content-Type', 'text/x-opml');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.write(`<?xml version="1.0" encoding="UTF-8"?>
 | 
					    res.write(`<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
@ -83,7 +83,7 @@ function prepareText(text: string) {
 | 
				
			|||||||
    const newLines = text.replace(/(<p[^>]*>|<br\s*\/?>)/g, '\n')
 | 
					    const newLines = text.replace(/(<p[^>]*>|<br\s*\/?>)/g, '\n')
 | 
				
			||||||
        .replace(/ /g, ' '); // nbsp isn't in XML standard (only HTML)
 | 
					        .replace(/ /g, ' '); // nbsp isn't in XML standard (only HTML)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const stripped = utils.stripTags(newLines);
 | 
					    const stripped = stripTags(newLines);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const escaped = escapeXmlAttribute(stripped);
 | 
					    const escaped = escapeXmlAttribute(stripped);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import mimeTypes from "mime-types";
 | 
					import mimeTypes from "mime-types";
 | 
				
			||||||
import html from "html";
 | 
					import html from "html";
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { getContentDisposition, escapeHtml } from "../utils.js";
 | 
				
			||||||
import mdService from "./md.js";
 | 
					import mdService from "./md.js";
 | 
				
			||||||
import becca from "../../becca/becca.js";
 | 
					import becca from "../../becca/becca.js";
 | 
				
			||||||
import TaskContext from "../task_context.js";
 | 
					import TaskContext from "../task_context.js";
 | 
				
			||||||
@ -61,7 +61,7 @@ function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "ht
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const fileName = `${note.title}.${extension}`;
 | 
					    const fileName = `${note.title}.${extension}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.setHeader('Content-Disposition', utils.getContentDisposition(fileName));
 | 
					    res.setHeader('Content-Disposition', getContentDisposition(fileName));
 | 
				
			||||||
    res.setHeader('Content-Type', `${mime}; charset=UTF-8`);
 | 
					    res.setHeader('Content-Type', `${mime}; charset=UTF-8`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.send(payload);
 | 
					    res.send(payload);
 | 
				
			||||||
@ -119,7 +119,7 @@ function inlineAttachments(content: string) {
 | 
				
			|||||||
        const base64Content = attachmentContent.toString('base64');
 | 
					        const base64Content = attachmentContent.toString('base64');
 | 
				
			||||||
        const hrefValue = `data:${attachment.mime};base64,${base64Content}`;
 | 
					        const hrefValue = `data:${attachment.mime};base64,${base64Content}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return `href="${hrefValue}" download="${utils.escapeHtml(attachment.title)}"`;
 | 
					        return `href="${hrefValue}" download="${escapeHtml(attachment.title)}"`;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return content;
 | 
					    return content;
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import path from "path";
 | 
				
			|||||||
import mimeTypes from "mime-types";
 | 
					import mimeTypes from "mime-types";
 | 
				
			||||||
import mdService from "./md.js";
 | 
					import mdService from "./md.js";
 | 
				
			||||||
import packageInfo from "../../../package.json" with { type: "json" };
 | 
					import packageInfo from "../../../package.json" with { type: "json" };
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { getContentDisposition, escapeHtml } from "../utils.js";
 | 
				
			||||||
import protectedSessionService from "../protected_session.js";
 | 
					import protectedSessionService from "../protected_session.js";
 | 
				
			||||||
import sanitize from "sanitize-filename";
 | 
					import sanitize from "sanitize-filename";
 | 
				
			||||||
import fs from "fs";
 | 
					import fs from "fs";
 | 
				
			||||||
@ -311,7 +311,7 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h
 | 
				
			|||||||
                if (!noteMeta?.notePath?.length) { throw new Error("Missing note path."); }
 | 
					                if (!noteMeta?.notePath?.length) { throw new Error("Missing note path."); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`;
 | 
					                const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`;
 | 
				
			||||||
                const htmlTitle = utils.escapeHtml(title);
 | 
					                const htmlTitle = escapeHtml(title);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
 | 
					                // <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
 | 
				
			||||||
                content = `<html>
 | 
					                content = `<html>
 | 
				
			||||||
@ -411,7 +411,7 @@ ${markdownContent}`;
 | 
				
			|||||||
        function saveNavigationInner(meta: NoteMeta) {
 | 
					        function saveNavigationInner(meta: NoteMeta) {
 | 
				
			||||||
            let html = '<li>';
 | 
					            let html = '<li>';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const escapedTitle = utils.escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`);
 | 
					            const escapedTitle = escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (meta.dataFileName && meta.noteId) {
 | 
					            if (meta.dataFileName && meta.noteId) {
 | 
				
			||||||
                const targetUrl = getNoteTargetUrl(meta.noteId, rootMeta);
 | 
					                const targetUrl = getNoteTargetUrl(meta.noteId, rootMeta);
 | 
				
			||||||
@ -568,7 +568,7 @@ ${markdownContent}`;
 | 
				
			|||||||
    const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`;
 | 
					    const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (setHeaders && "setHeader" in res) {
 | 
					    if (setHeaders && "setHeader" in res) {
 | 
				
			||||||
        res.setHeader('Content-Disposition', utils.getContentDisposition(zipFileName));
 | 
					        res.setHeader('Content-Disposition', getContentDisposition(zipFileName));
 | 
				
			||||||
        res.setHeader('Content-Type', 'application/zip');
 | 
					        res.setHeader('Content-Type', 'application/zip');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import config from "./config.js";
 | 
					import config from "./config.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron } from "./utils.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getHost() {
 | 
					function getHost() {
 | 
				
			||||||
    const envHost = process.env.TRILIUM_HOST;
 | 
					    const envHost = process.env.TRILIUM_HOST;
 | 
				
			||||||
    if (envHost && !utils.isElectron) {
 | 
					    if (envHost && !isElectron) {
 | 
				
			||||||
        return envHost;
 | 
					        return envHost;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import sax from "sax";
 | 
				
			|||||||
import stream from "stream";
 | 
					import stream from "stream";
 | 
				
			||||||
import { Throttle } from 'stream-throttle';
 | 
					import { Throttle } from 'stream-throttle';
 | 
				
			||||||
import log from "../log.js";
 | 
					import log from "../log.js";
 | 
				
			||||||
import utils from "../utils.js";
 | 
					import { md5, escapeHtml, fromBase64 } from "../utils.js";
 | 
				
			||||||
import sql from "../sql.js";
 | 
					import sql from "../sql.js";
 | 
				
			||||||
import noteService from "../notes.js";
 | 
					import noteService from "../notes.js";
 | 
				
			||||||
import imageService from "../image.js";
 | 
					import imageService from "../image.js";
 | 
				
			||||||
@ -291,10 +291,10 @@ function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Pr
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (typeof resource.content === "string") {
 | 
					            if (typeof resource.content === "string") {
 | 
				
			||||||
                resource.content = utils.fromBase64(resource.content);
 | 
					                resource.content = fromBase64(resource.content);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const hash = utils.md5(resource.content);
 | 
					            const hash = md5(resource.content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // skip all checked/unchecked checkboxes from OneNote
 | 
					            // skip all checked/unchecked checkboxes from OneNote
 | 
				
			||||||
            if (['74de5d3d1286f01bac98d32a09f601d9',
 | 
					            if (['74de5d3d1286f01bac98d32a09f601d9',
 | 
				
			||||||
@ -332,7 +332,7 @@ function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Pr
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                taskContext.increaseProgressCount();
 | 
					                taskContext.increaseProgressCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
 | 
					                const resourceLink = `<a href="#root/${resourceNote.noteId}">${escapeHtml(resource.title)}</a>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                content = (content || "").replace(mediaRegex, resourceLink);
 | 
					                content = (content || "").replace(mediaRegex, resourceLink);
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import imageService from "../../services/image.js";
 | 
				
			|||||||
import protectedSessionService from "../protected_session.js";
 | 
					import protectedSessionService from "../protected_session.js";
 | 
				
			||||||
import markdownService from "./markdown.js";
 | 
					import markdownService from "./markdown.js";
 | 
				
			||||||
import mimeService from "./mime.js";
 | 
					import mimeService from "./mime.js";
 | 
				
			||||||
import utils from "../../services/utils.js";
 | 
					import { getNoteTitle } from "../../services/utils.js";
 | 
				
			||||||
import importUtils from "./utils.js";
 | 
					import importUtils from "./utils.js";
 | 
				
			||||||
import htmlSanitizer from "../html_sanitizer.js";
 | 
					import htmlSanitizer from "../html_sanitizer.js";
 | 
				
			||||||
import { File } from "./common.js";
 | 
					import { File } from "./common.js";
 | 
				
			||||||
@ -68,7 +68,7 @@ function importFile(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
					function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
				
			||||||
    const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
					    const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
				
			||||||
    const content = file.buffer.toString("utf-8");
 | 
					    const content = file.buffer.toString("utf-8");
 | 
				
			||||||
    const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
 | 
					    const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
 | 
				
			||||||
    const mime = mimeService.normalizeMimeType(detectedMime);
 | 
					    const mime = mimeService.normalizeMimeType(detectedMime);
 | 
				
			||||||
@ -88,7 +88,7 @@ function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
					function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
				
			||||||
    const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
					    const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
				
			||||||
    const plainTextContent = file.buffer.toString("utf-8");
 | 
					    const plainTextContent = file.buffer.toString("utf-8");
 | 
				
			||||||
    const htmlContent = convertTextToHtml(plainTextContent);
 | 
					    const htmlContent = convertTextToHtml(plainTextContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -125,7 +125,7 @@ function convertTextToHtml(text: string) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
					function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
				
			||||||
    const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
					    const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const markdownContent = file.buffer.toString("utf-8");
 | 
					    const markdownContent = file.buffer.toString("utf-8");
 | 
				
			||||||
    let htmlContent = markdownService.renderToHtml(markdownContent, title);
 | 
					    let htmlContent = markdownService.renderToHtml(markdownContent, title);
 | 
				
			||||||
@ -154,7 +154,7 @@ function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) {
 | 
				
			|||||||
    // Try to get title from HTML first, fall back to filename
 | 
					    // Try to get title from HTML first, fall back to filename
 | 
				
			||||||
    // We do this before sanitization since that turns all <h1>s into <h2>
 | 
					    // We do this before sanitization since that turns all <h1>s into <h2>
 | 
				
			||||||
    const htmlTitle = importUtils.extractHtmlTitle(content);
 | 
					    const htmlTitle = importUtils.extractHtmlTitle(content);
 | 
				
			||||||
    const title = htmlTitle || utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
					    const title = htmlTitle || getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    content = importUtils.handleH1(content, title);
 | 
					    content = importUtils.handleH1(content, title);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BAttribute from "../../becca/entities/battribute.js";
 | 
					import BAttribute from "../../becca/entities/battribute.js";
 | 
				
			||||||
import utils from "../../services/utils.js";
 | 
					import { removeTextFileExtension, newEntityId, getNoteTitle } from "../../services/utils.js";
 | 
				
			||||||
import log from "../../services/log.js";
 | 
					import log from "../../services/log.js";
 | 
				
			||||||
import noteService from "../../services/notes.js";
 | 
					import noteService from "../../services/notes.js";
 | 
				
			||||||
import attributeService from "../../services/attributes.js";
 | 
					import attributeService from "../../services/attributes.js";
 | 
				
			||||||
@ -51,7 +51,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!noteIdMap[origNoteId]) {
 | 
					        if (!noteIdMap[origNoteId]) {
 | 
				
			||||||
            noteIdMap[origNoteId] = utils.newEntityId();
 | 
					            noteIdMap[origNoteId] = newEntityId();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return noteIdMap[origNoteId];
 | 
					        return noteIdMap[origNoteId];
 | 
				
			||||||
@ -64,7 +64,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!attachmentIdMap[origAttachmentId]) {
 | 
					        if (!attachmentIdMap[origAttachmentId]) {
 | 
				
			||||||
            attachmentIdMap[origAttachmentId] = utils.newEntityId();
 | 
					            attachmentIdMap[origAttachmentId] = newEntityId();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return attachmentIdMap[origAttachmentId];
 | 
					        return attachmentIdMap[origAttachmentId];
 | 
				
			||||||
@ -152,13 +152,13 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // in case we lack metadata, we treat e.g. "Programming.html" and "Programming" as the same note
 | 
					        // in case we lack metadata, we treat e.g. "Programming.html" and "Programming" as the same note
 | 
				
			||||||
        // (one data file, the other directory for children)
 | 
					        // (one data file, the other directory for children)
 | 
				
			||||||
        const filePathNoExt = utils.removeTextFileExtension(filePath);
 | 
					        const filePathNoExt = removeTextFileExtension(filePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (filePathNoExt in createdPaths) {
 | 
					        if (filePathNoExt in createdPaths) {
 | 
				
			||||||
            return createdPaths[filePathNoExt];
 | 
					            return createdPaths[filePathNoExt];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const noteId = utils.newEntityId();
 | 
					        const noteId = newEntityId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        createdPaths[filePathNoExt] = noteId;
 | 
					        createdPaths[filePathNoExt] = noteId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -225,7 +225,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const noteTitle = utils.getNoteTitle(filePath, !!taskContext.data?.replaceUnderscoresWithSpaces, noteMeta);
 | 
					        const noteTitle = getNoteTitle(filePath, !!taskContext.data?.replaceUnderscoresWithSpaces, noteMeta);
 | 
				
			||||||
        const parentNoteId = getParentNoteId(filePath, parentNoteMeta);
 | 
					        const parentNoteId = getParentNoteId(filePath, parentNoteMeta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!parentNoteId) {
 | 
					        if (!parentNoteId) {
 | 
				
			||||||
@ -466,7 +466,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
 | 
				
			|||||||
            content = content.toString("utf-8");
 | 
					            content = content.toString("utf-8");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const noteTitle = utils.getNoteTitle(filePath, taskContext.data?.replaceUnderscoresWithSpaces || false, noteMeta);
 | 
					        const noteTitle = getNoteTitle(filePath, taskContext.data?.replaceUnderscoresWithSpaces || false, noteMeta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        content = processNoteContent(noteMeta, type, mime, content, noteTitle || "", filePath);
 | 
					        content = processNoteContent(noteMeta, type, mime, content, noteTitle || "", filePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import utils from "./utils.js";
 | 
					import { randomString } from "./utils.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const instanceId = utils.randomString(12);
 | 
					const instanceId = randomString(12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default instanceId;
 | 
					export default instanceId;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import backupService from "./backup.js";
 | 
				
			|||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import fs from "fs-extra";
 | 
					import fs from "fs-extra";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { crash } from "./utils.js";
 | 
				
			||||||
import resourceDir from "./resource_dir.js";
 | 
					import resourceDir from "./resource_dir.js";
 | 
				
			||||||
import appInfo from "./app_info.js";
 | 
					import appInfo from "./app_info.js";
 | 
				
			||||||
import cls from "./cls.js";
 | 
					import cls from "./cls.js";
 | 
				
			||||||
@ -20,7 +20,7 @@ async function migrate() {
 | 
				
			|||||||
    if (currentDbVersion < 214) {
 | 
					    if (currentDbVersion < 214) {
 | 
				
			||||||
        log.error("Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version.");
 | 
					        log.error("Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await utils.crash();
 | 
					        await crash();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,7 +83,7 @@ async function migrate() {
 | 
				
			|||||||
                log.error(`error during migration to version ${mig.dbVersion}: ${e.stack}`);
 | 
					                log.error(`error during migration to version ${mig.dbVersion}: ${e.stack}`);
 | 
				
			||||||
                log.error("migration failed, crashing hard"); // this is not very user-friendly :-/
 | 
					                log.error("migration failed, crashing hard"); // this is not very user-friendly :-/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                utils.crash();
 | 
					                crash();
 | 
				
			||||||
                break; // crash() is sometimes async
 | 
					                break; // crash() is sometimes async
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -135,7 +135,7 @@ async function migrateIfNecessary() {
 | 
				
			|||||||
    if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== 'true') {
 | 
					    if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== 'true') {
 | 
				
			||||||
        log.error(`Current DB version ${currentDbVersion} is newer than the current DB version ${appInfo.dbVersion}, which means that it was created by a newer and incompatible version of Trilium. Upgrade to the latest version of Trilium to resolve this issue.`);
 | 
					        log.error(`Current DB version ${currentDbVersion} is newer than the current DB version ${appInfo.dbVersion}, which means that it was created by a newer and incompatible version of Trilium. Upgrade to the latest version of Trilium to resolve this issue.`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await utils.crash();
 | 
					        await crash();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!isDbUpToDate()) {
 | 
					    if (!isDbUpToDate()) {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import eventService from "./events.js";
 | 
				
			|||||||
import cls from "../services/cls.js";
 | 
					import cls from "../services/cls.js";
 | 
				
			||||||
import protectedSessionService from "../services/protected_session.js";
 | 
					import protectedSessionService from "../services/protected_session.js";
 | 
				
			||||||
import log from "../services/log.js";
 | 
					import log from "../services/log.js";
 | 
				
			||||||
import utils from "../services/utils.js";
 | 
					import { newEntityId, isString, unescapeHtml, quoteRegex, toMap } from "../services/utils.js";
 | 
				
			||||||
import revisionService from "./revisions.js";
 | 
					import revisionService from "./revisions.js";
 | 
				
			||||||
import request from "./request.js";
 | 
					import request from "./request.js";
 | 
				
			||||||
import path from "path";
 | 
					import path from "path";
 | 
				
			||||||
@ -465,7 +465,7 @@ function findRelationMapLinks(content: string, foundLinks: FoundLink[]) {
 | 
				
			|||||||
const imageUrlToAttachmentIdMapping: Record<string, string> = {};
 | 
					const imageUrlToAttachmentIdMapping: Record<string, string> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function downloadImage(noteId: string, imageUrl: string) {
 | 
					async function downloadImage(noteId: string, imageUrl: string) {
 | 
				
			||||||
    const unescapedUrl = utils.unescapeHtml(imageUrl);
 | 
					    const unescapedUrl = unescapeHtml(imageUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        let imageBuffer: Buffer;
 | 
					        let imageBuffer: Buffer;
 | 
				
			||||||
@ -508,7 +508,7 @@ async function downloadImage(noteId: string, imageUrl: string) {
 | 
				
			|||||||
const downloadImagePromises: Record<string, Promise<void>> = {};
 | 
					const downloadImagePromises: Record<string, Promise<void>> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function replaceUrl(content: string, url: string, attachment: Attachment) {
 | 
					function replaceUrl(content: string, url: string, attachment: Attachment) {
 | 
				
			||||||
    const quotedUrl = utils.quoteRegex(url);
 | 
					    const quotedUrl = quoteRegex(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}"`);
 | 
					    return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}"`);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -744,7 +744,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
 | 
				
			|||||||
    note.setContent(newContent, { forceFrontendReload });
 | 
					    note.setContent(newContent, { forceFrontendReload });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (attachments?.length > 0) {
 | 
					    if (attachments?.length > 0) {
 | 
				
			||||||
        const existingAttachmentsByTitle = utils.toMap(note.getAttachments({includeContentLength: false}), 'title');
 | 
					        const existingAttachmentsByTitle = toMap(note.getAttachments({includeContentLength: false}), 'title');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const {attachmentId, role, mime, title, position, content} of attachments) {
 | 
					        for (const {attachmentId, role, mime, title, position, content} of attachments) {
 | 
				
			||||||
            if (attachmentId || !(title in existingAttachmentsByTitle)) {
 | 
					            if (attachmentId || !(title in existingAttachmentsByTitle)) {
 | 
				
			||||||
@ -886,7 +886,7 @@ async function asyncPostProcessContent(note: BNote, content: string | Buffer) {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (note.hasStringContent() && !utils.isString(content)) {
 | 
					    if (note.hasStringContent() && !isString(content)) {
 | 
				
			||||||
        content = content.toString();
 | 
					        content = content.toString();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1035,7 +1035,7 @@ function getNoteIdMapping(origNote: BNote) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // pregenerate new noteIds since we'll need to fix relation references even for not yet created notes
 | 
					    // pregenerate new noteIds since we'll need to fix relation references even for not yet created notes
 | 
				
			||||||
    for (const origNoteId of origNote.getDescendantNoteIds()) {
 | 
					    for (const origNoteId of origNote.getDescendantNoteIds()) {
 | 
				
			||||||
        noteIdMapping[origNoteId] = utils.newEntityId();
 | 
					        noteIdMapping[origNoteId] = newEntityId();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return noteIdMapping;
 | 
					    return noteIdMapping;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,15 @@
 | 
				
			|||||||
import optionService from "./options.js";
 | 
					import optionService from "./options.js";
 | 
				
			||||||
import type { OptionMap } from "./options.js";
 | 
					import type { OptionMap } from "./options.js";
 | 
				
			||||||
import appInfo from "./app_info.js";
 | 
					import appInfo from "./app_info.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { randomSecureToken } from "./utils.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import dateUtils from "./date_utils.js";
 | 
					import dateUtils from "./date_utils.js";
 | 
				
			||||||
import keyboardActions from "./keyboard_actions.js";
 | 
					import keyboardActions from "./keyboard_actions.js";
 | 
				
			||||||
import { KeyboardShortcutWithRequiredActionName } from './keyboard_actions_interface.js';
 | 
					import { KeyboardShortcutWithRequiredActionName } from './keyboard_actions_interface.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function initDocumentOptions() {
 | 
					function initDocumentOptions() {
 | 
				
			||||||
    optionService.createOption('documentId', utils.randomSecureToken(16), false);
 | 
					    optionService.createOption('documentId', randomSecureToken(16), false);
 | 
				
			||||||
    optionService.createOption('documentSecret', utils.randomSecureToken(16), false);
 | 
					    optionService.createOption('documentSecret', randomSecureToken(16), false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import config from "./config.js";
 | 
					import config from "./config.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron } from "./utils.js";
 | 
				
			||||||
import env from "./env.js";
 | 
					import env from "./env.js";
 | 
				
			||||||
import dataDir from "./data_dir.js";
 | 
					import dataDir from "./data_dir.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,7 +18,7 @@ let port: number;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
if (process.env.TRILIUM_PORT) {
 | 
					if (process.env.TRILIUM_PORT) {
 | 
				
			||||||
    port = parseAndValidate(process.env.TRILIUM_PORT, "environment variable TRILIUM_PORT");
 | 
					    port = parseAndValidate(process.env.TRILIUM_PORT, "environment variable TRILIUM_PORT");
 | 
				
			||||||
} else if (utils.isElectron()) {
 | 
					} else if (isElectron()) {
 | 
				
			||||||
    port = env.isDev() ? 37740 : 37840;
 | 
					    port = env.isDev() ? 37740 : 37840;
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    port = parseAndValidate(config['Network']['port'] || '3000', `Network.port in ${dataDir.CONFIG_INI_PATH}`);
 | 
					    port = parseAndValidate(config['Network']['port'] || '3000', `Network.port in ${dataDir.CONFIG_INI_PATH}`);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron } from "./utils.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import url from "url";
 | 
					import url from "url";
 | 
				
			||||||
import syncOptions from "./sync_options.js";
 | 
					import syncOptions from "./sync_options.js";
 | 
				
			||||||
@ -210,7 +210,7 @@ async function getProxyAgent(opts: ClientOpts) {
 | 
				
			|||||||
async function getClient(opts: ClientOpts): Promise<Client> {
 | 
					async function getClient(opts: ClientOpts): Promise<Client> {
 | 
				
			||||||
    // it's not clear how to explicitly configure proxy (as opposed to system proxy),
 | 
					    // it's not clear how to explicitly configure proxy (as opposed to system proxy),
 | 
				
			||||||
    // so in that case, we always use node's modules
 | 
					    // so in that case, we always use node's modules
 | 
				
			||||||
    if (utils.isElectron() && !opts.proxy) {
 | 
					    if (isElectron() && !opts.proxy) {
 | 
				
			||||||
        return (await import('electron')).net as Client;
 | 
					        return (await import('electron')).net as Client;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const {protocol} = url.parse(opts.url);
 | 
					        const {protocol} = url.parse(opts.url);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import utils from "./utils.js";
 | 
					import { toObject } from "./utils.js";
 | 
				
			||||||
import BackendScriptApi from "./backend_script_api.js";
 | 
					import BackendScriptApi from "./backend_script_api.js";
 | 
				
			||||||
import BNote from "../becca/entities/bnote.js";
 | 
					import BNote from "../becca/entities/bnote.js";
 | 
				
			||||||
import { ApiParams } from './backend_script_api_interface.js';
 | 
					import { ApiParams } from './backend_script_api_interface.js';
 | 
				
			||||||
@ -16,8 +16,8 @@ class ScriptContext {
 | 
				
			|||||||
    constructor(allNotes: BNote[], apiParams: ApiParams) {
 | 
					    constructor(allNotes: BNote[], apiParams: ApiParams) {
 | 
				
			||||||
        this.allNotes = allNotes;
 | 
					        this.allNotes = allNotes;
 | 
				
			||||||
        this.modules = {};
 | 
					        this.modules = {};
 | 
				
			||||||
        this.notes = utils.toObject(allNotes, note => [note.noteId, note]);
 | 
					        this.notes = toObject(allNotes, note => [note.noteId, note]);
 | 
				
			||||||
        this.apis = utils.toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]);
 | 
					        this.apis = toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    require(moduleNoteIds: string[]) {
 | 
					    require(moduleNoteIds: string[]) {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import log from "../../log.js";
 | 
				
			|||||||
import becca from "../../../becca/becca.js";
 | 
					import becca from "../../../becca/becca.js";
 | 
				
			||||||
import protectedSessionService from "../../protected_session.js";
 | 
					import protectedSessionService from "../../protected_session.js";
 | 
				
			||||||
import striptags from "striptags";
 | 
					import striptags from "striptags";
 | 
				
			||||||
import utils from "../../utils.js";
 | 
					import { normalize } from "../../utils.js";
 | 
				
			||||||
import sql from "../../sql.js";
 | 
					import sql from "../../sql.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -125,7 +125,7 @@ class NoteContentFulltextExp extends Expression {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    preprocessContent(content: string | Buffer, type: string, mime: string) {
 | 
					    preprocessContent(content: string | Buffer, type: string, mime: string) {
 | 
				
			||||||
        content = utils.normalize(content.toString());
 | 
					        content = normalize(content.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (type === 'text' && mime === 'text/html') {
 | 
					        if (type === 'text' && mime === 'text/html') {
 | 
				
			||||||
            if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
 | 
					            if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
 | 
				
			||||||
@ -183,7 +183,7 @@ class NoteContentFulltextExp extends Expression {
 | 
				
			|||||||
            const topicsString = topicsArray.join(", ");
 | 
					            const topicsString = topicsArray.join(", ");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            content = utils.normalize(topicsString.toString());
 | 
					            content = normalize(topicsString.toString());
 | 
				
			||||||
          } 
 | 
					          } 
 | 
				
			||||||
        else if (type === 'canvas' && mime === 'application/json') {
 | 
					        else if (type === 'canvas' && mime === 'application/json') {
 | 
				
			||||||
            interface Element {
 | 
					            interface Element {
 | 
				
			||||||
@ -199,7 +199,7 @@ class NoteContentFulltextExp extends Expression {
 | 
				
			|||||||
                .filter((element: Element) => element.type === 'text' && element.text) // Filter for 'text' type elements with a 'text' property
 | 
					                .filter((element: Element) => element.type === 'text' && element.text) // Filter for 'text' type elements with a 'text' property
 | 
				
			||||||
                .map((element: Element) => element.text!); // Use `!` to assert `text` is defined after filtering
 | 
					                .map((element: Element) => element.text!); // Use `!` to assert `text` is defined after filtering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            content =utils.normalize(texts.toString())
 | 
					            content = normalize(texts.toString())
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import SearchContext from "../search_context.js";
 | 
				
			|||||||
import Expression from "./expression.js";
 | 
					import Expression from "./expression.js";
 | 
				
			||||||
import NoteSet from "../note_set.js";
 | 
					import NoteSet from "../note_set.js";
 | 
				
			||||||
import becca from "../../../becca/becca.js";
 | 
					import becca from "../../../becca/becca.js";
 | 
				
			||||||
import utils from "../../utils.js";
 | 
					import { normalize } from "../../utils.js";
 | 
				
			||||||
import beccaService from "../../../becca/becca_service.js";
 | 
					import beccaService from "../../../becca/becca_service.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoteFlatTextExp extends Expression {
 | 
					class NoteFlatTextExp extends Expression {
 | 
				
			||||||
@ -61,8 +61,8 @@ class NoteFlatTextExp extends Expression {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const attribute of note.getOwnedAttributes()) {
 | 
					            for (const attribute of note.getOwnedAttributes()) {
 | 
				
			||||||
                const normalizedName = utils.normalize(attribute.name);
 | 
					                const normalizedName = normalize(attribute.name);
 | 
				
			||||||
                const normalizedValue = utils.normalize(attribute.value);
 | 
					                const normalizedValue = normalize(attribute.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const token of remainingTokens) {
 | 
					                for (const token of remainingTokens) {
 | 
				
			||||||
                    if (normalizedName.includes(token) || normalizedValue.includes(token)) {
 | 
					                    if (normalizedName.includes(token) || normalizedValue.includes(token)) {
 | 
				
			||||||
@ -72,7 +72,7 @@ class NoteFlatTextExp extends Expression {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const parentNote of note.parents) {
 | 
					            for (const parentNote of note.parents) {
 | 
				
			||||||
                const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
 | 
					                const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
 | 
				
			||||||
                const foundTokens = foundAttrTokens.slice();
 | 
					                const foundTokens = foundAttrTokens.slice();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const token of remainingTokens) {
 | 
					                for (const token of remainingTokens) {
 | 
				
			||||||
@ -109,8 +109,8 @@ class NoteFlatTextExp extends Expression {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const attribute of note.ownedAttributes) {
 | 
					                for (const attribute of note.ownedAttributes) {
 | 
				
			||||||
                    if (utils.normalize(attribute.name).includes(token)
 | 
					                    if (normalize(attribute.name).includes(token)
 | 
				
			||||||
                        || utils.normalize(attribute.value).includes(token)) {
 | 
					                        || normalize(attribute.value).includes(token)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        foundAttrTokens.push(token);
 | 
					                        foundAttrTokens.push(token);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -118,7 +118,7 @@ class NoteFlatTextExp extends Expression {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const parentNote of note.parents) {
 | 
					            for (const parentNote of note.parents) {
 | 
				
			||||||
                const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
 | 
					                const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
 | 
				
			||||||
                const foundTokens = foundAttrTokens.slice();
 | 
					                const foundTokens = foundAttrTokens.slice();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const token of this.tokens) {
 | 
					                for (const token of this.tokens) {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ import OrderByAndLimitExp from "../expressions/order_by_and_limit.js";
 | 
				
			|||||||
import AncestorExp from "../expressions/ancestor.js";
 | 
					import AncestorExp from "../expressions/ancestor.js";
 | 
				
			||||||
import buildComparator from "./build_comparator.js";
 | 
					import buildComparator from "./build_comparator.js";
 | 
				
			||||||
import ValueExtractor from "../value_extractor.js";
 | 
					import ValueExtractor from "../value_extractor.js";
 | 
				
			||||||
import utils from "../../utils.js";
 | 
					import { removeDiacritic } from "../../utils.js";
 | 
				
			||||||
import TrueExp from "../expressions/true.js";
 | 
					import TrueExp from "../expressions/true.js";
 | 
				
			||||||
import IsHiddenExp from "../expressions/is_hidden.js";
 | 
					import IsHiddenExp from "../expressions/is_hidden.js";
 | 
				
			||||||
import SearchContext from "../search_context.js";
 | 
					import SearchContext from "../search_context.js";
 | 
				
			||||||
@ -25,7 +25,7 @@ import { TokenData, TokenStructure } from "./types.js";
 | 
				
			|||||||
import Expression from "../expressions/expression.js";
 | 
					import Expression from "../expressions/expression.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getFulltext(_tokens: TokenData[], searchContext: SearchContext) {
 | 
					function getFulltext(_tokens: TokenData[], searchContext: SearchContext) {
 | 
				
			||||||
    const tokens: string[] = _tokens.map(t => utils.removeDiacritic(t.token));
 | 
					    const tokens: string[] = _tokens.map(t => removeDiacritic(t.token));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    searchContext.highlightedTokens.push(...tokens);
 | 
					    searchContext.highlightedTokens.push(...tokens);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import SearchResult from "../search_result.js";
 | 
				
			|||||||
import SearchContext from "../search_context.js";
 | 
					import SearchContext from "../search_context.js";
 | 
				
			||||||
import becca from "../../../becca/becca.js";
 | 
					import becca from "../../../becca/becca.js";
 | 
				
			||||||
import beccaService from "../../../becca/becca_service.js";
 | 
					import beccaService from "../../../becca/becca_service.js";
 | 
				
			||||||
import utils from "../../utils.js";
 | 
					import { normalize, escapeHtml, escapeRegExp } from "../../utils.js";
 | 
				
			||||||
import log from "../../log.js";
 | 
					import log from "../../log.js";
 | 
				
			||||||
import hoistedNoteService from "../../hoisted_note.js";
 | 
					import hoistedNoteService from "../../hoisted_note.js";
 | 
				
			||||||
import BNote from "../../../becca/entities/bnote.js";
 | 
					import BNote from "../../../becca/entities/bnote.js";
 | 
				
			||||||
@ -403,8 +403,8 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
				
			|||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (highlightedTokens.find(token => utils.normalize(attr.name).includes(token)
 | 
					            if (highlightedTokens.find(token => normalize(attr.name).includes(token)
 | 
				
			||||||
                || utils.normalize(attr.value).includes(token))) {
 | 
					                || normalize(attr.value).includes(token))) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                result.highlightedNotePathTitle += ` "${formatAttribute(attr)}'`;
 | 
					                result.highlightedNotePathTitle += ` "${formatAttribute(attr)}'`;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -423,7 +423,7 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        for (const result of searchResults) {
 | 
					        for (const result of searchResults) {
 | 
				
			||||||
            // Reset token
 | 
					            // Reset token
 | 
				
			||||||
            const tokenRegex = new RegExp(utils.escapeRegExp(token), "gi");
 | 
					            const tokenRegex = new RegExp(escapeRegExp(token), "gi");
 | 
				
			||||||
            let match;
 | 
					            let match;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Find all matches
 | 
					            // Find all matches
 | 
				
			||||||
@ -449,15 +449,15 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function formatAttribute(attr: BAttribute) {
 | 
					function formatAttribute(attr: BAttribute) {
 | 
				
			||||||
    if (attr.type === 'relation') {
 | 
					    if (attr.type === 'relation') {
 | 
				
			||||||
        return `~${utils.escapeHtml(attr.name)}=…`;
 | 
					        return `~${escapeHtml(attr.name)}=…`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (attr.type === 'label') {
 | 
					    else if (attr.type === 'label') {
 | 
				
			||||||
        let label = `#${utils.escapeHtml(attr.name)}`;
 | 
					        let label = `#${escapeHtml(attr.name)}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (attr.value) {
 | 
					        if (attr.value) {
 | 
				
			||||||
            const val = /[^\w-]/.test(attr.value) ? `"${attr.value}"` : attr.value;
 | 
					            const val = /[^\w-]/.test(attr.value) ? `"${attr.value}"` : attr.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            label += `=${utils.escapeHtml(val)}`;
 | 
					            label += `=${escapeHtml(val)}`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return label;
 | 
					        return label;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
import fs from "fs";
 | 
					import fs from "fs";
 | 
				
			||||||
import dataDir from "./data_dir.js";
 | 
					import dataDir from "./data_dir.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import utils from "./utils.js"
 | 
					import { randomSecureToken } from "./utils.js"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sessionSecretPath = `${dataDir.TRILIUM_DATA_DIR}/session_secret.txt`;
 | 
					const sessionSecretPath = `${dataDir.TRILIUM_DATA_DIR}/session_secret.txt`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,7 +12,7 @@ let sessionSecret: string;
 | 
				
			|||||||
const ENCODING = "ascii";
 | 
					const ENCODING = "ascii";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (!fs.existsSync(sessionSecretPath)) {
 | 
					if (!fs.existsSync(sessionSecretPath)) {
 | 
				
			||||||
    sessionSecret = utils.randomSecureToken(64).slice(0, 64);
 | 
					    sessionSecret = randomSecureToken(64).slice(0, 64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log.info("Generated session secret");
 | 
					    log.info("Generated session secret");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import optionService from "./options.js";
 | 
				
			|||||||
import syncOptions from "./sync_options.js";
 | 
					import syncOptions from "./sync_options.js";
 | 
				
			||||||
import request from "./request.js";
 | 
					import request from "./request.js";
 | 
				
			||||||
import appInfo from "./app_info.js";
 | 
					import appInfo from "./app_info.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { timeLimit } from "./utils.js";
 | 
				
			||||||
import becca from "../becca/becca.js";
 | 
					import becca from "../becca/becca.js";
 | 
				
			||||||
import { SetupStatusResponse, SetupSyncSeedResponse } from './api-interface.js';
 | 
					import { SetupStatusResponse, SetupSyncSeedResponse } from './api-interface.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,7 +47,7 @@ async function sendSeedToSyncServer() {
 | 
				
			|||||||
async function requestToSyncServer<T>(method: string, path: string, body?: string | {}): Promise<T> {
 | 
					async function requestToSyncServer<T>(method: string, path: string, body?: string | {}): Promise<T> {
 | 
				
			||||||
    const timeout = syncOptions.getSyncTimeout();
 | 
					    const timeout = syncOptions.getSyncTimeout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await utils.timeLimit(request.exec({
 | 
					    return await timeLimit(request.exec({
 | 
				
			||||||
        method,
 | 
					        method,
 | 
				
			||||||
        url: syncOptions.getSyncServerHost() + path,
 | 
					        url: syncOptions.getSyncServerHost() + path,
 | 
				
			||||||
        body,
 | 
					        body,
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import log from "./log.js";
 | 
				
			|||||||
import fs from "fs";
 | 
					import fs from "fs";
 | 
				
			||||||
import resourceDir from "./resource_dir.js";
 | 
					import resourceDir from "./resource_dir.js";
 | 
				
			||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron, deferred } from "./utils.js";
 | 
				
			||||||
import optionService from "./options.js";
 | 
					import optionService from "./options.js";
 | 
				
			||||||
import port from "./port.js";
 | 
					import port from "./port.js";
 | 
				
			||||||
import BOption from "../becca/entities/boption.js";
 | 
					import BOption from "../becca/entities/boption.js";
 | 
				
			||||||
@ -18,7 +18,7 @@ import becca_loader from "../becca/becca_loader.js";
 | 
				
			|||||||
import password from "./encryption/password.js";
 | 
					import password from "./encryption/password.js";
 | 
				
			||||||
import backup from "./backup.js";
 | 
					import backup from "./backup.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dbReady = utils.deferred<void>();
 | 
					const dbReady = deferred<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function schemaExists() {
 | 
					function schemaExists() {
 | 
				
			||||||
    return !!sql.getValue(`SELECT name FROM sqlite_master
 | 
					    return !!sql.getValue(`SELECT name FROM sqlite_master
 | 
				
			||||||
@ -38,7 +38,7 @@ function isDbInitialized() {
 | 
				
			|||||||
async function initDbConnection() {
 | 
					async function initDbConnection() {
 | 
				
			||||||
    if (!isDbInitialized()) {
 | 
					    if (!isDbInitialized()) {
 | 
				
			||||||
        log.info(`DB not initialized, please visit setup page` +
 | 
					        log.info(`DB not initialized, please visit setup page` +
 | 
				
			||||||
            (utils.isElectron() ? '' : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`));
 | 
					            (isElectron() ? '' : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import optionService from "./options.js";
 | 
					import optionService from "./options.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { hmac, randomString, timeLimit } from "./utils.js";
 | 
				
			||||||
import instanceId from "./instance_id.js";
 | 
					import instanceId from "./instance_id.js";
 | 
				
			||||||
import dateUtils from "./date_utils.js";
 | 
					import dateUtils from "./date_utils.js";
 | 
				
			||||||
import syncUpdateService from "./sync_update.js";
 | 
					import syncUpdateService from "./sync_update.js";
 | 
				
			||||||
@ -121,7 +121,7 @@ async function doLogin(): Promise<SyncContext> {
 | 
				
			|||||||
    const timestamp = dateUtils.utcNowDateTime();
 | 
					    const timestamp = dateUtils.utcNowDateTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const documentSecret = optionService.getOption('documentSecret');
 | 
					    const documentSecret = optionService.getOption('documentSecret');
 | 
				
			||||||
    const hash = utils.hmac(documentSecret, timestamp);
 | 
					    const hash = hmac(documentSecret, timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const syncContext: SyncContext = { cookieJar: {} };
 | 
					    const syncContext: SyncContext = { cookieJar: {} };
 | 
				
			||||||
    const resp = await syncRequest<SyncResponse>(syncContext, 'POST', '/api/login/sync', {
 | 
					    const resp = await syncRequest<SyncResponse>(syncContext, 'POST', '/api/login/sync', {
 | 
				
			||||||
@ -156,7 +156,7 @@ async function doLogin(): Promise<SyncContext> {
 | 
				
			|||||||
async function pullChanges(syncContext: SyncContext) {
 | 
					async function pullChanges(syncContext: SyncContext) {
 | 
				
			||||||
    while (true) {
 | 
					    while (true) {
 | 
				
			||||||
        const lastSyncedPull = getLastSyncedPull();
 | 
					        const lastSyncedPull = getLastSyncedPull();
 | 
				
			||||||
        const logMarkerId = utils.randomString(10); // to easily pair sync events between client and server logs
 | 
					        const logMarkerId = randomString(10); // to easily pair sync events between client and server logs
 | 
				
			||||||
        const changesUri = `/api/sync/changed?instanceId=${instanceId}&lastEntityChangeId=${lastSyncedPull}&logMarkerId=${logMarkerId}`;
 | 
					        const changesUri = `/api/sync/changed?instanceId=${instanceId}&lastEntityChangeId=${lastSyncedPull}&logMarkerId=${logMarkerId}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const startDate = Date.now();
 | 
					        const startDate = Date.now();
 | 
				
			||||||
@ -234,7 +234,7 @@ async function pushChanges(syncContext: SyncContext) {
 | 
				
			|||||||
        const entityChangesRecords = getEntityChangeRecords(filteredEntityChanges);
 | 
					        const entityChangesRecords = getEntityChangeRecords(filteredEntityChanges);
 | 
				
			||||||
        const startDate = new Date();
 | 
					        const startDate = new Date();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const logMarkerId = utils.randomString(10); // to easily pair sync events between client and server logs
 | 
					        const logMarkerId = randomString(10); // to easily pair sync events between client and server logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await syncRequest(syncContext, 'PUT', `/api/sync/update?logMarkerId=${logMarkerId}`, {
 | 
					        await syncRequest(syncContext, 'PUT', `/api/sync/update?logMarkerId=${logMarkerId}`, {
 | 
				
			||||||
            entities: entityChangesRecords,
 | 
					            entities: entityChangesRecords,
 | 
				
			||||||
@ -310,7 +310,7 @@ async function syncRequest<T extends {}>(syncContext: SyncContext, method: strin
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let response;
 | 
					    let response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const requestId = utils.randomString(10);
 | 
					    const requestId = randomString(10);
 | 
				
			||||||
    const pageCount = Math.max(1, Math.ceil(body.length / PAGE_SIZE));
 | 
					    const pageCount = Math.max(1, Math.ceil(body.length / PAGE_SIZE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {
 | 
					    for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {
 | 
				
			||||||
@ -328,7 +328,7 @@ async function syncRequest<T extends {}>(syncContext: SyncContext, method: strin
 | 
				
			|||||||
            proxy: proxyToggle ? syncOptions.getSyncProxy() : null
 | 
					            proxy: proxyToggle ? syncOptions.getSyncProxy() : null
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = await utils.timeLimit(request.exec(opts), timeout) as T;
 | 
					        response = await timeLimit(request.exec(opts), timeout) as T;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return response;
 | 
					    return response;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import WebSocket from "ws";
 | 
					import WebSocket from "ws";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					import { isElectron, randomString } from "./utils.js";
 | 
				
			||||||
import log from "./log.js";
 | 
					import log from "./log.js";
 | 
				
			||||||
import sql from "./sql.js";
 | 
					import sql from "./sql.js";
 | 
				
			||||||
import cls from "./cls.js";
 | 
					import cls from "./cls.js";
 | 
				
			||||||
@ -18,7 +18,7 @@ if (env.isDev()) {
 | 
				
			|||||||
    const debounce = (await import("debounce")).default;
 | 
					    const debounce = (await import("debounce")).default;
 | 
				
			||||||
    const debouncedReloadFrontend = debounce(() => reloadFrontend("source code change"), 200);
 | 
					    const debouncedReloadFrontend = debounce(() => reloadFrontend("source code change"), 200);
 | 
				
			||||||
    chokidar
 | 
					    chokidar
 | 
				
			||||||
        .watch(utils.isElectron() ? 'dist/src/public' : 'src/public')
 | 
					        .watch(isElectron() ? 'dist/src/public' : 'src/public')
 | 
				
			||||||
        .on('add', debouncedReloadFrontend)
 | 
					        .on('add', debouncedReloadFrontend)
 | 
				
			||||||
        .on('change', debouncedReloadFrontend)
 | 
					        .on('change', debouncedReloadFrontend)
 | 
				
			||||||
        .on('unlink', debouncedReloadFrontend);
 | 
					        .on('unlink', debouncedReloadFrontend);
 | 
				
			||||||
@ -62,7 +62,7 @@ function init(httpServer: Server, sessionParser: SessionParser) {
 | 
				
			|||||||
    webSocketServer = new WebSocket.Server({
 | 
					    webSocketServer = new WebSocket.Server({
 | 
				
			||||||
        verifyClient: (info, done) => {
 | 
					        verifyClient: (info, done) => {
 | 
				
			||||||
            sessionParser(info.req, {}, () => {
 | 
					            sessionParser(info.req, {}, () => {
 | 
				
			||||||
                const allowed = utils.isElectron()
 | 
					                const allowed = isElectron()
 | 
				
			||||||
                    || (info.req as any).session.loggedIn
 | 
					                    || (info.req as any).session.loggedIn
 | 
				
			||||||
                    || (config.General && config.General.noAuthentication);
 | 
					                    || (config.General && config.General.noAuthentication);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -77,7 +77,7 @@ function init(httpServer: Server, sessionParser: SessionParser) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    webSocketServer.on('connection', (ws, req) => {
 | 
					    webSocketServer.on('connection', (ws, req) => {
 | 
				
			||||||
        (ws as any).id = utils.randomString(10);
 | 
					        (ws as any).id = randomString(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        console.log(`websocket client connected`);
 | 
					        console.log(`websocket client connected`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user