Merge pull request #42 from TriliumNext/feature/typescript_backend_6

Convert backend to TypeScript (67% -> 71%)
This commit is contained in:
Elian Doran 2024-04-15 21:12:55 +03:00 committed by GitHub
commit a072016fc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 282 additions and 208 deletions

13
package-lock.json generated
View File

@ -101,6 +101,7 @@
"@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7",
"@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.4",
"@types/ws": "^8.5.10",
"@types/xml2js": "^0.4.14",
@ -1628,6 +1629,12 @@
"@types/node": "*"
}
},
"node_modules/@types/tmp": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz",
"integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==",
"dev": true
},
"node_modules/@types/tough-cookie": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
@ -14570,6 +14577,12 @@
"@types/node": "*"
}
},
"@types/tmp": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz",
"integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==",
"dev": true
},
"@types/tough-cookie": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",

View File

@ -122,6 +122,7 @@
"@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7",
"@types/stream-throttle": "^0.1.4",
"@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.4",
"@types/ws": "^8.5.10",
"@types/xml2js": "^0.4.14",
@ -145,4 +146,4 @@
"optionalDependencies": {
"electron-installer-debian": "3.2.0"
}
}
}

View File

@ -129,7 +129,7 @@ export default class Becca {
return this.branches[branchId];
}
getBranchOrThrow(branchId: string): BBranch | null {
getBranchOrThrow(branchId: string): BBranch {
const branch = this.getBranch(branchId);
if (!branch) {
throw new NotFoundError(`Branch '${branchId}' was not found in becca.`);
@ -239,7 +239,7 @@ export default class Becca {
return (this as any)[camelCaseEntityName][entityId];
}
getRecentNotesFromQuery(query: string, params = []): BRecentNote[] {
getRecentNotesFromQuery(query: string, params: string[] = []): BRecentNote[] {
const rows = sql.getRows(query, params);
const BRecentNote = require('./entities/brecent_note'); // avoiding circular dependency problems

View File

@ -134,7 +134,7 @@ class BAttachment extends AbstractBeccaEntity<BAttachment> {
return this._getContent() as Buffer;
}
setContent(content: string | Buffer, opts: ContentOpts) {
setContent(content: string | Buffer, opts?: ContentOpts) {
this._setContent(content, opts);
}

View File

@ -203,9 +203,9 @@ class BNote extends AbstractBeccaEntity<BNote> {
return this.children && this.children.length > 0;
}
getChildBranches(): (BBranch | null)[] {
getChildBranches(): BBranch[] {
return this.children
.map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId));
.map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId)) as BBranch[];
}
/*
@ -1434,7 +1434,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
searchNotesInSubtree(searchString: string) {
const searchService = require('../../services/search/services/search');
return searchService.searchNotes(searchString);
return searchService.searchNotes(searchString) as BNote[];
}
searchNoteInSubtree(searchString: string) {

View File

@ -1,11 +1,11 @@
"use strict";
const appInfo = require('../../services/app_info');
import appInfo = require('../../services/app_info');
function getAppInfo() {
return appInfo;
}
module.exports = {
export = {
getAppInfo
};

View File

@ -1,27 +1,28 @@
const becca = require('../../becca/becca');
const blobService = require('../../services/blob');
const ValidationError = require('../../errors/validation_error');
const imageService = require("../../services/image");
import becca = require('../../becca/becca');
import blobService = require('../../services/blob');
import ValidationError = require('../../errors/validation_error');
import imageService = require("../../services/image");
import { Request } from 'express';
function getAttachmentBlob(req) {
function getAttachmentBlob(req: Request) {
const preview = req.query.preview === 'true';
return blobService.getBlobPojo('attachments', req.params.attachmentId, { preview });
}
function getAttachments(req) {
function getAttachments(req: Request) {
const note = becca.getNoteOrThrow(req.params.noteId);
return note.getAttachments({includeContentLength: true});
}
function getAttachment(req) {
function getAttachment(req: Request) {
const {attachmentId} = req.params;
return becca.getAttachmentOrThrow(attachmentId, {includeContentLength: true});
}
function getAllAttachments(req) {
function getAllAttachments(req: Request) {
const {attachmentId} = req.params;
// one particular attachment is requested, but return all note's attachments
@ -29,18 +30,18 @@ function getAllAttachments(req) {
return attachment.getNote()?.getAttachments({includeContentLength: true}) || [];
}
function saveAttachment(req) {
function saveAttachment(req: Request) {
const {noteId} = req.params;
const {attachmentId, role, mime, title, content} = req.body;
const {matchBy} = req.query;
const {matchBy} = req.query as any;
const note = becca.getNoteOrThrow(noteId);
note.saveAttachment({attachmentId, role, mime, title, content}, matchBy);
}
function uploadAttachment(req) {
function uploadAttachment(req: Request) {
const {noteId} = req.params;
const {file} = req;
const {file} = req as any;
const note = becca.getNoteOrThrow(noteId);
let url;
@ -65,7 +66,7 @@ function uploadAttachment(req) {
};
}
function renameAttachment(req) {
function renameAttachment(req: Request) {
const {title} = req.body;
const {attachmentId} = req.params;
@ -79,7 +80,7 @@ function renameAttachment(req) {
attachment.save();
}
function deleteAttachment(req) {
function deleteAttachment(req: Request) {
const {attachmentId} = req.params;
const attachment = becca.getAttachment(attachmentId);
@ -89,14 +90,14 @@ function deleteAttachment(req) {
}
}
function convertAttachmentToNote(req) {
function convertAttachmentToNote(req: Request) {
const {attachmentId} = req.params;
const attachment = becca.getAttachmentOrThrow(attachmentId);
return attachment.convertToNote();
}
module.exports = {
export = {
getAttachmentBlob,
getAttachments,
getAttachment,

View File

@ -1,19 +1,20 @@
"use strict";
const sql = require('../../services/sql');
const log = require('../../services/log');
const attributeService = require('../../services/attributes');
const BAttribute = require('../../becca/entities/battribute');
const becca = require('../../becca/becca');
const ValidationError = require('../../errors/validation_error');
import sql = require('../../services/sql');
import log = require('../../services/log');
import attributeService = require('../../services/attributes');
import BAttribute = require('../../becca/entities/battribute');
import becca = require('../../becca/becca');
import ValidationError = require('../../errors/validation_error');
import { Request } from 'express';
function getEffectiveNoteAttributes(req) {
function getEffectiveNoteAttributes(req: Request) {
const note = becca.getNote(req.params.noteId);
return note.getAttributes();
return note?.getAttributes();
}
function updateNoteAttribute(req) {
function updateNoteAttribute(req: Request) {
const noteId = req.params.noteId;
const body = req.body;
@ -70,14 +71,17 @@ function updateNoteAttribute(req) {
};
}
function setNoteAttribute(req) {
function setNoteAttribute(req: Request) {
const noteId = req.params.noteId;
const body = req.body;
const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = ? AND name = ?`, [noteId, body.type, body.name]);
const attributeId = sql.getValue<string | null>(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = ? AND name = ?`, [noteId, body.type, body.name]);
if (attributeId) {
const attr = becca.getAttribute(attributeId);
if (!attr) {
throw new ValidationError(`Missing attribute with ID ${attributeId}.`);
}
attr.value = body.value;
attr.save();
} else {
@ -88,14 +92,14 @@ function setNoteAttribute(req) {
}
}
function addNoteAttribute(req) {
function addNoteAttribute(req: Request) {
const noteId = req.params.noteId;
const body = req.body;
new BAttribute({...body, noteId}).save();
}
function deleteNoteAttribute(req) {
function deleteNoteAttribute(req: Request) {
const noteId = req.params.noteId;
const attributeId = req.params.attributeId;
@ -110,11 +114,14 @@ function deleteNoteAttribute(req) {
}
}
function updateNoteAttributes(req) {
function updateNoteAttributes(req: Request) {
const noteId = req.params.noteId;
const incomingAttributes = req.body;
const note = becca.getNote(noteId);
if (!note) {
throw new ValidationError(`Cannot find note with ID ${noteId}.`);
}
let existingAttrs = note.getOwnedAttributes().slice();
@ -179,25 +186,29 @@ function updateNoteAttributes(req) {
}
}
function getAttributeNames(req) {
function getAttributeNames(req: Request) {
const type = req.query.type;
const query = req.query.query;
if (typeof type !== "string" || typeof query !== "string") {
throw new ValidationError("Invalid data type.");
}
return attributeService.getAttributeNames(type, query);
}
function getValuesForAttribute(req) {
function getValuesForAttribute(req: Request) {
const attributeName = req.params.attributeName;
return sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND type = 'label' AND value != '' ORDER BY value", [attributeName]);
}
function createRelation(req) {
function createRelation(req: Request) {
const sourceNoteId = req.params.noteId;
const targetNoteId = req.params.targetNoteId;
const name = req.params.name;
const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]);
const attributeId = sql.getValue<string>(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]);
let attribute = becca.getAttribute(attributeId);
if (!attribute) {
@ -212,20 +223,22 @@ function createRelation(req) {
return attribute;
}
function deleteRelation(req) {
function deleteRelation(req: Request) {
const sourceNoteId = req.params.noteId;
const targetNoteId = req.params.targetNoteId;
const name = req.params.name;
const attributeId = sql.getValue(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]);
const attributeId = sql.getValue<string | null>(`SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ?`, [sourceNoteId, name, targetNoteId]);
if (attributeId) {
const attribute = becca.getAttribute(attributeId);
attribute.markAsDeleted();
if (attribute) {
attribute.markAsDeleted();
}
}
}
module.exports = {
export = {
updateNoteAttributes,
updateNoteAttribute,
setNoteAttribute,

View File

@ -1,21 +1,26 @@
"use strict";
const beccaService = require('../../becca/becca_service');
const searchService = require('../../services/search/services/search');
const log = require('../../services/log');
const utils = require('../../services/utils');
const cls = require('../../services/cls');
const becca = require('../../becca/becca');
import beccaService = require('../../becca/becca_service');
import searchService = require('../../services/search/services/search');
import log = require('../../services/log');
import utils = require('../../services/utils');
import cls = require('../../services/cls');
import becca = require('../../becca/becca');
import { Request } from 'express';
import ValidationError = require('../../errors/validation_error');
function getAutocomplete(req) {
const query = req.query.query.trim();
function getAutocomplete(req: Request) {
if (typeof req.query.query !== "string") {
throw new ValidationError("Invalid query data type.");
}
const query = (req.query.query || "").trim();
const activeNoteId = req.query.activeNoteId || 'none';
let results;
const timestampStarted = Date.now();
if (query.length === 0) {
if (query.length === 0 && typeof activeNoteId === "string") {
results = getRecentNotes(activeNoteId);
}
else {
@ -31,7 +36,7 @@ function getAutocomplete(req) {
return results;
}
function getRecentNotes(activeNoteId) {
function getRecentNotes(activeNoteId: string) {
let extraCondition = '';
const params = [activeNoteId];
@ -70,6 +75,6 @@ function getRecentNotes(activeNoteId) {
});
}
module.exports = {
export = {
getAutocomplete
};

View File

@ -1,8 +1,9 @@
"use strict";
const fs = require('fs');
const dateUtils = require('../../services/date_utils');
const {LOG_DIR} = require('../../services/data_dir');
import fs = require('fs');
import dateUtils = require('../../services/date_utils');
import dataDir = require('../../services/data_dir');
const { LOG_DIR } = dataDir;
function getBackendLog() {
const file = `${LOG_DIR}/trilium-${dateUtils.localNowDate()}.log`;
@ -16,6 +17,6 @@ function getBackendLog() {
}
}
module.exports = {
export = {
getBackendLog
};

View File

@ -1,23 +1,24 @@
"use strict";
const sql = require('../../services/sql');
const utils = require('../../services/utils');
const entityChangesService = require('../../services/entity_changes');
const treeService = require('../../services/tree');
const eraseService = require('../../services/erase');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const branchService = require('../../services/branches');
const log = require('../../services/log');
const ValidationError = require('../../errors/validation_error');
const eventService = require("../../services/events");
import sql = require('../../services/sql');
import utils = require('../../services/utils');
import entityChangesService = require('../../services/entity_changes');
import treeService = require('../../services/tree');
import eraseService = require('../../services/erase');
import becca = require('../../becca/becca');
import TaskContext = require('../../services/task_context');
import branchService = require('../../services/branches');
import log = require('../../services/log');
import ValidationError = require('../../errors/validation_error');
import eventService = require("../../services/events");
import { Request } from 'express';
/**
* Code in this file deals with moving and cloning branches. The relationship between note and parent note is unique
* for not deleted branches. There may be multiple deleted note-parent note relationships.
*/
function moveBranchToParent(req) {
function moveBranchToParent(req: Request) {
const {branchId, parentBranchId} = req.params;
const branchToMove = becca.getBranch(branchId);
@ -30,7 +31,7 @@ function moveBranchToParent(req) {
return branchService.moveBranchToBranch(branchToMove, targetParentBranch, branchId);
}
function moveBranchBeforeNote(req) {
function moveBranchBeforeNote(req: Request) {
const {branchId, beforeBranchId} = req.params;
const branchToMove = becca.getBranchOrThrow(branchId);
@ -51,7 +52,7 @@ function moveBranchBeforeNote(req) {
[beforeBranch.parentNoteId, originalBeforeNotePosition]);
// also need to update becca positions
const parentNote = becca.getNote(beforeBranch.parentNoteId);
const parentNote = becca.getNoteOrThrow(beforeBranch.parentNoteId);
for (const childBranch of parentNote.getChildBranches()) {
if (childBranch.notePosition >= originalBeforeNotePosition) {
@ -80,11 +81,11 @@ function moveBranchBeforeNote(req) {
return { success: true };
}
function moveBranchAfterNote(req) {
function moveBranchAfterNote(req: Request) {
const {branchId, afterBranchId} = req.params;
const branchToMove = becca.getBranch(branchId);
const afterNote = becca.getBranch(afterBranchId);
const branchToMove = becca.getBranchOrThrow(branchId);
const afterNote = becca.getBranchOrThrow(afterBranchId);
const validationResult = treeService.validateParentChild(afterNote.parentNoteId, branchToMove.noteId, branchId);
@ -100,7 +101,7 @@ function moveBranchAfterNote(req) {
[afterNote.parentNoteId, originalAfterNotePosition]);
// also need to update becca positions
const parentNote = becca.getNote(afterNote.parentNoteId);
const parentNote = becca.getNoteOrThrow(afterNote.parentNoteId);
for (const childBranch of parentNote.getChildBranches()) {
if (childBranch.notePosition > originalAfterNotePosition) {
@ -131,7 +132,7 @@ function moveBranchAfterNote(req) {
return { success: true };
}
function setExpanded(req) {
function setExpanded(req: Request) {
const {branchId} = req.params;
const expanded = parseInt(req.params.expanded);
@ -153,11 +154,11 @@ function setExpanded(req) {
}
}
function setExpandedForSubtree(req) {
function setExpandedForSubtree(req: Request) {
const {branchId} = req.params;
const expanded = parseInt(req.params.expanded);
let branchIds = sql.getColumn(`
let branchIds = sql.getColumn<string>(`
WITH RECURSIVE
tree(branchId, noteId) AS (
SELECT branchId, noteId FROM branches WHERE branchId = ?
@ -186,12 +187,12 @@ function setExpandedForSubtree(req) {
};
}
function deleteBranch(req) {
function deleteBranch(req: Request) {
const last = req.query.last === 'true';
const eraseNotes = req.query.eraseNotes === 'true';
const branch = becca.getBranchOrThrow(req.params.branchId);
const taskContext = TaskContext.getInstance(req.query.taskId, 'deleteNotes');
const taskContext = TaskContext.getInstance(req.query.taskId as string, 'deleteNotes');
const deleteId = utils.randomString(10);
let noteDeleted;
@ -214,16 +215,16 @@ function deleteBranch(req) {
};
}
function setPrefix(req) {
function setPrefix(req: Request) {
const branchId = req.params.branchId;
const prefix = utils.isEmptyOrWhitespace(req.body.prefix) ? null : req.body.prefix;
const branch = becca.getBranch(branchId);
const branch = becca.getBranchOrThrow(branchId);
branch.prefix = prefix;
branch.save();
}
module.exports = {
export = {
moveBranchToParent,
moveBranchBeforeNote,
moveBranchAfterNote,

View File

@ -1,17 +1,18 @@
const becca = require('../../becca/becca');
const bulkActionService = require('../../services/bulk_actions');
import { Request } from 'express';
import becca = require('../../becca/becca');
import bulkActionService = require('../../services/bulk_actions');
function execute(req) {
function execute(req: Request) {
const {noteIds, includeDescendants} = req.body;
const affectedNoteIds = getAffectedNoteIds(noteIds, includeDescendants);
const bulkActionNote = becca.getNote('_bulkAction');
const bulkActionNote = becca.getNoteOrThrow('_bulkAction');
bulkActionService.executeActions(bulkActionNote, affectedNoteIds);
}
function getAffectedNoteCount(req) {
function getAffectedNoteCount(req: Request) {
const {noteIds, includeDescendants} = req.body;
const affectedNoteIds = getAffectedNoteIds(noteIds, includeDescendants);
@ -21,8 +22,8 @@ function getAffectedNoteCount(req) {
};
}
function getAffectedNoteIds(noteIds, includeDescendants) {
const affectedNoteIds = new Set();
function getAffectedNoteIds(noteIds: string[], includeDescendants: boolean) {
const affectedNoteIds = new Set<string>();
for (const noteId of noteIds) {
const note = becca.getNote(noteId);
@ -42,7 +43,7 @@ function getAffectedNoteIds(noteIds, includeDescendants) {
return affectedNoteIds;
}
module.exports = {
export = {
execute,
getAffectedNoteCount
};

View File

@ -1,22 +1,32 @@
"use strict";
const attributeService = require('../../services/attributes');
const cloneService = require('../../services/cloning');
const noteService = require('../../services/notes');
const dateNoteService = require('../../services/date_notes');
const dateUtils = require('../../services/date_utils');
const imageService = require('../../services/image');
const appInfo = require('../../services/app_info');
const ws = require('../../services/ws');
const log = require('../../services/log');
const utils = require('../../services/utils');
const path = require('path');
const htmlSanitizer = require('../../services/html_sanitizer');
const {formatAttrForSearch} = require('../../services/attribute_formatter');
const jsdom = require("jsdom");
import { Request } from "express";
import attributeService = require('../../services/attributes');
import cloneService = require('../../services/cloning');
import noteService = require('../../services/notes');
import dateNoteService = require('../../services/date_notes');
import dateUtils = require('../../services/date_utils');
import imageService = require('../../services/image');
import appInfo = require('../../services/app_info');
import ws = require('../../services/ws');
import log = require('../../services/log');
import utils = require('../../services/utils');
import path = require('path');
import htmlSanitizer = require('../../services/html_sanitizer');
import attributeFormatter = require('../../services/attribute_formatter');
import jsdom = require("jsdom");
import BNote = require("../../becca/entities/bnote");
import ValidationError = require("../../errors/validation_error");
const { JSDOM } = jsdom;
function addClipping(req) {
interface Image {
src: string;
dataUrl: string;
imageId: string;
}
function addClipping(req: Request) {
// if a note under the clipperInbox has the same 'pageUrl' attribute,
// add the content to that note and clone it under today's inbox
// otherwise just create a new note under today's inbox
@ -44,10 +54,14 @@ function addClipping(req) {
const rewrittenContent = processContent(images, clippingNote, content);
const existingContent = clippingNote.getContent();
if (typeof existingContent !== "string") {
throw new ValidationError("Invalid note content type.");
}
clippingNote.setContent(`${existingContent}${existingContent.trim() ? "<br>" : ""}${rewrittenContent}`);
if (clippingNote.parentNoteId !== clipperInbox.noteId) {
// TODO: Is parentNoteId ever defined?
if ((clippingNote as any).parentNoteId !== clipperInbox.noteId) {
cloneService.cloneNoteToParentNote(clippingNote.noteId, clipperInbox.noteId);
}
@ -56,13 +70,13 @@ function addClipping(req) {
};
}
function findClippingNote(clipperInboxNote, pageUrl, clipType) {
function findClippingNote(clipperInboxNote: BNote, pageUrl: string, clipType: string | null) {
if (!pageUrl) {
return null;
}
const notes = clipperInboxNote.searchNotesInSubtree(
formatAttrForSearch({
attributeFormatter.formatAttrForSearch({
type: 'label',
name: "pageUrl",
value: pageUrl
@ -84,7 +98,7 @@ function getClipperInboxNote() {
return clipperInbox;
}
function createNote(req) {
function createNote(req: Request) {
let {title, content, pageUrl, images, clipType, labels} = req.body;
if (!title || !title.trim()) {
@ -123,6 +137,9 @@ function createNote(req) {
}
const existingContent = note.getContent();
if (typeof existingContent !== "string") {
throw new ValidationError("Invalid note content tpye.");
}
const rewrittenContent = processContent(images, note, content);
const newContent = `${existingContent}${existingContent.trim() ? "<br/>" : ""}${rewrittenContent}`;
note.setContent(newContent);
@ -134,7 +151,7 @@ function createNote(req) {
};
}
function processContent(images, note, content) {
function processContent(images: Image[], note: BNote, content: string) {
let rewrittenContent = htmlSanitizer.sanitize(content);
if (images) {
@ -178,7 +195,7 @@ function processContent(images, note, content) {
return rewrittenContent;
}
function openNote(req) {
function openNote(req: Request) {
if (utils.isElectron()) {
ws.sendMessageToAllClients({
type: 'openNote',
@ -203,7 +220,7 @@ function handshake() {
}
}
function findNotesByUrl(req){
function findNotesByUrl(req: Request){
let pageUrl = req.params.noteUrl;
const clipperInbox = getClipperInboxNote();
let foundPage = findClippingNote(clipperInbox, pageUrl, null);

View File

@ -1,34 +1,35 @@
"use strict";
const cloningService = require('../../services/cloning');
import { Request } from 'express';
import cloningService = require('../../services/cloning');
function cloneNoteToBranch(req) {
function cloneNoteToBranch(req: Request) {
const {noteId, parentBranchId} = req.params;
const {prefix} = req.body;
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
}
function cloneNoteToParentNote(req) {
function cloneNoteToParentNote(req: Request) {
const {noteId, parentNoteId} = req.params;
const {prefix} = req.body;
return cloningService.cloneNoteToParentNote(noteId, parentNoteId, prefix);
}
function cloneNoteAfter(req) {
function cloneNoteAfter(req: Request) {
const {noteId, afterBranchId} = req.params;
return cloningService.cloneNoteAfter(noteId, afterBranchId);
}
function toggleNoteInParent(req) {
function toggleNoteInParent(req: Request) {
const {noteId, parentNoteId, present} = req.params;
return cloningService.toggleNoteInParent(present === 'true', noteId, parentNoteId);
}
module.exports = {
export = {
cloneNoteToBranch,
cloneNoteToParentNote,
cloneNoteAfter,

View File

@ -1,10 +1,12 @@
"use strict";
const sql = require('../../services/sql');
const log = require('../../services/log');
const backupService = require('../../services/backup');
const anonymizationService = require('../../services/anonymization');
const consistencyChecksService = require('../../services/consistency_checks');
import sql = require('../../services/sql');
import log = require('../../services/log');
import backupService = require('../../services/backup');
import anonymizationService = require('../../services/anonymization');
import consistencyChecksService = require('../../services/consistency_checks');
import { Request } from 'express';
import ValidationError = require('../../errors/validation_error');
function getExistingBackups() {
return backupService.getExistingBackups();
@ -30,7 +32,10 @@ function getExistingAnonymizedDatabases() {
return anonymizationService.getExistingAnonymizedDatabases();
}
async function anonymize(req) {
async function anonymize(req: Request) {
if (req.params.type !== "full" && req.params.type !== "light") {
throw new ValidationError("Invalid type provided.");
}
return await anonymizationService.createAnonymizedCopy(req.params.type);
}
@ -44,7 +49,7 @@ function checkIntegrity() {
};
}
module.exports = {
export = {
getExistingBackups,
backupDatabase,
vacuumDatabase,

View File

@ -1,4 +1,5 @@
const etapiTokenService = require('../../services/etapi_tokens');
import { Request } from 'express';
import etapiTokenService = require('../../services/etapi_tokens');
function getTokens() {
const tokens = etapiTokenService.getTokens();
@ -8,19 +9,19 @@ function getTokens() {
return tokens;
}
function createToken(req) {
function createToken(req: Request) {
return etapiTokenService.createToken(req.body.tokenName);
}
function patchToken(req) {
function patchToken(req: Request) {
etapiTokenService.renameToken(req.params.etapiTokenId, req.body.name);
}
function deleteToken(req) {
function deleteToken(req: Request) {
etapiTokenService.deleteToken(req.params.etapiTokenId);
}
module.exports = {
export = {
getTokens,
createToken,
patchToken,

View File

@ -1,14 +1,16 @@
"use strict";
const zipExportService = require('../../services/export/zip');
const singleExportService = require('../../services/export/single');
const opmlExportService = require('../../services/export/opml');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const log = require('../../services/log');
const NotFoundError = require('../../errors/not_found_error');
import zipExportService = require('../../services/export/zip');
import singleExportService = require('../../services/export/single');
import opmlExportService = require('../../services/export/opml');
import becca = require('../../becca/becca');
import TaskContext = require('../../services/task_context');
import log = require('../../services/log');
import NotFoundError = require('../../errors/not_found_error');
import { Request, Response } from 'express';
import ValidationError = require('../../errors/validation_error');
function exportBranch(req, res) {
function exportBranch(req: Request, res: Response) {
const {branchId, type, format, version, taskId} = req.params;
const branch = becca.getBranch(branchId);
@ -29,6 +31,9 @@ function exportBranch(req, res) {
zipExportService.exportToZip(taskContext, branch, format, res);
}
else if (type === 'single') {
if (format !== "html" && format !== "markdown") {
throw new ValidationError("Invalid export type.");
}
singleExportService.exportSingleNote(taskContext, branch, format, res);
}
else if (format === 'opml') {
@ -38,7 +43,7 @@ function exportBranch(req, res) {
throw new NotFoundError(`Unrecognized export format '${format}'`);
}
}
catch (e) {
catch (e: any) {
const message = `Export failed with following error: '${e.message}'. More details might be in the logs.`;
taskContext.reportError(message);

View File

@ -1,21 +1,24 @@
"use strict";
const protectedSessionService = require('../../services/protected_session');
const utils = require('../../services/utils');
const log = require('../../services/log');
const noteService = require('../../services/notes');
const tmp = require('tmp');
const fs = require('fs');
const { Readable } = require('stream');
const chokidar = require('chokidar');
const ws = require('../../services/ws');
const becca = require('../../becca/becca');
const ValidationError = require('../../errors/validation_error');
import protectedSessionService = require('../../services/protected_session');
import utils = require('../../services/utils');
import log = require('../../services/log');
import noteService = require('../../services/notes');
import tmp = require('tmp');
import fs = require('fs');
import { Readable } from 'stream';
import chokidar = require('chokidar');
import ws = require('../../services/ws');
import becca = require('../../becca/becca');
import ValidationError = require('../../errors/validation_error');
import { Request, Response } from 'express';
import BNote = require('../../becca/entities/bnote');
import BAttachment = require('../../becca/entities/battachment');
function updateFile(req) {
function updateFile(req: Request) {
const note = becca.getNoteOrThrow(req.params.noteId);
const file = req.file;
const file = (req as any).file;
note.saveRevision();
note.mime = file.mimetype.toLowerCase();
@ -32,9 +35,9 @@ function updateFile(req) {
};
}
function updateAttachment(req) {
function updateAttachment(req: Request) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const file = req.file;
const file = (req as any).file;
attachment.getNote().saveRevision();
@ -46,12 +49,7 @@ function updateAttachment(req) {
};
}
/**
* @param {BNote|BAttachment} noteOrAttachment
* @param res
* @param {boolean} contentDisposition
*/
function downloadData(noteOrAttachment, res, contentDisposition) {
function downloadData(noteOrAttachment: BNote | BAttachment, res: Response, contentDisposition: boolean) {
if (noteOrAttachment.isProtected && !protectedSessionService.isProtectedSessionAvailable()) {
return res.status(401).send("Protected session not available");
}
@ -68,7 +66,7 @@ function downloadData(noteOrAttachment, res, contentDisposition) {
res.send(noteOrAttachment.getContent());
}
function downloadNoteInt(noteId, res, contentDisposition = true) {
function downloadNoteInt(noteId: string, res: Response, contentDisposition = true) {
const note = becca.getNote(noteId);
if (!note) {
@ -80,7 +78,7 @@ function downloadNoteInt(noteId, res, contentDisposition = true) {
return downloadData(note, res, contentDisposition);
}
function downloadAttachmentInt(attachmentId, res, contentDisposition = true) {
function downloadAttachmentInt(attachmentId: string, res: Response, contentDisposition = true) {
const attachment = becca.getAttachment(attachmentId);
if (!attachment) {
@ -92,34 +90,34 @@ function downloadAttachmentInt(attachmentId, res, contentDisposition = true) {
return downloadData(attachment, res, contentDisposition);
}
const downloadFile = (req, res) => downloadNoteInt(req.params.noteId, res, true);
const openFile = (req, res) => downloadNoteInt(req.params.noteId, res, false);
const downloadFile = (req: Request, res: Response) => downloadNoteInt(req.params.noteId, res, true);
const openFile = (req: Request, res: Response) => downloadNoteInt(req.params.noteId, res, false);
const downloadAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, true);
const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, false);
const downloadAttachment = (req: Request, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, true);
const openAttachment = (req: Request, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, false);
function fileContentProvider(req) {
function fileContentProvider(req: Request) {
// Read the file name from route params.
const note = becca.getNoteOrThrow(req.params.noteId);
return streamContent(note.getContent(), note.getFileName(), note.mime);
}
function attachmentContentProvider(req) {
function attachmentContentProvider(req: Request) {
// Read the file name from route params.
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
return streamContent(attachment.getContent(), attachment.getFileName(), attachment.mime);
}
function streamContent(content, fileName, mimeType) {
function streamContent(content: string | Buffer, fileName: string, mimeType: string) {
if (typeof content === "string") {
content = Buffer.from(content, 'utf8');
}
const totalSize = content.byteLength;
const getStream = range => {
const getStream = (range: { start: number, end: number }) => {
if (!range) {
// Request if for complete content.
return Readable.from(content);
@ -138,7 +136,7 @@ function streamContent(content, fileName, mimeType) {
};
}
function saveNoteToTmpDir(req) {
function saveNoteToTmpDir(req: Request) {
const note = becca.getNoteOrThrow(req.params.noteId);
const fileName = note.getFileName();
const content = note.getContent();
@ -146,18 +144,26 @@ function saveNoteToTmpDir(req) {
return saveToTmpDir(fileName, content, 'notes', note.noteId);
}
function saveAttachmentToTmpDir(req) {
function saveAttachmentToTmpDir(req: Request) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const fileName = attachment.getFileName();
const content = attachment.getContent();
if (!attachment.attachmentId) {
throw new ValidationError("Missing attachment ID.");
}
return saveToTmpDir(fileName, content, 'attachments', attachment.attachmentId);
}
function saveToTmpDir(fileName, content, entityType, entityId) {
function saveToTmpDir(fileName: string, content: string | Buffer, entityType: string, entityId: string) {
const tmpObj = tmp.fileSync({ postfix: fileName });
fs.writeSync(tmpObj.fd, content);
if (typeof content === "string") {
fs.writeSync(tmpObj.fd, content);
} else {
fs.writeSync(tmpObj.fd, content);
}
fs.closeSync(tmpObj.fd);
log.info(`Saved temporary file ${tmpObj.name}`);
@ -168,7 +174,7 @@ function saveToTmpDir(fileName, content, entityType, entityId) {
type: 'openedFileUpdated',
entityType: entityType,
entityId: entityId,
lastModifiedMs: stats.atimeMs,
lastModifiedMs: stats?.atimeMs,
filePath: tmpObj.name
});
});
@ -179,7 +185,7 @@ function saveToTmpDir(fileName, content, entityType, entityId) {
};
}
function uploadModifiedFileToNote(req) {
function uploadModifiedFileToNote(req: Request) {
const noteId = req.params.noteId;
const {filePath} = req.body;
@ -198,7 +204,7 @@ function uploadModifiedFileToNote(req) {
note.setContent(fileContent);
}
function uploadModifiedFileToAttachment(req) {
function uploadModifiedFileToAttachment(req: Request) {
const {attachmentId} = req.params;
const {filePath} = req.body;
@ -217,7 +223,7 @@ function uploadModifiedFileToAttachment(req) {
attachment.setContent(fileContent);
}
module.exports = {
export = {
updateFile,
updateAttachment,
openFile,

View File

@ -1,6 +1,7 @@
const optionService = require('../../services/options');
import { Request, Response } from 'express';
import optionService = require('../../services/options');
function getFontCss(req, res) {
function getFontCss(req: Request, res: Response) {
res.setHeader('Content-Type', 'text/css');
if (!optionService.getOptionBool('overrideThemeFonts')) {
@ -34,6 +35,6 @@ function getFontCss(req, res) {
res.send(style);
}
module.exports = {
export = {
getFontCss
};

View File

@ -1,5 +1,5 @@
const log = require('../services/log');
const fileService = require('./api/files.js');
const fileService = require('./api/files');
const scriptService = require('../services/script');
const cls = require('../services/cls');
const sql = require('../services/sql');

View File

@ -24,9 +24,9 @@ const indexRoute = require('./index.js');
// API routes
const treeApiRoute = require('./api/tree.js');
const notesApiRoute = require('./api/notes.js');
const branchesApiRoute = require('./api/branches.js');
const attachmentsApiRoute = require('./api/attachments.js');
const autocompleteApiRoute = require('./api/autocomplete.js');
const branchesApiRoute = require('./api/branches');
const attachmentsApiRoute = require('./api/attachments');
const autocompleteApiRoute = require('./api/autocomplete');
const cloningApiRoute = require('./api/cloning');
const revisionsApiRoute = require('./api/revisions');
const recentChangesApiRoute = require('./api/recent_changes.js');
@ -36,26 +36,26 @@ const syncApiRoute = require('./api/sync');
const loginApiRoute = require('./api/login.js');
const recentNotesRoute = require('./api/recent_notes.js');
const appInfoRoute = require('./api/app_info');
const exportRoute = require('./api/export.js');
const exportRoute = require('./api/export');
const importRoute = require('./api/import.js');
const setupApiRoute = require('./api/setup.js');
const sqlRoute = require('./api/sql');
const databaseRoute = require('./api/database.js');
const databaseRoute = require('./api/database');
const imageRoute = require('./api/image');
const attributesRoute = require('./api/attributes');
const scriptRoute = require('./api/script.js');
const senderRoute = require('./api/sender.js');
const filesRoute = require('./api/files.js');
const filesRoute = require('./api/files');
const searchRoute = require('./api/search');
const bulkActionRoute = require('./api/bulk_action.js');
const bulkActionRoute = require('./api/bulk_action');
const specialNotesRoute = require('./api/special_notes');
const noteMapRoute = require('./api/note_map.js');
const clipperRoute = require('./api/clipper.js');
const clipperRoute = require('./api/clipper');
const similarNotesRoute = require('./api/similar_notes.js');
const keysRoute = require('./api/keys.js');
const backendLogRoute = require('./api/backend_log.js');
const backendLogRoute = require('./api/backend_log');
const statsRoute = require('./api/stats.js');
const fontsRoute = require('./api/fonts.js');
const fontsRoute = require('./api/fonts');
const etapiTokensApiRoutes = require('./api/etapi_tokens');
const relationMapApiRoute = require('./api/relation-map');
const otherRoute = require('./api/other.js');

View File

@ -80,7 +80,6 @@ interface Api {
/**
* Axios library for HTTP requests. See {@link https://axios-http.com} for documentation
* @type {axios}
* @deprecated use native (browser compatible) fetch() instead
*/
axios: typeof axios;
@ -122,9 +121,6 @@ interface Api {
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "#dateModified =* MONTH AND #log". See {@link https://github.com/zadam/trilium/wiki/Search} for full documentation for all options
*
* @param {string} query
* @param {Object} [searchParams]
*/
searchForNote(query: string, searchParams: SearchParams): BNote | null;

View File

@ -4,7 +4,8 @@ import protectedSessionService = require('./protected_session');
import utils = require('./utils');
import type { Blob } from "./blob-interface";
function getBlobPojo(entityName: string, entityId: string) {
function getBlobPojo(entityName: string, entityId: string, opts?: { preview: boolean }) {
// TODO: Unused opts.
const entity = becca.getEntity(entityName, entityId);
if (!entity) {
throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`);

View File

@ -27,7 +27,8 @@ function moveBranchToNote(branchToMove: BBranch, targetParentNoteId: string) {
};
}
function moveBranchToBranch(branchToMove: BBranch, targetParentBranch: BBranch) {
function moveBranchToBranch(branchToMove: BBranch, targetParentBranch: BBranch, branchId: string) {
// TODO: Unused branch ID argument.
const res = moveBranchToNote(branchToMove, targetParentBranch.noteId);
if (!("success" in res) || !res.success) {

View File

@ -150,7 +150,7 @@ function getActions(note: BNote) {
.filter(a => !!a);
}
function executeActions(note: BNote, searchResultNoteIds: string[]) {
function executeActions(note: BNote, searchResultNoteIds: string[] | Set<string>) {
const actions = getActions(note);
for (const resultNoteId of searchResultNoteIds) {

View File

@ -58,7 +58,7 @@ function cloneNoteToBranch(noteId: string, parentBranchId: string, prefix: strin
return ret;
}
function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefix: string) {
function ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefix?: string) {
if (!(noteId in becca.notes)) {
return { branch: null, success: false, message: `Note '${noteId}' is deleted.` };
} else if (!(parentNoteId in becca.notes)) {
@ -109,7 +109,7 @@ function ensureNoteIsAbsentFromParent(noteId: string, parentNoteId: string) {
}
}
function toggleNoteInParent(present: boolean, noteId: string, parentNoteId: string, prefix: string) {
function toggleNoteInParent(present: boolean, noteId: string, parentNoteId: string, prefix?: string) {
if (present) {
return ensureNoteIsPresentInParent(noteId, parentNoteId, prefix);
}

View File

@ -913,7 +913,7 @@ sqlInit.dbReady.then(() => {
setTimeout(cls.wrap(runPeriodicChecks), 4 * 1000);
});
module.exports = {
export = {
runOnDemandChecks,
runEntityChangesChecks
};

View File

@ -49,8 +49,12 @@ interface Message {
messages?: string[];
startNoteId?: string;
currentNoteId?: string;
entityType?: string;
entityId?: string;
originEntityName?: "notes";
originEntityId?: string | null;
lastModifiedMs?: number;
filePath?: string;
}
type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void;