From 6265aa99d37d1af1ed7e1839557fca77c7a0f5d7 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 6 Apr 2024 22:32:03 +0300 Subject: [PATCH] server-ts: Convert routes/api/revisions --- src/becca/becca-interface.ts | 24 +++++- src/becca/entities/bnote.ts | 3 +- src/becca/entities/brevision.ts | 9 +-- src/routes/api/{revisions.js => revisions.ts} | 78 +++++++++++-------- 4 files changed, 75 insertions(+), 39 deletions(-) rename src/routes/api/{revisions.js => revisions.ts} (68%) diff --git a/src/becca/becca-interface.ts b/src/becca/becca-interface.ts index 7fa3ca60f..e7298fff5 100644 --- a/src/becca/becca-interface.ts +++ b/src/becca/becca-interface.ts @@ -164,6 +164,14 @@ export default class Becca { 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 { opts.includeContentLength = !!opts.includeContentLength; @@ -249,7 +257,7 @@ export default class Becca { return rows.map(row => new BRecentNote(row)); } - getRevisionsFromQuery(query: string, params = []): BRevision[] { + getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] { const rows = sql.getRows(query, params); const BRevision = require('./entities/brevision'); // avoiding circular dependency problems @@ -291,4 +299,18 @@ export interface ConstructorData> { primaryKeyName: string; entityName: string; 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; } \ No newline at end of file diff --git a/src/becca/entities/bnote.ts b/src/becca/entities/bnote.ts index 6b9118a67..c29945a93 100644 --- a/src/becca/entities/bnote.ts +++ b/src/becca/entities/bnote.ts @@ -15,6 +15,7 @@ import eventService = require('../../services/events'); import { AttachmentRow, NoteRow, NoteType, RevisionRow } from './rows'; import BBranch = require('./bbranch'); import BAttribute = require('./battribute'); +import { NotePojo } from '../becca-interface'; dayjs.extend(utc); const LABEL = 'label'; @@ -1679,7 +1680,7 @@ class BNote extends AbstractBeccaEntity { this.utcDateModified = dateUtils.utcNowDateTime(); } - getPojo() { + getPojo(): NotePojo { return { noteId: this.noteId, title: this.title || undefined, diff --git a/src/becca/entities/brevision.ts b/src/becca/entities/brevision.ts index 101506858..18a7a8df5 100644 --- a/src/becca/entities/brevision.ts +++ b/src/becca/entities/brevision.ts @@ -40,7 +40,7 @@ class BRevision extends AbstractBeccaEntity { utcDateLastEdited?: string; utcDateCreated!: string; contentLength?: number; - content?: string; + content?: string | Buffer; constructor(row: RevisionRow, titleDecrypted = false) { super(); @@ -91,9 +91,8 @@ class BRevision extends AbstractBeccaEntity { * * 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 { - return this._getContent() as string; + getContent(): string | Buffer { + return this._getContent(); } /** @@ -101,7 +100,7 @@ class BRevision extends AbstractBeccaEntity { getJsonContent(): {} | null { const content = this.getContent(); - if (!content || !content.trim()) { + if (!content || typeof content !== "string" || !content.trim()) { return null; } diff --git a/src/routes/api/revisions.js b/src/routes/api/revisions.ts similarity index 68% rename from src/routes/api/revisions.js rename to src/routes/api/revisions.ts index e317fec95..475b5b9b1 100644 --- a/src/routes/api/revisions.js +++ b/src/routes/api/revisions.ts @@ -1,22 +1,38 @@ "use strict"; -const beccaService = require('../../becca/becca_service'); -const revisionService = require('../../services/revisions'); -const utils = require('../../services/utils'); -const sql = require('../../services/sql'); -const cls = require('../../services/cls'); -const path = require('path'); -const becca = require('../../becca/becca'); -const blobService = require('../../services/blob'); -const eraseService = require("../../services/erase"); +import beccaService = require('../../becca/becca_service'); +import revisionService = require('../../services/revisions'); +import utils = require('../../services/utils'); +import sql = require('../../services/sql'); +import cls = require('../../services/cls'); +import path = require('path'); +import becca = require('../../becca/becca'); +import blobService = require('../../services/blob'); +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'; return blobService.getBlobPojo('revisions', req.params.revisionId, { preview }); } -function getRevisions(req) { +function getRevisions(req: Request) { return becca.getRevisionsFromQuery(` SELECT revisions.*, LENGTH(blobs.content) AS contentLength @@ -26,12 +42,12 @@ function getRevisions(req) { ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]); } -function getRevision(req) { - const revision = becca.getRevision(req.params.revisionId); +function getRevision(req: Request) { + const revision = becca.getRevisionOrThrow(req.params.revisionId); if (revision.type === 'file') { if (revision.hasStringContent()) { - revision.content = revision.getContent().substr(0, 10000); + revision.content = (revision.getContent() as string).substr(0, 10000); } } else { @@ -45,11 +61,7 @@ function getRevision(req) { return revision; } -/** - * @param {BRevision} revision - * @returns {string} - */ -function getRevisionFilename(revision) { +function getRevisionFilename(revision: BRevision) { let filename = utils.formatDownloadTitle(revision.title, revision.type, revision.mime); const extension = path.extname(filename); @@ -68,8 +80,8 @@ function getRevisionFilename(revision) { return filename; } -function downloadRevision(req, res) { - const revision = becca.getRevision(req.params.revisionId); +function downloadRevision(req: Request, res: Response) { + const revision = becca.getRevisionOrThrow(req.params.revisionId); if (!revision.isContentAvailable()) { return res.setHeader("Content-Type", "text/plain") @@ -85,18 +97,18 @@ function downloadRevision(req, res) { res.send(revision.getContent()); } -function eraseAllRevisions(req) { - const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?', +function eraseAllRevisions(req: Request) { + const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?', [req.params.noteId]); eraseService.eraseRevisions(revisionIdsToErase); } -function eraseRevision(req) { +function eraseRevision(req: Request) { eraseService.eraseRevisions([req.params.revisionId]); } -function restoreRevision(req) { +function restoreRevision(req: Request) { const revision = becca.getRevision(req.params.revisionId); if (revision) { @@ -117,7 +129,9 @@ function restoreRevision(req) { noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true }); // content is rewritten to point to the restored revision attachments - revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`); + if (typeof revisionContent === "string") { + revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`); + } } note.title = revision.title; @@ -126,8 +140,8 @@ function restoreRevision(req) { } } -function getEditedNotesOnDate(req) { - const noteIds = sql.getColumn(` +function getEditedNotesOnDate(req: Request) { + const noteIds = sql.getColumn(` SELECT notes.* FROM notes WHERE noteId IN ( @@ -152,7 +166,7 @@ function getEditedNotesOnDate(req) { return notes.map(note => { const notePath = getNotePathData(note); - const notePojo = note.getPojo(); + const notePojo: NotePojoWithNotePath = note.getPojo(); notePojo.notePath = notePath ? notePath.notePath : null; return notePojo; @@ -160,7 +174,7 @@ function getEditedNotesOnDate(req) { } -function getNotePathData(note) { +function getNotePathData(note: BNote): NotePath | undefined { const retPath = note.getBestNotePath(); if (retPath) { @@ -173,7 +187,7 @@ function getNotePathData(note) { } else { const parentNote = note.parents[0]; - branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId).branchId; + branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId)?.branchId; } return { @@ -186,7 +200,7 @@ function getNotePathData(note) { } } -module.exports = { +export = { getRevisionBlob, getRevisions, getRevision,