From 0ab137de03b7cf1d41a9557c6cbe837e75ab12f1 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 5 Apr 2024 22:22:18 +0300 Subject: [PATCH] server-ts: Convert routes/api/files --- package-lock.json | 13 +++++ package.json | 3 +- src/becca/entities/battachment.ts | 2 +- src/routes/api/{files.js => files.ts} | 84 ++++++++++++++------------- src/routes/custom.js | 2 +- src/routes/routes.js | 2 +- src/services/ws.ts | 4 ++ 7 files changed, 67 insertions(+), 43 deletions(-) rename src/routes/api/{files.js => files.ts} (66%) diff --git a/package-lock.json b/package-lock.json index 4123aca01..6adfcaed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,6 +101,7 @@ "@types/sanitize-html": "^2.11.0", "@types/sax": "^1.2.7", "@types/stream-throttle": "^0.1.4", + "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.4", "@types/ws": "^8.5.10", "@types/xml2js": "^0.4.14", @@ -1628,6 +1629,12 @@ "@types/node": "*" } }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -14570,6 +14577,12 @@ "@types/node": "*" } }, + "@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true + }, "@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", diff --git a/package.json b/package.json index 91e82dbcd..eead6dea0 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "@types/sanitize-html": "^2.11.0", "@types/sax": "^1.2.7", "@types/stream-throttle": "^0.1.4", + "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.4", "@types/ws": "^8.5.10", "@types/xml2js": "^0.4.14", @@ -145,4 +146,4 @@ "optionalDependencies": { "electron-installer-debian": "3.2.0" } -} \ No newline at end of file +} diff --git a/src/becca/entities/battachment.ts b/src/becca/entities/battachment.ts index 7b203839a..09f026ff0 100644 --- a/src/becca/entities/battachment.ts +++ b/src/becca/entities/battachment.ts @@ -134,7 +134,7 @@ class BAttachment extends AbstractBeccaEntity { return this._getContent() as Buffer; } - setContent(content: string | Buffer, opts: ContentOpts) { + setContent(content: string | Buffer, opts?: ContentOpts) { this._setContent(content, opts); } diff --git a/src/routes/api/files.js b/src/routes/api/files.ts similarity index 66% rename from src/routes/api/files.js rename to src/routes/api/files.ts index f368383d5..4a536954b 100644 --- a/src/routes/api/files.js +++ b/src/routes/api/files.ts @@ -1,21 +1,24 @@ "use strict"; -const protectedSessionService = require('../../services/protected_session'); -const utils = require('../../services/utils'); -const log = require('../../services/log'); -const noteService = require('../../services/notes'); -const tmp = require('tmp'); -const fs = require('fs'); -const { Readable } = require('stream'); -const chokidar = require('chokidar'); -const ws = require('../../services/ws'); -const becca = require('../../becca/becca'); -const ValidationError = require('../../errors/validation_error'); +import protectedSessionService = require('../../services/protected_session'); +import utils = require('../../services/utils'); +import log = require('../../services/log'); +import noteService = require('../../services/notes'); +import tmp = require('tmp'); +import fs = require('fs'); +import { Readable } from 'stream'; +import chokidar = require('chokidar'); +import ws = require('../../services/ws'); +import becca = require('../../becca/becca'); +import ValidationError = require('../../errors/validation_error'); +import { Request, Response } from 'express'; +import BNote = require('../../becca/entities/bnote'); +import BAttachment = require('../../becca/entities/battachment'); -function updateFile(req) { +function updateFile(req: Request) { const note = becca.getNoteOrThrow(req.params.noteId); - const file = req.file; + const file = (req as any).file; note.saveRevision(); note.mime = file.mimetype.toLowerCase(); @@ -32,9 +35,9 @@ function updateFile(req) { }; } -function updateAttachment(req) { +function updateAttachment(req: Request) { const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); - const file = req.file; + const file = (req as any).file; attachment.getNote().saveRevision(); @@ -46,12 +49,7 @@ function updateAttachment(req) { }; } -/** - * @param {BNote|BAttachment} noteOrAttachment - * @param res - * @param {boolean} contentDisposition - */ -function downloadData(noteOrAttachment, res, contentDisposition) { +function downloadData(noteOrAttachment: BNote | BAttachment, res: Response, contentDisposition: boolean) { if (noteOrAttachment.isProtected && !protectedSessionService.isProtectedSessionAvailable()) { return res.status(401).send("Protected session not available"); } @@ -68,7 +66,7 @@ function downloadData(noteOrAttachment, res, contentDisposition) { res.send(noteOrAttachment.getContent()); } -function downloadNoteInt(noteId, res, contentDisposition = true) { +function downloadNoteInt(noteId: string, res: Response, contentDisposition = true) { const note = becca.getNote(noteId); if (!note) { @@ -80,7 +78,7 @@ function downloadNoteInt(noteId, res, contentDisposition = true) { return downloadData(note, res, contentDisposition); } -function downloadAttachmentInt(attachmentId, res, contentDisposition = true) { +function downloadAttachmentInt(attachmentId: string, res: Response, contentDisposition = true) { const attachment = becca.getAttachment(attachmentId); if (!attachment) { @@ -92,34 +90,34 @@ function downloadAttachmentInt(attachmentId, res, contentDisposition = true) { return downloadData(attachment, res, contentDisposition); } -const downloadFile = (req, res) => downloadNoteInt(req.params.noteId, res, true); -const openFile = (req, res) => downloadNoteInt(req.params.noteId, res, false); +const downloadFile = (req: Request, res: Response) => downloadNoteInt(req.params.noteId, res, true); +const openFile = (req: Request, res: Response) => downloadNoteInt(req.params.noteId, res, false); -const downloadAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, true); -const openAttachment = (req, res) => downloadAttachmentInt(req.params.attachmentId, res, false); +const downloadAttachment = (req: Request, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, true); +const openAttachment = (req: Request, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, false); -function fileContentProvider(req) { +function fileContentProvider(req: Request) { // Read the file name from route params. const note = becca.getNoteOrThrow(req.params.noteId); return streamContent(note.getContent(), note.getFileName(), note.mime); } -function attachmentContentProvider(req) { +function attachmentContentProvider(req: Request) { // Read the file name from route params. const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); return streamContent(attachment.getContent(), attachment.getFileName(), attachment.mime); } -function streamContent(content, fileName, mimeType) { +function streamContent(content: string | Buffer, fileName: string, mimeType: string) { if (typeof content === "string") { content = Buffer.from(content, 'utf8'); } const totalSize = content.byteLength; - const getStream = range => { + const getStream = (range: { start: number, end: number }) => { if (!range) { // Request if for complete content. return Readable.from(content); @@ -138,7 +136,7 @@ function streamContent(content, fileName, mimeType) { }; } -function saveNoteToTmpDir(req) { +function saveNoteToTmpDir(req: Request) { const note = becca.getNoteOrThrow(req.params.noteId); const fileName = note.getFileName(); const content = note.getContent(); @@ -146,18 +144,26 @@ function saveNoteToTmpDir(req) { return saveToTmpDir(fileName, content, 'notes', note.noteId); } -function saveAttachmentToTmpDir(req) { +function saveAttachmentToTmpDir(req: Request) { const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); const fileName = attachment.getFileName(); const content = attachment.getContent(); + if (!attachment.attachmentId) { + throw new ValidationError("Missing attachment ID."); + } return saveToTmpDir(fileName, content, 'attachments', attachment.attachmentId); } -function saveToTmpDir(fileName, content, entityType, entityId) { +function saveToTmpDir(fileName: string, content: string | Buffer, entityType: string, entityId: string) { const tmpObj = tmp.fileSync({ postfix: fileName }); - fs.writeSync(tmpObj.fd, content); + if (typeof content === "string") { + fs.writeSync(tmpObj.fd, content); + } else { + fs.writeSync(tmpObj.fd, content); + } + fs.closeSync(tmpObj.fd); log.info(`Saved temporary file ${tmpObj.name}`); @@ -168,7 +174,7 @@ function saveToTmpDir(fileName, content, entityType, entityId) { type: 'openedFileUpdated', entityType: entityType, entityId: entityId, - lastModifiedMs: stats.atimeMs, + lastModifiedMs: stats?.atimeMs, filePath: tmpObj.name }); }); @@ -179,7 +185,7 @@ function saveToTmpDir(fileName, content, entityType, entityId) { }; } -function uploadModifiedFileToNote(req) { +function uploadModifiedFileToNote(req: Request) { const noteId = req.params.noteId; const {filePath} = req.body; @@ -198,7 +204,7 @@ function uploadModifiedFileToNote(req) { note.setContent(fileContent); } -function uploadModifiedFileToAttachment(req) { +function uploadModifiedFileToAttachment(req: Request) { const {attachmentId} = req.params; const {filePath} = req.body; @@ -217,7 +223,7 @@ function uploadModifiedFileToAttachment(req) { attachment.setContent(fileContent); } -module.exports = { +export = { updateFile, updateAttachment, openFile, diff --git a/src/routes/custom.js b/src/routes/custom.js index fbf6c42c3..2e32e5446 100644 --- a/src/routes/custom.js +++ b/src/routes/custom.js @@ -1,5 +1,5 @@ const log = require('../services/log'); -const fileService = require('./api/files.js'); +const fileService = require('./api/files'); const scriptService = require('../services/script'); const cls = require('../services/cls'); const sql = require('../services/sql'); diff --git a/src/routes/routes.js b/src/routes/routes.js index 27330f25d..fab432f58 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -45,7 +45,7 @@ const imageRoute = require('./api/image'); const attributesRoute = require('./api/attributes'); const scriptRoute = require('./api/script.js'); const senderRoute = require('./api/sender.js'); -const filesRoute = require('./api/files.js'); +const filesRoute = require('./api/files'); const searchRoute = require('./api/search'); const bulkActionRoute = require('./api/bulk_action'); const specialNotesRoute = require('./api/special_notes'); diff --git a/src/services/ws.ts b/src/services/ws.ts index 6ff81e1db..1b581d358 100644 --- a/src/services/ws.ts +++ b/src/services/ws.ts @@ -50,8 +50,12 @@ interface Message { messages?: string[]; startNoteId?: string; currentNoteId?: string; + entityType?: string; + entityId?: string; originEntityName?: "notes"; originEntityId?: string | null; + lastModifiedMs?: number; + filePath?: string; } type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void;