mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
refactor: add typesafety to TaskContext
This commit is contained in:
parent
777d5ab3b7
commit
9c8b0611ea
@ -111,7 +111,7 @@ ws.subscribeToMessages(async (message) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isProtecting = message.data.protect;
|
const isProtecting = message.data?.protect;
|
||||||
const title = isProtecting ? t("protected_session.protecting-title") : t("protected_session.unprotecting-title");
|
const title = isProtecting ? t("protected_session.protecting-title") : t("protected_session.unprotecting-title");
|
||||||
|
|
||||||
if (message.type === "taskError") {
|
if (message.type === "taskError") {
|
||||||
|
@ -137,13 +137,13 @@ class BBranch extends AbstractBeccaEntity<BBranch> {
|
|||||||
*
|
*
|
||||||
* @returns true if note has been deleted, false otherwise
|
* @returns true if note has been deleted, false otherwise
|
||||||
*/
|
*/
|
||||||
deleteBranch(deleteId?: string, taskContext?: TaskContext): boolean {
|
deleteBranch(deleteId?: string, taskContext?: TaskContext<"deleteNotes">): boolean {
|
||||||
if (!deleteId) {
|
if (!deleteId) {
|
||||||
deleteId = utils.randomString(10);
|
deleteId = utils.randomString(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!taskContext) {
|
if (!taskContext) {
|
||||||
taskContext = new TaskContext("no-progress-reporting");
|
taskContext = new TaskContext("no-progress-reporting", "deleteNotes", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
|
@ -1512,7 +1512,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
|||||||
*
|
*
|
||||||
* @param deleteId - optional delete identified
|
* @param deleteId - optional delete identified
|
||||||
*/
|
*/
|
||||||
deleteNote(deleteId: string | null = null, taskContext: TaskContext | null = null) {
|
deleteNote(deleteId: string | null = null, taskContext: TaskContext<"deleteNotes"> | null = null) {
|
||||||
if (this.isDeleted) {
|
if (this.isDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1522,7 +1522,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!taskContext) {
|
if (!taskContext) {
|
||||||
taskContext = new TaskContext("no-progress-reporting");
|
taskContext = new TaskContext("no-progress-reporting", "deleteNotes", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// needs to be run before branches and attributes are deleted and thus attached relations disappear
|
// needs to be run before branches and attributes are deleted and thus attached relations disappear
|
||||||
|
@ -108,7 +108,7 @@ function register(router: Router) {
|
|||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
}
|
}
|
||||||
|
|
||||||
note.deleteNote(null, new TaskContext("no-progress-reporting"));
|
note.deleteNote(null, new TaskContext("no-progress-reporting", "deleteNotes", null));
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
});
|
});
|
||||||
@ -153,7 +153,7 @@ function register(router: Router) {
|
|||||||
throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`);
|
throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskContext = new TaskContext("no-progress-reporting");
|
const taskContext = new TaskContext("no-progress-reporting", "export", null);
|
||||||
|
|
||||||
// technically a branch is being exported (includes prefix), but it's such a minor difference yet usability pain
|
// technically a branch is being exported (includes prefix), but it's such a minor difference yet usability pain
|
||||||
// (e.g. branchIds are not seen in UI), that we export "note export" instead.
|
// (e.g. branchIds are not seen in UI), that we export "note export" instead.
|
||||||
@ -164,7 +164,7 @@ function register(router: Router) {
|
|||||||
|
|
||||||
eu.route(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => {
|
eu.route(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => {
|
||||||
const note = eu.getAndCheckNote(req.params.noteId);
|
const note = eu.getAndCheckNote(req.params.noteId);
|
||||||
const taskContext = new TaskContext("no-progress-reporting");
|
const taskContext = new TaskContext("no-progress-reporting", "importNotes", null);
|
||||||
|
|
||||||
zipImportService.importZip(taskContext, req.body, note).then((importedNote) => {
|
zipImportService.importZip(taskContext, req.body, note).then((importedNote) => {
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
|
@ -236,7 +236,7 @@ function deleteBranch(req: Request) {
|
|||||||
const eraseNotes = req.query.eraseNotes === "true";
|
const eraseNotes = req.query.eraseNotes === "true";
|
||||||
const branch = becca.getBranchOrThrow(req.params.branchId);
|
const branch = becca.getBranchOrThrow(req.params.branchId);
|
||||||
|
|
||||||
const taskContext = TaskContext.getInstance(req.query.taskId as string, "deleteNotes");
|
const taskContext = TaskContext.getInstance(req.query.taskId as string, "deleteNotes", null);
|
||||||
|
|
||||||
const deleteId = utils.randomString(10);
|
const deleteId = utils.randomString(10);
|
||||||
let noteDeleted;
|
let noteDeleted;
|
||||||
@ -251,7 +251,7 @@ function deleteBranch(req: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -23,7 +23,7 @@ function exportBranch(req: Request, res: Response) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskContext = new TaskContext(taskId, "export");
|
const taskContext = new TaskContext(taskId, "export", null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (type === "subtree" && (format === "html" || format === "markdown")) {
|
if (type === "subtree" && (format === "html" || format === "markdown")) {
|
||||||
|
@ -116,7 +116,7 @@ function importAttachmentsToNote(req: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parentNote = becca.getNoteOrThrow(parentNoteId);
|
const parentNote = becca.getNoteOrThrow(parentNoteId);
|
||||||
const taskContext = TaskContext.getInstance(taskId, "importAttachment", options);
|
const taskContext = TaskContext.getInstance(taskId, "importNotes", options);
|
||||||
|
|
||||||
// unlike in note import, we let the events run, because a huge number of attachments is not likely
|
// unlike in note import, we let the events run, because a huge number of attachments is not likely
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ function deleteNote(req: Request) {
|
|||||||
if (typeof taskId !== "string") {
|
if (typeof taskId !== "string") {
|
||||||
throw new ValidationError("Missing or incorrect type for task ID.");
|
throw new ValidationError("Missing or incorrect type for task ID.");
|
||||||
}
|
}
|
||||||
const taskContext = TaskContext.getInstance(taskId, "deleteNotes");
|
const taskContext = TaskContext.getInstance(taskId, "deleteNotes", null);
|
||||||
|
|
||||||
note.deleteNote(deleteId, taskContext);
|
note.deleteNote(deleteId, taskContext);
|
||||||
|
|
||||||
@ -193,16 +193,16 @@ function deleteNote(req: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function undeleteNote(req: Request) {
|
function undeleteNote(req: Request) {
|
||||||
const taskContext = TaskContext.getInstance(utils.randomString(10), "undeleteNotes");
|
const taskContext = TaskContext.getInstance(utils.randomString(10), "undeleteNotes", null);
|
||||||
|
|
||||||
noteService.undeleteNote(req.params.noteId, taskContext);
|
noteService.undeleteNote(req.params.noteId, taskContext);
|
||||||
|
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortChildNotes(req: Request) {
|
function sortChildNotes(req: Request) {
|
||||||
@ -226,7 +226,7 @@ function protectNote(req: Request) {
|
|||||||
|
|
||||||
noteService.protectNoteRecursively(note, protect, includingSubTree, taskContext);
|
noteService.protectNoteRecursively(note, protect, includingSubTree, taskContext);
|
||||||
|
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNoteTypeMime(req: Request) {
|
function setNoteTypeMime(req: Request) {
|
||||||
|
@ -6,7 +6,7 @@ import type TaskContext from "../task_context.js";
|
|||||||
import type BBranch from "../../becca/entities/bbranch.js";
|
import type BBranch from "../../becca/entities/bbranch.js";
|
||||||
import type { Response } from "express";
|
import type { Response } from "express";
|
||||||
|
|
||||||
function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string, res: Response) {
|
function exportToOpml(taskContext: TaskContext<"export">, branch: BBranch, version: string, res: Response) {
|
||||||
if (!["1.0", "2.0"].includes(version)) {
|
if (!["1.0", "2.0"].includes(version)) {
|
||||||
throw new Error(`Unrecognized OPML version ${version}`);
|
throw new Error(`Unrecognized OPML version ${version}`);
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ function exportToOpml(taskContext: TaskContext, branch: BBranch, version: string
|
|||||||
</opml>`);
|
</opml>`);
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareText(text: string) {
|
function prepareText(text: string) {
|
||||||
|
@ -10,7 +10,7 @@ import type BBranch from "../../becca/entities/bbranch.js";
|
|||||||
import type { Response } from "express";
|
import type { Response } from "express";
|
||||||
import type BNote from "../../becca/entities/bnote.js";
|
import type BNote from "../../becca/entities/bnote.js";
|
||||||
|
|
||||||
function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response) {
|
function exportSingleNote(taskContext: TaskContext<"export">, branch: BBranch, format: "html" | "markdown", res: Response) {
|
||||||
const note = branch.getNote();
|
const note = branch.getNote();
|
||||||
|
|
||||||
if (note.type === "image" || note.type === "file") {
|
if (note.type === "image" || note.type === "file") {
|
||||||
@ -30,7 +30,7 @@ function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "ht
|
|||||||
res.send(payload);
|
res.send(payload);
|
||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapByNoteType(note: BNote, content: string | Buffer<ArrayBufferLike>, format: "html" | "markdown") {
|
export function mapByNoteType(note: BNote, content: string | Buffer<ArrayBufferLike>, format: "html" | "markdown") {
|
||||||
|
@ -40,7 +40,7 @@ export interface AdvancedExportOptions {
|
|||||||
customRewriteLinks?: (originalRewriteLinks: RewriteLinksFn, getNoteTargetUrl: (targetNoteId: string, sourceMeta: NoteMeta) => string | null) => RewriteLinksFn;
|
customRewriteLinks?: (originalRewriteLinks: RewriteLinksFn, getNoteTargetUrl: (targetNoteId: string, sourceMeta: NoteMeta) => string | null) => RewriteLinksFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) {
|
async function exportToZip(taskContext: TaskContext<"export">, branch: BBranch, format: "html" | "markdown", res: Response | fs.WriteStream, setHeaders = true, zipExportOptions?: AdvancedExportOptions) {
|
||||||
if (!["html", "markdown"].includes(format)) {
|
if (!["html", "markdown"].includes(format)) {
|
||||||
throw new ValidationError(`Only 'html' and 'markdown' allowed as export format, '${format}' given`);
|
throw new ValidationError(`Only 'html' and 'markdown' allowed as export format, '${format}' given`);
|
||||||
}
|
}
|
||||||
@ -611,7 +611,7 @@ ${markdownContent}`;
|
|||||||
|
|
||||||
archive.pipe(res);
|
archive.pipe(res);
|
||||||
await archive.finalize();
|
await archive.finalize();
|
||||||
taskContext.taskSucceeded();
|
taskContext.taskSucceeded(null);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
const message = `Export failed with error: ${e instanceof Error ? e.message : String(e)}`;
|
const message = `Export failed with error: ${e instanceof Error ? e.message : String(e)}`;
|
||||||
log.error(message);
|
log.error(message);
|
||||||
@ -627,7 +627,7 @@ ${markdownContent}`;
|
|||||||
|
|
||||||
async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string, zipExportOptions?: AdvancedExportOptions) {
|
async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string, zipExportOptions?: AdvancedExportOptions) {
|
||||||
const fileOutputStream = fs.createWriteStream(zipFilePath);
|
const fileOutputStream = fs.createWriteStream(zipFilePath);
|
||||||
const taskContext = new TaskContext("no-progress-reporting");
|
const taskContext = new TaskContext("no-progress-reporting", "export", null);
|
||||||
|
|
||||||
const note = becca.getNote(noteId);
|
const note = becca.getNote(noteId);
|
||||||
|
|
||||||
|
@ -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): Promise<BNote> {
|
function importEnex(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote): Promise<BNote> {
|
||||||
const saxStream = sax.createStream(true);
|
const saxStream = sax.createStream(true);
|
||||||
|
|
||||||
const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex") ? file.originalname.substr(0, file.originalname.length - 5) : file.originalname;
|
const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex") ? file.originalname.substr(0, file.originalname.length - 5) : file.originalname;
|
||||||
|
@ -91,14 +91,14 @@ function getMime(fileName: string) {
|
|||||||
return mimeFromExt || mimeTypes.lookup(fileNameLc);
|
return mimeFromExt || mimeTypes.lookup(fileNameLc);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getType(options: TaskData, mime: string): NoteType {
|
function getType(options: TaskData<"importNotes">, mime: string): NoteType {
|
||||||
const mimeLc = mime?.toLowerCase();
|
const mimeLc = mime?.toLowerCase();
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case options.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown", "text/mdx"].includes(mimeLc):
|
case options?.textImportedAsText && ["text/html", "text/markdown", "text/x-markdown", "text/mdx"].includes(mimeLc):
|
||||||
return "text";
|
return "text";
|
||||||
|
|
||||||
case options.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc):
|
case options?.codeImportedAsCode && CODE_MIME_TYPES.has(mimeLc):
|
||||||
return "code";
|
return "code";
|
||||||
|
|
||||||
case mime.startsWith("image/"):
|
case mime.startsWith("image/"):
|
||||||
|
@ -28,7 +28,7 @@ interface OpmlOutline {
|
|||||||
outline: OpmlOutline[];
|
outline: OpmlOutline[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importOpml(taskContext: TaskContext, fileBuffer: string | Buffer, parentNote: BNote) {
|
async function importOpml(taskContext: TaskContext<"importNotes">, fileBuffer: string | Buffer, parentNote: BNote) {
|
||||||
const xml = await new Promise<OpmlXml>(function (resolve, reject) {
|
const xml = await new Promise<OpmlXml>(function (resolve, reject) {
|
||||||
parseString(fileBuffer, function (err: any, result: OpmlXml) {
|
parseString(fileBuffer, function (err: any, result: OpmlXml) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -14,7 +14,7 @@ import htmlSanitizer from "../html_sanitizer.js";
|
|||||||
import type { File } from "./common.js";
|
import type { File } from "./common.js";
|
||||||
import type { NoteType } from "@triliumnext/commons";
|
import type { NoteType } from "@triliumnext/commons";
|
||||||
|
|
||||||
function importSingleFile(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importSingleFile(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const mime = mimeService.getMime(file.originalname) || file.mimetype;
|
const mime = mimeService.getMime(file.originalname) || file.mimetype;
|
||||||
|
|
||||||
if (taskContext?.data?.textImportedAsText) {
|
if (taskContext?.data?.textImportedAsText) {
|
||||||
@ -42,7 +42,7 @@ function importSingleFile(taskContext: TaskContext, file: File, parentNote: BNot
|
|||||||
return importFile(taskContext, file, parentNote);
|
return importFile(taskContext, file, parentNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
function importImage(file: File, parentNote: BNote, taskContext: TaskContext) {
|
function importImage(file: File, parentNote: BNote, taskContext: TaskContext<"importNotes">) {
|
||||||
if (typeof file.buffer === "string") {
|
if (typeof file.buffer === "string") {
|
||||||
throw new Error("Invalid file content for image.");
|
throw new Error("Invalid file content for image.");
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ function importImage(file: File, parentNote: BNote, taskContext: TaskContext) {
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importFile(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importFile(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const originalName = file.originalname;
|
const originalName = file.originalname;
|
||||||
|
|
||||||
const { note } = noteService.createNewNote({
|
const { note } = noteService.createNewNote({
|
||||||
@ -72,7 +72,7 @@ function importFile(taskContext: TaskContext, file: File, parentNote: BNote) {
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importCodeNote(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||||
const content = processStringOrBuffer(file.buffer);
|
const content = processStringOrBuffer(file.buffer);
|
||||||
const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
|
const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
|
||||||
@ -97,7 +97,7 @@ function importCodeNote(taskContext: TaskContext, file: File, parentNote: BNote)
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importCustomType(taskContext: TaskContext, file: File, parentNote: BNote, type: NoteType, mime: string) {
|
function importCustomType(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote, type: NoteType, mime: string) {
|
||||||
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||||
const content = processStringOrBuffer(file.buffer);
|
const content = processStringOrBuffer(file.buffer);
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ function importCustomType(taskContext: TaskContext, file: File, parentNote: BNot
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importPlainText(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importPlainText(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||||
const plainTextContent = processStringOrBuffer(file.buffer);
|
const plainTextContent = processStringOrBuffer(file.buffer);
|
||||||
const htmlContent = convertTextToHtml(plainTextContent);
|
const htmlContent = convertTextToHtml(plainTextContent);
|
||||||
@ -150,7 +150,7 @@ function convertTextToHtml(text: string) {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importMarkdown(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
const title = getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||||
|
|
||||||
const markdownContent = processStringOrBuffer(file.buffer);
|
const markdownContent = processStringOrBuffer(file.buffer);
|
||||||
@ -174,7 +174,7 @@ function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote)
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importHtml(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
let content = processStringOrBuffer(file.buffer);
|
let content = processStringOrBuffer(file.buffer);
|
||||||
|
|
||||||
// Try to get title from HTML first, fall back to filename
|
// Try to get title from HTML first, fall back to filename
|
||||||
@ -202,7 +202,7 @@ function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) {
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function importAttachment(taskContext: TaskContext, file: File, parentNote: BNote) {
|
function importAttachment(taskContext: TaskContext<"importNotes">, file: File, parentNote: BNote) {
|
||||||
const mime = mimeService.getMime(file.originalname) || file.mimetype;
|
const mime = mimeService.getMime(file.originalname) || file.mimetype;
|
||||||
|
|
||||||
if (mime.startsWith("image/") && typeof file.buffer !== "string") {
|
if (mime.startsWith("image/") && typeof file.buffer !== "string") {
|
||||||
|
@ -30,7 +30,7 @@ interface ImportZipOpts {
|
|||||||
preserveIds?: boolean;
|
preserveIds?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRootNote: BNote, opts?: ImportZipOpts): Promise<BNote> {
|
async function importZip(taskContext: TaskContext<"importNotes">, fileBuffer: Buffer, importRootNote: BNote, opts?: ImportZipOpts): Promise<BNote> {
|
||||||
/** maps from original noteId (in ZIP file) to newly generated noteId */
|
/** maps from original noteId (in ZIP file) to newly generated noteId */
|
||||||
const noteIdMap: Record<string, string> = {};
|
const noteIdMap: Record<string, string> = {};
|
||||||
/** type maps from original attachmentId (in ZIP file) to newly generated attachmentId */
|
/** type maps from original attachmentId (in ZIP file) to newly generated attachmentId */
|
||||||
@ -174,7 +174,7 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo
|
|||||||
return noteId;
|
return noteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectFileTypeAndMime(taskContext: TaskContext, filePath: string) {
|
function detectFileTypeAndMime(taskContext: TaskContext<"importNotes">, filePath: string) {
|
||||||
const mime = mimeService.getMime(filePath) || "application/octet-stream";
|
const mime = mimeService.getMime(filePath) || "application/octet-stream";
|
||||||
const type = mimeService.getType(taskContext.data || {}, mime);
|
const type = mimeService.getType(taskContext.data || {}, mime);
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ function createNewNoteWithTarget(target: "into" | "after" | "before", targetBran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function protectNoteRecursively(note: BNote, protect: boolean, includingSubTree: boolean, taskContext: TaskContext) {
|
function protectNoteRecursively(note: BNote, protect: boolean, includingSubTree: boolean, taskContext: TaskContext<"protectNotes">) {
|
||||||
protectNote(note, protect);
|
protectNote(note, protect);
|
||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
@ -765,7 +765,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function undeleteNote(noteId: string, taskContext: TaskContext) {
|
function undeleteNote(noteId: string, taskContext: TaskContext<"undeleteNotes">) {
|
||||||
const noteRow = sql.getRow<NoteRow>("SELECT * FROM notes WHERE noteId = ?", [noteId]);
|
const noteRow = sql.getRow<NoteRow>("SELECT * FROM notes WHERE noteId = ?", [noteId]);
|
||||||
|
|
||||||
if (!noteRow.isDeleted || !noteRow.deleteId) {
|
if (!noteRow.isDeleted || !noteRow.deleteId) {
|
||||||
@ -785,7 +785,7 @@ function undeleteNote(noteId: string, taskContext: TaskContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function undeleteBranch(branchId: string, deleteId: string, taskContext: TaskContext) {
|
function undeleteBranch(branchId: string, deleteId: string, taskContext: TaskContext<"undeleteNotes">) {
|
||||||
const branchRow = sql.getRow<BranchRow>("SELECT * FROM branches WHERE branchId = ?", [branchId]);
|
const branchRow = sql.getRow<BranchRow>("SELECT * FROM branches WHERE branchId = ?", [branchId]);
|
||||||
|
|
||||||
if (!branchRow.isDeleted) {
|
if (!branchRow.isDeleted) {
|
||||||
|
@ -122,7 +122,7 @@ async function createInitialDatabase(skipDemoDb?: boolean) {
|
|||||||
|
|
||||||
log.info("Importing demo content ...");
|
log.info("Importing demo content ...");
|
||||||
|
|
||||||
const dummyTaskContext = new TaskContext("no-progress-reporting", "import", false);
|
const dummyTaskContext = new TaskContext("no-progress-reporting", "importNotes", null);
|
||||||
|
|
||||||
if (demoFile) {
|
if (demoFile) {
|
||||||
await zipImportService.importZip(dummyTaskContext, demoFile, rootNote);
|
await zipImportService.importZip(dummyTaskContext, demoFile, rootNote);
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import type { TaskType } from "@triliumnext/commons";
|
import type { TaskData, TaskResult, TaskType, WebSocketMessage } from "@triliumnext/commons";
|
||||||
import ws from "./ws.js";
|
import ws from "./ws.js";
|
||||||
|
|
||||||
// taskId => TaskContext
|
// taskId => TaskContext
|
||||||
const taskContexts: Record<string, TaskContext<TaskType>> = {};
|
const taskContexts: Record<string, TaskContext<any>> = {};
|
||||||
|
|
||||||
class TaskContext<TaskTypeT extends TaskType> {
|
class TaskContext<T extends TaskType> {
|
||||||
private taskId: string;
|
private taskId: string;
|
||||||
private taskType: TaskType;
|
private taskType: TaskType;
|
||||||
private progressCount: number;
|
private progressCount: number;
|
||||||
private lastSentCountTs: number;
|
private lastSentCountTs: number;
|
||||||
data: TaskData | null;
|
data: TaskData<T>;
|
||||||
noteDeletionHandlerTriggered: boolean;
|
noteDeletionHandlerTriggered: boolean;
|
||||||
|
|
||||||
constructor(taskId: string, taskType: TaskTypeT, data: {} | null = {}) {
|
constructor(taskId: string, taskType: T, data: TaskData<T>) {
|
||||||
this.taskId = taskId;
|
this.taskId = taskId;
|
||||||
this.taskType = taskType;
|
this.taskType = taskType;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -31,7 +31,7 @@ class TaskContext<TaskTypeT extends TaskType> {
|
|||||||
this.increaseProgressCount();
|
this.increaseProgressCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getInstance<TaskTypeT extends TaskType>(taskId: string, taskType: TaskTypeT, data: {} | null = null): TaskContext<TaskTypeT> {
|
static getInstance<T extends TaskType>(taskId: string, taskType: T, data: TaskData<T>): TaskContext<T> {
|
||||||
if (!taskContexts[taskId]) {
|
if (!taskContexts[taskId]) {
|
||||||
taskContexts[taskId] = new TaskContext(taskId, taskType, data);
|
taskContexts[taskId] = new TaskContext(taskId, taskType, data);
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ class TaskContext<TaskTypeT extends TaskType> {
|
|||||||
taskType: this.taskType,
|
taskType: this.taskType,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
progressCount: this.progressCount
|
progressCount: this.progressCount
|
||||||
});
|
} as WebSocketMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,17 +62,17 @@ class TaskContext<TaskTypeT extends TaskType> {
|
|||||||
taskType: this.taskType,
|
taskType: this.taskType,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
message
|
message
|
||||||
});
|
} as WebSocketMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSucceeded(result?: string | Record<string, string | undefined>) {
|
taskSucceeded(result: TaskResult<T>) {
|
||||||
ws.sendMessageToAllClients({
|
ws.sendMessageToAllClients({
|
||||||
type: "taskSucceeded",
|
type: "taskSucceeded",
|
||||||
taskId: this.taskId,
|
taskId: this.taskId,
|
||||||
taskType: this.taskType,
|
taskType: this.taskType,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
result
|
result
|
||||||
});
|
} as WebSocketMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import type { IncomingMessage, Server as HttpServer } from "http";
|
|||||||
import { WebSocketMessage, type EntityChange } from "@triliumnext/commons";
|
import { WebSocketMessage, type EntityChange } from "@triliumnext/commons";
|
||||||
|
|
||||||
let webSocketServer!: WebSocketServer;
|
let webSocketServer!: WebSocketServer;
|
||||||
let lastSyncedPush: number | null = null;
|
let lastSyncedPush: number;
|
||||||
|
|
||||||
type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void;
|
type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void;
|
||||||
function init(httpServer: HttpServer, sessionParser: SessionParser) {
|
function init(httpServer: HttpServer, sessionParser: SessionParser) {
|
||||||
|
@ -26,37 +26,64 @@ export interface EntityChangeRecord {
|
|||||||
entity?: EntityRow;
|
entity?: EntityRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskStatus<TypeT, DataT, ResultT> = {
|
type TaskDataDefinitions = {
|
||||||
|
empty: null,
|
||||||
|
deleteNotes: null,
|
||||||
|
undeleteNotes: null,
|
||||||
|
export: null,
|
||||||
|
protectNotes: {
|
||||||
|
protect: boolean;
|
||||||
|
}
|
||||||
|
importNotes: {
|
||||||
|
textImportedAsText?: boolean;
|
||||||
|
codeImportedAsCode?: boolean;
|
||||||
|
replaceUnderscoresWithSpaces?: boolean;
|
||||||
|
shrinkImages?: boolean;
|
||||||
|
safeImport?: boolean;
|
||||||
|
} | null,
|
||||||
|
importAttachments: null
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskResultDefinitions = {
|
||||||
|
empty: null,
|
||||||
|
deleteNotes: null,
|
||||||
|
undeleteNotes: null,
|
||||||
|
export: null,
|
||||||
|
protectNotes: null,
|
||||||
|
importNotes: {
|
||||||
|
parentNoteId?: string;
|
||||||
|
importedNoteId?: string
|
||||||
|
};
|
||||||
|
importAttachments: {
|
||||||
|
parentNoteId?: string;
|
||||||
|
importedNoteId?: string
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TaskType = keyof TaskDataDefinitions | keyof TaskResultDefinitions;
|
||||||
|
export type TaskData<T extends TaskType> = TaskDataDefinitions[T];
|
||||||
|
export type TaskResult<T extends TaskType> = TaskResultDefinitions[T];
|
||||||
|
|
||||||
|
type TaskDefinition<T extends TaskType> = {
|
||||||
type: "taskProgressCount",
|
type: "taskProgressCount",
|
||||||
taskId: string;
|
taskId: string;
|
||||||
taskType: TypeT;
|
taskType: T;
|
||||||
data: DataT,
|
data: TaskData<T>,
|
||||||
progressCount: number
|
progressCount: number
|
||||||
} | {
|
} | {
|
||||||
type: "taskError",
|
type: "taskError",
|
||||||
taskId: string;
|
taskId: string;
|
||||||
taskType: TypeT;
|
taskType: T;
|
||||||
data: DataT;
|
data: TaskData<T>,
|
||||||
message: string;
|
message: string;
|
||||||
} | {
|
} | {
|
||||||
type: "taskSucceeded",
|
type: "taskSucceeded",
|
||||||
taskId: string;
|
taskId: string;
|
||||||
taskType: TypeT;
|
taskType: T;
|
||||||
data: DataT;
|
data: TaskData<T>,
|
||||||
result: ResultT;
|
result: TaskResult<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskDefinitions =
|
|
||||||
TaskStatus<"protectNotes", { protect: boolean; }, null>
|
|
||||||
| TaskStatus<"importNotes", null, { importedNoteId: string }>
|
|
||||||
| TaskStatus<"importAttachments", null, { parentNoteId?: string; importedNoteId: string }>
|
|
||||||
| TaskStatus<"deleteNotes", null, null>
|
|
||||||
| TaskStatus<"undeleteNotes", null, null>
|
|
||||||
| TaskStatus<"export", null, null>
|
|
||||||
;
|
|
||||||
|
|
||||||
export type TaskType = TaskDefinitions["taskType"];
|
|
||||||
|
|
||||||
export interface OpenedFileUpdateStatus {
|
export interface OpenedFileUpdateStatus {
|
||||||
entityType: string;
|
entityType: string;
|
||||||
entityId: string;
|
entityId: string;
|
||||||
@ -64,7 +91,16 @@ export interface OpenedFileUpdateStatus {
|
|||||||
filePath: string;
|
filePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WebSocketMessage = TaskDefinitions | {
|
type AllTaskDefinitions =
|
||||||
|
| TaskDefinition<"empty">
|
||||||
|
| TaskDefinition<"deleteNotes">
|
||||||
|
| TaskDefinition<"undeleteNotes">
|
||||||
|
| TaskDefinition<"export">
|
||||||
|
| TaskDefinition<"protectNotes">
|
||||||
|
| TaskDefinition<"importNotes">
|
||||||
|
| TaskDefinition<"importAttachments">;
|
||||||
|
|
||||||
|
export type WebSocketMessage = AllTaskDefinitions | {
|
||||||
type: "ping"
|
type: "ping"
|
||||||
} | {
|
} | {
|
||||||
type: "frontend-update",
|
type: "frontend-update",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user