diff --git a/package-lock.json b/package-lock.json index 4ef7cafac..972352ddd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,6 +89,7 @@ "trilium": "src/www.js" }, "devDependencies": { + "@types/archiver": "^6.0.2", "@types/better-sqlite3": "^7.6.9", "@types/escape-html": "^1.0.4", "@types/express": "^4.17.21", @@ -1337,6 +1338,15 @@ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-21.0.0.tgz", "integrity": "sha512-qVfOiFh0U8ZSkLgA6tf7kj2MciqRbSCWaJZRwftVO7UbtVDNsZAXpWXqvCDtIefvjC83UJB+vHTDOGm5ibXjEA==" }, + "node_modules/@types/archiver": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.2.tgz", + "integrity": "sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==", + "dev": true, + "dependencies": { + "@types/readdir-glob": "*" + } + }, "node_modules/@types/better-sqlite3": { "version": "7.6.9", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz", @@ -1647,6 +1657,15 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -16088,6 +16107,15 @@ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-21.0.0.tgz", "integrity": "sha512-qVfOiFh0U8ZSkLgA6tf7kj2MciqRbSCWaJZRwftVO7UbtVDNsZAXpWXqvCDtIefvjC83UJB+vHTDOGm5ibXjEA==" }, + "@types/archiver": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.2.tgz", + "integrity": "sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==", + "dev": true, + "requires": { + "@types/readdir-glob": "*" + } + }, "@types/better-sqlite3": { "version": "7.6.9", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz", @@ -16391,6 +16419,15 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", diff --git a/package.json b/package.json index 6cb0af649..c0576bc0e 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "yauzl": "2.10.0" }, "devDependencies": { + "@types/archiver": "^6.0.2", "@types/better-sqlite3": "^7.6.9", "@types/escape-html": "^1.0.4", "@types/express": "^4.17.21", diff --git a/src/becca/entities/abstract_becca_entity.ts b/src/becca/entities/abstract_becca_entity.ts index bc7d3f06b..95b16840a 100644 --- a/src/becca/entities/abstract_becca_entity.ts +++ b/src/becca/entities/abstract_becca_entity.ts @@ -29,7 +29,7 @@ interface ConstructorData> { */ abstract class AbstractBeccaEntity> { - protected utcDateModified?: string; + utcDateModified?: string; protected dateCreated?: string; protected dateModified?: string; protected isSynced?: boolean; diff --git a/src/etapi/notes.js b/src/etapi/notes.js index ebb12910b..3ca024367 100644 --- a/src/etapi/notes.js +++ b/src/etapi/notes.js @@ -7,8 +7,8 @@ const TaskContext = require('../services/task_context'); const v = require('./validators.js'); const searchService = require('../services/search/services/search'); const SearchContext = require('../services/search/search_context'); -const zipExportService = require('../services/export/zip.js'); -const zipImportService = require('../services/import/zip.js'); +const zipExportService = require('../services/export/zip'); +const zipImportService = require('../services/import/zip'); function register(router) { eu.route(router, 'get', '/etapi/notes', (req, res, next) => { diff --git a/src/routes/api/export.js b/src/routes/api/export.js index b51b5f689..4c098512b 100644 --- a/src/routes/api/export.js +++ b/src/routes/api/export.js @@ -1,6 +1,6 @@ "use strict"; -const zipExportService = require('../../services/export/zip.js'); +const zipExportService = require('../../services/export/zip'); const singleExportService = require('../../services/export/single'); const opmlExportService = require('../../services/export/opml'); const becca = require('../../becca/becca'); diff --git a/src/routes/api/import.js b/src/routes/api/import.js index 8fcb91d46..ca59f5159 100644 --- a/src/routes/api/import.js +++ b/src/routes/api/import.js @@ -2,7 +2,7 @@ const enexImportService = require('../../services/import/enex.js'); const opmlImportService = require('../../services/import/opml'); -const zipImportService = require('../../services/import/zip.js'); +const zipImportService = require('../../services/import/zip'); const singleImportService = require('../../services/import/single'); const cls = require('../../services/cls'); const path = require('path'); diff --git a/src/services/backend_script_api.js b/src/services/backend_script_api.js index e86efa8a9..a183f9d3b 100644 --- a/src/services/backend_script_api.js +++ b/src/services/backend_script_api.js @@ -18,7 +18,7 @@ const ws = require('./ws'); const SpacedUpdate = require('./spaced_update.js'); const specialNotesService = require('./special_notes.js'); const branchService = require('./branches'); -const exportService = require('./export/zip.js'); +const exportService = require('./export/zip'); const syncMutex = require('./sync_mutex'); const backupService = require('./backup'); const optionsService = require('./options'); diff --git a/src/services/export/single.ts b/src/services/export/single.ts index f7a51dacf..f4ac58692 100644 --- a/src/services/export/single.ts +++ b/src/services/export/single.ts @@ -125,6 +125,6 @@ function inlineAttachments(content: string) { return content; } -module.exports = { +export = { exportSingleNote }; diff --git a/src/services/export/zip.js b/src/services/export/zip.ts similarity index 71% rename from src/services/export/zip.js rename to src/services/export/zip.ts index 01b30a55e..9d369e593 100644 --- a/src/services/export/zip.js +++ b/src/services/export/zip.ts @@ -1,33 +1,28 @@ "use strict"; -const html = require('html'); -const dateUtils = require('../date_utils'); -const path = require('path'); -const mimeTypes = require('mime-types'); -const mdService = require('./md'); -const packageInfo = require('../../../package.json'); -const utils = require('../utils'); -const protectedSessionService = require('../protected_session'); -const sanitize = require("sanitize-filename"); -const fs = require("fs"); -const becca = require('../../becca/becca'); +import html = require('html'); +import dateUtils = require('../date_utils'); +import path = require('path'); +import mimeTypes = require('mime-types'); +import mdService = require('./md'); +import packageInfo = require('../../../package.json'); +import utils = require('../utils'); +import protectedSessionService = require('../protected_session'); +import sanitize = require("sanitize-filename"); +import fs = require("fs"); +import becca = require('../../becca/becca'); const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; -const archiver = require('archiver'); -const log = require('../log'); -const TaskContext = require('../task_context'); -const ValidationError = require('../../errors/validation_error'); -const NoteMeta = require('../meta/note_meta'); -const AttachmentMeta = require('../meta/attachment_meta'); -const AttributeMeta = require('../meta/attribute_meta'); +import archiver = require('archiver'); +import log = require('../log'); +import TaskContext = require('../task_context'); +import ValidationError = require('../../errors/validation_error'); +import NoteMeta = require('../meta/note_meta'); +import AttachmentMeta = require('../meta/attachment_meta'); +import AttributeMeta = require('../meta/attribute_meta'); +import BBranch = require('../../becca/entities/bbranch'); +import { Response } from 'express'; -/** - * @param {TaskContext} taskContext - * @param {BBranch} branch - * @param {string} format - 'html' or 'markdown' - * @param {object} res - express response - * @param {boolean} setHeaders - */ -async function exportToZip(taskContext, branch, format, res, setHeaders = true) { +async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true) { if (!['html', 'markdown'].includes(format)) { throw new ValidationError(`Only 'html' and 'markdown' allowed as export format, '${format}' given`); } @@ -36,15 +31,9 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) zlib: { level: 9 } // Sets the compression level. }); - /** @type {Object.} */ - const noteIdToMeta = {}; + const noteIdToMeta: Record = {}; - /** - * @param {Object.} existingFileNames - * @param {string} fileName - * @returns {string} - */ - function getUniqueFilename(existingFileNames, fileName) { + function getUniqueFilename(existingFileNames: Record, fileName: string) { const lcFileName = fileName.toLowerCase(); if (lcFileName in existingFileNames) { @@ -67,14 +56,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) } } - /** - * @param {string|null} type - * @param {string} mime - * @param {string} baseFileName - * @param {Object.} existingFileNames - * @return {string} - */ - function getDataFileName(type, mime, baseFileName, existingFileNames) { + function getDataFileName(type: string | null, mime: string, baseFileName: string, existingFileNames: Record): string { let fileName = baseFileName.trim(); if (fileName.length > 30) { fileName = fileName.substr(0, 30).trim(); @@ -115,13 +97,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) return getUniqueFilename(existingFileNames, fileName); } - /** - * @param {BBranch} branch - * @param {NoteMeta} parentMeta - * @param {Object.} existingFileNames - * @returns {NoteMeta|null} - */ - function createNoteMeta(branch, parentMeta, existingFileNames) { + function createNoteMeta(branch: BBranch, parentMeta: Partial, existingFileNames: Record): NoteMeta | null { const note = branch.getNote(); if (note.hasOwnedLabel('excludeFromExport')) { @@ -136,24 +112,26 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) baseFileName = baseFileName.substr(0, 200); } + if (!parentMeta.notePath) { throw new Error("Missing parent note path."); } const notePath = parentMeta.notePath.concat([note.noteId]); if (note.noteId in noteIdToMeta) { const fileName = getUniqueFilename(existingFileNames, `${baseFileName}.clone.${format === 'html' ? 'html' : 'md'}`); - const meta = new NoteMeta(); - meta.isClone = true; - meta.noteId = note.noteId; - meta.notePath = notePath; - meta.title = note.getTitleOrProtected(); - meta.prefix = branch.prefix; - meta.dataFileName = fileName; - meta.type = 'text'; // export will have text description - meta.format = format; + const meta: NoteMeta = { + isClone: true, + noteId: note.noteId, + notePath: notePath, + title: note.getTitleOrProtected(), + prefix: branch.prefix, + dataFileName: fileName, + type: 'text', // export will have text description + format: format + }; return meta; } - const meta = new NoteMeta(); + const meta: Partial = {}; meta.isClone = false; meta.noteId = note.noteId; meta.notePath = notePath; @@ -164,12 +142,14 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) meta.type = note.type; meta.mime = note.mime; meta.attributes = note.getOwnedAttributes().map(attribute => { - const attrMeta = new AttributeMeta(); - attrMeta.type = attribute.type; - attrMeta.name = attribute.name; - attrMeta.value = attribute.value; - attrMeta.isInheritable = attribute.isInheritable; - attrMeta.position = attribute.position; + const attrMeta = { + type: attribute.type, + name: attribute.name, + value: attribute.value, + isInheritable: attribute.isInheritable, + position: attribute.position + }; + return attrMeta; }); @@ -179,12 +159,12 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) meta.format = format; } - noteIdToMeta[note.noteId] = meta; + noteIdToMeta[note.noteId] = meta as NoteMeta; // sort children for having a stable / reproducible export format note.sortChildren(); const childBranches = note.getChildBranches() - .filter(branch => branch.noteId !== '_hidden'); + .filter(branch => branch?.noteId !== '_hidden'); const available = !note.isProtected || protectedSessionService.isProtectedSessionAvailable(); @@ -196,18 +176,19 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) const attachments = note.getAttachments(); meta.attachments = attachments .map(attachment => { - const attMeta = new AttachmentMeta(); - attMeta.attachmentId = attachment.attachmentId; - attMeta.title = attachment.title; - attMeta.role = attachment.role; - attMeta.mime = attachment.mime; - attMeta.position = attachment.position; - attMeta.dataFileName = getDataFileName( - null, - attachment.mime, - baseFileName + "_" + attachment.title, - existingFileNames - ); + const attMeta: AttachmentMeta = { + attachmentId: attachment.attachmentId, + title: attachment.title, + role: attachment.role, + mime: attachment.mime, + position: attachment.position, + dataFileName: getDataFileName( + null, + attachment.mime, + baseFileName + "_" + attachment.title, + existingFileNames + ) + }; return attMeta; }); @@ -219,7 +200,9 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) const childExistingNames = {}; for (const childBranch of childBranches) { - const note = createNoteMeta(childBranch, meta, childExistingNames); + if (!childBranch) { continue; } + + const note = createNoteMeta(childBranch, meta as NoteMeta, childExistingNames); // can be undefined if export is disabled for this note if (note) { @@ -228,18 +211,13 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) } } - return meta; + return meta as NoteMeta; } - /** - * @param {string} targetNoteId - * @param {NoteMeta} sourceMeta - * @return {string|null} - */ - function getNoteTargetUrl(targetNoteId, sourceMeta) { + function getNoteTargetUrl(targetNoteId: string, sourceMeta: NoteMeta): string | null { const targetMeta = noteIdToMeta[targetNoteId]; - if (!targetMeta) { + if (!targetMeta || !targetMeta.notePath || !sourceMeta.notePath) { return null; } @@ -256,24 +234,20 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) for (let i = 0; i < targetPath.length - 1; i++) { const meta = noteIdToMeta[targetPath[i]]; - - url += `${encodeURIComponent(meta.dirFileName)}/`; + if (meta.dirFileName) { + url += `${encodeURIComponent(meta.dirFileName)}/`; + } } const meta = noteIdToMeta[targetPath[targetPath.length - 1]]; // link can target note which is only "folder-note" and as such, will not have a file in an export - url += encodeURIComponent(meta.dataFileName || meta.dirFileName); + url += encodeURIComponent(meta.dataFileName || meta.dirFileName || ""); return url; } - /** - * @param {string} content - * @param {NoteMeta} noteMeta - * @return {string} - */ - function rewriteLinks(content, noteMeta) { + function rewriteLinks(content: string, noteMeta: NoteMeta): string { content = content.replace(/src="[^"]*api\/images\/([a-zA-Z0-9_]+)\/[^"]*"/g, (match, targetNoteId) => { const url = getNoteTargetUrl(targetNoteId, noteMeta); @@ -300,10 +274,10 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) return content; - function findAttachment(targetAttachmentId) { + function findAttachment(targetAttachmentId: string) { let url; - const attachmentMeta = noteMeta.attachments.find(attMeta => attMeta.attachmentId === targetAttachmentId); + const attachmentMeta = (noteMeta.attachments || []).find(attMeta => attMeta.attachmentId === targetAttachmentId); if (attachmentMeta) { // easy job here, because attachment will be in the same directory as the note's data file. url = attachmentMeta.dataFileName; @@ -314,21 +288,17 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) } } - /** - * @param {string} title - * @param {string|Buffer} content - * @param {NoteMeta} noteMeta - * @return {string|Buffer} - */ - function prepareContent(title, content, noteMeta) { - if (['html', 'markdown'].includes(noteMeta.format)) { + function prepareContent(title: string, content: string | Buffer, noteMeta: NoteMeta): string | Buffer { + if (['html', 'markdown'].includes(noteMeta?.format || "")) { content = content.toString(); content = rewriteLinks(content, noteMeta); } - if (noteMeta.format === 'html') { + if (noteMeta.format === 'html' && typeof content === "string") { if (!content.substr(0, 100).toLowerCase().includes(" 0 && !markdownContent.startsWith("# ")) { @@ -368,17 +338,17 @@ ${markdownContent}`; } } - /** - * @param {NoteMeta} noteMeta - * @param {string} filePathPrefix - */ - function saveNote(noteMeta, filePathPrefix) { + function saveNote(noteMeta: NoteMeta, filePathPrefix: string) { log.info(`Exporting note '${noteMeta.noteId}'`); + if (!noteMeta.noteId || !noteMeta.title) { + throw new Error("Missing note meta."); + } + if (noteMeta.isClone) { const targetUrl = getNoteTargetUrl(noteMeta.noteId, noteMeta); - let content = `

This is a clone of a note. Go to its primary location.

`; + let content: string | Buffer = `

This is a clone of a note. Go to its primary location.

`; content = prepareContent(noteMeta.title, content, noteMeta); @@ -388,6 +358,8 @@ ${markdownContent}`; } const note = becca.getNote(noteMeta.noteId); + if (!note) { throw new Error("Unable to find note."); } + if (!note.utcDateModified) { throw new Error("Unable to find modification date."); } if (noteMeta.dataFileName) { const content = prepareContent(noteMeta.title, note.getContent(), noteMeta); @@ -400,7 +372,9 @@ ${markdownContent}`; taskContext.increaseProgressCount(); - for (const attachmentMeta of noteMeta.attachments) { + for (const attachmentMeta of noteMeta.attachments || []) { + if (!attachmentMeta.attachmentId) { continue; } + const attachment = note.getAttachmentById(attachmentMeta.attachmentId); const content = attachment.getContent(); @@ -410,29 +384,25 @@ ${markdownContent}`; }); } - if (noteMeta.children?.length > 0) { + if (noteMeta.children?.length || 0 > 0) { const directoryPath = filePathPrefix + noteMeta.dirFileName; // create directory archive.append('', { name: `${directoryPath}/`, date: dateUtils.parseDateTime(note.utcDateModified) }); - for (const childMeta of noteMeta.children) { + for (const childMeta of noteMeta.children || []) { saveNote(childMeta, `${directoryPath}/`); } } } - /** - * @param {NoteMeta} rootMeta - * @param {NoteMeta} navigationMeta - */ - function saveNavigation(rootMeta, navigationMeta) { - function saveNavigationInner(meta) { + function saveNavigation(rootMeta: NoteMeta, navigationMeta: NoteMeta) { + function saveNavigationInner(meta: NoteMeta) { let html = '
  • '; const escapedTitle = utils.escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`); - if (meta.dataFileName) { + if (meta.dataFileName && meta.noteId) { const targetUrl = getNoteTargetUrl(meta.noteId, rootMeta); html += `${escapedTitle}`; @@ -470,16 +440,12 @@ ${markdownContent}`; archive.append(prettyHtml, { name: navigationMeta.dataFileName }); } - /** - * @param {NoteMeta} rootMeta - * @param {NoteMeta} indexMeta - */ - function saveIndex(rootMeta, indexMeta) { + function saveIndex(rootMeta: NoteMeta, indexMeta: NoteMeta) { let firstNonEmptyNote; let curMeta = rootMeta; while (!firstNonEmptyNote) { - if (curMeta.dataFileName) { + if (curMeta.dataFileName && curMeta.noteId) { firstNonEmptyNote = getNoteTargetUrl(curMeta.noteId, rootMeta); } @@ -506,17 +472,13 @@ ${markdownContent}`; archive.append(fullHtml, { name: indexMeta.dataFileName }); } - /** - * @param {NoteMeta} rootMeta - * @param {NoteMeta} cssMeta - */ - function saveCss(rootMeta, cssMeta) { + function saveCss(rootMeta: NoteMeta, cssMeta: NoteMeta) { const cssContent = fs.readFileSync(`${RESOURCE_DIR}/libraries/ckeditor/ckeditor-content.css`); archive.append(cssContent, { name: cssMeta.dataFileName }); } - const existingFileNames = format === 'html' ? ['navigation', 'index'] : []; + const existingFileNames: Record = format === 'html' ? {'navigation': 0, 'index': 1} : {}; const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames); const metaFile = { @@ -525,7 +487,9 @@ ${markdownContent}`; files: [ rootMeta ] }; - let navigationMeta, indexMeta, cssMeta; + let navigationMeta: NoteMeta | null = null; + let indexMeta: NoteMeta | null = null; + let cssMeta: NoteMeta | null = null; if (format === 'html') { navigationMeta = { @@ -552,7 +516,7 @@ ${markdownContent}`; for (const noteMeta of Object.values(noteIdToMeta)) { // filter out relations which are not inside this export - noteMeta.attributes = noteMeta.attributes.filter(attr => { + noteMeta.attributes = (noteMeta.attributes || []).filter(attr => { if (attr.type !== 'relation') { return true; } else if (attr.value in noteIdToMeta) { @@ -567,7 +531,9 @@ ${markdownContent}`; } if (!rootMeta) { // corner case of disabled export for exported note - res.sendStatus(400); + if ("sendStatus" in res) { + res.sendStatus(400); + } return; } @@ -578,6 +544,10 @@ ${markdownContent}`; saveNote(rootMeta, ''); if (format === 'html') { + if (!navigationMeta || !indexMeta || !cssMeta) { + throw new Error("Missing meta."); + } + saveNavigation(rootMeta, navigationMeta); saveIndex(rootMeta, indexMeta); saveCss(rootMeta, cssMeta); @@ -586,7 +556,7 @@ ${markdownContent}`; const note = branch.getNote(); const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`; - if (setHeaders) { + if (setHeaders && "setHeader" in res) { res.setHeader('Content-Disposition', utils.getContentDisposition(zipFileName)); res.setHeader('Content-Type', 'application/zip'); } @@ -597,7 +567,7 @@ ${markdownContent}`; taskContext.taskSucceeded(); } -async function exportToZipFile(noteId, format, zipFilePath) { +async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string) { const fileOutputStream = fs.createWriteStream(zipFilePath); const taskContext = new TaskContext('no-progress-reporting'); @@ -612,7 +582,7 @@ async function exportToZipFile(noteId, format, zipFilePath) { log.info(`Exported '${noteId}' with format '${format}' to '${zipFilePath}'`); } -module.exports = { +export = { exportToZip, exportToZipFile }; diff --git a/src/services/meta/attachment_meta.ts b/src/services/meta/attachment_meta.ts index b84830591..8c237ab21 100644 --- a/src/services/meta/attachment_meta.ts +++ b/src/services/meta/attachment_meta.ts @@ -1,9 +1,9 @@ interface AttachmentMeta { - attachmentId: string; + attachmentId?: string; title: string; role: string; mime: string; - position: number; + position?: number; dataFileName: string; } diff --git a/src/services/meta/note_meta.ts b/src/services/meta/note_meta.ts index 3492c9c63..a82d84e58 100644 --- a/src/services/meta/note_meta.ts +++ b/src/services/meta/note_meta.ts @@ -1,23 +1,24 @@ +import AttachmentMeta = require("./attachment_meta"); import AttributeMeta = require("./attribute_meta"); interface NoteMeta { - noteId: string; - notePath: string; - isClone: boolean; - title: string; - notePosition: number; - prefix: string; - isExpanded: boolean; - type: string; - mime: string; + noteId?: string; + notePath?: string[]; + isClone?: boolean; + title?: string; + notePosition?: number; + prefix?: string | null; + isExpanded?: boolean; + type?: string; + mime?: string; /** 'html' or 'markdown', applicable to text notes only */ - format: "html" | "markdown"; + format?: "html" | "markdown"; dataFileName: string; - dirFileName: string; + dirFileName?: string; /** this file should not be imported (e.g., HTML navigation) */ - noImport: boolean; - attributes: AttributeMeta[]; - attachments: AttributeMeta[]; + noImport?: boolean; + attributes?: AttributeMeta[]; + attachments?: AttachmentMeta[]; children?: NoteMeta[]; } diff --git a/src/services/sql_init.ts b/src/services/sql_init.ts index c8cae5ad2..dbdde89c5 100644 --- a/src/services/sql_init.ts +++ b/src/services/sql_init.ts @@ -96,7 +96,7 @@ async function createInitialDatabase() { const dummyTaskContext = new TaskContext("no-progress-reporting", 'import', false); - const zipImportService = require('./import/zip.js'); + const zipImportService = require('./import/zip'); await zipImportService.importZip(dummyTaskContext, demoFile, rootNote); sql.transactional(() => {