mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	Merge pull request #43 from TriliumNext/feature/typescript_backend_7
Convert backend to TypeScript (71% -> 80%)
This commit is contained in:
		
						commit
						98d12901a5
					
				| @ -161,6 +161,14 @@ export default class Becca { | |||||||
|         return row ? new BRevision(row) : null; |         return row ? new BRevision(row) : null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     getRevisionOrThrow(revisionId: string): BRevision { | ||||||
|  |         const revision = this.getRevision(revisionId); | ||||||
|  |         if (!revision) { | ||||||
|  |             throw new NotFoundError(`Revision '${revisionId}' has not been found.`); | ||||||
|  |         } | ||||||
|  |         return revision; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     getAttachment(attachmentId: string, opts: AttachmentOpts = {}): BAttachment | null { |     getAttachment(attachmentId: string, opts: AttachmentOpts = {}): BAttachment | null { | ||||||
|         opts.includeContentLength = !!opts.includeContentLength; |         opts.includeContentLength = !!opts.includeContentLength; | ||||||
| 
 | 
 | ||||||
| @ -246,7 +254,7 @@ export default class Becca { | |||||||
|         return rows.map(row => new BRecentNote(row)); |         return rows.map(row => new BRecentNote(row)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getRevisionsFromQuery(query: string, params = []): BRevision[] { |     getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] { | ||||||
|         const rows = sql.getRows<RevisionRow>(query, params); |         const rows = sql.getRows<RevisionRow>(query, params); | ||||||
| 
 | 
 | ||||||
|         const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
 |         const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
 | ||||||
| @ -289,3 +297,17 @@ export interface ConstructorData<T extends AbstractBeccaEntity<T>> { | |||||||
|     entityName: string; |     entityName: string; | ||||||
|     hashedProperties: (keyof T)[]; |     hashedProperties: (keyof T)[]; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export interface NotePojo { | ||||||
|  |     noteId: string; | ||||||
|  |     title?: string; | ||||||
|  |     isProtected?: boolean; | ||||||
|  |     type: string; | ||||||
|  |     mime: string; | ||||||
|  |     blobId?: string; | ||||||
|  |     isDeleted: boolean; | ||||||
|  |     dateCreated?: string; | ||||||
|  |     dateModified?: string; | ||||||
|  |     utcDateCreated: string; | ||||||
|  |     utcDateModified?: string; | ||||||
|  | } | ||||||
| @ -26,8 +26,8 @@ interface ContentOpts { | |||||||
| abstract class AbstractBeccaEntity<T extends AbstractBeccaEntity<T>> { | abstract class AbstractBeccaEntity<T extends AbstractBeccaEntity<T>> { | ||||||
| 
 | 
 | ||||||
|     utcDateModified?: string; |     utcDateModified?: string; | ||||||
|     protected dateCreated?: string; |     dateCreated?: string; | ||||||
|     protected dateModified?: string; |     dateModified?: string; | ||||||
|      |      | ||||||
|     utcDateCreated!: string; |     utcDateCreated!: string; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import eventService = require('../../services/events'); | |||||||
| import { AttachmentRow, NoteRow, NoteType, RevisionRow } from './rows'; | import { AttachmentRow, NoteRow, NoteType, RevisionRow } from './rows'; | ||||||
| import BBranch = require('./bbranch'); | import BBranch = require('./bbranch'); | ||||||
| import BAttribute = require('./battribute'); | import BAttribute = require('./battribute'); | ||||||
|  | import { NotePojo } from '../becca-interface'; | ||||||
| dayjs.extend(utc); | dayjs.extend(utc); | ||||||
| 
 | 
 | ||||||
| const LABEL = 'label'; | const LABEL = 'label'; | ||||||
| @ -222,7 +223,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @throws Error in case of invalid JSON */ |      * @throws Error in case of invalid JSON */ | ||||||
|     getJsonContent(): {} | null { |     getJsonContent(): any | null { | ||||||
|         const content = this.getContent(); |         const content = this.getContent(); | ||||||
| 
 | 
 | ||||||
|         if (typeof content !== "string" || !content || !content.trim()) { |         if (typeof content !== "string" || !content || !content.trim()) { | ||||||
| @ -1679,7 +1680,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|         this.utcDateModified = dateUtils.utcNowDateTime(); |         this.utcDateModified = dateUtils.utcNowDateTime(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getPojo() { |     getPojo(): NotePojo { | ||||||
|         return { |         return { | ||||||
|             noteId: this.noteId, |             noteId: this.noteId, | ||||||
|             title: this.title || undefined, |             title: this.title || undefined, | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> { | |||||||
|     utcDateLastEdited?: string; |     utcDateLastEdited?: string; | ||||||
|     utcDateCreated!: string; |     utcDateCreated!: string; | ||||||
|     contentLength?: number; |     contentLength?: number; | ||||||
|     content?: string; |     content?: string | Buffer; | ||||||
| 
 | 
 | ||||||
|     constructor(row: RevisionRow, titleDecrypted = false) { |     constructor(row: RevisionRow, titleDecrypted = false) { | ||||||
|         super(); |         super(); | ||||||
| @ -91,9 +91,8 @@ class BRevision extends AbstractBeccaEntity<BRevision> { | |||||||
|      * |      * | ||||||
|      * This is the same approach as is used for Note's content. |      * This is the same approach as is used for Note's content. | ||||||
|      */ |      */ | ||||||
|     // TODO: initial declaration included Buffer, but everywhere it's treated as a string.
 |     getContent(): string | Buffer { | ||||||
|     getContent(): string { |         return this._getContent(); | ||||||
|         return this._getContent() as string; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -101,7 +100,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> { | |||||||
|     getJsonContent(): {} | null { |     getJsonContent(): {} | null { | ||||||
|         const content = this.getContent(); |         const content = this.getContent(); | ||||||
| 
 | 
 | ||||||
|         if (!content || !content.trim()) { |         if (!content || typeof content !== "string" || !content.trim()) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,27 +1,26 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const imageService = require('../../services/image'); | import imageService = require('../../services/image'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; | const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; | ||||||
| const fs = require('fs'); | import fs = require('fs'); | ||||||
|  | import { Request, Response } from 'express'; | ||||||
|  | import BNote = require('../../becca/entities/bnote'); | ||||||
|  | import BRevision = require('../../becca/entities/brevision'); | ||||||
| 
 | 
 | ||||||
| function returnImageFromNote(req, res) { | function returnImageFromNote(req: Request, res: Response) { | ||||||
|     const image = becca.getNote(req.params.noteId); |     const image = becca.getNote(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     return returnImageInt(image, res); |     return returnImageInt(image, res); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function returnImageFromRevision(req, res) { | function returnImageFromRevision(req: Request, res: Response) { | ||||||
|     const image = becca.getRevision(req.params.revisionId); |     const image = becca.getRevision(req.params.revisionId); | ||||||
| 
 | 
 | ||||||
|     return returnImageInt(image, res); |     return returnImageInt(image, res); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | function returnImageInt(image: BNote | BRevision | null, res: Response) { | ||||||
|  * @param {BNote|BRevision} image |  | ||||||
|  * @param res |  | ||||||
|  */ |  | ||||||
| function returnImageInt(image, res) { |  | ||||||
|     if (!image) { |     if (!image) { | ||||||
|         res.set('Content-Type', 'image/png'); |         res.set('Content-Type', 'image/png'); | ||||||
|         return res.send(fs.readFileSync(`${RESOURCE_DIR}/db/image-deleted.png`)); |         return res.send(fs.readFileSync(`${RESOURCE_DIR}/db/image-deleted.png`)); | ||||||
| @ -40,12 +39,13 @@ function returnImageInt(image, res) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function renderSvgAttachment(image, res, attachmentName) { | function renderSvgAttachment(image: BNote | BRevision, res: Response, attachmentName: string) { | ||||||
|     let svgString = '<svg/>' |     let svgString = '<svg/>' | ||||||
|     const attachment = image.getAttachmentByTitle(attachmentName); |     const attachment = image.getAttachmentByTitle(attachmentName); | ||||||
| 
 | 
 | ||||||
|     if (attachment) { |     const content = attachment.getContent(); | ||||||
|         svgString = attachment.getContent(); |     if (attachment && typeof content === "string") { | ||||||
|  |         svgString = content; | ||||||
|     } else { |     } else { | ||||||
|         // backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
 |         // backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
 | ||||||
|         const contentSvg = image.getJsonContentSafely()?.svg; |         const contentSvg = image.getJsonContentSafely()?.svg; | ||||||
| @ -62,7 +62,7 @@ function renderSvgAttachment(image, res, attachmentName) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function returnAttachedImage(req, res) { | function returnAttachedImage(req: Request, res: Response) { | ||||||
|     const attachment = becca.getAttachment(req.params.attachmentId); |     const attachment = becca.getAttachment(req.params.attachmentId); | ||||||
| 
 | 
 | ||||||
|     if (!attachment) { |     if (!attachment) { | ||||||
| @ -81,9 +81,9 @@ function returnAttachedImage(req, res) { | |||||||
|     res.send(attachment.getContent()); |     res.send(attachment.getContent()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateImage(req) { | function updateImage(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const {file} = req; |     const {file} = (req as any); | ||||||
| 
 | 
 | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
| @ -99,7 +99,7 @@ function updateImage(req) { | |||||||
|     return { uploaded: true }; |     return { uploaded: true }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     returnImageFromNote, |     returnImageFromNote, | ||||||
|     returnImageFromRevision, |     returnImageFromRevision, | ||||||
|     returnAttachedImage, |     returnAttachedImage, | ||||||
| @ -1,18 +1,20 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const enexImportService = require('../../services/import/enex'); | import enexImportService = require('../../services/import/enex'); | ||||||
| const opmlImportService = require('../../services/import/opml'); | import opmlImportService = require('../../services/import/opml'); | ||||||
| const zipImportService = require('../../services/import/zip'); | import zipImportService = require('../../services/import/zip'); | ||||||
| const singleImportService = require('../../services/import/single'); | import singleImportService = require('../../services/import/single'); | ||||||
| const cls = require('../../services/cls'); | import cls = require('../../services/cls'); | ||||||
| const path = require('path'); | import path = require('path'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const beccaLoader = require('../../becca/becca_loader'); | import beccaLoader = require('../../becca/becca_loader'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const TaskContext = require('../../services/task_context'); | import TaskContext = require('../../services/task_context'); | ||||||
| const ValidationError = require('../../errors/validation_error'); | import ValidationError = require('../../errors/validation_error'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import BNote = require('../../becca/entities/bnote'); | ||||||
| 
 | 
 | ||||||
| async function importNotesToBranch(req) { | async function importNotesToBranch(req: Request) { | ||||||
|     const { parentNoteId } = req.params; |     const { parentNoteId } = req.params; | ||||||
|     const { taskId, last } = req.body; |     const { taskId, last } = req.body; | ||||||
| 
 | 
 | ||||||
| @ -25,7 +27,7 @@ async function importNotesToBranch(req) { | |||||||
|         replaceUnderscoresWithSpaces: req.body.replaceUnderscoresWithSpaces !== 'false' |         replaceUnderscoresWithSpaces: req.body.replaceUnderscoresWithSpaces !== 'false' | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const file = req.file; |     const file = (req as any).file; | ||||||
| 
 | 
 | ||||||
|     if (!file) { |     if (!file) { | ||||||
|         throw new ValidationError("No file has been uploaded"); |         throw new ValidationError("No file has been uploaded"); | ||||||
| @ -42,7 +44,7 @@ async function importNotesToBranch(req) { | |||||||
|     // eliminate flickering during import
 |     // eliminate flickering during import
 | ||||||
|     cls.ignoreEntityChangeIds(); |     cls.ignoreEntityChangeIds(); | ||||||
| 
 | 
 | ||||||
|     let note; // typically root of the import - client can show it after finishing the import
 |     let note: BNote | null; // typically root of the import - client can show it after finishing the import
 | ||||||
| 
 | 
 | ||||||
|     const taskContext = TaskContext.getInstance(taskId, 'importNotes', options); |     const taskContext = TaskContext.getInstance(taskId, 'importNotes', options); | ||||||
| 
 | 
 | ||||||
| @ -50,14 +52,24 @@ async function importNotesToBranch(req) { | |||||||
|         if (extension === '.zip' && options.explodeArchives) { |         if (extension === '.zip' && options.explodeArchives) { | ||||||
|             note = await zipImportService.importZip(taskContext, file.buffer, parentNote); |             note = await zipImportService.importZip(taskContext, file.buffer, parentNote); | ||||||
|         } else if (extension === '.opml' && options.explodeArchives) { |         } else if (extension === '.opml' && options.explodeArchives) { | ||||||
|             note = await opmlImportService.importOpml(taskContext, file.buffer, parentNote); |             const importResult = await opmlImportService.importOpml(taskContext, file.buffer, parentNote); | ||||||
|  |             if (!Array.isArray(importResult)) { | ||||||
|  |                 note = importResult; | ||||||
|  |             } else { | ||||||
|  |                 return importResult; | ||||||
|  |             } | ||||||
|         } else if (extension === '.enex' && options.explodeArchives) { |         } else if (extension === '.enex' && options.explodeArchives) { | ||||||
|             note = await enexImportService.importEnex(taskContext, file, parentNote); |             const importResult = await enexImportService.importEnex(taskContext, file, parentNote); | ||||||
|  |             if (!Array.isArray(importResult)) { | ||||||
|  |                 note = importResult; | ||||||
|  |             } else { | ||||||
|  |                 return importResult; | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             note = await singleImportService.importSingleFile(taskContext, file, parentNote); |             note = await singleImportService.importSingleFile(taskContext, file, parentNote); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`; |         const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
| 
 | 
 | ||||||
| @ -66,11 +78,15 @@ async function importNotesToBranch(req) { | |||||||
|         return [500, message]; |         return [500, message]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (!note) { | ||||||
|  |         return [500, "No note was generated as a result of the import."]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (last === "true") { |     if (last === "true") { | ||||||
|         // small timeout to avoid race condition (the message is received before the transaction is committed)
 |         // small timeout to avoid race condition (the message is received before the transaction is committed)
 | ||||||
|         setTimeout(() => taskContext.taskSucceeded({ |         setTimeout(() => taskContext.taskSucceeded({ | ||||||
|             parentNoteId: parentNoteId, |             parentNoteId: parentNoteId, | ||||||
|             importedNoteId: note.noteId |             importedNoteId: note?.noteId | ||||||
|         }), 1000); |         }), 1000); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -80,7 +96,7 @@ async function importNotesToBranch(req) { | |||||||
|     return note.getPojo(); |     return note.getPojo(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function importAttachmentsToNote(req) { | async function importAttachmentsToNote(req: Request) { | ||||||
|     const { parentNoteId } = req.params; |     const { parentNoteId } = req.params; | ||||||
|     const { taskId, last } = req.body; |     const { taskId, last } = req.body; | ||||||
| 
 | 
 | ||||||
| @ -88,7 +104,7 @@ async function importAttachmentsToNote(req) { | |||||||
|         shrinkImages: req.body.shrinkImages !== 'false', |         shrinkImages: req.body.shrinkImages !== 'false', | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const file = req.file; |     const file = (req as any).file; | ||||||
| 
 | 
 | ||||||
|     if (!file) { |     if (!file) { | ||||||
|         throw new ValidationError("No file has been uploaded"); |         throw new ValidationError("No file has been uploaded"); | ||||||
| @ -102,7 +118,7 @@ async function importAttachmentsToNote(req) { | |||||||
|     try { |     try { | ||||||
|         await singleImportService.importAttachment(taskContext, file, parentNote); |         await singleImportService.importAttachment(taskContext, file, parentNote); | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`; |         const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
| 
 | 
 | ||||||
| @ -119,7 +135,7 @@ async function importAttachmentsToNote(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     importNotesToBranch, |     importNotesToBranch, | ||||||
|     importAttachmentsToNote |     importAttachmentsToNote | ||||||
| }; | }; | ||||||
| @ -1,7 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const keyboardActions = require('../../services/keyboard_actions'); | import keyboardActions = require('../../services/keyboard_actions'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| 
 | 
 | ||||||
| function getKeyboardActions() { | function getKeyboardActions() { | ||||||
|     return keyboardActions.getKeyboardActions(); |     return keyboardActions.getKeyboardActions(); | ||||||
| @ -14,7 +14,7 @@ function getShortcutsForNotes() { | |||||||
|     return labels.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher'); |     return labels.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getKeyboardActions, |     getKeyboardActions, | ||||||
|     getShortcutsForNotes |     getShortcutsForNotes | ||||||
| }; | }; | ||||||
| @ -1,19 +1,20 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const options = require('../../services/options'); | import options = require('../../services/options'); | ||||||
| const utils = require('../../services/utils'); | import utils = require('../../services/utils'); | ||||||
| const dateUtils = require('../../services/date_utils'); | import dateUtils = require('../../services/date_utils'); | ||||||
| const instanceId = require('../../services/instance_id'); | import instanceId = require('../../services/instance_id'); | ||||||
| const passwordEncryptionService = require('../../services/encryption/password_encryption'); | import passwordEncryptionService = require('../../services/encryption/password_encryption'); | ||||||
| const protectedSessionService = require('../../services/protected_session'); | import protectedSessionService = require('../../services/protected_session'); | ||||||
| const appInfo = require('../../services/app_info'); | import appInfo = require('../../services/app_info'); | ||||||
| const eventService = require('../../services/events'); | import eventService = require('../../services/events'); | ||||||
| const sqlInit = require('../../services/sql_init'); | import sqlInit = require('../../services/sql_init'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const ws = require('../../services/ws'); | import ws = require('../../services/ws'); | ||||||
| const etapiTokenService = require('../../services/etapi_tokens'); | import etapiTokenService = require('../../services/etapi_tokens'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function loginSync(req) { | function loginSync(req: Request) { | ||||||
|     if (!sqlInit.schemaExists()) { |     if (!sqlInit.schemaExists()) { | ||||||
|         return [500, { message: "DB schema does not exist, can't sync." }]; |         return [500, { message: "DB schema does not exist, can't sync." }]; | ||||||
|     } |     } | ||||||
| @ -44,7 +45,7 @@ function loginSync(req) { | |||||||
|         return [400, { message: "Sync login credentials are incorrect. It looks like you're trying to sync two different initialized documents which is not possible." }]; |         return [400, { message: "Sync login credentials are incorrect. It looks like you're trying to sync two different initialized documents which is not possible." }]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     req.session.loggedIn = true; |     (req as any).session.loggedIn = true; | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|         instanceId: instanceId, |         instanceId: instanceId, | ||||||
| @ -52,7 +53,7 @@ function loginSync(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function loginToProtectedSession(req) { | function loginToProtectedSession(req: Request) { | ||||||
|     const password = req.body.password; |     const password = req.body.password; | ||||||
| 
 | 
 | ||||||
|     if (!passwordEncryptionService.verifyPassword(password)) { |     if (!passwordEncryptionService.verifyPassword(password)) { | ||||||
| @ -63,6 +64,12 @@ function loginToProtectedSession(req) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const decryptedDataKey = passwordEncryptionService.getDataKey(password); |     const decryptedDataKey = passwordEncryptionService.getDataKey(password); | ||||||
|  |     if (!decryptedDataKey) { | ||||||
|  |         return { | ||||||
|  |             success: false, | ||||||
|  |             message: "Unable to obtain data key." | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     protectedSessionService.setDataKey(decryptedDataKey); |     protectedSessionService.setDataKey(decryptedDataKey); | ||||||
| 
 | 
 | ||||||
| @ -87,7 +94,7 @@ function touchProtectedSession() { | |||||||
|     protectedSessionService.touchProtectedSession(); |     protectedSessionService.touchProtectedSession(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function token(req) { | function token(req: Request) { | ||||||
|     const password = req.body.password; |     const password = req.body.password; | ||||||
| 
 | 
 | ||||||
|     if (!passwordEncryptionService.verifyPassword(password)) { |     if (!passwordEncryptionService.verifyPassword(password)) { | ||||||
| @ -102,7 +109,7 @@ function token(req) { | |||||||
|     return { token: authToken }; |     return { token: authToken }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     loginSync, |     loginSync, | ||||||
|     loginToProtectedSession, |     loginToProtectedSession, | ||||||
|     logoutFromProtectedSession, |     logoutFromProtectedSession, | ||||||
| @ -1,18 +1,25 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const { JSDOM } = require("jsdom"); | import { JSDOM } from "jsdom"; | ||||||
|  | import BNote = require('../../becca/entities/bnote'); | ||||||
|  | import BAttribute = require('../../becca/entities/battribute'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import ValidationError = require('../../errors/validation_error'); | ||||||
| 
 | 
 | ||||||
| function buildDescendantCountMap(noteIdsToCount) { | function buildDescendantCountMap(noteIdsToCount: string[]) { | ||||||
|     if (!Array.isArray(noteIdsToCount)) { |     if (!Array.isArray(noteIdsToCount)) { | ||||||
|         throw new Error('noteIdsToCount: type error'); |         throw new Error('noteIdsToCount: type error'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const noteIdToCountMap = Object.create(null); |     const noteIdToCountMap = Object.create(null); | ||||||
| 
 | 
 | ||||||
|     function getCount(noteId) { |     function getCount(noteId: string) { | ||||||
|         if (!(noteId in noteIdToCountMap)) { |         if (!(noteId in noteIdToCountMap)) { | ||||||
|             const note = becca.getNote(noteId); |             const note = becca.getNote(noteId); | ||||||
|  |             if (!note) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             const hiddenImageNoteIds = note.getRelations('imageLink').map(rel => rel.value); |             const hiddenImageNoteIds = note.getRelations('imageLink').map(rel => rel.value); | ||||||
|             const childNoteIds = note.children.map(child => child.noteId); |             const childNoteIds = note.children.map(child => child.noteId); | ||||||
| @ -33,19 +40,14 @@ function buildDescendantCountMap(noteIdsToCount) { | |||||||
| 
 | 
 | ||||||
|     return noteIdToCountMap; |     return noteIdToCountMap; | ||||||
| } | } | ||||||
| /** | function getNeighbors(note: BNote, depth: number): string[] { | ||||||
|  * @param {BNote} note |  | ||||||
|  * @param {int} depth |  | ||||||
|  * @returns {string[]} noteIds |  | ||||||
|  */ |  | ||||||
| function getNeighbors(note, depth) { |  | ||||||
|     if (depth === 0) { |     if (depth === 0) { | ||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const retNoteIds = []; |     const retNoteIds = []; | ||||||
| 
 | 
 | ||||||
|     function isIgnoredRelation(relation) { |     function isIgnoredRelation(relation: BAttribute) { | ||||||
|         return ['relationMapLink', 'template', 'inherit', 'image', 'ancestor'].includes(relation.name); |         return ['relationMapLink', 'template', 'inherit', 'image', 'ancestor'].includes(relation.name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -90,8 +92,9 @@ function getNeighbors(note, depth) { | |||||||
|     return retNoteIds; |     return retNoteIds; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getLinkMap(req) { | function getLinkMap(req: Request) { | ||||||
|     const mapRootNote = becca.getNote(req.params.noteId); |     const mapRootNote = becca.getNoteOrThrow(req.params.noteId); | ||||||
|  | 
 | ||||||
|     // if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything
 |     // if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything
 | ||||||
|     // to display, so we'll just ignore it
 |     // to display, so we'll just ignore it
 | ||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); |     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); | ||||||
| @ -125,7 +128,7 @@ function getLinkMap(req) { | |||||||
|     const noteIdsArray = Array.from(noteIds) |     const noteIdsArray = Array.from(noteIds) | ||||||
| 
 | 
 | ||||||
|     const notes = noteIdsArray.map(noteId => { |     const notes = noteIdsArray.map(noteId => { | ||||||
|         const note = becca.getNote(noteId); |         const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
|         return [ |         return [ | ||||||
|             note.noteId, |             note.noteId, | ||||||
| @ -144,6 +147,9 @@ function getLinkMap(req) { | |||||||
|         } |         } | ||||||
|         else if (rel.name === 'imageLink') { |         else if (rel.name === 'imageLink') { | ||||||
|             const parentNote = becca.getNote(rel.noteId); |             const parentNote = becca.getNote(rel.noteId); | ||||||
|  |             if (!parentNote) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             return !parentNote.getChildNotes().find(childNote => childNote.noteId === rel.value); |             return !parentNote.getChildNotes().find(childNote => childNote.noteId === rel.value); | ||||||
|         } |         } | ||||||
| @ -165,8 +171,8 @@ function getLinkMap(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getTreeMap(req) { | function getTreeMap(req: Request) { | ||||||
|     const mapRootNote = becca.getNote(req.params.noteId); |     const mapRootNote = becca.getNoteOrThrow(req.params.noteId); | ||||||
|     // if the map root itself has "excludeFromNoteMap" (journal typically) then there wouldn't be anything to display,
 |     // if the map root itself has "excludeFromNoteMap" (journal typically) then there wouldn't be anything to display,
 | ||||||
|     // so we'll just ignore it
 |     // so we'll just ignore it
 | ||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); |     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap'); | ||||||
| @ -198,8 +204,8 @@ function getTreeMap(req) { | |||||||
|             note.getLabelValue('color') |             note.getLabelValue('color') | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|     const noteIds = new Set(); |     const noteIds = new Set<string>(); | ||||||
|     notes.forEach(([noteId]) => noteIds.add(noteId)); |     notes.forEach(([noteId]) => noteId && noteIds.add(noteId)); | ||||||
| 
 | 
 | ||||||
|     const links = []; |     const links = []; | ||||||
| 
 | 
 | ||||||
| @ -225,7 +231,7 @@ function getTreeMap(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateDescendantCountMapForSearch(noteIdToDescendantCountMap, relationships) { | function updateDescendantCountMapForSearch(noteIdToDescendantCountMap: Record<string, number>, relationships: { parentNoteId: string, childNoteId: string }[]) { | ||||||
|     for (const {parentNoteId, childNoteId} of relationships) { |     for (const {parentNoteId, childNoteId} of relationships) { | ||||||
|         const parentNote = becca.notes[parentNoteId]; |         const parentNote = becca.notes[parentNoteId]; | ||||||
|         if (!parentNote || parentNote.type !== 'search') { |         if (!parentNote || parentNote.type !== 'search') { | ||||||
| @ -237,16 +243,17 @@ function updateDescendantCountMapForSearch(noteIdToDescendantCountMap, relations | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function removeImages(document) { | function removeImages(document: Document) { | ||||||
|     const images = document.getElementsByTagName('img'); |     const images = document.getElementsByTagName('img'); | ||||||
|     while (images.length > 0) { |     while (images && images.length > 0) { | ||||||
|         images[0].parentNode.removeChild(images[0]); |         images[0]?.parentNode?.removeChild(images[0]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const EXCERPT_CHAR_LIMIT = 200; | const EXCERPT_CHAR_LIMIT = 200; | ||||||
|  | type ElementOrText = (Element | Text); | ||||||
| 
 | 
 | ||||||
| function findExcerpts(sourceNote, referencedNoteId) { | function findExcerpts(sourceNote: BNote, referencedNoteId: string) { | ||||||
|     const html = sourceNote.getContent(); |     const html = sourceNote.getContent(); | ||||||
|     const document = new JSDOM(html).window.document; |     const document = new JSDOM(html).window.document; | ||||||
| 
 | 
 | ||||||
| @ -263,25 +270,24 @@ function findExcerpts(sourceNote, referencedNoteId) { | |||||||
| 
 | 
 | ||||||
|         linkEl.classList.add("backlink-link"); |         linkEl.classList.add("backlink-link"); | ||||||
| 
 | 
 | ||||||
|         let centerEl = linkEl; |         let centerEl: HTMLElement = linkEl; | ||||||
| 
 | 
 | ||||||
|         while (centerEl.tagName !== 'BODY' && centerEl.parentElement?.textContent?.length <= EXCERPT_CHAR_LIMIT) { |         while (centerEl.tagName !== 'BODY' && centerEl.parentElement && (centerEl.parentElement?.textContent?.length || 0) <= EXCERPT_CHAR_LIMIT) { | ||||||
|             centerEl = centerEl.parentElement; |             centerEl = centerEl.parentElement; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /** @var {HTMLElement[]} */ |         const excerptEls: ElementOrText[] = [centerEl]; | ||||||
|         const excerptEls = [centerEl]; |         let excerptLength = centerEl.textContent?.length || 0; | ||||||
|         let excerptLength = centerEl.textContent.length; |         let left: ElementOrText = centerEl; | ||||||
|         let left = centerEl; |         let right: ElementOrText = centerEl; | ||||||
|         let right = centerEl; |  | ||||||
| 
 | 
 | ||||||
|         while (excerptLength < EXCERPT_CHAR_LIMIT) { |         while (excerptLength < EXCERPT_CHAR_LIMIT) { | ||||||
|             let added = false; |             let added = false; | ||||||
| 
 | 
 | ||||||
|             const prev = left.previousElementSibling; |             const prev: Element | null = left.previousElementSibling; | ||||||
| 
 | 
 | ||||||
|             if (prev) { |             if (prev) { | ||||||
|                 const prevText = prev.textContent; |                 const prevText = prev.textContent || ""; | ||||||
| 
 | 
 | ||||||
|                 if (prevText.length + excerptLength > EXCERPT_CHAR_LIMIT) { |                 if (prevText.length + excerptLength > EXCERPT_CHAR_LIMIT) { | ||||||
|                     const prefix = prevText.substr(prevText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); |                     const prefix = prevText.substr(prevText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); | ||||||
| @ -298,12 +304,12 @@ function findExcerpts(sourceNote, referencedNoteId) { | |||||||
|                 added = true; |                 added = true; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const next = right.nextElementSibling; |             const next: Element | null = right.nextElementSibling; | ||||||
| 
 | 
 | ||||||
|             if (next) { |             if (next) { | ||||||
|                 const nextText = next.textContent; |                 const nextText = next.textContent; | ||||||
| 
 | 
 | ||||||
|                 if (nextText.length + excerptLength > EXCERPT_CHAR_LIMIT) { |                 if (nextText && nextText.length + excerptLength > EXCERPT_CHAR_LIMIT) { | ||||||
|                     const suffix = nextText.substr(nextText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); |                     const suffix = nextText.substr(nextText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); | ||||||
| 
 | 
 | ||||||
|                     const textNode = document.createTextNode(`${suffix}…`); |                     const textNode = document.createTextNode(`${suffix}…`); | ||||||
| @ -314,7 +320,7 @@ function findExcerpts(sourceNote, referencedNoteId) { | |||||||
| 
 | 
 | ||||||
|                 right = next; |                 right = next; | ||||||
|                 excerptEls.push(right); |                 excerptEls.push(right); | ||||||
|                 excerptLength += nextText.length; |                 excerptLength += nextText?.length || 0; | ||||||
|                 added = true; |                 added = true; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -336,13 +342,13 @@ function findExcerpts(sourceNote, referencedNoteId) { | |||||||
|     return excerpts; |     return excerpts; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getFilteredBacklinks(note) { | function getFilteredBacklinks(note: BNote) { | ||||||
|     return note.getTargetRelations() |     return note.getTargetRelations() | ||||||
|         // search notes have "ancestor" relations which are not interesting
 |         // search notes have "ancestor" relations which are not interesting
 | ||||||
|         .filter(relation => !!relation.getNote() && relation.getNote().type !== 'search'); |         .filter(relation => !!relation.getNote() && relation.getNote().type !== 'search'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getBacklinkCount(req) { | function getBacklinkCount(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| 
 | 
 | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| @ -352,7 +358,7 @@ function getBacklinkCount(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getBacklinks(req) { | function getBacklinks(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
| @ -379,7 +385,7 @@ function getBacklinks(req) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getLinkMap, |     getLinkMap, | ||||||
|     getTreeMap, |     getTreeMap, | ||||||
|     getBacklinkCount, |     getBacklinkCount, | ||||||
| @ -1,25 +1,28 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const noteService = require('../../services/notes'); | import noteService = require('../../services/notes'); | ||||||
| const eraseService = require('../../services/erase'); | import eraseService = require('../../services/erase'); | ||||||
| const treeService = require('../../services/tree'); | import treeService = require('../../services/tree'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const utils = require('../../services/utils'); | import utils = require('../../services/utils'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const TaskContext = require('../../services/task_context'); | import TaskContext = require('../../services/task_context'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const ValidationError = require('../../errors/validation_error'); | import ValidationError = require('../../errors/validation_error'); | ||||||
| const blobService = require('../../services/blob'); | import blobService = require('../../services/blob'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import BBranch = require('../../becca/entities/bbranch'); | ||||||
|  | import { AttributeRow } from '../../becca/entities/rows'; | ||||||
| 
 | 
 | ||||||
| function getNote(req) { | function getNote(req: Request) { | ||||||
|     return becca.getNoteOrThrow(req.params.noteId); |     return becca.getNoteOrThrow(req.params.noteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getNoteBlob(req) { | function getNoteBlob(req: Request) { | ||||||
|     return blobService.getBlobPojo('notes', req.params.noteId); |     return blobService.getBlobPojo('notes', req.params.noteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getNoteMetadata(req) { | function getNoteMetadata(req: Request) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
| @ -30,12 +33,20 @@ function getNoteMetadata(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createNote(req) { | function createNote(req: Request) { | ||||||
|     const params = Object.assign({}, req.body); // clone
 |     const params = Object.assign({}, req.body); // clone
 | ||||||
|     params.parentNoteId = req.params.parentNoteId; |     params.parentNoteId = req.params.parentNoteId; | ||||||
| 
 | 
 | ||||||
|     const { target, targetBranchId } = req.query; |     const { target, targetBranchId } = req.query; | ||||||
| 
 | 
 | ||||||
|  |     if (target !== "into" && target !== "after") { | ||||||
|  |         throw new ValidationError("Invalid target type."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (targetBranchId && typeof targetBranchId !== "string") { | ||||||
|  |         throw new ValidationError("Missing or incorrect type for target branch ID."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const { note, branch } = noteService.createNewNoteWithTarget(target, targetBranchId, params); |     const { note, branch } = noteService.createNewNoteWithTarget(target, targetBranchId, params); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
| @ -44,14 +55,14 @@ function createNote(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateNoteData(req) { | function updateNoteData(req: Request) { | ||||||
|     const {content, attachments} = req.body; |     const {content, attachments} = req.body; | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| 
 | 
 | ||||||
|     return noteService.updateNoteData(noteId, content, attachments); |     return noteService.updateNoteData(noteId, content, attachments); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function deleteNote(req) { | function deleteNote(req: Request) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const taskId = req.query.taskId; |     const taskId = req.query.taskId; | ||||||
|     const eraseNotes = req.query.eraseNotes === 'true'; |     const eraseNotes = req.query.eraseNotes === 'true'; | ||||||
| @ -60,8 +71,11 @@ function deleteNote(req) { | |||||||
|     // note how deleteId is separate from taskId - single taskId produces separate deleteId for each "top level" deleted note
 |     // note how deleteId is separate from taskId - single taskId produces separate deleteId for each "top level" deleted note
 | ||||||
|     const deleteId = utils.randomString(10); |     const deleteId = utils.randomString(10); | ||||||
| 
 | 
 | ||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
|  |     if (typeof taskId !== "string") { | ||||||
|  |         throw new ValidationError("Missing or incorrect type for task ID."); | ||||||
|  |     } | ||||||
|     const taskContext = TaskContext.getInstance(taskId, 'deleteNotes'); |     const taskContext = TaskContext.getInstance(taskId, 'deleteNotes'); | ||||||
| 
 | 
 | ||||||
|     note.deleteNote(deleteId, taskContext); |     note.deleteNote(deleteId, taskContext); | ||||||
| @ -75,7 +89,7 @@ function deleteNote(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function undeleteNote(req) { | function undeleteNote(req: Request) { | ||||||
|     const taskContext = TaskContext.getInstance(utils.randomString(10), 'undeleteNotes'); |     const taskContext = TaskContext.getInstance(utils.randomString(10), 'undeleteNotes'); | ||||||
| 
 | 
 | ||||||
|     noteService.undeleteNote(req.params.noteId, taskContext); |     noteService.undeleteNote(req.params.noteId, taskContext); | ||||||
| @ -83,7 +97,7 @@ function undeleteNote(req) { | |||||||
|     taskContext.taskSucceeded(); |     taskContext.taskSucceeded(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function sortChildNotes(req) { | function sortChildNotes(req: Request) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const {sortBy, sortDirection, foldersFirst, sortNatural, sortLocale} = req.body; |     const {sortBy, sortDirection, foldersFirst, sortNatural, sortLocale} = req.body; | ||||||
| 
 | 
 | ||||||
| @ -94,11 +108,11 @@ function sortChildNotes(req) { | |||||||
|     treeService.sortNotes(noteId, sortBy, reverse, foldersFirst, sortNatural, sortLocale); |     treeService.sortNotes(noteId, sortBy, reverse, foldersFirst, sortNatural, sortLocale); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function protectNote(req) { | function protectNote(req: Request) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const note = becca.notes[noteId]; |     const note = becca.notes[noteId]; | ||||||
|     const protect = !!parseInt(req.params.isProtected); |     const protect = !!parseInt(req.params.isProtected); | ||||||
|     const includingSubTree = !!parseInt(req.query.subtree); |     const includingSubTree = !!parseInt(req.query?.subtree as string); | ||||||
| 
 | 
 | ||||||
|     const taskContext = new TaskContext(utils.randomString(10), 'protectNotes', {protect}); |     const taskContext = new TaskContext(utils.randomString(10), 'protectNotes', {protect}); | ||||||
| 
 | 
 | ||||||
| @ -107,18 +121,18 @@ function protectNote(req) { | |||||||
|     taskContext.taskSucceeded(); |     taskContext.taskSucceeded(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function setNoteTypeMime(req) { | function setNoteTypeMime(req: Request) { | ||||||
|     // can't use [] destructuring because req.params is not iterable
 |     // can't use [] destructuring because req.params is not iterable
 | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const {type, mime} = req.body; |     const {type, mime} = req.body; | ||||||
| 
 | 
 | ||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
|     note.type = type; |     note.type = type; | ||||||
|     note.mime = mime; |     note.mime = mime; | ||||||
|     note.save(); |     note.save(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function changeTitle(req) { | function changeTitle(req: Request) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const title = req.body.title; |     const title = req.body.title; | ||||||
| 
 | 
 | ||||||
| @ -145,7 +159,7 @@ function changeTitle(req) { | |||||||
|     return note; |     return note; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function duplicateSubtree(req) { | function duplicateSubtree(req: Request) { | ||||||
|     const {noteId, parentNoteId} = req.params; |     const {noteId, parentNoteId} = req.params; | ||||||
| 
 | 
 | ||||||
|     return noteService.duplicateSubtree(noteId, parentNoteId); |     return noteService.duplicateSubtree(noteId, parentNoteId); | ||||||
| @ -159,14 +173,14 @@ function eraseUnusedAttachmentsNow() { | |||||||
|     eraseService.eraseUnusedAttachmentsNow(); |     eraseService.eraseUnusedAttachmentsNow(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getDeleteNotesPreview(req) { | function getDeleteNotesPreview(req: Request) { | ||||||
|     const {branchIdsToDelete, deleteAllClones} = req.body; |     const {branchIdsToDelete, deleteAllClones} = req.body; | ||||||
| 
 | 
 | ||||||
|     const noteIdsToBeDeleted = new Set(); |     const noteIdsToBeDeleted = new Set<string>(); | ||||||
|     const strongBranchCountToDelete = {}; // noteId => count (integer)
 |     const strongBranchCountToDelete: Record<string, number> = {}; // noteId => count
 | ||||||
| 
 | 
 | ||||||
|     function branchPreviewDeletion(branch) { |     function branchPreviewDeletion(branch: BBranch) { | ||||||
|         if (branch.isWeak) { |         if (branch.isWeak || !branch.branchId) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -196,18 +210,18 @@ function getDeleteNotesPreview(req) { | |||||||
|         branchPreviewDeletion(branch); |         branchPreviewDeletion(branch); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let brokenRelations = []; |     let brokenRelations: AttributeRow[] = []; | ||||||
| 
 | 
 | ||||||
|     if (noteIdsToBeDeleted.size > 0) { |     if (noteIdsToBeDeleted.size > 0) { | ||||||
|         sql.fillParamList(noteIdsToBeDeleted); |         sql.fillParamList(noteIdsToBeDeleted); | ||||||
| 
 | 
 | ||||||
|         // FIXME: No need to do this in database, can be done with becca data
 |         // FIXME: No need to do this in database, can be done with becca data
 | ||||||
|         brokenRelations = sql.getRows(` |         brokenRelations = sql.getRows<AttributeRow>(` | ||||||
|             SELECT attr.noteId, attr.name, attr.value |             SELECT attr.noteId, attr.name, attr.value | ||||||
|             FROM attributes attr |             FROM attributes attr | ||||||
|                      JOIN param_list ON param_list.paramId = attr.value |                      JOIN param_list ON param_list.paramId = attr.value | ||||||
|             WHERE attr.isDeleted = 0 |             WHERE attr.isDeleted = 0 | ||||||
|               AND attr.type = 'relation'`).filter(attr => !noteIdsToBeDeleted.has(attr.noteId));
 |               AND attr.type = 'relation'`).filter(attr => attr.noteId && !noteIdsToBeDeleted.has(attr.noteId));
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
| @ -216,7 +230,7 @@ function getDeleteNotesPreview(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function forceSaveRevision(req) { | function forceSaveRevision(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
| @ -227,7 +241,7 @@ function forceSaveRevision(req) { | |||||||
|     note.saveRevision(); |     note.saveRevision(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function convertNoteToAttachment(req) { | function convertNoteToAttachment(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const note = becca.getNoteOrThrow(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
| @ -236,7 +250,7 @@ function convertNoteToAttachment(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getNote, |     getNote, | ||||||
|     getNoteBlob, |     getNoteBlob, | ||||||
|     getNoteMetadata, |     getNoteMetadata, | ||||||
| @ -1,9 +1,10 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const optionService = require('../../services/options'); | import optionService = require('../../services/options'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const searchService = require('../../services/search/services/search'); | import searchService = require('../../services/search/services/search'); | ||||||
| const ValidationError = require('../../errors/validation_error'); | import ValidationError = require('../../errors/validation_error'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| // options allowed to be updated directly in the Options dialog
 | // options allowed to be updated directly in the Options dialog
 | ||||||
| const ALLOWED_OPTIONS = new Set([ | const ALLOWED_OPTIONS = new Set([ | ||||||
| @ -62,7 +63,7 @@ const ALLOWED_OPTIONS = new Set([ | |||||||
| 
 | 
 | ||||||
| function getOptions() { | function getOptions() { | ||||||
|     const optionMap = optionService.getOptionMap(); |     const optionMap = optionService.getOptionMap(); | ||||||
|     const resultMap = {}; |     const resultMap: Record<string, string> = {}; | ||||||
| 
 | 
 | ||||||
|     for (const optionName in optionMap) { |     for (const optionName in optionMap) { | ||||||
|         if (isAllowed(optionName)) { |         if (isAllowed(optionName)) { | ||||||
| @ -75,7 +76,7 @@ function getOptions() { | |||||||
|     return resultMap; |     return resultMap; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateOption(req) { | function updateOption(req: Request) { | ||||||
|     const {name, value} = req.params; |     const {name, value} = req.params; | ||||||
| 
 | 
 | ||||||
|     if (!update(name, value)) { |     if (!update(name, value)) { | ||||||
| @ -83,7 +84,7 @@ function updateOption(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateOptions(req) { | function updateOptions(req: Request) { | ||||||
|     for (const optionName in req.body) { |     for (const optionName in req.body) { | ||||||
|         if (!update(optionName, req.body[optionName])) { |         if (!update(optionName, req.body[optionName])) { | ||||||
|             // this should be improved
 |             // this should be improved
 | ||||||
| @ -93,7 +94,7 @@ function updateOptions(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function update(name, value) { | function update(name: string, value: string) { | ||||||
|     if (!isAllowed(name)) { |     if (!isAllowed(name)) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @ -128,14 +129,14 @@ function getUserThemes() { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function isAllowed(name) { | function isAllowed(name: string) { | ||||||
|     return ALLOWED_OPTIONS.has(name) |     return ALLOWED_OPTIONS.has(name) | ||||||
|         || name.startsWith("keyboardShortcuts") |         || name.startsWith("keyboardShortcuts") | ||||||
|         || name.endsWith("Collapsed") |         || name.endsWith("Collapsed") | ||||||
|         || name.startsWith("hideArchivedNotes"); |         || name.startsWith("hideArchivedNotes"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getOptions, |     getOptions, | ||||||
|     updateOption, |     updateOption, | ||||||
|     updateOptions, |     updateOptions, | ||||||
| @ -1,8 +1,10 @@ | |||||||
| const becca = require('../../becca/becca'); | import { Request } from "express"; | ||||||
| const markdownService = require('../../services/import/markdown'); | 
 | ||||||
|  | import becca = require('../../becca/becca'); | ||||||
|  | import markdownService = require('../../services/import/markdown'); | ||||||
| 
 | 
 | ||||||
| function getIconUsage() { | function getIconUsage() { | ||||||
|     const iconClassToCountMap = {}; |     const iconClassToCountMap: Record<string, number> = {}; | ||||||
| 
 | 
 | ||||||
|     for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) { |     for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) { | ||||||
|         if (noteId.startsWith("_")) { |         if (noteId.startsWith("_")) { | ||||||
| @ -25,7 +27,7 @@ function getIconUsage() { | |||||||
|     return { iconClassToCountMap }; |     return { iconClassToCountMap }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function renderMarkdown(req) { | function renderMarkdown(req: Request) { | ||||||
|     const { markdownContent } = req.body; |     const { markdownContent } = req.body; | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
| @ -33,7 +35,7 @@ function renderMarkdown(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getIconUsage, |     getIconUsage, | ||||||
|     renderMarkdown |     renderMarkdown | ||||||
| }; | }; | ||||||
| @ -1,9 +1,10 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const passwordService = require('../../services/encryption/password'); | import passwordService = require('../../services/encryption/password'); | ||||||
| const ValidationError = require('../../errors/validation_error'); | import ValidationError = require('../../errors/validation_error'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function changePassword(req) { | function changePassword(req: Request) { | ||||||
|     if (passwordService.isPasswordSet()) { |     if (passwordService.isPasswordSet()) { | ||||||
|         return passwordService.changePassword(req.body.current_password, req.body.new_password); |         return passwordService.changePassword(req.body.current_password, req.body.new_password); | ||||||
|     } |     } | ||||||
| @ -12,7 +13,7 @@ function changePassword(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function resetPassword(req) { | function resetPassword(req: Request) { | ||||||
|     // protection against accidental call (not a security measure)
 |     // protection against accidental call (not a security measure)
 | ||||||
|     if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") { |     if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") { | ||||||
|         throw new ValidationError("Incorrect password reset confirmation"); |         throw new ValidationError("Incorrect password reset confirmation"); | ||||||
| @ -21,7 +22,7 @@ function resetPassword(req) { | |||||||
|     return passwordService.resetPassword(); |     return passwordService.resetPassword(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     changePassword, |     changePassword, | ||||||
|     resetPassword |     resetPassword | ||||||
| }; | }; | ||||||
| @ -1,16 +1,30 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const protectedSessionService = require('../../services/protected_session'); | import protectedSessionService = require('../../services/protected_session'); | ||||||
| const noteService = require('../../services/notes'); | import noteService = require('../../services/notes'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import { RevisionRow } from '../../becca/entities/rows'; | ||||||
| 
 | 
 | ||||||
| function getRecentChanges(req) { | interface RecentChangeRow { | ||||||
|  |     noteId: string; | ||||||
|  |     current_isDeleted: boolean; | ||||||
|  |     current_deleteId: string; | ||||||
|  |     current_title: string; | ||||||
|  |     current_isProtected: boolean, | ||||||
|  |     title: string; | ||||||
|  |     utcDate: string; | ||||||
|  |     date: string; | ||||||
|  |     canBeUndeleted?: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getRecentChanges(req: Request) { | ||||||
|     const {ancestorNoteId} = req.params; |     const {ancestorNoteId} = req.params; | ||||||
| 
 | 
 | ||||||
|     let recentChanges = []; |     let recentChanges = []; | ||||||
| 
 | 
 | ||||||
|     const revisionRows = sql.getRows(` |     const revisionRows = sql.getRows<RecentChangeRow>(` | ||||||
|         SELECT  |         SELECT  | ||||||
|             notes.noteId, |             notes.noteId, | ||||||
|             notes.isDeleted AS current_isDeleted, |             notes.isDeleted AS current_isDeleted, | ||||||
| @ -36,7 +50,7 @@ function getRecentChanges(req) { | |||||||
|     // now we need to also collect date points not represented in note revisions:
 |     // now we need to also collect date points not represented in note revisions:
 | ||||||
|     // 1. creation for all notes (dateCreated)
 |     // 1. creation for all notes (dateCreated)
 | ||||||
|     // 2. deletion for deleted notes (dateModified)
 |     // 2. deletion for deleted notes (dateModified)
 | ||||||
|     const noteRows = sql.getRows(` |     const noteRows = sql.getRows<RecentChangeRow>(` | ||||||
|             SELECT |             SELECT | ||||||
|                 notes.noteId, |                 notes.noteId, | ||||||
|                 notes.isDeleted AS current_isDeleted, |                 notes.isDeleted AS current_isDeleted, | ||||||
| @ -76,8 +90,8 @@ function getRecentChanges(req) { | |||||||
|     for (const change of recentChanges) { |     for (const change of recentChanges) { | ||||||
|         if (change.current_isProtected) { |         if (change.current_isProtected) { | ||||||
|             if (protectedSessionService.isProtectedSessionAvailable()) { |             if (protectedSessionService.isProtectedSessionAvailable()) { | ||||||
|                 change.title = protectedSessionService.decryptString(change.title); |                 change.title = protectedSessionService.decryptString(change.title) || "[protected]"; | ||||||
|                 change.current_title = protectedSessionService.decryptString(change.current_title); |                 change.current_title = protectedSessionService.decryptString(change.current_title) || "[protected]"; | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 change.title = change.current_title = "[protected]"; |                 change.title = change.current_title = "[protected]"; | ||||||
| @ -97,6 +111,6 @@ function getRecentChanges(req) { | |||||||
|     return recentChanges; |     return recentChanges; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getRecentChanges |     getRecentChanges | ||||||
| }; | }; | ||||||
| @ -1,10 +1,11 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const BRecentNote = require('../../becca/entities/brecent_note'); | import BRecentNote = require('../../becca/entities/brecent_note'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const dateUtils = require('../../services/date_utils'); | import dateUtils = require('../../services/date_utils'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function addRecentNote(req) { | function addRecentNote(req: Request) { | ||||||
|     new BRecentNote({ |     new BRecentNote({ | ||||||
|         noteId: req.body.noteId, |         noteId: req.body.noteId, | ||||||
|         notePath: req.body.notePath |         notePath: req.body.notePath | ||||||
| @ -18,6 +19,6 @@ function addRecentNote(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     addRecentNote |     addRecentNote | ||||||
| }; | }; | ||||||
| @ -1,10 +1,22 @@ | |||||||
| const becca = require('../../becca/becca'); | import { Request } from 'express'; | ||||||
| const sql = require('../../services/sql'); | import becca = require('../../becca/becca'); | ||||||
|  | import sql = require('../../services/sql'); | ||||||
| 
 | 
 | ||||||
| function getRelationMap(req) { | interface ResponseData { | ||||||
|  |     noteTitles: Record<string, string>; | ||||||
|  |     relations: { | ||||||
|  |         attributeId: string, | ||||||
|  |         sourceNoteId: string, | ||||||
|  |         targetNoteId: string, | ||||||
|  |         name: string | ||||||
|  |     }[]; | ||||||
|  |     inverseRelations: Record<string, string>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getRelationMap(req: Request) { | ||||||
|     const {relationMapNoteId, noteIds} = req.body; |     const {relationMapNoteId, noteIds} = req.body; | ||||||
| 
 | 
 | ||||||
|     const resp = { |     const resp: ResponseData = { | ||||||
|         // noteId => title
 |         // noteId => title
 | ||||||
|         noteTitles: {}, |         noteTitles: {}, | ||||||
|         relations: [], |         relations: [], | ||||||
| @ -14,13 +26,13 @@ function getRelationMap(req) { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (noteIds.length === 0) { |     if (!Array.isArray(noteIds) || noteIds.length === 0) { | ||||||
|         return resp; |         return resp; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const questionMarks = noteIds.map(noteId => '?').join(','); |     const questionMarks = noteIds.map(noteId => '?').join(','); | ||||||
| 
 | 
 | ||||||
|     const relationMapNote = becca.getNote(relationMapNoteId); |     const relationMapNote = becca.getNoteOrThrow(relationMapNoteId); | ||||||
| 
 | 
 | ||||||
|     const displayRelationsVal = relationMapNote.getLabelValue('displayRelations'); |     const displayRelationsVal = relationMapNote.getLabelValue('displayRelations'); | ||||||
|     const displayRelations = !displayRelationsVal ? [] : displayRelationsVal |     const displayRelations = !displayRelationsVal ? [] : displayRelationsVal | ||||||
| @ -32,7 +44,7 @@ function getRelationMap(req) { | |||||||
|         .split(",") |         .split(",") | ||||||
|         .map(token => token.trim()); |         .map(token => token.trim()); | ||||||
| 
 | 
 | ||||||
|     const foundNoteIds = sql.getColumn(`SELECT noteId FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); |     const foundNoteIds = sql.getColumn<string>(`SELECT noteId FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds); | ||||||
|     const notes = becca.getNotes(foundNoteIds); |     const notes = becca.getNotes(foundNoteIds); | ||||||
| 
 | 
 | ||||||
|     for (const note of notes) { |     for (const note of notes) { | ||||||
| @ -64,6 +76,6 @@ function getRelationMap(req) { | |||||||
|     return resp; |     return resp; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getRelationMap |     getRelationMap | ||||||
| }; | }; | ||||||
| @ -1,22 +1,38 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const beccaService = require('../../becca/becca_service'); | import beccaService = require('../../becca/becca_service'); | ||||||
| const revisionService = require('../../services/revisions'); | import revisionService = require('../../services/revisions'); | ||||||
| const utils = require('../../services/utils'); | import utils = require('../../services/utils'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const cls = require('../../services/cls'); | import cls = require('../../services/cls'); | ||||||
| const path = require('path'); | import path = require('path'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const blobService = require('../../services/blob'); | import blobService = require('../../services/blob'); | ||||||
| const eraseService = require("../../services/erase"); | import eraseService = require("../../services/erase"); | ||||||
|  | import { Request, Response } from 'express'; | ||||||
|  | import BRevision = require('../../becca/entities/brevision'); | ||||||
|  | import BNote = require('../../becca/entities/bnote'); | ||||||
|  | import { NotePojo } from '../../becca/becca-interface'; | ||||||
| 
 | 
 | ||||||
| function getRevisionBlob(req) { | interface NotePath { | ||||||
|  |     noteId: string; | ||||||
|  |     branchId?: string; | ||||||
|  |     title: string; | ||||||
|  |     notePath: string[]; | ||||||
|  |     path: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface NotePojoWithNotePath extends NotePojo { | ||||||
|  |     notePath?: string[] | null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getRevisionBlob(req: Request) { | ||||||
|     const preview = req.query.preview === 'true'; |     const preview = req.query.preview === 'true'; | ||||||
| 
 | 
 | ||||||
|     return blobService.getBlobPojo('revisions', req.params.revisionId, { preview }); |     return blobService.getBlobPojo('revisions', req.params.revisionId, { preview }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getRevisions(req) { | function getRevisions(req: Request) { | ||||||
|     return becca.getRevisionsFromQuery(` |     return becca.getRevisionsFromQuery(` | ||||||
|         SELECT revisions.*, |         SELECT revisions.*, | ||||||
|                LENGTH(blobs.content) AS contentLength |                LENGTH(blobs.content) AS contentLength | ||||||
| @ -26,12 +42,12 @@ function getRevisions(req) { | |||||||
|         ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]);
 |         ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]);
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getRevision(req) { | function getRevision(req: Request) { | ||||||
|     const revision = becca.getRevision(req.params.revisionId); |     const revision = becca.getRevisionOrThrow(req.params.revisionId); | ||||||
| 
 | 
 | ||||||
|     if (revision.type === 'file') { |     if (revision.type === 'file') { | ||||||
|         if (revision.hasStringContent()) { |         if (revision.hasStringContent()) { | ||||||
|             revision.content = revision.getContent().substr(0, 10000); |             revision.content = (revision.getContent() as string).substr(0, 10000); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
| @ -45,11 +61,7 @@ function getRevision(req) { | |||||||
|     return revision; |     return revision; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | function getRevisionFilename(revision: BRevision) { | ||||||
|  * @param {BRevision} revision |  | ||||||
|  * @returns {string} |  | ||||||
|  */ |  | ||||||
| function getRevisionFilename(revision) { |  | ||||||
|     let filename = utils.formatDownloadTitle(revision.title, revision.type, revision.mime); |     let filename = utils.formatDownloadTitle(revision.title, revision.type, revision.mime); | ||||||
| 
 | 
 | ||||||
|     const extension = path.extname(filename); |     const extension = path.extname(filename); | ||||||
| @ -68,8 +80,8 @@ function getRevisionFilename(revision) { | |||||||
|     return filename; |     return filename; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function downloadRevision(req, res) { | function downloadRevision(req: Request, res: Response) { | ||||||
|     const revision = becca.getRevision(req.params.revisionId); |     const revision = becca.getRevisionOrThrow(req.params.revisionId); | ||||||
| 
 | 
 | ||||||
|     if (!revision.isContentAvailable()) { |     if (!revision.isContentAvailable()) { | ||||||
|         return res.setHeader("Content-Type", "text/plain") |         return res.setHeader("Content-Type", "text/plain") | ||||||
| @ -85,18 +97,18 @@ function downloadRevision(req, res) { | |||||||
|     res.send(revision.getContent()); |     res.send(revision.getContent()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function eraseAllRevisions(req) { | function eraseAllRevisions(req: Request) { | ||||||
|     const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?', |     const revisionIdsToErase = sql.getColumn<string>('SELECT revisionId FROM revisions WHERE noteId = ?', | ||||||
|         [req.params.noteId]); |         [req.params.noteId]); | ||||||
| 
 | 
 | ||||||
|     eraseService.eraseRevisions(revisionIdsToErase); |     eraseService.eraseRevisions(revisionIdsToErase); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function eraseRevision(req) { | function eraseRevision(req: Request) { | ||||||
|     eraseService.eraseRevisions([req.params.revisionId]); |     eraseService.eraseRevisions([req.params.revisionId]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function restoreRevision(req) { | function restoreRevision(req: Request) { | ||||||
|     const revision = becca.getRevision(req.params.revisionId); |     const revision = becca.getRevision(req.params.revisionId); | ||||||
| 
 | 
 | ||||||
|     if (revision) { |     if (revision) { | ||||||
| @ -117,8 +129,10 @@ function restoreRevision(req) { | |||||||
|                 noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true }); |                 noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true }); | ||||||
| 
 | 
 | ||||||
|                 // content is rewritten to point to the restored revision attachments
 |                 // content is rewritten to point to the restored revision attachments
 | ||||||
|  |                 if (typeof revisionContent === "string") { | ||||||
|                     revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`); |                     revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`); | ||||||
|                 } |                 } | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             note.title = revision.title; |             note.title = revision.title; | ||||||
|             note.setContent(revisionContent, { forceSave: true }); |             note.setContent(revisionContent, { forceSave: true }); | ||||||
| @ -126,8 +140,8 @@ function restoreRevision(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getEditedNotesOnDate(req) { | function getEditedNotesOnDate(req: Request) { | ||||||
|     const noteIds = sql.getColumn(` |     const noteIds = sql.getColumn<string>(` | ||||||
|         SELECT notes.* |         SELECT notes.* | ||||||
|         FROM notes |         FROM notes | ||||||
|         WHERE noteId IN ( |         WHERE noteId IN ( | ||||||
| @ -152,7 +166,7 @@ function getEditedNotesOnDate(req) { | |||||||
|     return notes.map(note => { |     return notes.map(note => { | ||||||
|         const notePath = getNotePathData(note); |         const notePath = getNotePathData(note); | ||||||
| 
 | 
 | ||||||
|         const notePojo = note.getPojo(); |         const notePojo: NotePojoWithNotePath = note.getPojo(); | ||||||
|         notePojo.notePath = notePath ? notePath.notePath : null; |         notePojo.notePath = notePath ? notePath.notePath : null; | ||||||
| 
 | 
 | ||||||
|         return notePojo; |         return notePojo; | ||||||
| @ -160,7 +174,7 @@ function getEditedNotesOnDate(req) { | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getNotePathData(note) { | function getNotePathData(note: BNote): NotePath | undefined { | ||||||
|     const retPath = note.getBestNotePath(); |     const retPath = note.getBestNotePath(); | ||||||
| 
 | 
 | ||||||
|     if (retPath) { |     if (retPath) { | ||||||
| @ -173,7 +187,7 @@ function getNotePathData(note) { | |||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             const parentNote = note.parents[0]; |             const parentNote = note.parents[0]; | ||||||
|             branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId).branchId; |             branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId)?.branchId; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return { |         return { | ||||||
| @ -186,7 +200,7 @@ function getNotePathData(note) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getRevisionBlob, |     getRevisionBlob, | ||||||
|     getRevisions, |     getRevisions, | ||||||
|     getRevision, |     getRevision, | ||||||
| @ -1,19 +1,30 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const scriptService = require('../../services/script'); | import scriptService = require('../../services/script'); | ||||||
| const attributeService = require('../../services/attributes'); | import attributeService = require('../../services/attributes'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const syncService = require('../../services/sync'); | import syncService = require('../../services/sync'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | 
 | ||||||
|  | interface ScriptBody { | ||||||
|  |     script: string; | ||||||
|  |     params: any[]; | ||||||
|  |     startNoteId: string; | ||||||
|  |     currentNoteId: string; | ||||||
|  |     originEntityName: string; | ||||||
|  |     originEntityId: string; | ||||||
|  |     transactional: boolean; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // The async/await here is very confusing, because the body.script may, but may not be async. If it is async, then we
 | // The async/await here is very confusing, because the body.script may, but may not be async. If it is async, then we
 | ||||||
| // need to await it and make the complete response including metadata available in a Promise, so that the route detects
 | // need to await it and make the complete response including metadata available in a Promise, so that the route detects
 | ||||||
| // this and does result.then().
 | // this and does result.then().
 | ||||||
| async function exec(req) { | async function exec(req: Request) { | ||||||
|     try { |     try { | ||||||
|         const { body } = req; |         const body = (req.body as ScriptBody); | ||||||
| 
 | 
 | ||||||
|         const execute = body => scriptService.executeScript( |         const execute = (body: ScriptBody) => scriptService.executeScript( | ||||||
|             body.script, |             body.script, | ||||||
|             body.params, |             body.params, | ||||||
|             body.startNoteId, |             body.startNoteId, | ||||||
| @ -32,20 +43,20 @@ async function exec(req) { | |||||||
|             maxEntityChangeId: syncService.getMaxEntityChangeId() |             maxEntityChangeId: syncService.getMaxEntityChangeId() | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         return { success: false, error: e.message }; |         return { success: false, error: e.message }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function run(req) { | function run(req: Request) { | ||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     const result = scriptService.executeNote(note, { originEntity: note }); |     const result = scriptService.executeNote(note, { originEntity: note }); | ||||||
| 
 | 
 | ||||||
|     return { executionResult: result }; |     return { executionResult: result }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getBundlesWithLabel(label, value) { | function getBundlesWithLabel(label: string, value?: string) { | ||||||
|     const notes = attributeService.getNotesWithLabel(label, value); |     const notes = attributeService.getNotesWithLabel(label, value); | ||||||
| 
 | 
 | ||||||
|     const bundles = []; |     const bundles = []; | ||||||
| @ -61,7 +72,7 @@ function getBundlesWithLabel(label, value) { | |||||||
|     return bundles; |     return bundles; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getStartupBundles(req) { | function getStartupBundles(req: Request) { | ||||||
|     if (!process.env.TRILIUM_SAFE_MODE) { |     if (!process.env.TRILIUM_SAFE_MODE) { | ||||||
|         if (req.query.mobile === "true") { |         if (req.query.mobile === "true") { | ||||||
|             return getBundlesWithLabel("run", "mobileStartup"); |             return getBundlesWithLabel("run", "mobileStartup"); | ||||||
| @ -84,9 +95,9 @@ function getWidgetBundles() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getRelationBundles(req) { | function getRelationBundles(req: Request) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNoteOrThrow(noteId); | ||||||
|     const relationName = req.params.relationName; |     const relationName = req.params.relationName; | ||||||
| 
 | 
 | ||||||
|     const attributes = note.getAttributes(); |     const attributes = note.getAttributes(); | ||||||
| @ -97,7 +108,7 @@ function getRelationBundles(req) { | |||||||
|     const bundles = []; |     const bundles = []; | ||||||
| 
 | 
 | ||||||
|     for (const noteId of uniqueNoteIds) { |     for (const noteId of uniqueNoteIds) { | ||||||
|         const note = becca.getNote(noteId); |         const note = becca.getNoteOrThrow(noteId); | ||||||
| 
 | 
 | ||||||
|         if (!note.isJavaScript() || note.getScriptEnv() !== 'frontend') { |         if (!note.isJavaScript() || note.getScriptEnv() !== 'frontend') { | ||||||
|             continue; |             continue; | ||||||
| @ -113,14 +124,14 @@ function getRelationBundles(req) { | |||||||
|     return bundles; |     return bundles; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getBundle(req) { | function getBundle(req: Request) { | ||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
|     const { script, params } = req.body; |     const { script, params } = req.body; | ||||||
| 
 | 
 | ||||||
|     return scriptService.getScriptBundleForFrontend(note, script, params); |     return scriptService.getScriptBundleForFrontend(note, script, params); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     exec, |     exec, | ||||||
|     run, |     run, | ||||||
|     getStartupBundles, |     getStartupBundles, | ||||||
| @ -1,14 +1,17 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const becca = require('../../becca/becca'); | import { Request } from "express"; | ||||||
| const SearchContext = require('../../services/search/search_context'); |  | ||||||
| const searchService = require('../../services/search/services/search'); |  | ||||||
| const bulkActionService = require('../../services/bulk_actions'); |  | ||||||
| const cls = require('../../services/cls'); |  | ||||||
| const {formatAttrForSearch} = require('../../services/attribute_formatter'); |  | ||||||
| const ValidationError = require('../../errors/validation_error'); |  | ||||||
| 
 | 
 | ||||||
| function searchFromNote(req) { | import becca = require('../../becca/becca'); | ||||||
|  | import SearchContext = require('../../services/search/search_context'); | ||||||
|  | import searchService = require('../../services/search/services/search'); | ||||||
|  | import bulkActionService = require('../../services/bulk_actions'); | ||||||
|  | import cls = require('../../services/cls'); | ||||||
|  | import attributeFormatter = require('../../services/attribute_formatter'); | ||||||
|  | import ValidationError = require('../../errors/validation_error'); | ||||||
|  | import SearchResult = require("../../services/search/search_result"); | ||||||
|  | 
 | ||||||
|  | function searchFromNote(req: Request) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
| @ -23,7 +26,7 @@ function searchFromNote(req) { | |||||||
|     return searchService.searchFromNote(note); |     return searchService.searchFromNote(note); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function searchAndExecute(req) { | function searchAndExecute(req: Request) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
| @ -40,7 +43,7 @@ function searchAndExecute(req) { | |||||||
|     bulkActionService.executeActions(note, searchResultNoteIds); |     bulkActionService.executeActions(note, searchResultNoteIds); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function quickSearch(req) { | function quickSearch(req: Request) { | ||||||
|     const {searchString} = req.params; |     const {searchString} = req.params; | ||||||
| 
 | 
 | ||||||
|     const searchContext = new SearchContext({ |     const searchContext = new SearchContext({ | ||||||
| @ -58,7 +61,7 @@ function quickSearch(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function search(req) { | function search(req: Request) { | ||||||
|     const {searchString} = req.params; |     const {searchString} = req.params; | ||||||
| 
 | 
 | ||||||
|     const searchContext = new SearchContext({ |     const searchContext = new SearchContext({ | ||||||
| @ -72,7 +75,7 @@ function search(req) { | |||||||
|         .map(sr => sr.noteId); |         .map(sr => sr.noteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getRelatedNotes(req) { | function getRelatedNotes(req: Request) { | ||||||
|     const attr = req.body; |     const attr = req.body; | ||||||
| 
 | 
 | ||||||
|     const searchSettings = { |     const searchSettings = { | ||||||
| @ -81,10 +84,10 @@ function getRelatedNotes(req) { | |||||||
|         fuzzyAttributeSearch: false |         fuzzyAttributeSearch: false | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const matchingNameAndValue = searchService.findResultsWithQuery(formatAttrForSearch(attr, true), new SearchContext(searchSettings)); |     const matchingNameAndValue = searchService.findResultsWithQuery(attributeFormatter.formatAttrForSearch(attr, true), new SearchContext(searchSettings)); | ||||||
|     const matchingName = searchService.findResultsWithQuery(formatAttrForSearch(attr, false), new SearchContext(searchSettings)); |     const matchingName = searchService.findResultsWithQuery(attributeFormatter.formatAttrForSearch(attr, false), new SearchContext(searchSettings)); | ||||||
| 
 | 
 | ||||||
|     const results = []; |     const results: SearchResult[] = []; | ||||||
| 
 | 
 | ||||||
|     const allResults = matchingNameAndValue.concat(matchingName); |     const allResults = matchingNameAndValue.concat(matchingName); | ||||||
| 
 | 
 | ||||||
| @ -123,7 +126,7 @@ function searchTemplates() { | |||||||
|     }).map(note => note.noteId); |     }).map(note => note.noteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     searchFromNote, |     searchFromNote, | ||||||
|     searchAndExecute, |     searchAndExecute, | ||||||
|     getRelatedNotes, |     getRelatedNotes, | ||||||
| @ -1,66 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| const imageType = require('image-type'); |  | ||||||
| const imageService = require('../../services/image'); |  | ||||||
| const noteService = require('../../services/notes'); |  | ||||||
| const { sanitizeAttributeName } = require('../../services/sanitize_attribute_name'); |  | ||||||
| const specialNotesService = require('../../services/special_notes'); |  | ||||||
| 
 |  | ||||||
| function uploadImage(req) { |  | ||||||
|     const file = req.file; |  | ||||||
| 
 |  | ||||||
|     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { |  | ||||||
|         return [400, `Unknown image type: ${file.mimetype}`]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const originalName = `Sender image.${imageType(file.buffer).ext}`; |  | ||||||
| 
 |  | ||||||
|     const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']); |  | ||||||
| 
 |  | ||||||
|     const { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true); |  | ||||||
| 
 |  | ||||||
|     const labelsStr = req.headers['x-labels']; |  | ||||||
| 
 |  | ||||||
|     if (labelsStr?.trim()) { |  | ||||||
|         const labels = JSON.parse(labelsStr); |  | ||||||
| 
 |  | ||||||
|         for (const { name, value } of labels) { |  | ||||||
|             note.setLabel(sanitizeAttributeName(name), value); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     note.setLabel("sentFromSender"); |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         noteId: noteId |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function saveNote(req) { |  | ||||||
|     const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']); |  | ||||||
| 
 |  | ||||||
|     const { note, branch } = noteService.createNewNote({ |  | ||||||
|         parentNoteId: parentNote.noteId, |  | ||||||
|         title: req.body.title, |  | ||||||
|         content: req.body.content, |  | ||||||
|         isProtected: false, |  | ||||||
|         type: 'text', |  | ||||||
|         mime: 'text/html' |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     if (req.body.labels) { |  | ||||||
|         for (const { name, value } of req.body.labels) { |  | ||||||
|             note.setLabel(sanitizeAttributeName(name), value); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         noteId: note.noteId, |  | ||||||
|         branchId: branch.branchId |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| module.exports = { |  | ||||||
|     uploadImage, |  | ||||||
|     saveNote |  | ||||||
| }; |  | ||||||
							
								
								
									
										83
									
								
								src/routes/api/sender.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/routes/api/sender.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | import imageType = require('image-type'); | ||||||
|  | import imageService = require('../../services/image'); | ||||||
|  | import noteService = require('../../services/notes'); | ||||||
|  | import sanitize_attribute_name = require('../../services/sanitize_attribute_name'); | ||||||
|  | import specialNotesService = require('../../services/special_notes'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | 
 | ||||||
|  | function uploadImage(req: Request) { | ||||||
|  |     const file = (req as any).file; | ||||||
|  | 
 | ||||||
|  |     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { | ||||||
|  |         return [400, `Unknown image type: ${file.mimetype}`]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const uploadedImageType = imageType(file.buffer); | ||||||
|  |     if (!uploadedImageType) { | ||||||
|  |         return [400, "Unable to determine image type."]; | ||||||
|  |     } | ||||||
|  |     const originalName = `Sender image.${uploadedImageType.ext}`; | ||||||
|  | 
 | ||||||
|  |     if (!req.headers["x-local-date"] || Array.isArray(req.headers["x-local-date"])) { | ||||||
|  |         return [400, "Invalid local date"]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (Array.isArray(req.headers["x-labels"])) { | ||||||
|  |         return [400, "Invalid value type."]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']); | ||||||
|  | 
 | ||||||
|  |     const { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true); | ||||||
|  | 
 | ||||||
|  |     const labelsStr = req.headers['x-labels']; | ||||||
|  | 
 | ||||||
|  |     if (labelsStr?.trim()) { | ||||||
|  |         const labels = JSON.parse(labelsStr); | ||||||
|  | 
 | ||||||
|  |         for (const { name, value } of labels) { | ||||||
|  |             note.setLabel(sanitize_attribute_name.sanitizeAttributeName(name), value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     note.setLabel("sentFromSender"); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         noteId: noteId | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function saveNote(req: Request) { | ||||||
|  |     if (!req.headers["x-local-date"] || Array.isArray(req.headers["x-local-date"])) { | ||||||
|  |         return [400, "Invalid local date"]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']); | ||||||
|  | 
 | ||||||
|  |     const { note, branch } = noteService.createNewNote({ | ||||||
|  |         parentNoteId: parentNote.noteId, | ||||||
|  |         title: req.body.title, | ||||||
|  |         content: req.body.content, | ||||||
|  |         isProtected: false, | ||||||
|  |         type: 'text', | ||||||
|  |         mime: 'text/html' | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (req.body.labels) { | ||||||
|  |         for (const { name, value } of req.body.labels) { | ||||||
|  |             note.setLabel(sanitize_attribute_name.sanitizeAttributeName(name), value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         noteId: note.noteId, | ||||||
|  |         branchId: branch.branchId | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export = { | ||||||
|  |     uploadImage, | ||||||
|  |     saveNote | ||||||
|  | }; | ||||||
| @ -1,9 +1,10 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const sqlInit = require('../../services/sql_init'); | import sqlInit = require('../../services/sql_init'); | ||||||
| const setupService = require('../../services/setup'); | import setupService = require('../../services/setup'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const appInfo = require('../../services/app_info'); | import appInfo = require('../../services/app_info'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function getStatus() { | function getStatus() { | ||||||
|     return { |     return { | ||||||
| @ -17,13 +18,13 @@ async function setupNewDocument() { | |||||||
|     await sqlInit.createInitialDatabase(); |     await sqlInit.createInitialDatabase(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function setupSyncFromServer(req) { | function setupSyncFromServer(req: Request) { | ||||||
|     const { syncServerHost, syncProxy, password } = req.body; |     const { syncServerHost, syncProxy, password } = req.body; | ||||||
| 
 | 
 | ||||||
|     return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password); |     return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function saveSyncSeed(req) { | function saveSyncSeed(req: Request) { | ||||||
|     const { options, syncVersion } = req.body; |     const { options, syncVersion } = req.body; | ||||||
| 
 | 
 | ||||||
|     if (appInfo.syncVersion !== syncVersion) { |     if (appInfo.syncVersion !== syncVersion) { | ||||||
| @ -50,7 +51,7 @@ function getSyncSeed() { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getStatus, |     getStatus, | ||||||
|     setupNewDocument, |     setupNewDocument, | ||||||
|     setupSyncFromServer, |     setupSyncFromServer, | ||||||
| @ -1,16 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| const similarityService = require('../../becca/similarity'); |  | ||||||
| const becca = require('../../becca/becca'); |  | ||||||
| 
 |  | ||||||
| async function getSimilarNotes(req) { |  | ||||||
|     const noteId = req.params.noteId; |  | ||||||
| 
 |  | ||||||
|     const note = becca.getNoteOrThrow(noteId); |  | ||||||
| 
 |  | ||||||
|     return await similarityService.findSimilarNotes(noteId); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| module.exports = { |  | ||||||
|     getSimilarNotes |  | ||||||
| }; |  | ||||||
							
								
								
									
										18
									
								
								src/routes/api/similar_notes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/routes/api/similar_notes.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | import { Request } from "express"; | ||||||
|  | 
 | ||||||
|  | import similarityService = require('../../becca/similarity'); | ||||||
|  | import becca = require('../../becca/becca'); | ||||||
|  | 
 | ||||||
|  | async function getSimilarNotes(req: Request) { | ||||||
|  |     const noteId = req.params.noteId; | ||||||
|  | 
 | ||||||
|  |     const note = becca.getNoteOrThrow(noteId); | ||||||
|  | 
 | ||||||
|  |     return await similarityService.findSimilarNotes(noteId); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export = { | ||||||
|  |     getSimilarNotes | ||||||
|  | }; | ||||||
| @ -1,32 +1,33 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const dateNoteService = require('../../services/date_notes'); | import dateNoteService = require('../../services/date_notes'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const cls = require('../../services/cls'); | import cls = require('../../services/cls'); | ||||||
| const specialNotesService = require('../../services/special_notes'); | import specialNotesService = require('../../services/special_notes'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function getInboxNote(req) { | function getInboxNote(req: Request) { | ||||||
|     return specialNotesService.getInboxNote(req.params.date); |     return specialNotesService.getInboxNote(req.params.date); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getDayNote(req) { | function getDayNote(req: Request) { | ||||||
|     return dateNoteService.getDayNote(req.params.date); |     return dateNoteService.getDayNote(req.params.date); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getWeekNote(req) { | function getWeekNote(req: Request) { | ||||||
|     return dateNoteService.getWeekNote(req.params.date); |     return dateNoteService.getWeekNote(req.params.date); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getMonthNote(req) { | function getMonthNote(req: Request) { | ||||||
|     return dateNoteService.getMonthNote(req.params.month); |     return dateNoteService.getMonthNote(req.params.month); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getYearNote(req) { | function getYearNote(req: Request) { | ||||||
|     return dateNoteService.getYearNote(req.params.year); |     return dateNoteService.getYearNote(req.params.year); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getDayNotesForMonth(req) { | function getDayNotesForMonth(req: Request) { | ||||||
|     const month = req.params.month; |     const month = req.params.month; | ||||||
| 
 | 
 | ||||||
|     return sql.getMap(` |     return sql.getMap(` | ||||||
| @ -42,7 +43,7 @@ function getDayNotesForMonth(req) { | |||||||
|             AND attr.value LIKE '${month}%'`);
 |             AND attr.value LIKE '${month}%'`);
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function saveSqlConsole(req) { | function saveSqlConsole(req: Request) { | ||||||
|     return specialNotesService.saveSqlConsole(req.body.sqlConsoleNoteId); |     return specialNotesService.saveSqlConsole(req.body.sqlConsoleNoteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -50,14 +51,14 @@ function createSqlConsole() { | |||||||
|     return specialNotesService.createSqlConsole(); |     return specialNotesService.createSqlConsole(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function saveSearchNote(req) { | function saveSearchNote(req: Request) { | ||||||
|     return specialNotesService.saveSearchNote(req.body.searchNoteId); |     return specialNotesService.saveSearchNote(req.body.searchNoteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createSearchNote(req) { | function createSearchNote(req: Request) { | ||||||
|     const hoistedNote = getHoistedNote(); |     const hoistedNote = getHoistedNote(); | ||||||
|     const searchString = req.body.searchString || ""; |     const searchString = req.body.searchString || ""; | ||||||
|     const ancestorNoteId = req.body.ancestorNoteId || hoistedNote.noteId; |     const ancestorNoteId = req.body.ancestorNoteId || hoistedNote?.noteId; | ||||||
| 
 | 
 | ||||||
|     return specialNotesService.createSearchNote(searchString, ancestorNoteId); |     return specialNotesService.createSearchNote(searchString, ancestorNoteId); | ||||||
| } | } | ||||||
| @ -66,22 +67,22 @@ function getHoistedNote() { | |||||||
|     return becca.getNote(cls.getHoistedNoteId()); |     return becca.getNote(cls.getHoistedNoteId()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createLauncher(req) { | function createLauncher(req: Request) { | ||||||
|     return specialNotesService.createLauncher({ |     return specialNotesService.createLauncher({ | ||||||
|         parentNoteId: req.params.parentNoteId, |         parentNoteId: req.params.parentNoteId, | ||||||
|         launcherType: req.params.launcherType |         launcherType: req.params.launcherType | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function resetLauncher(req) { | function resetLauncher(req: Request) { | ||||||
|     return specialNotesService.resetLauncher(req.params.noteId); |     return specialNotesService.resetLauncher(req.params.noteId); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createOrUpdateScriptLauncherFromApi(req) { | function createOrUpdateScriptLauncherFromApi(req: Request) { | ||||||
|     return specialNotesService.createOrUpdateScriptLauncherFromApi(req.body); |     return specialNotesService.createOrUpdateScriptLauncherFromApi(req.body); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getInboxNote, |     getInboxNote, | ||||||
|     getDayNote, |     getDayNote, | ||||||
|     getWeekNote, |     getWeekNote, | ||||||
| @ -1,7 +1,9 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import ValidationError = require('../../errors/validation_error'); | ||||||
| 
 | 
 | ||||||
| function getSchema() { | function getSchema() { | ||||||
|     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); |     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); | ||||||
| @ -17,10 +19,15 @@ function getSchema() { | |||||||
|     return tables; |     return tables; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function execute(req) { | function execute(req: Request) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     const queries = note.getContent().split("\n---"); |     const content = note.getContent(); | ||||||
|  |     if (typeof content !== "string") { | ||||||
|  |         throw new ValidationError("Invalid note type."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const queries = content.split("\n---"); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         const results = []; |         const results = []; | ||||||
| @ -51,7 +58,7 @@ function execute(req) { | |||||||
|             results |             results | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         return { |         return { | ||||||
|             success: false, |             success: false, | ||||||
|             error: e.message |             error: e.message | ||||||
| @ -59,7 +66,7 @@ function execute(req) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getSchema, |     getSchema, | ||||||
|     execute |     execute | ||||||
| }; | }; | ||||||
| @ -1,10 +1,11 @@ | |||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
|  | import { Request } from 'express'; | ||||||
| 
 | 
 | ||||||
| function getNoteSize(req) { | function getNoteSize(req: Request) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| 
 | 
 | ||||||
|     const blobSizes = sql.getMap(` |     const blobSizes = sql.getMap<string, number>(` | ||||||
|         SELECT blobs.blobId, LENGTH(content) |         SELECT blobs.blobId, LENGTH(content) | ||||||
|         FROM blobs |         FROM blobs | ||||||
|         LEFT JOIN notes ON notes.blobId = blobs.blobId AND notes.noteId = ? AND notes.isDeleted = 0 |         LEFT JOIN notes ON notes.blobId = blobs.blobId AND notes.noteId = ? AND notes.isDeleted = 0 | ||||||
| @ -21,14 +22,14 @@ function getNoteSize(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getSubtreeSize(req) { | function getSubtreeSize(req: Request) { | ||||||
|     const note = becca.getNoteOrThrow(req.params.noteId); |     const note = becca.getNoteOrThrow(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     const subTreeNoteIds = note.getSubtreeNoteIds(); |     const subTreeNoteIds = note.getSubtreeNoteIds(); | ||||||
| 
 | 
 | ||||||
|     sql.fillParamList(subTreeNoteIds); |     sql.fillParamList(subTreeNoteIds); | ||||||
| 
 | 
 | ||||||
|     const blobSizes = sql.getMap(` |     const blobSizes = sql.getMap<string, number>(` | ||||||
|         SELECT blobs.blobId, LENGTH(content) |         SELECT blobs.blobId, LENGTH(content) | ||||||
|         FROM param_list |         FROM param_list | ||||||
|         JOIN notes ON notes.noteId = param_list.paramId AND notes.isDeleted = 0 |         JOIN notes ON notes.noteId = param_list.paramId AND notes.isDeleted = 0 | ||||||
| @ -44,7 +45,7 @@ function getSubtreeSize(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getNoteSize, |     getNoteSize, | ||||||
|     getSubtreeSize |     getSubtreeSize | ||||||
| }; | }; | ||||||
| @ -1,16 +1,19 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const syncService = require('../../services/sync'); | import syncService = require('../../services/sync'); | ||||||
| const syncUpdateService = require('../../services/sync_update'); | import syncUpdateService = require('../../services/sync_update'); | ||||||
| const entityChangesService = require('../../services/entity_changes'); | import entityChangesService = require('../../services/entity_changes'); | ||||||
| const sql = require('../../services/sql'); | import sql = require('../../services/sql'); | ||||||
| const sqlInit = require('../../services/sql_init'); | import sqlInit = require('../../services/sql_init'); | ||||||
| const optionService = require('../../services/options'); | import optionService = require('../../services/options'); | ||||||
| const contentHashService = require('../../services/content_hash'); | import contentHashService = require('../../services/content_hash'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const syncOptions = require('../../services/sync_options'); | import syncOptions = require('../../services/sync_options'); | ||||||
| const utils = require('../../services/utils'); | import utils = require('../../services/utils'); | ||||||
| const ws = require('../../services/ws'); | import ws = require('../../services/ws'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import { EntityChange, EntityChangeRecord } from '../../services/entity_changes_interface'; | ||||||
|  | import ValidationError = require('../../errors/validation_error'); | ||||||
| 
 | 
 | ||||||
| async function testSync() { | async function testSync() { | ||||||
|     try { |     try { | ||||||
| @ -26,7 +29,7 @@ async function testSync() { | |||||||
| 
 | 
 | ||||||
|         return { success: true, message: "Sync server handshake has been successful, sync has been started." }; |         return { success: true, message: "Sync server handshake has been successful, sync has been started." }; | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         return { |         return { | ||||||
|             success: false, |             success: false, | ||||||
|             message: e.message |             message: e.message | ||||||
| @ -82,15 +85,19 @@ function forceFullSync() { | |||||||
|     syncService.sync(); |     syncService.sync(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getChanged(req) { | function getChanged(req: Request) { | ||||||
|     const startTime = Date.now(); |     const startTime = Date.now(); | ||||||
| 
 | 
 | ||||||
|     let lastEntityChangeId = parseInt(req.query.lastEntityChangeId); |     if (typeof req.query.lastEntityChangeId !== "string") { | ||||||
|  |         throw new ValidationError("Missing or invalid last entity change ID."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let lastEntityChangeId: number | null | undefined = parseInt(req.query.lastEntityChangeId); | ||||||
|     const clientInstanceId = req.query.instanceId; |     const clientInstanceId = req.query.instanceId; | ||||||
|     let filteredEntityChanges = []; |     let filteredEntityChanges: EntityChange[] = []; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         const entityChanges = sql.getRows(` |         const entityChanges: EntityChange[] = sql.getRows<EntityChange>(` | ||||||
|             SELECT * |             SELECT * | ||||||
|             FROM entity_changes |             FROM entity_changes | ||||||
|             WHERE isSynced = 1 |             WHERE isSynced = 1 | ||||||
| @ -129,16 +136,22 @@ function getChanged(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const partialRequests = {}; | const partialRequests: Record<string, { | ||||||
|  |     createdAt: number, | ||||||
|  |     payload: string | ||||||
|  | }> = {}; | ||||||
| 
 | 
 | ||||||
| function update(req) { | function update(req: Request) { | ||||||
|     let { body } = req; |     let { body } = req; | ||||||
| 
 | 
 | ||||||
|     const pageCount = parseInt(req.get('pageCount')); |     const pageCount = parseInt(req.get('pageCount') as string); | ||||||
|     const pageIndex = parseInt(req.get('pageIndex')); |     const pageIndex = parseInt(req.get('pageIndex') as string); | ||||||
| 
 | 
 | ||||||
|     if (pageCount !== 1) { |     if (pageCount !== 1) { | ||||||
|         const requestId = req.get('requestId'); |         const requestId = req.get('requestId'); | ||||||
|  |         if (!requestId) { | ||||||
|  |             throw new Error("Missing request ID."); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (pageIndex === 0) { |         if (pageIndex === 0) { | ||||||
|             partialRequests[requestId] = { |             partialRequests[requestId] = { | ||||||
| @ -185,7 +198,7 @@ function syncFinished() { | |||||||
|     sqlInit.setDbAsInitialized(); |     sqlInit.setDbAsInitialized(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function queueSector(req) { | function queueSector(req: Request) { | ||||||
|     const entityName = utils.sanitizeSqlIdentifier(req.params.entityName); |     const entityName = utils.sanitizeSqlIdentifier(req.params.entityName); | ||||||
|     const sector = utils.sanitizeSqlIdentifier(req.params.sector); |     const sector = utils.sanitizeSqlIdentifier(req.params.sector); | ||||||
| 
 | 
 | ||||||
| @ -196,7 +209,7 @@ function checkEntityChanges() { | |||||||
|     require('../../services/consistency_checks').runEntityChangesChecks(); |     require('../../services/consistency_checks').runEntityChangesChecks(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     testSync, |     testSync, | ||||||
|     checkSync, |     checkSync, | ||||||
|     syncNow, |     syncNow, | ||||||
| @ -1,16 +1,18 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const becca = require('../../becca/becca'); | import becca = require('../../becca/becca'); | ||||||
| const log = require('../../services/log'); | import log = require('../../services/log'); | ||||||
| const NotFoundError = require('../../errors/not_found_error'); | import NotFoundError = require('../../errors/not_found_error'); | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import BNote = require('../../becca/entities/bnote'); | ||||||
| 
 | 
 | ||||||
| function getNotesAndBranchesAndAttributes(noteIds) { | function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) { | ||||||
|     noteIds = new Set(noteIds); |     const noteIds = new Set(_noteIds); | ||||||
|     const collectedNoteIds = new Set(); |     const collectedNoteIds = new Set<string>(); | ||||||
|     const collectedAttributeIds = new Set(); |     const collectedAttributeIds = new Set<string>(); | ||||||
|     const collectedBranchIds = new Set(); |     const collectedBranchIds = new Set<string>(); | ||||||
| 
 | 
 | ||||||
|     function collectEntityIds(note) { |     function collectEntityIds(note?: BNote) { | ||||||
|         if (!note || collectedNoteIds.has(note.noteId)) { |         if (!note || collectedNoteIds.has(note.noteId)) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -18,16 +20,19 @@ function getNotesAndBranchesAndAttributes(noteIds) { | |||||||
|         collectedNoteIds.add(note.noteId); |         collectedNoteIds.add(note.noteId); | ||||||
| 
 | 
 | ||||||
|         for (const branch of note.getParentBranches()) { |         for (const branch of note.getParentBranches()) { | ||||||
|  |             if (branch.branchId) { | ||||||
|                 collectedBranchIds.add(branch.branchId); |                 collectedBranchIds.add(branch.branchId); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             collectEntityIds(branch.parentNote); |             collectEntityIds(branch.parentNote); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (const childNote of note.children) { |         for (const childNote of note.children) { | ||||||
|             const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, note.noteId); |             const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, note.noteId); | ||||||
| 
 |             if (childBranch && childBranch.branchId) { | ||||||
|                 collectedBranchIds.add(childBranch.branchId); |                 collectedBranchIds.add(childBranch.branchId); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         for (const attr of note.ownedAttributes) { |         for (const attr of note.ownedAttributes) { | ||||||
|             collectedAttributeIds.add(attr.attributeId); |             collectedAttributeIds.add(attr.attributeId); | ||||||
| @ -122,11 +127,11 @@ function getNotesAndBranchesAndAttributes(noteIds) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getTree(req) { | function getTree(req: Request) { | ||||||
|     const subTreeNoteId = req.query.subTreeNoteId || 'root'; |     const subTreeNoteId = typeof req.query.subTreeNoteId === "string" ? req.query.subTreeNoteId : 'root'; | ||||||
|     const collectedNoteIds = new Set([subTreeNoteId]); |     const collectedNoteIds = new Set<string>([subTreeNoteId]); | ||||||
| 
 | 
 | ||||||
|     function collect(parentNote) { |     function collect(parentNote: BNote) { | ||||||
|         if (!parentNote) { |         if (!parentNote) { | ||||||
|             console.trace(parentNote); |             console.trace(parentNote); | ||||||
|         } |         } | ||||||
| @ -136,7 +141,7 @@ function getTree(req) { | |||||||
| 
 | 
 | ||||||
|             const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, parentNote.noteId); |             const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, parentNote.noteId); | ||||||
| 
 | 
 | ||||||
|             if (childBranch.isExpanded) { |             if (childBranch?.isExpanded) { | ||||||
|                 collect(childBranch.childNote); |                 collect(childBranch.childNote); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -151,11 +156,11 @@ function getTree(req) { | |||||||
|     return getNotesAndBranchesAndAttributes(collectedNoteIds); |     return getNotesAndBranchesAndAttributes(collectedNoteIds); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function load(req) { | function load(req: Request) { | ||||||
|     return getNotesAndBranchesAndAttributes(req.body.noteIds); |     return getNotesAndBranchesAndAttributes(req.body.noteIds); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     getTree, |     getTree, | ||||||
|     load |     load | ||||||
| }; | }; | ||||||
| @ -17,48 +17,48 @@ const NotFoundError = require('../errors/not_found_error'); | |||||||
| const ValidationError = require('../errors/validation_error'); | const ValidationError = require('../errors/validation_error'); | ||||||
| 
 | 
 | ||||||
| // page routes
 | // page routes
 | ||||||
| const setupRoute = require('./setup.js'); | const setupRoute = require('./setup'); | ||||||
| const loginRoute = require('./login.js'); | const loginRoute = require('./login.js'); | ||||||
| const indexRoute = require('./index.js'); | const indexRoute = require('./index.js'); | ||||||
| 
 | 
 | ||||||
| // API routes
 | // API routes
 | ||||||
| const treeApiRoute = require('./api/tree.js'); | const treeApiRoute = require('./api/tree'); | ||||||
| const notesApiRoute = require('./api/notes.js'); | const notesApiRoute = require('./api/notes'); | ||||||
| const branchesApiRoute = require('./api/branches'); | const branchesApiRoute = require('./api/branches'); | ||||||
| const attachmentsApiRoute = require('./api/attachments'); | const attachmentsApiRoute = require('./api/attachments'); | ||||||
| const autocompleteApiRoute = require('./api/autocomplete'); | const autocompleteApiRoute = require('./api/autocomplete'); | ||||||
| const cloningApiRoute = require('./api/cloning'); | const cloningApiRoute = require('./api/cloning'); | ||||||
| const revisionsApiRoute = require('./api/revisions'); | const revisionsApiRoute = require('./api/revisions'); | ||||||
| const recentChangesApiRoute = require('./api/recent_changes.js'); | const recentChangesApiRoute = require('./api/recent_changes'); | ||||||
| const optionsApiRoute = require('./api/options.js'); | const optionsApiRoute = require('./api/options'); | ||||||
| const passwordApiRoute = require('./api/password'); | const passwordApiRoute = require('./api/password'); | ||||||
| const syncApiRoute = require('./api/sync'); | const syncApiRoute = require('./api/sync'); | ||||||
| const loginApiRoute = require('./api/login.js'); | const loginApiRoute = require('./api/login'); | ||||||
| const recentNotesRoute = require('./api/recent_notes.js'); | const recentNotesRoute = require('./api/recent_notes'); | ||||||
| const appInfoRoute = require('./api/app_info'); | const appInfoRoute = require('./api/app_info'); | ||||||
| const exportRoute = require('./api/export'); | const exportRoute = require('./api/export'); | ||||||
| const importRoute = require('./api/import.js'); | const importRoute = require('./api/import'); | ||||||
| const setupApiRoute = require('./api/setup.js'); | const setupApiRoute = require('./api/setup'); | ||||||
| const sqlRoute = require('./api/sql'); | const sqlRoute = require('./api/sql'); | ||||||
| const databaseRoute = require('./api/database'); | const databaseRoute = require('./api/database'); | ||||||
| const imageRoute = require('./api/image'); | const imageRoute = require('./api/image'); | ||||||
| const attributesRoute = require('./api/attributes'); | const attributesRoute = require('./api/attributes'); | ||||||
| const scriptRoute = require('./api/script.js'); | const scriptRoute = require('./api/script'); | ||||||
| const senderRoute = require('./api/sender.js'); | const senderRoute = require('./api/sender'); | ||||||
| const filesRoute = require('./api/files'); | const filesRoute = require('./api/files'); | ||||||
| const searchRoute = require('./api/search'); | const searchRoute = require('./api/search'); | ||||||
| const bulkActionRoute = require('./api/bulk_action'); | const bulkActionRoute = require('./api/bulk_action'); | ||||||
| const specialNotesRoute = require('./api/special_notes'); | const specialNotesRoute = require('./api/special_notes'); | ||||||
| const noteMapRoute = require('./api/note_map.js'); | const noteMapRoute = require('./api/note_map'); | ||||||
| const clipperRoute = require('./api/clipper'); | const clipperRoute = require('./api/clipper'); | ||||||
| const similarNotesRoute = require('./api/similar_notes.js'); | const similarNotesRoute = require('./api/similar_notes'); | ||||||
| const keysRoute = require('./api/keys.js'); | const keysRoute = require('./api/keys'); | ||||||
| const backendLogRoute = require('./api/backend_log'); | const backendLogRoute = require('./api/backend_log'); | ||||||
| const statsRoute = require('./api/stats.js'); | const statsRoute = require('./api/stats'); | ||||||
| const fontsRoute = require('./api/fonts'); | const fontsRoute = require('./api/fonts'); | ||||||
| const etapiTokensApiRoutes = require('./api/etapi_tokens'); | const etapiTokensApiRoutes = require('./api/etapi_tokens'); | ||||||
| const relationMapApiRoute = require('./api/relation-map'); | const relationMapApiRoute = require('./api/relation-map'); | ||||||
| const otherRoute = require('./api/other.js'); | const otherRoute = require('./api/other'); | ||||||
| const shareRoutes = require('../share/routes.js'); | const shareRoutes = require('../share/routes.js'); | ||||||
| 
 | 
 | ||||||
| const etapiAuthRoutes = require('../etapi/auth.js'); | const etapiAuthRoutes = require('../etapi/auth.js'); | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ function subscribeBeccaLoader(eventTypes: EventType, listener: EventListener) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function emit(eventType: string, data: any) { | function emit(eventType: string, data?: any) { | ||||||
|     const listeners = eventListeners[eventType]; |     const listeners = eventListeners[eventType]; | ||||||
| 
 | 
 | ||||||
|     if (listeners) { |     if (listeners) { | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ interface Note { | |||||||
| let note: Partial<Note> = {}; | let note: Partial<Note> = {}; | ||||||
| let resource: Resource; | let resource: Resource; | ||||||
| 
 | 
 | ||||||
| function importEnex(taskContext: TaskContext, file: File, parentNote: BNote) { | function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Promise<BNote> { | ||||||
|     const saxStream = sax.createStream(true); |     const saxStream = sax.createStream(true); | ||||||
| 
 | 
 | ||||||
|     const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex") |     const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex") | ||||||
|  | |||||||
| @ -251,7 +251,7 @@ function createNewNote(params: NoteParams): { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: string, params: NoteParams) { | function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: string | undefined, params: NoteParams) { | ||||||
|     if (!params.type) { |     if (!params.type) { | ||||||
|         const parentNote = becca.notes[params.parentNoteId]; |         const parentNote = becca.notes[params.parentNoteId]; | ||||||
| 
 | 
 | ||||||
| @ -263,7 +263,7 @@ function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: str | |||||||
|     if (target === 'into') { |     if (target === 'into') { | ||||||
|         return createNewNote(params); |         return createNewNote(params); | ||||||
|     } |     } | ||||||
|     else if (target === 'after') { |     else if (target === 'after' && targetBranchId) { | ||||||
|         const afterBranch = becca.branches[targetBranchId]; |         const afterBranch = becca.branches[targetBranchId]; | ||||||
| 
 | 
 | ||||||
|         // not updating utcDateModified to avoid having to sync whole rows
 |         // not updating utcDateModified to avoid having to sync whole rows
 | ||||||
|  | |||||||
| @ -106,7 +106,7 @@ function execute(ctx: ScriptContext, script: string) { | |||||||
|     return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); |     return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getParams(params: ScriptParams) { | function getParams(params?: ScriptParams) { | ||||||
|     if (!params) { |     if (!params) { | ||||||
|         return params; |         return params; | ||||||
|     } |     } | ||||||
| @ -121,7 +121,7 @@ function getParams(params: ScriptParams) { | |||||||
|     }).join(","); |     }).join(","); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getScriptBundleForFrontend(note: BNote, script: string, params: ScriptParams) { | function getScriptBundleForFrontend(note: BNote, script?: string, params?: ScriptParams) { | ||||||
|     let overrideContent = null; |     let overrideContent = null; | ||||||
| 
 | 
 | ||||||
|     if (script) { |     if (script) { | ||||||
|  | |||||||
| @ -110,7 +110,7 @@ function getSyncSeedOptions() { | |||||||
|     ]; |     ]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     hasSyncServerSchemaAndSeed, |     hasSyncServerSchemaAndSeed, | ||||||
|     triggerSync, |     triggerSync, | ||||||
|     sendSeedToSyncServer, |     sendSeedToSyncServer, | ||||||
|  | |||||||
| @ -166,7 +166,7 @@ function createScriptLauncher(parentNoteId: string, forceNoteId?: string) { | |||||||
| interface LauncherConfig { | interface LauncherConfig { | ||||||
|     parentNoteId: string; |     parentNoteId: string; | ||||||
|     launcherType: string; |     launcherType: string; | ||||||
|     noteId: string; |     noteId?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createLauncher({ parentNoteId, launcherType, noteId }: LauncherConfig) { | function createLauncher({ parentNoteId, launcherType, noteId }: LauncherConfig) { | ||||||
|  | |||||||
| @ -269,8 +269,8 @@ function transactional<T>(func: (statement: Statement) => T) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function fillParamList(paramIds: string[], truncate = true) { | function fillParamList(paramIds: string[] | Set<string>, truncate = true) { | ||||||
|     if (paramIds.length === 0) { |     if ("length" in paramIds && paramIds.length === 0) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -66,7 +66,7 @@ class TaskContext { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     taskSucceeded(result?: string) { |     taskSucceeded(result?: string | Record<string, string | undefined>) { | ||||||
|         ws.sendMessageToAllClients({ |         ws.sendMessageToAllClients({ | ||||||
|             type: 'taskSucceeded', |             type: 'taskSucceeded', | ||||||
|             taskId: this.taskId, |             taskId: this.taskId, | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ interface Message { | |||||||
|     taskType?: string | null; |     taskType?: string | null; | ||||||
|     message?: string; |     message?: string; | ||||||
|     reason?: string;     |     reason?: string;     | ||||||
|     result?: string; |     result?: string | Record<string, string | undefined>; | ||||||
| 
 | 
 | ||||||
|     script?: string; |     script?: string; | ||||||
|     params?: any[]; |     params?: any[]; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran