From b13ad5d01ed1e65eb22cff09274b49fd8d1f224b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:12:54 +0300 Subject: [PATCH 01/16] server-ts: Convert routes/api/app_info --- src/routes/api/{app_info.js => app_info.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/routes/api/{app_info.js => app_info.ts} (53%) diff --git a/src/routes/api/app_info.js b/src/routes/api/app_info.ts similarity index 53% rename from src/routes/api/app_info.js rename to src/routes/api/app_info.ts index aec592909..66a9bc366 100644 --- a/src/routes/api/app_info.js +++ b/src/routes/api/app_info.ts @@ -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 }; From 9330241045af3cacad2a48bc3f8fea838ccd1e79 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:16:46 +0300 Subject: [PATCH 02/16] server-ts: Convert routes/api/attachments --- .../api/{attachments.js => attachments.ts} | 33 ++++++++++--------- src/routes/routes.js | 2 +- src/services/blob.ts | 3 +- 3 files changed, 20 insertions(+), 18 deletions(-) rename src/routes/api/{attachments.js => attachments.ts} (77%) diff --git a/src/routes/api/attachments.js b/src/routes/api/attachments.ts similarity index 77% rename from src/routes/api/attachments.js rename to src/routes/api/attachments.ts index 4cb782cb2..9fde39fc5 100644 --- a/src/routes/api/attachments.js +++ b/src/routes/api/attachments.ts @@ -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, diff --git a/src/routes/routes.js b/src/routes/routes.js index c782f5e9c..269f2a6ae 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -25,7 +25,7 @@ const indexRoute = require('./index.js'); 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 attachmentsApiRoute = require('./api/attachments'); const autocompleteApiRoute = require('./api/autocomplete.js'); const cloningApiRoute = require('./api/cloning'); const revisionsApiRoute = require('./api/revisions'); diff --git a/src/services/blob.ts b/src/services/blob.ts index fac1adfad..41d23adc5 100644 --- a/src/services/blob.ts +++ b/src/services/blob.ts @@ -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.`); From c0349b3f84bebbd8a7b84bf2693109ea461af34e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:22:10 +0300 Subject: [PATCH 03/16] server-ts: Convert routes/api/attributes --- src/becca/becca-interface.ts | 5 +- .../api/{attributes.js => attributes.ts} | 57 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) rename src/routes/api/{attributes.js => attributes.ts} (76%) diff --git a/src/becca/becca-interface.ts b/src/becca/becca-interface.ts index 5a454335c..29c2d080e 100644 --- a/src/becca/becca-interface.ts +++ b/src/becca/becca-interface.ts @@ -137,7 +137,10 @@ export default class Becca { return branch; } - getAttribute(attributeId: string): BAttribute | null { + getAttribute(attributeId: string | null): BAttribute | null { + if (!attributeId) { + return null; + } return this.attributes[attributeId]; } diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.ts similarity index 76% rename from src/routes/api/attributes.js rename to src/routes/api/attributes.ts index 2c151fccf..966c12078 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/attributes.ts @@ -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(`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(`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(`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, From 82f5553980da763fdbc1483bd5afcec09f77d124 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:26:45 +0300 Subject: [PATCH 04/16] server-ts: Convert routes/api/autocomplete --- src/becca/becca-interface.ts | 2 +- .../api/{autocomplete.js => autocomplete.ts} | 27 +++++++++++-------- src/routes/routes.js | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) rename src/routes/api/{autocomplete.js => autocomplete.ts} (66%) diff --git a/src/becca/becca-interface.ts b/src/becca/becca-interface.ts index 29c2d080e..48a096eda 100644 --- a/src/becca/becca-interface.ts +++ b/src/becca/becca-interface.ts @@ -242,7 +242,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 diff --git a/src/routes/api/autocomplete.js b/src/routes/api/autocomplete.ts similarity index 66% rename from src/routes/api/autocomplete.js rename to src/routes/api/autocomplete.ts index 9ed36f89e..0aaa3f537 100644 --- a/src/routes/api/autocomplete.js +++ b/src/routes/api/autocomplete.ts @@ -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 }; diff --git a/src/routes/routes.js b/src/routes/routes.js index 269f2a6ae..27465e442 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -26,7 +26,7 @@ const treeApiRoute = require('./api/tree.js'); const notesApiRoute = require('./api/notes.js'); const branchesApiRoute = require('./api/branches.js'); const attachmentsApiRoute = require('./api/attachments'); -const autocompleteApiRoute = require('./api/autocomplete.js'); +const autocompleteApiRoute = require('./api/autocomplete'); const cloningApiRoute = require('./api/cloning'); const revisionsApiRoute = require('./api/revisions'); const recentChangesApiRoute = require('./api/recent_changes.js'); From 706b9d0f463a95f33c40f0cfbff60342c98c02ee Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:28:19 +0300 Subject: [PATCH 05/16] server-ts: Convert routes/api/backend_log --- src/routes/api/{backend_log.js => backend_log.ts} | 9 +++++---- src/routes/routes.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) rename src/routes/api/{backend_log.js => backend_log.ts} (65%) diff --git a/src/routes/api/backend_log.js b/src/routes/api/backend_log.ts similarity index 65% rename from src/routes/api/backend_log.js rename to src/routes/api/backend_log.ts index 4a07a219e..1210c404b 100644 --- a/src/routes/api/backend_log.js +++ b/src/routes/api/backend_log.ts @@ -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.js'); +const { LOG_DIR } = dataDir; function getBackendLog() { const file = `${LOG_DIR}/trilium-${dateUtils.localNowDate()}.log`; @@ -16,6 +17,6 @@ function getBackendLog() { } } -module.exports = { +export = { getBackendLog }; diff --git a/src/routes/routes.js b/src/routes/routes.js index 27465e442..ffa5d4051 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -53,7 +53,7 @@ const noteMapRoute = require('./api/note_map.js'); const clipperRoute = require('./api/clipper.js'); 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 etapiTokensApiRoutes = require('./api/etapi_tokens'); From fd77c5e8c41d864577cfb317aaf86dcdc6956eae Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:33:04 +0300 Subject: [PATCH 06/16] server-ts: Convert routes/api/branches --- src/becca/becca-interface.ts | 2 +- src/becca/entities/bnote.ts | 4 +- src/routes/api/{branches.js => branches.ts} | 53 +++++++++++---------- src/services/branches.ts | 3 +- 4 files changed, 32 insertions(+), 30 deletions(-) rename src/routes/api/{branches.js => branches.ts} (83%) diff --git a/src/becca/becca-interface.ts b/src/becca/becca-interface.ts index 48a096eda..7fa3ca60f 100644 --- a/src/becca/becca-interface.ts +++ b/src/becca/becca-interface.ts @@ -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.`); diff --git a/src/becca/entities/bnote.ts b/src/becca/entities/bnote.ts index 54bb0c427..8542070c2 100644 --- a/src/becca/entities/bnote.ts +++ b/src/becca/entities/bnote.ts @@ -203,9 +203,9 @@ class BNote extends AbstractBeccaEntity { 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[]; } /* diff --git a/src/routes/api/branches.js b/src/routes/api/branches.ts similarity index 83% rename from src/routes/api/branches.js rename to src/routes/api/branches.ts index 4638fb63e..8c7562128 100644 --- a/src/routes/api/branches.js +++ b/src/routes/api/branches.ts @@ -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(` 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, diff --git a/src/services/branches.ts b/src/services/branches.ts index 7ee32c949..8e6971560 100644 --- a/src/services/branches.ts +++ b/src/services/branches.ts @@ -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) { From 40ef533c5f2892b3bb14908830e8dbac7ae405ba Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:36:10 +0300 Subject: [PATCH 07/16] server-ts: Convert routes/api/bulk_action --- src/routes/api/backend_log.ts | 2 +- .../api/{bulk_action.js => bulk_action.ts} | 17 +++++++++-------- src/routes/routes.js | 4 ++-- src/services/bulk_actions.ts | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) rename src/routes/api/{bulk_action.js => bulk_action.ts} (66%) diff --git a/src/routes/api/backend_log.ts b/src/routes/api/backend_log.ts index 1210c404b..a4dbac9ab 100644 --- a/src/routes/api/backend_log.ts +++ b/src/routes/api/backend_log.ts @@ -2,7 +2,7 @@ import fs = require('fs'); import dateUtils = require('../../services/date_utils'); -import dataDir = require('../../services/data_dir.js'); +import dataDir = require('../../services/data_dir'); const { LOG_DIR } = dataDir; function getBackendLog() { diff --git a/src/routes/api/bulk_action.js b/src/routes/api/bulk_action.ts similarity index 66% rename from src/routes/api/bulk_action.js rename to src/routes/api/bulk_action.ts index 27955cd35..4d1a3e98f 100644 --- a/src/routes/api/bulk_action.js +++ b/src/routes/api/bulk_action.ts @@ -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(); 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 }; diff --git a/src/routes/routes.js b/src/routes/routes.js index ffa5d4051..7b7d98daf 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -24,7 +24,7 @@ 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 branchesApiRoute = require('./api/branches'); const attachmentsApiRoute = require('./api/attachments'); const autocompleteApiRoute = require('./api/autocomplete'); const cloningApiRoute = require('./api/cloning'); @@ -47,7 +47,7 @@ const scriptRoute = require('./api/script.js'); const senderRoute = require('./api/sender.js'); const filesRoute = require('./api/files.js'); 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'); diff --git a/src/services/bulk_actions.ts b/src/services/bulk_actions.ts index c91b1c0c9..8abc11460 100644 --- a/src/services/bulk_actions.ts +++ b/src/services/bulk_actions.ts @@ -150,7 +150,7 @@ function getActions(note: BNote) { .filter(a => !!a); } -function executeActions(note: BNote, searchResultNoteIds: string[]) { +function executeActions(note: BNote, searchResultNoteIds: string[] | Set) { const actions = getActions(note); for (const resultNoteId of searchResultNoteIds) { From f98f84d41941c7acbf4387564061ee0a639052fa Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:45:57 +0300 Subject: [PATCH 08/16] server-ts: Convert routes/api/bnote --- src/becca/entities/bnote.ts | 2 +- src/routes/api/{clipper.js => clipper.ts} | 61 +++++++++++++++-------- src/routes/routes.js | 2 +- 3 files changed, 41 insertions(+), 24 deletions(-) rename src/routes/api/{clipper.js => clipper.ts} (77%) diff --git a/src/becca/entities/bnote.ts b/src/becca/entities/bnote.ts index 8542070c2..ec094cde6 100644 --- a/src/becca/entities/bnote.ts +++ b/src/becca/entities/bnote.ts @@ -1434,7 +1434,7 @@ class BNote extends AbstractBeccaEntity { searchNotesInSubtree(searchString: string) { const searchService = require('../../services/search/services/search'); - return searchService.searchNotes(searchString); + return searchService.searchNotes(searchString) as BNote[]; } searchNoteInSubtree(searchString: string) { diff --git a/src/routes/api/clipper.js b/src/routes/api/clipper.ts similarity index 77% rename from src/routes/api/clipper.js rename to src/routes/api/clipper.ts index 74cd4d8a7..070a6c7e9 100644 --- a/src/routes/api/clipper.js +++ b/src/routes/api/clipper.ts @@ -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() ? "
" : ""}${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() ? "
" : ""}${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); diff --git a/src/routes/routes.js b/src/routes/routes.js index 7b7d98daf..dce10189e 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -50,7 +50,7 @@ const searchRoute = require('./api/search'); 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'); From 122ff3bb1dc35abe8e1f62d2b9503f20f522e277 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:47:07 +0300 Subject: [PATCH 09/16] server-ts: Convert routes/api/cloning --- src/routes/api/{cloning.js => cloning.ts} | 13 +++++++------ src/services/cloning.ts | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) rename src/routes/api/{cloning.js => cloning.ts} (71%) diff --git a/src/routes/api/cloning.js b/src/routes/api/cloning.ts similarity index 71% rename from src/routes/api/cloning.js rename to src/routes/api/cloning.ts index 75a42e675..8ab02900d 100644 --- a/src/routes/api/cloning.js +++ b/src/routes/api/cloning.ts @@ -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, diff --git a/src/services/cloning.ts b/src/services/cloning.ts index bb21425bc..36c61a653 100644 --- a/src/services/cloning.ts +++ b/src/services/cloning.ts @@ -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); } From 27637b0483c364cbe87c4d43dc03558eea917e70 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:55:21 +0300 Subject: [PATCH 10/16] server-ts: Convert routes/api/consistency_checks --- src/routes/api/{database.js => database.ts} | 19 ++++++++++++------- src/routes/routes.js | 2 +- src/services/consistency_checks.ts | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) rename src/routes/api/{database.js => database.ts} (62%) diff --git a/src/routes/api/database.js b/src/routes/api/database.ts similarity index 62% rename from src/routes/api/database.js rename to src/routes/api/database.ts index d8d8cfa9a..199862b41 100644 --- a/src/routes/api/database.js +++ b/src/routes/api/database.ts @@ -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, diff --git a/src/routes/routes.js b/src/routes/routes.js index dce10189e..88d74beac 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -40,7 +40,7 @@ const exportRoute = require('./api/export.js'); 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'); diff --git a/src/services/consistency_checks.ts b/src/services/consistency_checks.ts index 81ce810ca..36fdeef78 100644 --- a/src/services/consistency_checks.ts +++ b/src/services/consistency_checks.ts @@ -914,7 +914,7 @@ sqlInit.dbReady.then(() => { setTimeout(cls.wrap(runPeriodicChecks), 4 * 1000); }); -module.exports = { +export = { runOnDemandChecks, runEntityChangesChecks }; From e4512373615cb5413c4afee35c22874726863b01 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:56:23 +0300 Subject: [PATCH 11/16] server-ts: Convert routes/api/etapi_tokens --- src/routes/api/{etapi_tokens.js => etapi_tokens.ts} | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) rename src/routes/api/{etapi_tokens.js => etapi_tokens.ts} (66%) diff --git a/src/routes/api/etapi_tokens.js b/src/routes/api/etapi_tokens.ts similarity index 66% rename from src/routes/api/etapi_tokens.js rename to src/routes/api/etapi_tokens.ts index b0d29db3e..d7dd078bd 100644 --- a/src/routes/api/etapi_tokens.js +++ b/src/routes/api/etapi_tokens.ts @@ -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, From 952c3cc12f2d76bd2067d14a82a260f60c3a0b1a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 20:58:31 +0300 Subject: [PATCH 12/16] server-ts: Convert routes/api/export --- src/routes/api/{export.js => export.ts} | 23 ++++++++++++++--------- src/routes/routes.js | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) rename src/routes/api/{export.js => export.ts} (64%) diff --git a/src/routes/api/export.js b/src/routes/api/export.ts similarity index 64% rename from src/routes/api/export.js rename to src/routes/api/export.ts index 4c098512b..499198aef 100644 --- a/src/routes/api/export.js +++ b/src/routes/api/export.ts @@ -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); diff --git a/src/routes/routes.js b/src/routes/routes.js index 88d74beac..27330f25d 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -36,7 +36,7 @@ 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'); From 0ab137de03b7cf1d41a9557c6cbe837e75ab12f1 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 22:22:18 +0300 Subject: [PATCH 13/16] server-ts: Convert routes/api/files --- package-lock.json | 13 +++++ package.json | 3 +- src/becca/entities/battachment.ts | 2 +- src/routes/api/{files.js => files.ts} | 84 ++++++++++++++------------- src/routes/custom.js | 2 +- src/routes/routes.js | 2 +- src/services/ws.ts | 4 ++ 7 files changed, 67 insertions(+), 43 deletions(-) rename src/routes/api/{files.js => files.ts} (66%) diff --git a/package-lock.json b/package-lock.json index 4123aca01..6adfcaed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 91e82dbcd..eead6dea0 100644 --- a/package.json +++ b/package.json @@ -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" } -} \ No newline at end of file +} diff --git a/src/becca/entities/battachment.ts b/src/becca/entities/battachment.ts index 7b203839a..09f026ff0 100644 --- a/src/becca/entities/battachment.ts +++ b/src/becca/entities/battachment.ts @@ -134,7 +134,7 @@ class BAttachment extends AbstractBeccaEntity { return this._getContent() as Buffer; } - setContent(content: string | Buffer, opts: ContentOpts) { + setContent(content: string | Buffer, opts?: ContentOpts) { this._setContent(content, opts); } diff --git a/src/routes/api/files.js b/src/routes/api/files.ts similarity index 66% rename from src/routes/api/files.js rename to src/routes/api/files.ts index f368383d5..4a536954b 100644 --- a/src/routes/api/files.js +++ b/src/routes/api/files.ts @@ -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, diff --git a/src/routes/custom.js b/src/routes/custom.js index fbf6c42c3..2e32e5446 100644 --- a/src/routes/custom.js +++ b/src/routes/custom.js @@ -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'); diff --git a/src/routes/routes.js b/src/routes/routes.js index 27330f25d..fab432f58 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -45,7 +45,7 @@ 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'); const specialNotesRoute = require('./api/special_notes'); diff --git a/src/services/ws.ts b/src/services/ws.ts index 6ff81e1db..1b581d358 100644 --- a/src/services/ws.ts +++ b/src/services/ws.ts @@ -50,8 +50,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; From b552f40ae855c2819f5b67f17d231834c31533ad Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 22:24:21 +0300 Subject: [PATCH 14/16] server-ts: Convert routes/api/fonts --- src/routes/api/{fonts.js => fonts.ts} | 7 ++++--- src/routes/routes.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) rename src/routes/api/{fonts.js => fonts.ts} (89%) diff --git a/src/routes/api/fonts.js b/src/routes/api/fonts.ts similarity index 89% rename from src/routes/api/fonts.js rename to src/routes/api/fonts.ts index d21db07c8..fc68dc920 100644 --- a/src/routes/api/fonts.js +++ b/src/routes/api/fonts.ts @@ -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 }; diff --git a/src/routes/routes.js b/src/routes/routes.js index fab432f58..21dbe6166 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -55,7 +55,7 @@ const similarNotesRoute = require('./api/similar_notes.js'); const keysRoute = require('./api/keys.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'); From 74441273a8636b428e1e78441456e3722e564b9b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 9 Apr 2024 21:43:42 +0300 Subject: [PATCH 15/16] server-ts: Fix getAttribute permitting null --- src/becca/becca-interface.ts | 5 +---- src/routes/api/attributes.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/becca/becca-interface.ts b/src/becca/becca-interface.ts index 7fa3ca60f..055740416 100644 --- a/src/becca/becca-interface.ts +++ b/src/becca/becca-interface.ts @@ -137,10 +137,7 @@ export default class Becca { return branch; } - getAttribute(attributeId: string | null): BAttribute | null { - if (!attributeId) { - return null; - } + getAttribute(attributeId: string): BAttribute | null { return this.attributes[attributeId]; } diff --git a/src/routes/api/attributes.ts b/src/routes/api/attributes.ts index 966c12078..e66906f37 100644 --- a/src/routes/api/attributes.ts +++ b/src/routes/api/attributes.ts @@ -208,7 +208,7 @@ function createRelation(req: Request) { 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(`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) { From 97230ca82a773514f9d95acaf07aa208d9861e11 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 15 Apr 2024 21:12:34 +0300 Subject: [PATCH 16/16] server-ts: Remove some esdoc types --- src/services/backend_script_api.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/services/backend_script_api.ts b/src/services/backend_script_api.ts index 34767ae75..627cd3c49 100644 --- a/src/services/backend_script_api.ts +++ b/src/services/backend_script_api.ts @@ -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;