mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-09-18 01:01:42 +08:00
Merge pull request #872 from pano9000/refactor_utils-export
refactor(utils): use named exports for the utils functions
This commit is contained in:
commit
d07aa0990b
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import entityChangesService from "./entity_changes.js";
|
|||||||
import optionsService from "./options.js";
|
import optionsService from "./options.js";
|
||||||
import BBranch from "../becca/entities/bbranch.js";
|
import BBranch from "../becca/entities/bbranch.js";
|
||||||
import becca from "../becca/becca.js";
|
import becca from "../becca/becca.js";
|
||||||
import utils from "../services/utils.js";
|
import { hash as getHash, hashedBlobId, randomString } from "../services/utils.js";
|
||||||
import eraseService from "../services/erase.js";
|
import eraseService from "../services/erase.js";
|
||||||
import sanitizeAttributeName from "./sanitize_attribute_name.js";
|
import sanitizeAttributeName from "./sanitize_attribute_name.js";
|
||||||
import noteTypesService from "../services/note_types.js";
|
import noteTypesService from "../services/note_types.js";
|
||||||
@ -454,7 +454,7 @@ class ConsistencyChecks {
|
|||||||
logError(`Unable to recover note ${noteId} since it's content could not be retrieved (might be protected note).`);
|
logError(`Unable to recover note ${noteId} since it's content could not be retrieved (might be protected note).`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const blobId = utils.hashedBlobId(blankContent);
|
const blobId = hashedBlobId(blankContent);
|
||||||
const blobAlreadyExists = !!sql.getValue("SELECT 1 FROM blobs WHERE blobId = ?", [blobId]);
|
const blobAlreadyExists = !!sql.getValue("SELECT 1 FROM blobs WHERE blobId = ?", [blobId]);
|
||||||
|
|
||||||
if (!blobAlreadyExists) {
|
if (!blobAlreadyExists) {
|
||||||
@ -466,7 +466,7 @@ class ConsistencyChecks {
|
|||||||
dateModified: fakeDate
|
dateModified: fakeDate
|
||||||
});
|
});
|
||||||
|
|
||||||
const hash = utils.hash(utils.randomString(10));
|
const hash = getHash(randomString(10));
|
||||||
|
|
||||||
entityChangesService.putEntityChange({
|
entityChangesService.putEntityChange({
|
||||||
entityName: 'blobs',
|
entityName: 'blobs',
|
||||||
@ -689,7 +689,7 @@ class ConsistencyChecks {
|
|||||||
entityChangesService.putEntityChange({
|
entityChangesService.putEntityChange({
|
||||||
entityName,
|
entityName,
|
||||||
entityId,
|
entityId,
|
||||||
hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK
|
hash: randomString(10), // doesn't matter, will force sync, but that's OK
|
||||||
isErased: false,
|
isErased: false,
|
||||||
utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated,
|
utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated,
|
||||||
isSynced: entityName !== 'options' || entityRow.isSynced
|
isSynced: entityName !== 'options' || entityRow.isSynced
|
||||||
|
@ -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,12 +2,12 @@
|
|||||||
|
|
||||||
import optionService from "./options.js";
|
import optionService from "./options.js";
|
||||||
import log from "./log.js";
|
import log from "./log.js";
|
||||||
import utils from "./utils.js";
|
import { isElectron as getIsElectron } from "./utils.js";
|
||||||
import { KeyboardShortcut } from './keyboard_actions_interface.js';
|
import { KeyboardShortcut } from './keyboard_actions_interface.js';
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
|
|
||||||
const isMac = process.platform === "darwin";
|
const isMac = process.platform === "darwin";
|
||||||
const isElectron = utils.isElectron();
|
const isElectron = getIsElectron();
|
||||||
|
|
||||||
function getDefaultKeyboardActions() {
|
function getDefaultKeyboardActions() {
|
||||||
if (!t("keyboard_actions.note-navigation")) {
|
if (!t("keyboard_actions.note-navigation")) {
|
||||||
|
@ -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;
|
||||||
|
@ -13,23 +13,23 @@ import { dirname, join } from "path";
|
|||||||
|
|
||||||
const randtoken = generator({source: 'crypto'});
|
const randtoken = generator({source: 'crypto'});
|
||||||
|
|
||||||
function newEntityId() {
|
export function newEntityId() {
|
||||||
return randomString(12);
|
return randomString(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomString(length: number): string {
|
export function randomString(length: number): string {
|
||||||
return randtoken.generate(length);
|
return randtoken.generate(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomSecureToken(bytes = 32) {
|
export function randomSecureToken(bytes = 32) {
|
||||||
return crypto.randomBytes(bytes).toString('base64');
|
return crypto.randomBytes(bytes).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function md5(content: crypto.BinaryLike) {
|
export function md5(content: crypto.BinaryLike) {
|
||||||
return crypto.createHash('md5').update(content).digest('hex');
|
return crypto.createHash('md5').update(content).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashedBlobId(content: string | Buffer) {
|
export function hashedBlobId(content: string | Buffer) {
|
||||||
if (content === null || content === undefined) {
|
if (content === null || content === undefined) {
|
||||||
content = "";
|
content = "";
|
||||||
}
|
}
|
||||||
@ -46,47 +46,47 @@ function hashedBlobId(content: string | Buffer) {
|
|||||||
return kindaBase62Hash.substr(0, 20);
|
return kindaBase62Hash.substr(0, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toBase64(plainText: string | Buffer) {
|
export function toBase64(plainText: string | Buffer) {
|
||||||
return Buffer.from(plainText).toString('base64');
|
return Buffer.from(plainText).toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromBase64(encodedText: string) {
|
export function fromBase64(encodedText: string) {
|
||||||
return Buffer.from(encodedText, 'base64');
|
return Buffer.from(encodedText, 'base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function hmac(secret: any, value: any) {
|
export function hmac(secret: any, value: any) {
|
||||||
const hmac = crypto.createHmac('sha256', Buffer.from(secret.toString(), 'ascii'));
|
const hmac = crypto.createHmac('sha256', Buffer.from(secret.toString(), 'ascii'));
|
||||||
hmac.update(value.toString());
|
hmac.update(value.toString());
|
||||||
return hmac.digest('base64');
|
return hmac.digest('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isElectron() {
|
export function isElectron() {
|
||||||
return !!process.versions['electron'];
|
return !!process.versions['electron'];
|
||||||
}
|
}
|
||||||
|
|
||||||
function hash(text: string) {
|
export function hash(text: string) {
|
||||||
text = text.normalize();
|
text = text.normalize();
|
||||||
|
|
||||||
return crypto.createHash('sha1').update(text).digest('base64');
|
return crypto.createHash('sha1').update(text).digest('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmptyOrWhitespace(str: string) {
|
export function isEmptyOrWhitespace(str: string) {
|
||||||
return str === null || str.match(/^ *$/) !== null;
|
return str === null || str.match(/^ *$/) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeSqlIdentifier(str: string) {
|
export function sanitizeSqlIdentifier(str: string) {
|
||||||
return str.replace(/[^A-Za-z0-9_]/g, "");
|
return str.replace(/[^A-Za-z0-9_]/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(str: string) {
|
export function escapeHtml(str: string) {
|
||||||
return escape(str);
|
return escape(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unescapeHtml(str: string) {
|
export function unescapeHtml(str: string) {
|
||||||
return unescape(str);
|
return unescape(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toObject<T, K extends string | number | symbol, V>(array: T[], fn: (item: T) => [K, V]): Record<K, V> {
|
export function toObject<T, K extends string | number | symbol, V>(array: T[], fn: (item: T) => [K, V]): Record<K, V> {
|
||||||
const obj: Record<K, V> = {} as Record<K, V>; // TODO: unsafe?
|
const obj: Record<K, V> = {} as Record<K, V>; // TODO: unsafe?
|
||||||
|
|
||||||
for (const item of array) {
|
for (const item of array) {
|
||||||
@ -98,11 +98,11 @@ function toObject<T, K extends string | number | symbol, V>(array: T[], fn: (ite
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripTags(text: string) {
|
export function stripTags(text: string) {
|
||||||
return text.replace(/<(?:.|\n)*?>/gm, '');
|
return text.replace(/<(?:.|\n)*?>/gm, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function union<T extends string | number | symbol>(a: T[], b: T[]): T[] {
|
export function union<T extends string | number | symbol>(a: T[], b: T[]): T[] {
|
||||||
const obj: Record<T, T> = {} as Record<T, T>; // TODO: unsafe?
|
const obj: Record<T, T> = {} as Record<T, T>; // TODO: unsafe?
|
||||||
|
|
||||||
for (let i = a.length-1; i >= 0; i--) {
|
for (let i = a.length-1; i >= 0; i--) {
|
||||||
@ -124,11 +124,11 @@ function union<T extends string | number | symbol>(a: T[], b: T[]): T[] {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeRegExp(str: string) {
|
export function escapeRegExp(str: string) {
|
||||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function crash() {
|
export async function crash() {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
(await import("electron")).app.exit(1);
|
(await import("electron")).app.exit(1);
|
||||||
} else {
|
} else {
|
||||||
@ -136,7 +136,7 @@ async function crash() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeFilenameForHeader(filename: string) {
|
export function sanitizeFilenameForHeader(filename: string) {
|
||||||
let sanitizedFilename = sanitize(filename);
|
let sanitizedFilename = sanitize(filename);
|
||||||
|
|
||||||
if (sanitizedFilename.trim().length === 0) {
|
if (sanitizedFilename.trim().length === 0) {
|
||||||
@ -146,7 +146,7 @@ function sanitizeFilenameForHeader(filename: string) {
|
|||||||
return encodeURIComponent(sanitizedFilename);
|
return encodeURIComponent(sanitizedFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContentDisposition(filename: string) {
|
export function getContentDisposition(filename: string) {
|
||||||
const sanitizedFilename = sanitizeFilenameForHeader(filename);
|
const sanitizedFilename = sanitizeFilenameForHeader(filename);
|
||||||
|
|
||||||
return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`;
|
return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`;
|
||||||
@ -160,24 +160,24 @@ const STRING_MIME_TYPES = [
|
|||||||
"image/svg+xml"
|
"image/svg+xml"
|
||||||
];
|
];
|
||||||
|
|
||||||
function isStringNote(type: string | undefined, mime: string) {
|
export function isStringNote(type: string | undefined, mime: string) {
|
||||||
// render and book are string note in the sense that they are expected to contain empty string
|
// render and book are string note in the sense that they are expected to contain empty string
|
||||||
return (type && ["text", "code", "relationMap", "search", "render", "book", "mermaid", "canvas"].includes(type))
|
return (type && ["text", "code", "relationMap", "search", "render", "book", "mermaid", "canvas"].includes(type))
|
||||||
|| mime.startsWith('text/')
|
|| mime.startsWith('text/')
|
||||||
|| STRING_MIME_TYPES.includes(mime);
|
|| STRING_MIME_TYPES.includes(mime);
|
||||||
}
|
}
|
||||||
|
|
||||||
function quoteRegex(url: string) {
|
export function quoteRegex(url: string) {
|
||||||
return url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
|
return url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAll(string: string, replaceWhat: string, replaceWith: string) {
|
export function replaceAll(string: string, replaceWhat: string, replaceWith: string) {
|
||||||
const quotedReplaceWhat = quoteRegex(replaceWhat);
|
const quotedReplaceWhat = quoteRegex(replaceWhat);
|
||||||
|
|
||||||
return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith);
|
return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDownloadTitle(fileName: string, type: string | null, mime: string) {
|
export function formatDownloadTitle(fileName: string, type: string | null, mime: string) {
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
fileName = "untitled";
|
fileName = "untitled";
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ function formatDownloadTitle(fileName: string, type: string | null, mime: string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTextFileExtension(filePath: string) {
|
export function removeTextFileExtension(filePath: string) {
|
||||||
const extension = path.extname(filePath).toLowerCase();
|
const extension = path.extname(filePath).toLowerCase();
|
||||||
|
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
@ -227,13 +227,13 @@ function removeTextFileExtension(filePath: string) {
|
|||||||
case ".markdown":
|
case ".markdown":
|
||||||
case ".html":
|
case ".html":
|
||||||
case ".htm":
|
case ".htm":
|
||||||
return filePath.substr(0, filePath.length - extension.length);
|
return filePath.substring(0, filePath.length - extension.length);
|
||||||
default:
|
default:
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, noteMeta?: { title?: string }) {
|
export function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, noteMeta?: { title?: string }) {
|
||||||
if (noteMeta?.title) {
|
if (noteMeta?.title) {
|
||||||
return noteMeta.title;
|
return noteMeta.title;
|
||||||
} else {
|
} else {
|
||||||
@ -245,7 +245,7 @@ function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boolean, n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeLimit<T>(promise: Promise<T>, limitMs: number, errorMessage?: string): Promise<T> {
|
export function timeLimit<T>(promise: Promise<T>, limitMs: number, errorMessage?: string): Promise<T> {
|
||||||
if (!promise || !promise.then) { // it's not actually a promise
|
if (!promise || !promise.then) { // it's not actually a promise
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@ -276,7 +276,7 @@ interface DeferredPromise<T> extends Promise<T> {
|
|||||||
reject: (reason?: any) => void
|
reject: (reason?: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function deferred<T>(): DeferredPromise<T> {
|
export function deferred<T>(): DeferredPromise<T> {
|
||||||
return (() => {
|
return (() => {
|
||||||
let resolve!: (value: T | PromiseLike<T>) => void;
|
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||||
let reject!: (reason?: any) => void;
|
let reject!: (reason?: any) => void;
|
||||||
@ -292,7 +292,7 @@ function deferred<T>(): DeferredPromise<T> {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDiacritic(str: string) {
|
export function removeDiacritic(str: string) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -300,11 +300,11 @@ function removeDiacritic(str: string) {
|
|||||||
return str.normalize("NFD").replace(/\p{Diacritic}/gu, "");
|
return str.normalize("NFD").replace(/\p{Diacritic}/gu, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize(str: string) {
|
export function normalize(str: string) {
|
||||||
return removeDiacritic(str).toLowerCase();
|
return removeDiacritic(str).toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMap<T extends Record<string, any>>(list: T[], key: keyof T): Record<string, T> {
|
export function toMap<T extends Record<string, any>>(list: T[], key: keyof T): Record<string, T> {
|
||||||
const map: Record<string, T> = {};
|
const map: Record<string, T> = {};
|
||||||
|
|
||||||
for (const el of list) {
|
for (const el of list) {
|
||||||
@ -314,7 +314,7 @@ function toMap<T extends Record<string, any>>(list: T[], key: keyof T): Record<s
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isString(x: any) {
|
export function isString(x: any) {
|
||||||
return Object.prototype.toString.call(x) === "[object String]";
|
return Object.prototype.toString.call(x) === "[object String]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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