refactor(server): use strong typing for routes

This commit is contained in:
Elian Doran 2026-02-26 21:08:54 +02:00
parent fd805a5279
commit 0c6326b678
No known key found for this signature in database
34 changed files with 396 additions and 393 deletions

View File

@ -1,14 +1,15 @@
import type { AttachmentRow } from "@triliumnext/commons";
import type { Router } from "express";
import becca from "../becca/becca.js";
import utils from "../services/utils.js";
import eu from "./etapi_utils.js";
import type { ValidatorMap } from "./etapi-interface.js";
import mappers from "./mappers.js";
import v from "./validators.js";
import utils from "../services/utils.js";
import type { Router } from "express";
import type { AttachmentRow } from "@triliumnext/commons";
import type { ValidatorMap } from "./etapi-interface.js";
function register(router: Router) {
eu.route(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
const attachments = note.getAttachments();
res.json(attachments.map((attachment) => mappers.mapAttachmentToPojo(attachment)));
@ -23,7 +24,7 @@ function register(router: Router) {
content: [v.isString]
};
eu.route(router, "post", "/etapi/attachments", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "post", "/etapi/attachments", (req, res, next) => {
const _params: Partial<AttachmentRow> = {};
eu.validateAndPatch(_params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT);
const params = _params as AttachmentRow;
@ -41,7 +42,7 @@ function register(router: Router) {
}
});
eu.route(router, "get", "/etapi/attachments/:attachmentId", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "get", "/etapi/attachments/:attachmentId", (req, res, next) => {
const attachment = eu.getAndCheckAttachment(req.params.attachmentId);
res.json(mappers.mapAttachmentToPojo(attachment));
@ -54,7 +55,7 @@ function register(router: Router) {
position: [v.notNull, v.isInteger]
};
eu.route(router, "patch", "/etapi/attachments/:attachmentId", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "patch", "/etapi/attachments/:attachmentId", (req, res, next) => {
const attachment = eu.getAndCheckAttachment(req.params.attachmentId);
if (attachment.isProtected) {
@ -67,7 +68,7 @@ function register(router: Router) {
res.json(mappers.mapAttachmentToPojo(attachment));
});
eu.route(router, "get", "/etapi/attachments/:attachmentId/content", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "get", "/etapi/attachments/:attachmentId/content", (req, res, next) => {
const attachment = eu.getAndCheckAttachment(req.params.attachmentId);
if (attachment.isProtected) {
@ -84,7 +85,7 @@ function register(router: Router) {
res.send(attachment.getContent());
});
eu.route(router, "put", "/etapi/attachments/:attachmentId/content", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "put", "/etapi/attachments/:attachmentId/content", (req, res, next) => {
const attachment = eu.getAndCheckAttachment(req.params.attachmentId);
if (attachment.isProtected) {
@ -96,7 +97,7 @@ function register(router: Router) {
return res.sendStatus(204);
});
eu.route(router, "delete", "/etapi/attachments/:attachmentId", (req, res, next) => {
eu.route<{ attachmentId: string }>(router, "delete", "/etapi/attachments/:attachmentId", (req, res, next) => {
const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) {

View File

@ -1,14 +1,15 @@
import becca from "../becca/becca.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import attributeService from "../services/attributes.js";
import v from "./validators.js";
import type { Router } from "express";
import type { AttributeRow } from "@triliumnext/commons";
import type { Router } from "express";
import becca from "../becca/becca.js";
import attributeService from "../services/attributes.js";
import eu from "./etapi_utils.js";
import type { ValidatorMap } from "./etapi-interface.js";
import mappers from "./mappers.js";
import v from "./validators.js";
function register(router: Router) {
eu.route(router, "get", "/etapi/attributes/:attributeId", (req, res, next) => {
eu.route<{ attributeId: string }>(router, "get", "/etapi/attributes/:attributeId", (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId);
res.json(mappers.mapAttributeToPojo(attribute));
@ -51,7 +52,7 @@ function register(router: Router) {
position: [v.notNull, v.isInteger]
};
eu.route(router, "patch", "/etapi/attributes/:attributeId", (req, res, next) => {
eu.route<{ attributeId: string }>(router, "patch", "/etapi/attributes/:attributeId", (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId);
if (attribute.type === "label") {
@ -67,7 +68,7 @@ function register(router: Router) {
res.json(mappers.mapAttributeToPojo(attribute));
});
eu.route(router, "delete", "/etapi/attributes/:attributeId", (req, res, next) => {
eu.route<{ attributeId: string }>(router, "delete", "/etapi/attributes/:attributeId", (req, res, next) => {
const attribute = becca.getAttribute(req.params.attributeId);
if (!attribute) {

View File

@ -1,10 +1,10 @@
import type { Router } from "express";
import eu from "./etapi_utils.js";
import backupService from "../services/backup.js";
import eu from "./etapi_utils.js";
function register(router: Router) {
eu.route(router, "put", "/etapi/backup/:backupName", (req, res, next) => {
eu.route<{ backupName: string }>(router, "put", "/etapi/backup/:backupName", (req, res, next) => {
backupService.backupNow(req.params.backupName)
.then(() => res.sendStatus(204))
.catch(() => res.sendStatus(500));

View File

@ -1,15 +1,15 @@
import type { BranchRow } from "@triliumnext/commons";
import type { Router } from "express";
import becca from "../becca/becca.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import BBranch from "../becca/entities/bbranch.js";
import entityChangesService from "../services/entity_changes.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import v from "./validators.js";
import type { BranchRow } from "@triliumnext/commons";
function register(router: Router) {
eu.route(router, "get", "/etapi/branches/:branchId", (req, res, next) => {
eu.route<{ branchId: string }>(router, "get", "/etapi/branches/:branchId", (req, res, next) => {
const branch = eu.getAndCheckBranch(req.params.branchId);
res.json(mappers.mapBranchToPojo(branch));
@ -37,15 +37,15 @@ function register(router: Router) {
existing.save();
return res.status(200).json(mappers.mapBranchToPojo(existing));
} else {
try {
const branch = new BBranch(params).save();
}
try {
const branch = new BBranch(params).save();
res.status(201).json(mappers.mapBranchToPojo(branch));
} catch (e: any) {
throw new eu.EtapiError(400, eu.GENERIC_CODE, e.message);
}
res.status(201).json(mappers.mapBranchToPojo(branch));
} catch (e: any) {
throw new eu.EtapiError(400, eu.GENERIC_CODE, e.message);
}
});
const ALLOWED_PROPERTIES_FOR_PATCH = {
@ -54,7 +54,7 @@ function register(router: Router) {
isExpanded: [v.notNull, v.isBoolean]
};
eu.route(router, "patch", "/etapi/branches/:branchId", (req, res, next) => {
eu.route<{ branchId: string }>(router, "patch", "/etapi/branches/:branchId", (req, res, next) => {
const branch = eu.getAndCheckBranch(req.params.branchId);
eu.validateAndPatch(branch, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
@ -63,7 +63,7 @@ function register(router: Router) {
res.json(mappers.mapBranchToPojo(branch));
});
eu.route(router, "delete", "/etapi/branches/:branchId", (req, res, next) => {
eu.route<{ branchId: string }>(router, "delete", "/etapi/branches/:branchId", (req, res, next) => {
const branch = becca.getBranch(req.params.branchId);
if (!branch) {
@ -75,7 +75,7 @@ function register(router: Router) {
res.sendStatus(204);
});
eu.route(router, "post", "/etapi/refresh-note-ordering/:parentNoteId", (req, res, next) => {
eu.route<{ parentNoteId: string }>(router, "post", "/etapi/refresh-note-ordering/:parentNoteId", (req, res, next) => {
eu.getAndCheckNote(req.params.parentNoteId);
entityChangesService.putNoteReorderingEntityChange(req.params.parentNoteId, "etapi");

View File

@ -1,12 +1,14 @@
import cls from "../services/cls.js";
import sql from "../services/sql.js";
import log from "../services/log.js";
import becca from "../becca/becca.js";
import etapiTokenService from "../services/etapi_tokens.js";
import config from "../services/config.js";
import type { NextFunction, Request, RequestHandler, Response, Router } from "express";
import type { ValidatorMap } from "./etapi-interface.js";
import type { ParamsDictionary } from "express-serve-static-core";
import becca from "../becca/becca.js";
import type { ApiRequestHandler, SyncRouteRequestHandler } from "../routes/route_api.js";
import cls from "../services/cls.js";
import config from "../services/config.js";
import etapiTokenService from "../services/etapi_tokens.js";
import log from "../services/log.js";
import sql from "../services/sql.js";
import type { ValidatorMap } from "./etapi-interface.js";
const GENERIC_CODE = "GENERIC";
type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head";
@ -35,8 +37,8 @@ function sendError(res: Response, statusCode: number, code: string, message: str
.send(
JSON.stringify({
status: statusCode,
code: code,
message: message
code,
message
})
);
}
@ -49,7 +51,7 @@ function checkEtapiAuth(req: Request, res: Response, next: NextFunction) {
}
}
function processRequest(req: Request, res: Response, routeHandler: ApiRequestHandler, next: NextFunction, method: string, path: string) {
function processRequest<P extends ParamsDictionary>(req: Request<P>, res: Response, routeHandler: ApiRequestHandler<P>, next: NextFunction, method: string, path: string) {
try {
cls.namespace.bindEmitter(req);
cls.namespace.bindEmitter(res);
@ -73,12 +75,12 @@ function processRequest(req: Request, res: Response, routeHandler: ApiRequestHan
}
}
function route(router: Router, method: HttpMethod, path: string, routeHandler: SyncRouteRequestHandler) {
router[method](path, checkEtapiAuth, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path));
function route<P extends ParamsDictionary>(router: Router, method: HttpMethod, path: string, routeHandler: SyncRouteRequestHandler<P>) {
router[method](path, checkEtapiAuth, (req: Request<P>, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path));
}
function NOT_AUTHENTICATED_ROUTE(router: Router, method: HttpMethod, path: string, middleware: RequestHandler[], routeHandler: SyncRouteRequestHandler) {
router[method](path, ...middleware, (req: Request, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path));
function NOT_AUTHENTICATED_ROUTE<P extends ParamsDictionary>(router: Router, method: HttpMethod, path: string, middleware: RequestHandler[], routeHandler: SyncRouteRequestHandler<P>) {
router[method](path, ...middleware, (req: Request<P>, res: Response, next: NextFunction) => processRequest(req, res, routeHandler, next, method, path));
}
function getAndCheckNote(noteId: string) {
@ -86,9 +88,8 @@ function getAndCheckNote(noteId: string) {
if (note) {
return note;
} else {
throw new EtapiError(404, "NOTE_NOT_FOUND", `Note '${noteId}' not found.`);
}
throw new EtapiError(404, "NOTE_NOT_FOUND", `Note '${noteId}' not found.`);
}
function getAndCheckAttachment(attachmentId: string) {
@ -96,9 +97,9 @@ function getAndCheckAttachment(attachmentId: string) {
if (attachment) {
return attachment;
} else {
throw new EtapiError(404, "ATTACHMENT_NOT_FOUND", `Attachment '${attachmentId}' not found.`);
}
throw new EtapiError(404, "ATTACHMENT_NOT_FOUND", `Attachment '${attachmentId}' not found.`);
}
function getAndCheckBranch(branchId: string) {
@ -106,9 +107,8 @@ function getAndCheckBranch(branchId: string) {
if (branch) {
return branch;
} else {
throw new EtapiError(404, "BRANCH_NOT_FOUND", `Branch '${branchId}' not found.`);
}
throw new EtapiError(404, "BRANCH_NOT_FOUND", `Branch '${branchId}' not found.`);
}
function getAndCheckAttribute(attributeId: string) {
@ -116,9 +116,8 @@ function getAndCheckAttribute(attributeId: string) {
if (attribute) {
return attribute;
} else {
throw new EtapiError(404, "ATTRIBUTE_NOT_FOUND", `Attribute '${attributeId}' not found.`);
}
throw new EtapiError(404, "ATTRIBUTE_NOT_FOUND", `Attribute '${attributeId}' not found.`);
}
function getAndCheckRevision(revisionId: string) {
@ -126,9 +125,8 @@ function getAndCheckRevision(revisionId: string) {
if (revision) {
return revision;
} else {
throw new EtapiError(404, "REVISION_NOT_FOUND", `Revision '${revisionId}' not found.`);
}
throw new EtapiError(404, "REVISION_NOT_FOUND", `Revision '${revisionId}' not found.`);
}
function validateAndPatch(target: any, source: any, allowedProperties: ValidatorMap) {

View File

@ -1,20 +1,21 @@
import becca from "../becca/becca.js";
import utils from "../services/utils.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import noteService from "../services/notes.js";
import TaskContext from "../services/task_context.js";
import v from "./validators.js";
import searchService from "../services/search/services/search.js";
import SearchContext from "../services/search/search_context.js";
import zipExportService from "../services/export/zip.js";
import zipImportService from "../services/import/zip.js";
import type { Request, Router } from "express";
import type { ParsedQs } from "qs";
import type { NoteParams } from "../services/note-interface.js";
import type { SearchParams } from "../services/search/services/types.js";
import type { ValidatorMap } from "./etapi-interface.js";
import becca from "../becca/becca.js";
import zipExportService from "../services/export/zip.js";
import type { ExportFormat } from "../services/export/zip/abstract_provider.js";
import zipImportService from "../services/import/zip.js";
import type { NoteParams } from "../services/note-interface.js";
import noteService from "../services/notes.js";
import SearchContext from "../services/search/search_context.js";
import searchService from "../services/search/services/search.js";
import type { SearchParams } from "../services/search/services/types.js";
import TaskContext from "../services/task_context.js";
import utils from "../services/utils.js";
import eu from "./etapi_utils.js";
import type { ValidatorMap } from "./etapi-interface.js";
import mappers from "./mappers.js";
import v from "./validators.js";
function register(router: Router) {
eu.route(router, "get", "/etapi/notes", (req, res, next) => {
@ -41,7 +42,7 @@ function register(router: Router) {
res.json(resp);
});
eu.route(router, "get", "/etapi/notes/:noteId", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
res.json(mappers.mapNoteToPojo(note));
@ -86,7 +87,7 @@ function register(router: Router) {
utcDateCreated: [v.notNull, v.isString, v.isUtcDateTime]
};
eu.route(router, "patch", "/etapi/notes/:noteId", (req, res, next) => {
eu.route<{ noteId: string }>(router, "patch", "/etapi/notes/:noteId", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
if (note.isProtected) {
@ -100,7 +101,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "delete", "/etapi/notes/:noteId", (req, res, next) => {
eu.route<{ noteId: string }>(router, "delete", "/etapi/notes/:noteId", (req, res, next) => {
const { noteId } = req.params;
const note = becca.getNote(noteId);
@ -114,7 +115,7 @@ function register(router: Router) {
res.sendStatus(204);
});
eu.route(router, "get", "/etapi/notes/:noteId/content", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId/content", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
if (note.isProtected) {
@ -131,7 +132,7 @@ function register(router: Router) {
res.send(note.getContent());
});
eu.route(router, "put", "/etapi/notes/:noteId/content", (req, res, next) => {
eu.route<{ noteId: string }>(router, "put", "/etapi/notes/:noteId/content", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
if (note.isProtected) {
@ -146,7 +147,7 @@ function register(router: Router) {
return res.sendStatus(204);
});
eu.route(router, "get", "/etapi/notes/:noteId/export", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId/export", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
const format = req.query.format || "html";
@ -163,7 +164,7 @@ function register(router: Router) {
zipExportService.exportToZip(taskContext, branch, format as ExportFormat, res);
});
eu.route(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => {
eu.route<{ noteId: string }>(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
const taskContext = new TaskContext("no-progress-reporting", "importNotes", null);
@ -175,7 +176,7 @@ function register(router: Router) {
}); // we need better error handling here, async errors won't be properly processed.
});
eu.route(router, "post", "/etapi/notes/:noteId/revision", (req, res, next) => {
eu.route<{ noteId: string }>(router, "post", "/etapi/notes/:noteId/revision", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
note.saveRevision();
@ -183,7 +184,7 @@ function register(router: Router) {
return res.sendStatus(204);
});
eu.route(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId/attachments", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
const attachments = note.getAttachments();

View File

@ -1,13 +1,14 @@
import type { NoteRow, RecentChangeRow } from "@triliumnext/commons";
import type { Router } from "express";
import becca from "../becca/becca.js";
import noteService from "../services/notes.js";
import protectedSessionService from "../services/protected_session.js";
import sql from "../services/sql.js";
import TaskContext from "../services/task_context.js";
import utils from "../services/utils.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import noteService from "../services/notes.js";
import TaskContext from "../services/task_context.js";
import protectedSessionService from "../services/protected_session.js";
import utils from "../services/utils.js";
import type { Router } from "express";
import type { NoteRow, RecentChangeRow } from "@triliumnext/commons";
function register(router: Router) {
// GET /etapi/notes/history - must be registered before /etapi/notes/:noteId routes
@ -130,7 +131,7 @@ function register(router: Router) {
});
// GET /etapi/notes/:noteId/revisions - List all revisions for a note
eu.route(router, "get", "/etapi/notes/:noteId/revisions", (req, res, next) => {
eu.route<{ noteId: string }>(router, "get", "/etapi/notes/:noteId/revisions", (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
const revisions = becca.getRevisionsFromQuery(
@ -146,7 +147,7 @@ function register(router: Router) {
});
// POST /etapi/notes/:noteId/undelete - Restore a deleted note
eu.route(router, "post", "/etapi/notes/:noteId/undelete", (req, res, next) => {
eu.route<{ noteId: string }>(router, "post", "/etapi/notes/:noteId/undelete", (req, res, next) => {
const { noteId } = req.params;
const noteRow = sql.getRow<NoteRow | null>("SELECT * FROM notes WHERE noteId = ?", [noteId]);
@ -172,7 +173,7 @@ function register(router: Router) {
});
// GET /etapi/revisions/:revisionId - Get revision metadata
eu.route(router, "get", "/etapi/revisions/:revisionId", (req, res, next) => {
eu.route<{ revisionId: string }>(router, "get", "/etapi/revisions/:revisionId", (req, res, next) => {
const revision = eu.getAndCheckRevision(req.params.revisionId);
if (revision.isProtected) {
@ -183,7 +184,7 @@ function register(router: Router) {
});
// GET /etapi/revisions/:revisionId/content - Get revision content
eu.route(router, "get", "/etapi/revisions/:revisionId/content", (req, res, next) => {
eu.route<{ revisionId: string }>(router, "get", "/etapi/revisions/:revisionId/content", (req, res, next) => {
const revision = eu.getAndCheckRevision(req.params.revisionId);
if (revision.isProtected) {

View File

@ -1,8 +1,9 @@
import specialNotesService from "../services/special_notes.js";
import type { Router } from "express";
import dateNotesService from "../services/date_notes.js";
import specialNotesService from "../services/special_notes.js";
import eu from "./etapi_utils.js";
import mappers from "./mappers.js";
import type { Router } from "express";
const getDateInvalidError = (date: string) => new eu.EtapiError(400, "DATE_INVALID", `Date "${date}" is not valid.`);
const getWeekInvalidError = (week: string) => new eu.EtapiError(400, "WEEK_INVALID", `Week "${week}" is not valid.`);
@ -15,7 +16,7 @@ function isValidDate(date: string) {
}
function register(router: Router) {
eu.route(router, "get", "/etapi/inbox/:date", (req, res, next) => {
eu.route<{ date: string }>(router, "get", "/etapi/inbox/:date", (req, res, next) => {
const { date } = req.params;
if (!isValidDate(date)) {
@ -25,7 +26,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "get", "/etapi/calendar/days/:date", (req, res, next) => {
eu.route<{ date: string }>(router, "get", "/etapi/calendar/days/:date", (req, res, next) => {
const { date } = req.params;
if (!isValidDate(date)) {
@ -36,7 +37,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "get", "/etapi/calendar/week-first-day/:date", (req, res, next) => {
eu.route<{ date: string }>(router, "get", "/etapi/calendar/week-first-day/:date", (req, res, next) => {
const { date } = req.params;
if (!isValidDate(date)) {
@ -47,7 +48,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "get", "/etapi/calendar/weeks/:week", (req, res, next) => {
eu.route<{ week: string }>(router, "get", "/etapi/calendar/weeks/:week", (req, res, next) => {
const { week } = req.params;
if (!/[0-9]{4}-W[0-9]{2}/.test(week)) {
@ -63,7 +64,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "get", "/etapi/calendar/months/:month", (req, res, next) => {
eu.route<{ month: string }>(router, "get", "/etapi/calendar/months/:month", (req, res, next) => {
const { month } = req.params;
if (!/[0-9]{4}-[0-9]{2}/.test(month)) {
@ -74,7 +75,7 @@ function register(router: Router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, "get", "/etapi/calendar/years/:year", (req, res, next) => {
eu.route<{ year: string }>(router, "get", "/etapi/calendar/years/:year", (req, res, next) => {
const { year } = req.params;
if (!/[0-9]{4}/.test(year)) {

View File

@ -1,29 +1,30 @@
import becca from "../../becca/becca.js";
import blobService from "../../services/blob.js";
import ValidationError from "../../errors/validation_error.js";
import imageService from "../../services/image.js";
import type { Request } from "express";
import { ConvertAttachmentToNoteResponse } from "@triliumnext/commons";
import type { Request } from "express";
function getAttachmentBlob(req: Request) {
import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js";
import blobService from "../../services/blob.js";
import imageService from "../../services/image.js";
function getAttachmentBlob(req: Request<{ attachmentId: string }>) {
const preview = req.query.preview === "true";
return blobService.getBlobPojo("attachments", req.params.attachmentId, { preview });
}
function getAttachments(req: Request) {
function getAttachments(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
return note.getAttachments();
}
function getAttachment(req: Request) {
function getAttachment(req: Request<{ attachmentId: string }>) {
const { attachmentId } = req.params;
return becca.getAttachmentOrThrow(attachmentId);
}
function getAllAttachments(req: Request) {
function getAllAttachments(req: Request<{ attachmentId: string }>) {
const { attachmentId } = req.params;
// one particular attachment is requested, but return all note's attachments
@ -31,10 +32,10 @@ function getAllAttachments(req: Request) {
return attachment.getNote()?.getAttachments() || [];
}
function saveAttachment(req: Request) {
function saveAttachment(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const { attachmentId, role, mime, title, content } = req.body;
const matchByQuery = req.query.matchBy
const matchByQuery = req.query.matchBy;
const isValidMatchBy = (typeof matchByQuery === "string") && (matchByQuery === "attachmentId" || matchByQuery === "title");
const matchBy = isValidMatchBy ? matchByQuery : undefined;
@ -42,7 +43,7 @@ function saveAttachment(req: Request) {
note.saveAttachment({ attachmentId, role, mime, title, content }, matchBy);
}
function uploadAttachment(req: Request) {
function uploadAttachment(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const { file } = req;
@ -76,7 +77,7 @@ function uploadAttachment(req: Request) {
};
}
function renameAttachment(req: Request) {
function renameAttachment(req: Request<{ attachmentId: string }>) {
const { title } = req.body;
const { attachmentId } = req.params;
@ -90,7 +91,7 @@ function renameAttachment(req: Request) {
attachment.save();
}
function deleteAttachment(req: Request) {
function deleteAttachment(req: Request<{ attachmentId: string }>) {
const { attachmentId } = req.params;
const attachment = becca.getAttachment(attachmentId);
@ -100,7 +101,7 @@ function deleteAttachment(req: Request) {
}
}
function convertAttachmentToNote(req: Request) {
function convertAttachmentToNote(req: Request<{ attachmentId: string }>) {
const { attachmentId } = req.params;
const attachment = becca.getAttachmentOrThrow(attachmentId);

View File

@ -1,21 +1,22 @@
"use strict";
import sql from "../../services/sql.js";
import log from "../../services/log.js";
import attributeService from "../../services/attributes.js";
import BAttribute from "../../becca/entities/battribute.js";
import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express";
import { UpdateAttributeResponse } from "@triliumnext/commons";
import type { Request } from "express";
function getEffectiveNoteAttributes(req: Request) {
import becca from "../../becca/becca.js";
import BAttribute from "../../becca/entities/battribute.js";
import ValidationError from "../../errors/validation_error.js";
import attributeService from "../../services/attributes.js";
import log from "../../services/log.js";
import sql from "../../services/sql.js";
function getEffectiveNoteAttributes(req: Request<{ noteId: string }>) {
const note = becca.getNote(req.params.noteId);
return note?.getAttributes();
}
function updateNoteAttribute(req: Request) {
function updateNoteAttribute(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const body = req.body;
@ -47,7 +48,7 @@ function updateNoteAttribute(req: Request) {
}
attribute = new BAttribute({
noteId: noteId,
noteId,
name: body.name,
type: body.type,
isInheritable: body.isInheritable
@ -96,7 +97,7 @@ function addNoteAttribute(req: Request) {
new BAttribute({ ...body, noteId }).save();
}
function deleteNoteAttribute(req: Request) {
function deleteNoteAttribute(req: Request<{ noteId: string; attributeId: string }>) {
const noteId = req.params.noteId;
const attributeId = req.params.attributeId;
@ -111,7 +112,7 @@ function deleteNoteAttribute(req: Request) {
}
}
function updateNoteAttributes(req: Request) {
function updateNoteAttributes(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const incomingAttributes = req.body;
@ -193,7 +194,7 @@ function getValuesForAttribute(req: Request) {
return sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND type = 'label' AND value != '' ORDER BY value", [attributeName]);
}
function createRelation(req: Request) {
function createRelation(req: Request<{ noteId: string; targetNoteId: string; name: string }>) {
const sourceNoteId = req.params.noteId;
const targetNoteId = req.params.targetNoteId;
const name = req.params.name;
@ -208,7 +209,7 @@ function createRelation(req: Request) {
if (!attribute) {
attribute = new BAttribute({
noteId: sourceNoteId,
name: name,
name,
type: "relation",
value: targetNoteId
}).save();

View File

@ -1,24 +1,23 @@
"use strict";
import sql from "../../services/sql.js";
import utils from "../../services/utils.js";
import entityChangesService from "../../services/entity_changes.js";
import treeService from "../../services/tree.js";
import eraseService from "../../services/erase.js";
import becca from "../../becca/becca.js";
import TaskContext from "../../services/task_context.js";
import branchService from "../../services/branches.js";
import log from "../../services/log.js";
import ValidationError from "../../errors/validation_error.js";
import eventService from "../../services/events.js";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js";
import branchService from "../../services/branches.js";
import entityChangesService from "../../services/entity_changes.js";
import eraseService from "../../services/erase.js";
import eventService from "../../services/events.js";
import log from "../../services/log.js";
import sql from "../../services/sql.js";
import TaskContext from "../../services/task_context.js";
import treeService from "../../services/tree.js";
import utils from "../../services/utils.js";
/**
* Code in this file deals with moving and cloning branches. The relationship between note and parent note is unique
* for not deleted branches. There may be multiple deleted note-parent note relationships.
*/
function moveBranchToParent(req: Request) {
function moveBranchToParent(req: Request<{ branchId: string, parentBranchId: string }>) {
const { branchId, parentBranchId } = req.params;
const branchToMove = becca.getBranch(branchId);
@ -31,7 +30,7 @@ function moveBranchToParent(req: Request) {
return branchService.moveBranchToBranch(branchToMove, targetParentBranch, branchId);
}
function moveBranchBeforeNote(req: Request) {
function moveBranchBeforeNote(req: Request<{ branchId: string, beforeBranchId: string }>) {
const { branchId, beforeBranchId } = req.params;
const branchToMove = becca.getBranchOrThrow(branchId);
@ -79,7 +78,7 @@ function moveBranchBeforeNote(req: Request) {
return { success: true };
}
function moveBranchAfterNote(req: Request) {
function moveBranchAfterNote(req: Request<{ branchId: string, afterBranchId: string }>) {
const { branchId, afterBranchId } = req.params;
const branchToMove = becca.getBranchOrThrow(branchId);
@ -128,7 +127,7 @@ function moveBranchAfterNote(req: Request) {
return { success: true };
}
function setExpanded(req: Request) {
function setExpanded(req: Request<{ branchId: string, expanded: string }>) {
const { branchId } = req.params;
const expanded = parseInt(req.params.expanded);
@ -150,7 +149,7 @@ function setExpanded(req: Request) {
}
}
function setExpandedForSubtree(req: Request) {
function setExpandedForSubtree(req: Request<{ branchId: string, expanded: string }>) {
const { branchId } = req.params;
const expanded = parseInt(req.params.expanded);
@ -232,7 +231,7 @@ function setExpandedForSubtree(req: Request) {
* - session: []
* tags: ["data"]
*/
function deleteBranch(req: Request) {
function deleteBranch(req: Request<{ branchId: string }>) {
const last = req.query.last === "true";
const eraseNotes = req.query.eraseNotes === "true";
const branch = becca.getBranchOrThrow(req.params.branchId);
@ -256,11 +255,11 @@ function deleteBranch(req: Request) {
}
return {
noteDeleted: noteDeleted
noteDeleted
};
}
function setPrefix(req: Request) {
function setPrefix(req: Request<{ branchId: string }>) {
const branchId = req.params.branchId;
//TriliumNextTODO: req.body arrives as string, so req.body.prefix will be undefined did the code below ever even work?
const prefix = utils.isEmptyOrWhitespace(req.body.prefix) ? null : req.body.prefix;
@ -272,7 +271,7 @@ function setPrefix(req: Request) {
function setPrefixBatch(req: Request) {
const { branchIds, prefix } = req.body;
if (!Array.isArray(branchIds)) {
throw new ValidationError("branchIds must be an array");
}

View File

@ -38,7 +38,7 @@ async function addClipping(req: Request) {
if (!clippingNote) {
clippingNote = noteService.createNewNote({
parentNoteId: clipperInbox.noteId,
title: title,
title,
content: "",
type: "text"
}).note;
@ -188,7 +188,7 @@ export function processContent(images: Image[], note: BNote, content: string) {
return rewrittenContent;
}
function openNote(req: Request) {
function openNote(req: Request<{ noteId: string }>) {
if (utils.isElectron) {
ws.sendMessageToAllClients({
type: "openNote",
@ -198,11 +198,11 @@ function openNote(req: Request) {
return {
result: "ok"
};
} else {
return {
result: "open-in-browser"
};
}
}
return {
result: "open-in-browser"
};
}
function handshake() {
@ -212,7 +212,7 @@ function handshake() {
};
}
async function findNotesByUrl(req: Request) {
async function findNotesByUrl(req: Request<{ noteUrl: string }>) {
const pageUrl = req.params.noteUrl;
const clipperInbox = await getClipperInboxNote();
const foundPage = findClippingNote(clipperInbox, pageUrl, null);

View File

@ -1,29 +1,28 @@
"use strict";
import type { Request } from "express";
import cloningService from "../../services/cloning.js";
function cloneNoteToBranch(req: Request) {
function cloneNoteToBranch(req: Request<{ noteId: string; parentBranchId: string }>) {
const { noteId, parentBranchId } = req.params;
const { prefix } = req.body;
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
}
function cloneNoteToParentNote(req: Request) {
function cloneNoteToParentNote(req: Request<{ noteId: string; parentNoteId: string }>) {
const { noteId, parentNoteId } = req.params;
const { prefix } = req.body;
return cloningService.cloneNoteToParentNote(noteId, parentNoteId, prefix);
}
function cloneNoteAfter(req: Request) {
function cloneNoteAfter(req: Request<{ noteId: string; afterBranchId: string }>) {
const { noteId, afterBranchId } = req.params;
return cloningService.cloneNoteAfter(noteId, afterBranchId);
}
function toggleNoteInParent(req: Request) {
function toggleNoteInParent(req: Request<{ noteId: string; parentNoteId: string; present: string }>) {
const { noteId, parentNoteId, present } = req.params;
return cloningService.toggleNoteInParent(present === "true", noteId, parentNoteId);

View File

@ -1,6 +1,7 @@
import type { Request } from "express";
import etapiTokenService from "../../services/etapi_tokens.js";
import { EtapiToken, PostTokensResponse } from "@triliumnext/commons";
import type { Request } from "express";
import etapiTokenService from "../../services/etapi_tokens.js";
function getTokens() {
const tokens = etapiTokenService.getTokens();
@ -14,11 +15,11 @@ function createToken(req: Request) {
return etapiTokenService.createToken(req.body.tokenName) satisfies PostTokensResponse;
}
function patchToken(req: Request) {
function patchToken(req: Request<{ etapiTokenId: string }>) {
etapiTokenService.renameToken(req.params.etapiTokenId, req.body.name);
}
function deleteToken(req: Request) {
function deleteToken(req: Request<{ etapiTokenId: string }>) {
etapiTokenService.deleteToken(req.params.etapiTokenId);
}

View File

@ -1,17 +1,16 @@
"use strict";
import zipExportService from "../../services/export/zip.js";
import singleExportService from "../../services/export/single.js";
import opmlExportService from "../../services/export/opml.js";
import becca from "../../becca/becca.js";
import TaskContext from "../../services/task_context.js";
import log from "../../services/log.js";
import NotFoundError from "../../errors/not_found_error.js";
import type { Request, Response } from "express";
import becca from "../../becca/becca.js";
import NotFoundError from "../../errors/not_found_error.js";
import ValidationError from "../../errors/validation_error.js";
import opmlExportService from "../../services/export/opml.js";
import singleExportService from "../../services/export/single.js";
import zipExportService from "../../services/export/zip.js";
import log from "../../services/log.js";
import TaskContext from "../../services/task_context.js";
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
function exportBranch(req: Request, res: Response) {
function exportBranch(req: Request<{ branchId: string; type: string; format: string; version: string; taskId: string }>, res: Response) {
const { branchId, type, format, version, taskId } = req.params;
const branch = becca.getBranch(branchId);

View File

@ -1,5 +1,3 @@
import chokidar from "chokidar";
import type { Request, Response } from "express";
import fs from "fs";
@ -17,7 +15,7 @@ import protectedSessionService from "../../services/protected_session.js";
import utils from "../../services/utils.js";
import ws from "../../services/ws.js";
function updateFile(req: Request) {
function updateFile(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const file = req.file;
@ -46,7 +44,7 @@ function updateFile(req: Request) {
};
}
function updateAttachment(req: Request) {
function updateAttachment(req: Request<{ attachmentId: string }>) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const file = req.file;
if (!file) {
@ -103,20 +101,20 @@ function downloadAttachmentInt(attachmentId: string, res: Response, contentDispo
return downloadData(attachment, res, contentDisposition);
}
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 downloadFile = (req: Request<{ noteId: string }>, res: Response) => downloadNoteInt(req.params.noteId, res, true);
const openFile = (req: Request<{ noteId: string }>, res: Response) => downloadNoteInt(req.params.noteId, 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);
const downloadAttachment = (req: Request<{ attachmentId: string }>, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, true);
const openAttachment = (req: Request<{ attachmentId: string }>, res: Response) => downloadAttachmentInt(req.params.attachmentId, res, false);
function fileContentProvider(req: Request) {
function fileContentProvider(req: Request<{ noteId: string }>) {
// 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: Request) {
function attachmentContentProvider(req: Request<{ attachmentId: string }>) {
// Read the file name from route params.
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
@ -149,7 +147,7 @@ async function streamContent(content: string | Buffer, fileName: string, mimeTyp
};
}
function saveNoteToTmpDir(req: Request) {
function saveNoteToTmpDir(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const fileName = note.getFileName();
const content = note.getContent();
@ -157,7 +155,7 @@ function saveNoteToTmpDir(req: Request) {
return saveToTmpDir(fileName, content, "notes", note.noteId);
}
function saveAttachmentToTmpDir(req: Request) {
function saveAttachmentToTmpDir(req: Request<{ attachmentId: string }>) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const fileName = attachment.getFileName();
const content = attachment.getContent();
@ -205,7 +203,7 @@ function saveToTmpDir(fileName: string, content: string | Buffer, entityType: st
};
}
function uploadModifiedFileToNote(req: Request) {
function uploadModifiedFileToNote(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const { filePath } = req.body;
@ -228,7 +226,7 @@ function uploadModifiedFileToNote(req: Request) {
note.setContent(fileContent);
}
function uploadModifiedFileToAttachment(req: Request) {
function uploadModifiedFileToAttachment(req: Request<{ attachmentId: string }>) {
const { attachmentId } = req.params;
const { filePath } = req.body;

View File

@ -1,20 +1,19 @@
"use strict";
import imageService from "../../services/image.js";
import becca from "../../becca/becca.js";
import fs from "fs";
import type { Request, Response } from "express";
import fs from "fs";
import becca from "../../becca/becca.js";
import type BNote from "../../becca/entities/bnote.js";
import type BRevision from "../../becca/entities/brevision.js";
import imageService from "../../services/image.js";
import { RESOURCE_DIR } from "../../services/resource_dir.js";
function returnImageFromNote(req: Request, res: Response) {
function returnImageFromNote(req: Request<{ noteId: string }>, res: Response) {
const image = becca.getNote(req.params.noteId);
return returnImageInt(image, res);
}
function returnImageFromRevision(req: Request, res: Response) {
function returnImageFromRevision(req: Request<{ revisionId: string }>, res: Response) {
const image = becca.getRevision(req.params.revisionId);
return returnImageInt(image, res);
@ -61,7 +60,7 @@ export function renderSvgAttachment(image: BNote | BRevision, res: Response, att
res.send(svg);
}
function returnAttachedImage(req: Request, res: Response) {
function returnAttachedImage(req: Request<{ attachmentId: string }>, res: Response) {
const attachment = becca.getAttachment(req.params.attachmentId);
if (!attachment) {
@ -78,7 +77,7 @@ function returnAttachedImage(req: Request, res: Response) {
res.send(attachment.getContent());
}
function updateImage(req: Request) {
function updateImage(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const { file } = req;

View File

@ -1,21 +1,20 @@
"use strict";
import enexImportService from "../../services/import/enex.js";
import opmlImportService from "../../services/import/opml.js";
import zipImportService from "../../services/import/zip.js";
import singleImportService from "../../services/import/single.js";
import cls from "../../services/cls.js";
import type { Request } from "express";
import path from "path";
import becca from "../../becca/becca.js";
import beccaLoader from "../../becca/becca_loader.js";
import type BNote from "../../becca/entities/bnote.js";
import ValidationError from "../../errors/validation_error.js";
import cls from "../../services/cls.js";
import enexImportService from "../../services/import/enex.js";
import opmlImportService from "../../services/import/opml.js";
import singleImportService from "../../services/import/single.js";
import zipImportService from "../../services/import/zip.js";
import log from "../../services/log.js";
import TaskContext from "../../services/task_context.js";
import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express";
import type BNote from "../../becca/entities/bnote.js";
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
async function importNotesToBranch(req: Request) {
async function importNotesToBranch(req: Request<{ parentNoteId: string }>) {
const { parentNoteId } = req.params;
const { taskId, last } = req.body;
@ -88,7 +87,7 @@ async function importNotesToBranch(req: Request) {
setTimeout(
() =>
taskContext.taskSucceeded({
parentNoteId: parentNoteId,
parentNoteId,
importedNoteId: note?.noteId
}),
1000
@ -101,7 +100,7 @@ async function importNotesToBranch(req: Request) {
return note.getPojo();
}
function importAttachmentsToNote(req: Request) {
function importAttachmentsToNote(req: Request<{ parentNoteId: string }>) {
const { parentNoteId } = req.params;
const { taskId, last } = req.body;
@ -138,7 +137,7 @@ function importAttachmentsToNote(req: Request) {
setTimeout(
() =>
taskContext.taskSucceeded({
parentNoteId: parentNoteId
parentNoteId
}),
1000
);

View File

@ -1,11 +1,12 @@
"use strict";
import becca from "../../becca/becca.js";
import type BNote from "../../becca/entities/bnote.js";
import type BAttribute from "../../becca/entities/battribute.js";
import { BacklinkCountResponse, BacklinksResponse } from "@triliumnext/commons";
import type { Request } from "express";
import { HTMLElement, parse, TextNode } from "node-html-parser";
import { BacklinkCountResponse, BacklinksResponse } from "@triliumnext/commons";
import becca from "../../becca/becca.js";
import type BAttribute from "../../becca/entities/battribute.js";
import type BNote from "../../becca/entities/bnote.js";
interface TreeLink {
sourceNoteId: string;
@ -97,7 +98,7 @@ function getNeighbors(note: BNote, depth: number): string[] {
return retNoteIds;
}
function getLinkMap(req: Request) {
function getLinkMap(req: Request<{ noteId: string }>) {
const mapRootNote = becca.getNoteOrThrow(req.params.noteId);
// if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything
@ -156,9 +157,9 @@ function getLinkMap(req: Request) {
return false;
} else if (excludeRelations.has(rel.name)) {
return false;
} else {
return true;
}
}
return true;
})
.map((rel) => ({
id: `${rel.noteId}-${rel.name}-${rel.value}`,
@ -168,13 +169,13 @@ function getLinkMap(req: Request) {
}));
return {
notes: notes,
notes,
noteIdToDescendantCountMap: buildDescendantCountMap(noteIdsArray),
links: links
links
};
}
function getTreeMap(req: Request) {
function getTreeMap(req: Request<{ noteId: string }>) {
const mapRootNote = becca.getNoteOrThrow(req.params.noteId);
// if the map root itself has "excludeFromNoteMap" (journal typically) then there wouldn't be anything to display,
// so we'll just ignore it
@ -223,9 +224,9 @@ function getTreeMap(req: Request) {
updateDescendantCountMapForSearch(noteIdToDescendantCountMap, subtree.relationships);
return {
notes: notes,
noteIdToDescendantCountMap: noteIdToDescendantCountMap,
links: links
notes,
noteIdToDescendantCountMap,
links
};
}
@ -350,7 +351,7 @@ function getFilteredBacklinks(note: BNote): BAttribute[] {
);
}
function getBacklinkCount(req: Request) {
function getBacklinkCount(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const note = becca.getNoteOrThrow(noteId);
@ -360,7 +361,7 @@ function getBacklinkCount(req: Request) {
} satisfies BacklinkCountResponse;
}
function getBacklinks(req: Request): BacklinksResponse {
function getBacklinks(req: Request<{ noteId: string }>): BacklinksResponse {
const { noteId } = req.params;
const note = becca.getNoteOrThrow(noteId);

View File

@ -1,18 +1,19 @@
"use strict";
import noteService from "../../services/notes.js";
import eraseService from "../../services/erase.js";
import treeService from "../../services/tree.js";
import sql from "../../services/sql.js";
import utils from "../../services/utils.js";
import log from "../../services/log.js";
import TaskContext from "../../services/task_context.js";
import type { AttributeRow, CreateChildrenResponse, DeleteNotesPreview, MetadataResponse } from "@triliumnext/commons";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import type BBranch from "../../becca/entities/bbranch.js";
import ValidationError from "../../errors/validation_error.js";
import blobService from "../../services/blob.js";
import type { Request } from "express";
import type BBranch from "../../becca/entities/bbranch.js";
import type { AttributeRow, CreateChildrenResponse, DeleteNotesPreview, MetadataResponse } from "@triliumnext/commons";
import eraseService from "../../services/erase.js";
import log from "../../services/log.js";
import noteService from "../../services/notes.js";
import sql from "../../services/sql.js";
import TaskContext from "../../services/task_context.js";
import treeService from "../../services/tree.js";
import utils from "../../services/utils.js";
/**
* @swagger
@ -39,7 +40,7 @@ import type { AttributeRow, CreateChildrenResponse, DeleteNotesPreview, Metadata
* - session: []
* tags: ["data"]
*/
function getNote(req: Request) {
function getNote(req: Request<{ noteId: string }>) {
return becca.getNoteOrThrow(req.params.noteId);
}
@ -66,7 +67,7 @@ function getNote(req: Request) {
* - session: []
* tags: ["data"]
*/
function getNoteBlob(req: Request) {
function getNoteBlob(req: Request<{ noteId: string }>) {
return blobService.getBlobPojo("notes", req.params.noteId);
}
@ -93,7 +94,7 @@ function getNoteBlob(req: Request) {
* - session: []
* tags: ["data"]
*/
function getNoteMetadata(req: Request) {
function getNoteMetadata(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
return {
@ -126,7 +127,7 @@ function createNote(req: Request) {
} satisfies CreateChildrenResponse;
}
function updateNoteData(req: Request) {
function updateNoteData(req: Request<{ noteId: string }>) {
const { content, attachments } = req.body;
const { noteId } = req.params;
@ -170,7 +171,7 @@ function updateNoteData(req: Request) {
* - session: []
* tags: ["data"]
*/
function deleteNote(req: Request) {
function deleteNote(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const taskId = req.query.taskId;
const eraseNotes = req.query.eraseNotes === "true";
@ -197,7 +198,7 @@ function deleteNote(req: Request) {
}
}
function undeleteNote(req: Request) {
function undeleteNote(req: Request<{ noteId: string }>) {
const taskContext = TaskContext.getInstance(utils.randomString(10), "undeleteNotes", null);
noteService.undeleteNote(req.params.noteId, taskContext);
@ -205,7 +206,7 @@ function undeleteNote(req: Request) {
taskContext.taskSucceeded(null);
}
function sortChildNotes(req: Request) {
function sortChildNotes(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const { sortBy, sortDirection, foldersFirst, sortNatural, sortLocale } = req.body;
@ -216,7 +217,7 @@ function sortChildNotes(req: Request) {
treeService.sortNotes(noteId, sortBy, reverse, foldersFirst, sortNatural, sortLocale);
}
function protectNote(req: Request) {
function protectNote(req: Request<{ noteId: string; isProtected: string }>) {
const noteId = req.params.noteId;
const note = becca.notes[noteId];
const protect = !!parseInt(req.params.isProtected);
@ -229,7 +230,7 @@ function protectNote(req: Request) {
taskContext.taskSucceeded(null);
}
function setNoteTypeMime(req: Request) {
function setNoteTypeMime(req: Request<{ noteId: string }>) {
// can't use [] destructuring because req.params is not iterable
const { noteId } = req.params;
const { type, mime } = req.body;
@ -240,7 +241,7 @@ function setNoteTypeMime(req: Request) {
note.save();
}
function changeTitle(req: Request) {
function changeTitle(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const title = req.body.title;
@ -267,7 +268,7 @@ function changeTitle(req: Request) {
return note;
}
function duplicateSubtree(req: Request) {
function duplicateSubtree(req: Request<{ noteId: string; parentNoteId: string }>) {
const { noteId, parentNoteId } = req.params;
return noteService.duplicateSubtree(noteId, parentNoteId);
@ -342,7 +343,7 @@ function getDeleteNotesPreview(req: Request) {
} satisfies DeleteNotesPreview;
}
function forceSaveRevision(req: Request) {
function forceSaveRevision(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const note = becca.getNoteOrThrow(noteId);
@ -353,7 +354,7 @@ function forceSaveRevision(req: Request) {
note.saveRevision();
}
function convertNoteToAttachment(req: Request) {
function convertNoteToAttachment(req: Request<{ noteId: string }>) {
const { noteId } = req.params;
const note = becca.getNoteOrThrow(noteId);

View File

@ -128,7 +128,7 @@ function getOptions() {
return resultMap;
}
function updateOption(req: Request) {
function updateOption(req: Request<{ name: string; value: string }>) {
const { name, value } = req.params;
if (!update(name, value)) {

View File

@ -1,13 +1,12 @@
"use strict";
import sql from "../../services/sql.js";
import protectedSessionService from "../../services/protected_session.js";
import noteService from "../../services/notes.js";
import becca from "../../becca/becca.js";
import type { Request } from "express";
import type { RecentChangeRow } from "@triliumnext/commons";
import type { Request } from "express";
function getRecentChanges(req: Request) {
import becca from "../../becca/becca.js";
import noteService from "../../services/notes.js";
import protectedSessionService from "../../services/protected_session.js";
import sql from "../../services/sql.js";
function getRecentChanges(req: Request<{ ancestorNoteId: string }>) {
const { ancestorNoteId } = req.params;
let recentChanges: RecentChangeRow[] = [];

View File

@ -1,18 +1,19 @@
"use strict";
import beccaService from "../../becca/becca_service.js";
import utils from "../../services/utils.js";
import sql from "../../services/sql.js";
import cls from "../../services/cls.js";
import path from "path";
import becca from "../../becca/becca.js";
import blobService from "../../services/blob.js";
import eraseService from "../../services/erase.js";
import type { Request, Response } from "express";
import type BRevision from "../../becca/entities/brevision.js";
import type BNote from "../../becca/entities/bnote.js";
import type { NotePojo } from "../../becca/becca-interface.js";
import { EditedNotesResponse, RevisionItem, RevisionPojo, RevisionRow } from "@triliumnext/commons";
import type { Request, Response } from "express";
import path from "path";
import becca from "../../becca/becca.js";
import beccaService from "../../becca/becca_service.js";
import type { NotePojo } from "../../becca/becca-interface.js";
import type BNote from "../../becca/entities/bnote.js";
import type BRevision from "../../becca/entities/brevision.js";
import blobService from "../../services/blob.js";
import cls from "../../services/cls.js";
import eraseService from "../../services/erase.js";
import sql from "../../services/sql.js";
import utils from "../../services/utils.js";
interface NotePath {
noteId: string;
@ -26,13 +27,13 @@ interface NotePojoWithNotePath extends NotePojo {
notePath?: string[] | null;
}
function getRevisionBlob(req: Request) {
function getRevisionBlob(req: Request<{ revisionId: string }>) {
const preview = req.query.preview === "true";
return blobService.getBlobPojo("revisions", req.params.revisionId, { preview });
}
function getRevisions(req: Request) {
function getRevisions(req: Request<{ noteId: string }>) {
return becca.getRevisionsFromQuery(
`
SELECT revisions.*,
@ -45,7 +46,7 @@ function getRevisions(req: Request) {
) satisfies RevisionItem[];
}
function getRevision(req: Request) {
function getRevision(req: Request<{ revisionId: string }>) {
const revision = becca.getRevisionOrThrow(req.params.revisionId);
if (revision.type === "file") {
@ -85,7 +86,7 @@ function getRevisionFilename(revision: BRevision) {
return filename;
}
function downloadRevision(req: Request, res: Response) {
function downloadRevision(req: Request<{ revisionId: string }>, res: Response) {
const revision = becca.getRevisionOrThrow(req.params.revisionId);
if (!revision.isContentAvailable()) {
@ -100,13 +101,13 @@ function downloadRevision(req: Request, res: Response) {
res.send(revision.getContent());
}
function eraseAllRevisions(req: Request) {
function eraseAllRevisions(req: Request<{ noteId: string }>) {
const revisionIdsToErase = sql.getColumn<string>("SELECT revisionId FROM revisions WHERE noteId = ?", [req.params.noteId]);
eraseService.eraseRevisions(revisionIdsToErase);
}
function eraseRevision(req: Request) {
function eraseRevision(req: Request<{ revisionId: string }>) {
eraseService.eraseRevisions([req.params.revisionId]);
}
@ -117,7 +118,7 @@ function eraseAllExcessRevisions() {
});
}
function restoreRevision(req: Request) {
function restoreRevision(req: Request<{ revisionId: string }>) {
const revision = becca.getRevision(req.params.revisionId);
if (revision) {
@ -166,7 +167,7 @@ function getEditedNotesOnDate(req: Request) {
)
ORDER BY isDeleted
LIMIT 50`,
{ date: `${req.params.date}%` }
{ date: `${req.params.date}%` }
);
let notes = becca.getNotes(noteIds, true);
@ -204,7 +205,7 @@ function getNotePathData(note: BNote): NotePath | undefined {
return {
noteId: note.noteId,
branchId: branchId,
branchId,
title: noteTitle,
notePath: retPath,
path: retPath.join("/")

View File

@ -1,11 +1,12 @@
"use strict";
import scriptService, { type Bundle } from "../../services/script.js";
import attributeService from "../../services/attributes.js";
import becca from "../../becca/becca.js";
import syncService from "../../services/sync.js";
import sql from "../../services/sql.js";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import attributeService from "../../services/attributes.js";
import scriptService, { type Bundle } from "../../services/script.js";
import sql from "../../services/sql.js";
import syncService from "../../services/sync.js";
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
interface ScriptBody {
@ -43,7 +44,7 @@ async function exec(req: Request) {
}
}
function run(req: Request) {
function run(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const result = scriptService.executeNote(note, { originEntity: note });
@ -71,23 +72,23 @@ function getStartupBundles(req: Request) {
if (!process.env.TRILIUM_SAFE_MODE) {
if (req.query.mobile === "true") {
return getBundlesWithLabel("run", "mobileStartup");
} else {
return getBundlesWithLabel("run", "frontendStartup");
}
} else {
return [];
}
}
return getBundlesWithLabel("run", "frontendStartup");
}
return [];
}
function getWidgetBundles() {
if (!process.env.TRILIUM_SAFE_MODE) {
return getBundlesWithLabel("widget");
} else {
return [];
}
}
return [];
}
function getRelationBundles(req: Request) {
function getRelationBundles(req: Request<{ noteId: string, relationName: string }>) {
const noteId = req.params.noteId;
const note = becca.getNoteOrThrow(noteId);
const relationName = req.params.relationName;
@ -116,7 +117,7 @@ function getRelationBundles(req: Request) {
return bundles;
}
function getBundle(req: Request) {
function getBundle(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const { script, params } = req.body ?? {};

View File

@ -1,19 +1,19 @@
"use strict";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import SearchContext from "../../services/search/search_context.js";
import searchService, { EMPTY_RESULT, type SearchNoteResult } from "../../services/search/services/search.js";
import beccaService from "../../becca/becca_service.js";
import ValidationError from "../../errors/validation_error.js";
import attributeFormatter from "../../services/attribute_formatter.js";
import bulkActionService from "../../services/bulk_actions.js";
import cls from "../../services/cls.js";
import attributeFormatter from "../../services/attribute_formatter.js";
import ValidationError from "../../errors/validation_error.js";
import type SearchResult from "../../services/search/search_result.js";
import hoistedNoteService from "../../services/hoisted_note.js";
import beccaService from "../../becca/becca_service.js";
import SearchContext from "../../services/search/search_context.js";
import type SearchResult from "../../services/search/search_result.js";
import searchService, { EMPTY_RESULT, type SearchNoteResult } from "../../services/search/services/search.js";
function searchFromNote(req: Request): SearchNoteResult {
function searchFromNote(req: Request<{ noteId: string }>): SearchNoteResult {
const note = becca.getNoteOrThrow(req.params.noteId);
if (!note) {
@ -28,7 +28,7 @@ function searchFromNote(req: Request): SearchNoteResult {
return searchService.searchFromNote(note);
}
function searchAndExecute(req: Request) {
function searchAndExecute(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
if (!note) {
@ -45,7 +45,7 @@ function searchAndExecute(req: Request) {
bulkActionService.executeActionsFromNote(note, searchResultNoteIds);
}
function quickSearch(req: Request) {
function quickSearch(req: Request<{ searchString: string }>) {
const { searchString } = req.params;
const searchContext = new SearchContext({
@ -82,7 +82,7 @@ function quickSearch(req: Request) {
highlightedContentSnippet: result.highlightedContentSnippet,
attributeSnippet: result.attributeSnippet,
highlightedAttributeSnippet: result.highlightedAttributeSnippet,
icon: icon
icon
};
});
@ -90,12 +90,12 @@ function quickSearch(req: Request) {
return {
searchResultNoteIds: resultNoteIds,
searchResults: searchResults,
searchResults,
error: searchContext.getError()
};
}
function search(req: Request) {
function search(req: Request<{ searchString: string }>) {
const { searchString } = req.params;
const searchContext = new SearchContext({

View File

@ -1,12 +1,10 @@
"use strict";
import { SimilarNoteResponse } from "@triliumnext/commons";
import type { Request } from "express";
import similarityService from "../../becca/similarity.js";
import becca from "../../becca/becca.js";
import { SimilarNoteResponse } from "@triliumnext/commons";
import similarityService from "../../becca/similarity.js";
async function getSimilarNotes(req: Request) {
async function getSimilarNotes(req: Request<{ noteId: string }>) {
const noteId = req.params.noteId;
const _note = becca.getNoteOrThrow(noteId);

View File

@ -6,33 +6,33 @@ import dateNoteService from "../../services/date_notes.js";
import specialNotesService, { type LauncherType } from "../../services/special_notes.js";
import sql from "../../services/sql.js";
function getInboxNote(req: Request) {
function getInboxNote(req: Request<{ date: string }>) {
return specialNotesService.getInboxNote(req.params.date);
}
function getDayNote(req: Request) {
function getDayNote(req: Request<{ date: string }>) {
const calendarRootId = req.query.calendarRootId;
const calendarRoot = typeof calendarRootId === "string" ? becca.getNoteOrThrow(calendarRootId) : null;
return dateNoteService.getDayNote(req.params.date, calendarRoot);
}
function getWeekFirstDayNote(req: Request) {
function getWeekFirstDayNote(req: Request<{ date: string }>) {
return dateNoteService.getWeekFirstDayNote(req.params.date);
}
function getWeekNote(req: Request) {
function getWeekNote(req: Request<{ week: string }>) {
return dateNoteService.getWeekNote(req.params.week);
}
function getMonthNote(req: Request) {
function getMonthNote(req: Request<{ month: string }>) {
return dateNoteService.getMonthNote(req.params.month);
}
function getQuarterNote(req: Request) {
function getQuarterNote(req: Request<{ quarter: string }>) {
return dateNoteService.getQuarterNote(req.params.quarter);
}
function getYearNote(req: Request) {
function getYearNote(req: Request<{ year: string }>) {
return dateNoteService.getYearNote(req.params.year);
}
@ -90,7 +90,7 @@ function getHoistedNote() {
return becca.getNote(cls.getHoistedNoteId());
}
function createLauncher(req: Request) {
function createLauncher(req: Request<{ parentNoteId: string, launcherType: string }>) {
return specialNotesService.createLauncher({
parentNoteId: req.params.parentNoteId,
// TODO: Validate the parameter
@ -98,7 +98,7 @@ function createLauncher(req: Request) {
});
}
function resetLauncher(req: Request) {
function resetLauncher(req: Request<{ noteId: string }>) {
return specialNotesService.resetLauncher(req.params.noteId);
}

View File

@ -1,9 +1,8 @@
"use strict";
import sql from "../../services/sql.js";
import becca from "../../becca/becca.js";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import ValidationError from "../../errors/validation_error.js";
import sql from "../../services/sql.js";
import { safeExtractMessageAndStackFromError } from "../../services/utils.js";
interface Table {
@ -25,7 +24,7 @@ function getSchema() {
return tables;
}
function execute(req: Request) {
function execute(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const content = note.getContent();

View File

@ -1,7 +1,8 @@
import sql from "../../services/sql.js";
import becca from "../../becca/becca.js";
import type { Request } from "express";
import { NoteSizeResponse, SubtreeSizeResponse } from "@triliumnext/commons";
import type { Request } from "express";
import becca from "../../becca/becca.js";
import sql from "../../services/sql.js";
function getNoteSize(req: Request) {
const { noteId } = req.params;
@ -26,7 +27,7 @@ function getNoteSize(req: Request) {
} satisfies NoteSizeResponse;
}
function getSubtreeSize(req: Request) {
function getSubtreeSize(req: Request<{ noteId: string }>) {
const note = becca.getNoteOrThrow(req.params.noteId);
const subTreeNoteIds = note.getSubtreeNoteIds();

View File

@ -1,21 +1,22 @@
"use strict";
import syncService from "../../services/sync.js";
import syncUpdateService from "../../services/sync_update.js";
import entityChangesService from "../../services/entity_changes.js";
import sql from "../../services/sql.js";
import sqlInit from "../../services/sql_init.js";
import optionService from "../../services/options.js";
import contentHashService from "../../services/content_hash.js";
import log from "../../services/log.js";
import syncOptions from "../../services/sync_options.js";
import utils, { safeExtractMessageAndStackFromError } from "../../services/utils.js";
import ws from "../../services/ws.js";
import { type EntityChange,SyncTestResponse } from "@triliumnext/commons";
import type { Request } from "express";
import { t } from "i18next";
import ValidationError from "../../errors/validation_error.js";
import consistencyChecksService from "../../services/consistency_checks.js";
import { t } from "i18next";
import { SyncTestResponse, type EntityChange } from "@triliumnext/commons";
import contentHashService from "../../services/content_hash.js";
import entityChangesService from "../../services/entity_changes.js";
import log from "../../services/log.js";
import optionService from "../../services/options.js";
import sql from "../../services/sql.js";
import sqlInit from "../../services/sql_init.js";
import syncService from "../../services/sync.js";
import syncOptions from "../../services/sync_options.js";
import syncUpdateService from "../../services/sync_update.js";
import utils, { safeExtractMessageAndStackFromError } from "../../services/utils.js";
import ws from "../../services/ws.js";
async function testSync(): Promise<SyncTestResponse> {
try {
@ -287,10 +288,10 @@ function update(req: Request) {
if (pageIndex !== pageCount - 1) {
return;
} else {
body = JSON.parse(partialRequests[requestId].payload);
delete partialRequests[requestId];
}
}
body = JSON.parse(partialRequests[requestId].payload);
delete partialRequests[requestId];
}
const { entities, instanceId } = body;
@ -314,7 +315,7 @@ function syncFinished() {
sqlInit.setDbAsInitialized();
}
function queueSector(req: Request) {
function queueSector(req: Request<{ entityName: string; sector: string }>) {
const entityName = utils.sanitizeSqlIdentifier(req.params.entityName);
const sector = utils.sanitizeSqlIdentifier(req.params.sector);

View File

@ -1,15 +1,17 @@
import express, { type RequestHandler } from "express";
import type { ParamsDictionary } from "express-serve-static-core";
import multer from "multer";
import log from "../services/log.js";
import cls from "../services/cls.js";
import sql from "../services/sql.js";
import entityChangesService from "../services/entity_changes.js";
import AbstractBeccaEntity from "../becca/entities/abstract_becca_entity.js";
import NotFoundError from "../errors/not_found_error.js";
import ValidationError from "../errors/validation_error.js";
import auth from "../services/auth.js";
import { doubleCsrfProtection as csrfMiddleware } from "./csrf_protection.js";
import cls from "../services/cls.js";
import entityChangesService from "../services/entity_changes.js";
import log from "../services/log.js";
import sql from "../services/sql.js";
import { safeExtractMessageAndStackFromError } from "../services/utils.js";
import { doubleCsrfProtection as csrfMiddleware } from "./csrf_protection.js";
const MAX_ALLOWED_FILE_SIZE_MB = 250;
export const router = express.Router();
@ -20,8 +22,8 @@ type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options
export type ApiResultHandler = (req: express.Request, res: express.Response, result: unknown) => number;
type NotAPromise<T> = T & { then?: void };
export type ApiRequestHandler = (req: express.Request, res: express.Response, next: express.NextFunction) => unknown;
export type SyncRouteRequestHandler = (req: express.Request, res: express.Response, next: express.NextFunction) => NotAPromise<object> | number | string | void | null;
export type ApiRequestHandler<P extends ParamsDictionary> = (req: express.Request<P>, res: express.Response, next: express.NextFunction) => unknown;
export type SyncRouteRequestHandler<P extends ParamsDictionary> = (req: express.Request<P>, res: express.Response, next: express.NextFunction) => NotAPromise<object> | number | string | void | null;
/** Handling common patterns. If entity is not caught, serialization to JSON will fail */
function convertEntitiesToPojo(result: unknown) {
@ -67,9 +69,9 @@ export function apiResultHandler(req: express.Request, res: express.Response, re
return send(res, statusCode, response);
} else if (result === undefined) {
return send(res, 204, "");
} else {
return send(res, 200, result);
}
return send(res, 200, result);
}
function send(res: express.Response, statusCode: number, response: unknown) {
@ -81,34 +83,34 @@ function send(res: express.Response, statusCode: number, response: unknown) {
res.status(statusCode).send(response);
return response.length;
} else {
const json = JSON.stringify(response);
res.setHeader("Content-Type", "application/json");
res.status(statusCode).send(json);
return json.length;
}
const json = JSON.stringify(response);
res.setHeader("Content-Type", "application/json");
res.status(statusCode).send(json);
return json.length;
}
export function apiRoute(method: HttpMethod, path: string, routeHandler: SyncRouteRequestHandler) {
export function apiRoute<P extends ParamsDictionary>(method: HttpMethod, path: string, routeHandler: SyncRouteRequestHandler<P>) {
route(method, path, [auth.checkApiAuth, csrfMiddleware], routeHandler, apiResultHandler);
}
export function asyncApiRoute(method: HttpMethod, path: string, routeHandler: ApiRequestHandler) {
export function asyncApiRoute<P extends ParamsDictionary>(method: HttpMethod, path: string, routeHandler: ApiRequestHandler<P>) {
asyncRoute(method, path, [auth.checkApiAuth, csrfMiddleware], routeHandler, apiResultHandler);
}
export function route(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: SyncRouteRequestHandler, resultHandler: ApiResultHandler | null = null) {
export function route<P extends ParamsDictionary>(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: SyncRouteRequestHandler<P>, resultHandler: ApiResultHandler | null = null) {
internalRoute(method, path, middleware, routeHandler, resultHandler, true);
}
export function asyncRoute(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: ApiRequestHandler, resultHandler: ApiResultHandler | null = null) {
export function asyncRoute<P extends ParamsDictionary>(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: ApiRequestHandler<P>, resultHandler: ApiResultHandler | null = null) {
internalRoute(method, path, middleware, routeHandler, resultHandler, false);
}
function internalRoute(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: ApiRequestHandler, resultHandler: ApiResultHandler | null = null, transactional: boolean) {
router[method](path, ...(middleware as express.Handler[]), (req: express.Request, res: express.Response, next: express.NextFunction) => {
function internalRoute<P extends ParamsDictionary>(method: HttpMethod, path: string, middleware: express.Handler[], routeHandler: ApiRequestHandler<P>, resultHandler: ApiResultHandler | null = null, transactional: boolean) {
router[method](path, ...(middleware as express.Handler[]), (req: express.Request<P>, res: express.Response, next: express.NextFunction) => {
const start = Date.now();
try {
@ -193,7 +195,7 @@ export function createUploadMiddleware(): RequestHandler {
const uploadMiddleware = createUploadMiddleware();
export const uploadMiddlewareWithErrorHandling = function (req: express.Request, res: express.Response, next: express.NextFunction) {
uploadMiddleware(req, res, function (err) {
uploadMiddleware(req, res, (err) => {
if (err?.code === "LIMIT_FILE_SIZE") {
res.setHeader("Content-Type", "text/plain").status(400).send(`Cannot upload file because it excceeded max allowed file size of ${MAX_ALLOWED_FILE_SIZE_MB} MiB`);
} else {

View File

@ -10,14 +10,13 @@ import etapiBackupRoute from "../etapi/backup.js";
import etapiBranchRoutes from "../etapi/branches.js";
import etapiMetricsRoute from "../etapi/metrics.js";
import etapiNoteRoutes from "../etapi/notes.js";
import etapiRevisionsRoutes from "../etapi/revisions.js";
import etapiSpecRoute from "../etapi/spec.js";
import etapiSpecialNoteRoutes from "../etapi/special_notes.js";
import etapiRevisionsRoutes from "../etapi/revisions.js";
import auth from "../services/auth.js";
import openID from '../services/open_id.js';
import { isElectron } from "../services/utils.js";
import shareRoutes from "../share/routes.js";
import appInfoRoute from "./api/app_info.js";
import attachmentsApiRoute from "./api/attachments.js";
import attributesRoute from "./api/attributes.js";
@ -35,12 +34,10 @@ import fontsRoute from "./api/fonts.js";
import imageRoute from "./api/image.js";
import importRoute from "./api/import.js";
import keysRoute from "./api/keys.js";
import loginApiRoute from "./api/login.js";
import metricsRoute from "./api/metrics.js";
import noteMapRoute from "./api/note_map.js";
import notesApiRoute from "./api/notes.js";
import optionsApiRoute from "./api/options.js";
import otherRoute from "./api/other.js";
import passwordApiRoute from "./api/password.js";

View File

@ -1,6 +1,8 @@
import type { Request } from "express";
import type { Content } from "./Content.js";
import type { ParamsDictionary } from "express-serve-static-core";
/**
* @type {function (Request): Promise<Content>}
*/
export type ContentProvider = (req: Request) => Promise<Content>;
export type ContentProvider<P extends ParamsDictionary> = (req: Request<P>) => Promise<Content>;

View File

@ -3,6 +3,7 @@ import { parseRangeHeader } from "./parseRangeHeader.js";
import { RangeParserError } from "./RangeParserError.js";
import type { Logger } from "./Logger.js";
import type { ContentProvider } from "./ContentProvider.js";
import type { ParamsDictionary } from "express-serve-static-core";
import { ContentDoesNotExistError } from "./ContentDoesNotExistError.js";
import {
getRangeHeader,
@ -13,8 +14,8 @@ import {
setContentLengthHeader,
setCacheControlHeaderNoCache
} from "./utils.js";
export function createPartialContentHandler(contentProvider: ContentProvider, logger: Logger) {
return async function handler(req: Request, res: Response) {
export function createPartialContentHandler<P extends ParamsDictionary>(contentProvider: ContentProvider<P>, logger: Logger) {
return async function handler(req: Request<P>, res: Response) {
let content;
try {
content = await contentProvider(req);