Merge pull request #44 from TriliumNext/feature/typescript_backend_8

Convert backend to TypeScript (80% -> 81%)
This commit is contained in:
Elian Doran 2024-04-16 21:01:20 +03:00 committed by GitHub
commit 625d935f08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 213 additions and 109 deletions

57
package-lock.json generated
View File

@ -91,12 +91,15 @@
"@types/archiver": "^6.0.2", "@types/archiver": "^6.0.2",
"@types/better-sqlite3": "^7.6.9", "@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8", "@types/cls-hooked": "^4.3.8",
"@types/csurf": "^1.11.5",
"@types/escape-html": "^1.0.4", "@types/escape-html": "^1.0.4",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/express-session": "^1.18.0",
"@types/html": "^1.0.4", "@types/html": "^1.0.4",
"@types/ini": "^4.1.0", "@types/ini": "^4.1.0",
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.11",
"@types/node": "^20.11.19", "@types/node": "^20.11.19",
"@types/sanitize-html": "^2.11.0", "@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7", "@types/sax": "^1.2.7",
@ -1233,6 +1236,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/csurf": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@types/csurf/-/csurf-1.11.5.tgz",
"integrity": "sha512-5rw87+5YGixyL2W8wblSUl5DSZi5YOlXE6Awwn2ofLvqKr/1LruKffrQipeJKUX44VaxKj8m5es3vfhltJTOoA==",
"dev": true,
"dependencies": {
"@types/express-serve-static-core": "*"
}
},
"node_modules/@types/d3-scale": { "node_modules/@types/d3-scale": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
@ -1315,6 +1327,15 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/express-session": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz",
"integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/fs-extra": { "node_modules/@types/fs-extra": {
"version": "9.0.13", "version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
@ -1459,6 +1480,15 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz",
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==" "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
}, },
"node_modules/@types/multer": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz",
"integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.11.19", "version": "20.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
@ -14211,6 +14241,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/csurf": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@types/csurf/-/csurf-1.11.5.tgz",
"integrity": "sha512-5rw87+5YGixyL2W8wblSUl5DSZi5YOlXE6Awwn2ofLvqKr/1LruKffrQipeJKUX44VaxKj8m5es3vfhltJTOoA==",
"dev": true,
"requires": {
"@types/express-serve-static-core": "*"
}
},
"@types/d3-scale": { "@types/d3-scale": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
@ -14293,6 +14332,15 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"@types/express-session": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz",
"integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/fs-extra": { "@types/fs-extra": {
"version": "9.0.13", "version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
@ -14430,6 +14478,15 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz",
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==" "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
}, },
"@types/multer": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz",
"integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/node": { "@types/node": {
"version": "20.11.19", "version": "20.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",

View File

@ -112,12 +112,15 @@
"@types/archiver": "^6.0.2", "@types/archiver": "^6.0.2",
"@types/better-sqlite3": "^7.6.9", "@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8", "@types/cls-hooked": "^4.3.8",
"@types/csurf": "^1.11.5",
"@types/escape-html": "^1.0.4", "@types/escape-html": "^1.0.4",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/express-session": "^1.18.0",
"@types/html": "^1.0.4", "@types/html": "^1.0.4",
"@types/ini": "^4.1.0", "@types/ini": "^4.1.0",
"@types/jsdom": "^21.1.6", "@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.11",
"@types/node": "^20.11.19", "@types/node": "^20.11.19",
"@types/sanitize-html": "^2.11.0", "@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7", "@types/sax": "^1.2.7",

View File

@ -4,7 +4,7 @@ const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const helmet = require('helmet'); const helmet = require('helmet');
const compression = require('compression'); const compression = require('compression');
const sessionParser = require('./routes/session_parser.js'); const sessionParser = require('./routes/session_parser');
const utils = require('./services/utils'); const utils = require('./services/utils');
require('./services/handlers'); require('./services/handlers');
@ -37,10 +37,10 @@ app.use(`/robots.txt`, express.static(path.join(__dirname, 'public/robots.txt'))
app.use(sessionParser); app.use(sessionParser);
app.use(favicon(`${__dirname}/../images/app-icons/win/icon.ico`)); app.use(favicon(`${__dirname}/../images/app-icons/win/icon.ico`));
require('./routes/assets.js').register(app); require('./routes/assets').register(app);
require('./routes/routes.js').register(app); require('./routes/routes.js').register(app);
require('./routes/custom.js').register(app); require('./routes/custom').register(app);
require('./routes/error_handlers.js').register(app); require('./routes/error_handlers').register(app);
// triggers sync timer // triggers sync timer
require('./services/sync'); require('./services/sync');

View File

@ -14,11 +14,12 @@ import ValidationError = require('../../errors/validation_error');
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import BNote = require('../../becca/entities/bnote'); import BNote = require('../../becca/entities/bnote');
import BAttachment = require('../../becca/entities/battachment'); import BAttachment = require('../../becca/entities/battachment');
import { AppRequest } from '../route-interface';
function updateFile(req: Request) { function updateFile(req: AppRequest) {
const note = becca.getNoteOrThrow(req.params.noteId); const note = becca.getNoteOrThrow(req.params.noteId);
const file = (req as any).file; const file = req.file;
note.saveRevision(); note.saveRevision();
note.mime = file.mimetype.toLowerCase(); note.mime = file.mimetype.toLowerCase();
@ -35,9 +36,9 @@ function updateFile(req: Request) {
}; };
} }
function updateAttachment(req: Request) { function updateAttachment(req: AppRequest) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId); const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const file = (req as any).file; const file = req.file;
attachment.getNote().saveRevision(); attachment.getNote().saveRevision();

View File

@ -7,6 +7,7 @@ import fs = require('fs');
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import BNote = require('../../becca/entities/bnote'); import BNote = require('../../becca/entities/bnote');
import BRevision = require('../../becca/entities/brevision'); import BRevision = require('../../becca/entities/brevision');
import { AppRequest } from '../route-interface';
function returnImageFromNote(req: Request, res: Response) { function returnImageFromNote(req: Request, res: Response) {
const image = becca.getNote(req.params.noteId); const image = becca.getNote(req.params.noteId);
@ -81,9 +82,9 @@ function returnAttachedImage(req: Request, res: Response) {
res.send(attachment.getContent()); res.send(attachment.getContent());
} }
function updateImage(req: Request) { function updateImage(req: AppRequest) {
const {noteId} = req.params; const {noteId} = req.params;
const {file} = (req as any); const {file} = req;
const note = becca.getNoteOrThrow(noteId); const note = becca.getNoteOrThrow(noteId);
@ -94,6 +95,13 @@ function updateImage(req: Request) {
}; };
} }
if (typeof file.buffer === "string") {
return {
uploaded: false,
message: "Invalid image content."
};
}
imageService.updateImage(noteId, file.buffer, file.originalname); imageService.updateImage(noteId, file.buffer, file.originalname);
return { uploaded: true }; return { uploaded: true };

View File

@ -13,8 +13,9 @@ import TaskContext = require('../../services/task_context');
import ValidationError = require('../../errors/validation_error'); import ValidationError = require('../../errors/validation_error');
import { Request } from 'express'; import { Request } from 'express';
import BNote = require('../../becca/entities/bnote'); import BNote = require('../../becca/entities/bnote');
import { AppRequest } from '../route-interface';
async function importNotesToBranch(req: Request) { async function importNotesToBranch(req: AppRequest) {
const { parentNoteId } = req.params; const { parentNoteId } = req.params;
const { taskId, last } = req.body; const { taskId, last } = req.body;
@ -27,7 +28,7 @@ async function importNotesToBranch(req: Request) {
replaceUnderscoresWithSpaces: req.body.replaceUnderscoresWithSpaces !== 'false' replaceUnderscoresWithSpaces: req.body.replaceUnderscoresWithSpaces !== 'false'
}; };
const file = (req as any).file; const file = req.file;
if (!file) { if (!file) {
throw new ValidationError("No file has been uploaded"); throw new ValidationError("No file has been uploaded");
@ -49,7 +50,7 @@ async function importNotesToBranch(req: Request) {
const taskContext = TaskContext.getInstance(taskId, 'importNotes', options); const taskContext = TaskContext.getInstance(taskId, 'importNotes', options);
try { try {
if (extension === '.zip' && options.explodeArchives) { if (extension === '.zip' && options.explodeArchives && typeof file.buffer !== "string") {
note = await zipImportService.importZip(taskContext, file.buffer, parentNote); note = await zipImportService.importZip(taskContext, file.buffer, parentNote);
} else if (extension === '.opml' && options.explodeArchives) { } else if (extension === '.opml' && options.explodeArchives) {
const importResult = await opmlImportService.importOpml(taskContext, file.buffer, parentNote); const importResult = await opmlImportService.importOpml(taskContext, file.buffer, parentNote);
@ -96,7 +97,7 @@ async function importNotesToBranch(req: Request) {
return note.getPojo(); return note.getPojo();
} }
async function importAttachmentsToNote(req: Request) { async function importAttachmentsToNote(req: AppRequest) {
const { parentNoteId } = req.params; const { parentNoteId } = req.params;
const { taskId, last } = req.body; const { taskId, last } = req.body;
@ -104,7 +105,7 @@ async function importAttachmentsToNote(req: Request) {
shrinkImages: req.body.shrinkImages !== 'false', shrinkImages: req.body.shrinkImages !== 'false',
}; };
const file = (req as any).file; const file = req.file;
if (!file) { if (!file) {
throw new ValidationError("No file has been uploaded"); throw new ValidationError("No file has been uploaded");

View File

@ -13,8 +13,9 @@ import sql = require('../../services/sql');
import ws = require('../../services/ws'); import ws = require('../../services/ws');
import etapiTokenService = require('../../services/etapi_tokens'); import etapiTokenService = require('../../services/etapi_tokens');
import { Request } from 'express'; import { Request } from 'express';
import { AppRequest } from '../route-interface';
function loginSync(req: Request) { function loginSync(req: AppRequest) {
if (!sqlInit.schemaExists()) { if (!sqlInit.schemaExists()) {
return [500, { message: "DB schema does not exist, can't sync." }]; return [500, { message: "DB schema does not exist, can't sync." }];
} }
@ -45,7 +46,7 @@ function loginSync(req: Request) {
return [400, { message: "Sync login credentials are incorrect. It looks like you're trying to sync two different initialized documents which is not possible." }]; return [400, { message: "Sync login credentials are incorrect. It looks like you're trying to sync two different initialized documents which is not possible." }];
} }
(req as any).session.loggedIn = true; req.session.loggedIn = true;
return { return {
instanceId: instanceId, instanceId: instanceId,

View File

@ -6,13 +6,17 @@ import noteService = require('../../services/notes');
import sanitize_attribute_name = require('../../services/sanitize_attribute_name'); import sanitize_attribute_name = require('../../services/sanitize_attribute_name');
import specialNotesService = require('../../services/special_notes'); import specialNotesService = require('../../services/special_notes');
import { Request } from 'express'; import { Request } from 'express';
import { AppRequest } from '../route-interface';
function uploadImage(req: Request) { function uploadImage(req: AppRequest) {
const file = (req as any).file; const file = req.file;
if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
return [400, `Unknown image type: ${file.mimetype}`]; return [400, `Unknown image type: ${file.mimetype}`];
} }
if (typeof file.buffer === "string") {
return [400, "Invalid image content type."];
}
const uploadedImageType = imageType(file.buffer); const uploadedImageType = imageType(file.buffer);
if (!uploadedImageType) { if (!uploadedImageType) {
@ -20,14 +24,10 @@ function uploadImage(req: Request) {
} }
const originalName = `Sender image.${uploadedImageType.ext}`; const originalName = `Sender image.${uploadedImageType.ext}`;
if (!req.headers["x-local-date"] || Array.isArray(req.headers["x-local-date"])) { if (!req.headers["x-local-date"]) {
return [400, "Invalid local date"]; return [400, "Invalid local date"];
} }
if (Array.isArray(req.headers["x-labels"])) {
return [400, "Invalid value type."];
}
const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']); const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']);
const { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true); const { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true);

View File

@ -1,9 +1,10 @@
const assetPath = require('../services/asset_path'); import assetPath = require('../services/asset_path');
const path = require("path"); import path = require("path");
const express = require("express"); import express = require("express");
const env = require('../services/env'); import env = require('../services/env');
import serveStatic = require('serve-static');
const persistentCacheStatic = (root, options) => { const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<any, Record<string, any>>>) => {
if (!env.isDev()) { if (!env.isDev()) {
options = { options = {
maxAge: '1y', maxAge: '1y',
@ -13,7 +14,7 @@ const persistentCacheStatic = (root, options) => {
return express.static(root, options); return express.static(root, options);
}; };
function register(app) { function register(app: express.Application) {
const srcRoot = path.join(__dirname, '..'); const srcRoot = path.join(__dirname, '..');
app.use(`/${assetPath}/app`, persistentCacheStatic(path.join(srcRoot, 'public/app'))); app.use(`/${assetPath}/app`, persistentCacheStatic(path.join(srcRoot, 'public/app')));
app.use(`/${assetPath}/app-dist`, persistentCacheStatic(path.join(srcRoot, 'public/app-dist'))); app.use(`/${assetPath}/app-dist`, persistentCacheStatic(path.join(srcRoot, 'public/app-dist')));
@ -70,6 +71,6 @@ function register(app) {
app.use(`/${assetPath}/node_modules/panzoom/dist/`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/panzoom/dist/'))); app.use(`/${assetPath}/node_modules/panzoom/dist/`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/panzoom/dist/')));
} }
module.exports = { export = {
register register
}; };

View File

@ -1,21 +1,22 @@
const log = require('../services/log'); import log = require('../services/log');
const fileService = require('./api/files'); import fileService = require('./api/files');
const scriptService = require('../services/script'); import scriptService = require('../services/script');
const cls = require('../services/cls'); import cls = require('../services/cls');
const sql = require('../services/sql'); import sql = require('../services/sql');
const becca = require('../becca/becca'); import becca = require('../becca/becca');
import { Request, Response, Router } from 'express';
function handleRequest(req, res) { function handleRequest(req: Request, res: Response) {
// express puts content after first slash into 0 index element // express puts content after first slash into 0 index element
const path = req.params.path + req.params[0]; const path = req.params.path + req.params[0];
const attributeIds = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')"); const attributeIds = sql.getColumn<string>("SELECT attributeId FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
const attrs = attributeIds.map(attrId => becca.getAttribute(attrId)); const attrs = attributeIds.map(attrId => becca.getAttribute(attrId));
for (const attr of attrs) { for (const attr of attrs) {
if (!attr.value.trim()) { if (!attr?.value.trim()) {
continue; continue;
} }
@ -25,7 +26,7 @@ function handleRequest(req, res) {
try { try {
match = path.match(regex); match = path.match(regex);
} }
catch (e) { catch (e: any) {
log.error(`Testing path for label '${attr.attributeId}', regex '${attr.value}' failed with error: ${e.message}, stack: ${e.stack}`); log.error(`Testing path for label '${attr.attributeId}', regex '${attr.value}' failed with error: ${e.message}, stack: ${e.stack}`);
continue; continue;
} }
@ -46,7 +47,7 @@ function handleRequest(req, res) {
res res
}); });
} }
catch (e) { catch (e: any) {
log.error(`Custom handler '${note.noteId}' failed with: ${e.message}, ${e.stack}`); log.error(`Custom handler '${note.noteId}' failed with: ${e.message}, ${e.stack}`);
res.setHeader("Content-Type", "text/plain") res.setHeader("Content-Type", "text/plain")
@ -72,10 +73,10 @@ function handleRequest(req, res) {
.send(message); .send(message);
} }
function register(router) { function register(router: Router) {
// explicitly no CSRF middleware since it's meant to allow integration from external services // explicitly no CSRF middleware since it's meant to allow integration from external services
router.all('/custom/:path*', (req, res, next) => { router.all('/custom/:path*', (req: Request, res: Response, next) => {
cls.namespace.bindEmitter(req); cls.namespace.bindEmitter(req);
cls.namespace.bindEmitter(res); cls.namespace.bindEmitter(res);
@ -83,6 +84,6 @@ function register(router) {
}); });
} }
module.exports = { export = {
register register
}; };

View File

@ -1,6 +1,17 @@
import { Application } from "express";
const ipcMain = require('electron').ipcMain; const ipcMain = require('electron').ipcMain;
function init(app) { interface Response {
statusCode: number;
getHeader: (name: string) => string;
setHeader: (name: string, value: string) => Response;
header: (name: string, value: string) => Response;
status: (statusCode: number) => Response;
send: (obj: {}) => void;
}
function init(app: Application) {
ipcMain.on('server-request', (event, arg) => { ipcMain.on('server-request', (event, arg) => {
const req = { const req = {
url: arg.url, url: arg.url,
@ -12,9 +23,9 @@ function init(app) {
} }
}; };
const respHeaders = {}; const respHeaders: Record<string, string> = {};
const res = { const res: Response = {
statusCode: 200, statusCode: 200,
getHeader: name => respHeaders[name], getHeader: name => respHeaders[name],
setHeader: (name, value) => { setHeader: (name, value) => {
@ -45,4 +56,4 @@ function init(app) {
}); });
} }
module.exports = init; export = init;

View File

@ -1,7 +1,8 @@
const log = require('../services/log'); import { Application, NextFunction, Request, Response } from 'express';
import log = require('../services/log');
function register(app) { function register(app: Application) {
app.use((err, req, res, next) => { app.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err.code !== 'EBADCSRFTOKEN') { if (err.code !== 'EBADCSRFTOKEN') {
return next(err); return next(err);
} }
@ -16,12 +17,12 @@ function register(app) {
// catch 404 and forward to error handler // catch 404 and forward to error handler
app.use((req, res, next) => { app.use((req, res, next) => {
const err = new Error(`Router not found for request ${req.method} ${req.url}`); const err = new Error(`Router not found for request ${req.method} ${req.url}`);
err.status = 404; (err as any).status = 404;
next(err); next(err);
}); });
// error handler // error handler
app.use((err, req, res, next) => { app.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err && err.message && ( if (err && err.message && (
(err.message.includes("Router not found for request") && err.message.includes(".js.map")) (err.message.includes("Router not found for request") && err.message.includes(".js.map"))
|| (err.message.includes("Router not found for request") && err.message.includes(".css.map")) || (err.message.includes("Router not found for request") && err.message.includes(".css.map"))
@ -38,6 +39,6 @@ function register(app) {
}); });
} }
module.exports = { export = {
register register
}; };

View File

@ -1,18 +1,19 @@
"use strict"; "use strict";
const sql = require('../services/sql'); import sql = require('../services/sql');
const attributeService = require('../services/attributes'); import attributeService = require('../services/attributes');
const config = require('../services/config'); import config = require('../services/config');
const optionService = require('../services/options'); import optionService = require('../services/options');
const log = require('../services/log'); import log = require('../services/log');
const env = require('../services/env'); import env = require('../services/env');
const utils = require('../services/utils'); import utils = require('../services/utils');
const protectedSessionService = require('../services/protected_session'); import protectedSessionService = require('../services/protected_session');
const packageJson = require('../../package.json'); import packageJson = require('../../package.json');
const assetPath = require('../services/asset_path'); import assetPath = require('../services/asset_path');
const appPath = require('../services/app_path'); import appPath = require('../services/app_path');
import { Request, Response } from 'express';
function index(req, res) { function index(req: Request, res: Response) {
const options = optionService.getOptionMap(); const options = optionService.getOptionMap();
const view = (!utils.isElectron() && req.cookies['trilium-device'] === 'mobile') const view = (!utils.isElectron() && req.cookies['trilium-device'] === 'mobile')
@ -43,7 +44,7 @@ function index(req, res) {
}); });
} }
function getThemeCssUrl(theme) { function getThemeCssUrl(theme: string) {
if (theme === 'light') { if (theme === 'light') {
return false; // light theme is always loaded as baseline return false; // light theme is always loaded as baseline
} else if (theme === 'dark') { } else if (theme === 'dark') {
@ -63,6 +64,6 @@ function getAppCssNoteIds() {
return attributeService.getNotesWithLabel('appCss').map(note => note.noteId); return attributeService.getNotesWithLabel('appCss').map(note => note.noteId);
} }
module.exports = { export = {
index index
}; };

View File

@ -1,15 +1,17 @@
"use strict"; "use strict";
const utils = require('../services/utils'); import utils = require('../services/utils');
const optionService = require('../services/options'); import optionService = require('../services/options');
const myScryptService = require('../services/encryption/my_scrypt'); import myScryptService = require('../services/encryption/my_scrypt');
const log = require('../services/log'); import log = require('../services/log');
const passwordService = require('../services/encryption/password'); import passwordService = require('../services/encryption/password');
const assetPath = require('../services/asset_path'); import assetPath = require('../services/asset_path');
const appPath = require('../services/app_path'); import appPath = require('../services/app_path');
const ValidationError = require('../errors/validation_error'); import ValidationError = require('../errors/validation_error');
import { Request, Response } from 'express';
import { AppRequest } from './route-interface';
function loginPage(req, res) { function loginPage(req: Request, res: Response) {
res.render('login', { res.render('login', {
failedAuth: false, failedAuth: false,
assetPath: assetPath, assetPath: assetPath,
@ -17,7 +19,7 @@ function loginPage(req, res) {
}); });
} }
function setPasswordPage(req, res) { function setPasswordPage(req: Request, res: Response) {
res.render('set_password', { res.render('set_password', {
error: false, error: false,
assetPath: assetPath, assetPath: assetPath,
@ -25,7 +27,7 @@ function setPasswordPage(req, res) {
}); });
} }
function setPassword(req, res) { function setPassword(req: Request, res: Response) {
if (passwordService.isPasswordSet()) { if (passwordService.isPasswordSet()) {
throw new ValidationError("Password has been already set"); throw new ValidationError("Password has been already set");
} }
@ -55,7 +57,7 @@ function setPassword(req, res) {
res.redirect('login'); res.redirect('login');
} }
function login(req, res) { function login(req: AppRequest, res: Response) {
const guessedPassword = req.body.password; const guessedPassword = req.body.password;
if (verifyPassword(guessedPassword)) { if (verifyPassword(guessedPassword)) {
@ -83,7 +85,7 @@ function login(req, res) {
} }
} }
function verifyPassword(guessedPassword) { function verifyPassword(guessedPassword: string) {
const hashed_password = utils.fromBase64(optionService.getOption('passwordVerificationHash')); const hashed_password = utils.fromBase64(optionService.getOption('passwordVerificationHash'));
const guess_hashed = myScryptService.getVerificationHash(guessedPassword); const guess_hashed = myScryptService.getVerificationHash(guessedPassword);
@ -91,7 +93,7 @@ function verifyPassword(guessedPassword) {
return guess_hashed.equals(hashed_password); return guess_hashed.equals(hashed_password);
} }
function logout(req, res) { function logout(req: AppRequest, res: Response) {
req.session.regenerate(() => { req.session.regenerate(() => {
req.session.loggedIn = false; req.session.loggedIn = false;
@ -100,7 +102,7 @@ function logout(req, res) {
} }
module.exports = { export = {
loginPage, loginPage,
setPasswordPage, setPasswordPage,
setPassword, setPassword,

View File

@ -0,0 +1,20 @@
import { Request } from "express";
import { File } from "../services/import/common";
export interface AppRequest extends Request {
headers: {
authorization?: string;
"trilium-cred"?: string;
"x-local-date"?: string;
"x-labels"?: string;
}
session: {
loggedIn: boolean;
cookie: {
maxAge: number;
expires: boolean
};
regenerate: (callback: () => void) => void;
}
file: File;
}

View File

@ -18,8 +18,8 @@ const ValidationError = require('../errors/validation_error');
// page routes // page routes
const setupRoute = require('./setup'); const setupRoute = require('./setup');
const loginRoute = require('./login.js'); const loginRoute = require('./login');
const indexRoute = require('./index.js'); const indexRoute = require('./index');
// API routes // API routes
const treeApiRoute = require('./api/tree'); const treeApiRoute = require('./api/tree');

View File

@ -1,6 +1,6 @@
const session = require("express-session"); import session = require("express-session");
const sessionSecret = require('../services/session_secret'); import sessionSecret = require('../services/session_secret');
const dataDir = require('../services/data_dir'); import dataDir = require('../services/data_dir');
const FileStore = require('session-file-store')(session); const FileStore = require('session-file-store')(session);
const sessionParser = session({ const sessionParser = session({
@ -19,4 +19,4 @@ const sessionParser = session({
}) })
}); });
module.exports = sessionParser; export = sessionParser;

View File

@ -1,12 +1,13 @@
"use strict"; "use strict";
const sqlInit = require('../services/sql_init'); import sqlInit = require('../services/sql_init');
const setupService = require('../services/setup'); import setupService = require('../services/setup');
const utils = require('../services/utils'); import utils = require('../services/utils');
const assetPath = require('../services/asset_path'); import assetPath = require('../services/asset_path');
const appPath = require('../services/app_path'); import appPath = require('../services/app_path');
import { Request, Response } from 'express';
function setupPage(req, res) { function setupPage(req: Request, res: Response) {
if (sqlInit.isDbInitialized()) { if (sqlInit.isDbInitialized()) {
if (utils.isElectron()) { if (utils.isElectron()) {
const windowService = require('../services/window'); const windowService = require('../services/window');
@ -37,6 +38,6 @@ function setupPage(req, res) {
}); });
} }
module.exports = { export = {
setupPage setupPage
}; };

View File

@ -8,19 +8,10 @@ import passwordEncryptionService = require('./encryption/password_encryption');
import config = require('./config'); import config = require('./config');
import passwordService = require('./encryption/password'); import passwordService = require('./encryption/password');
import type { NextFunction, Request, Response } from 'express'; import type { NextFunction, Request, Response } from 'express';
import { AppRequest } from '../routes/route-interface';
const noAuthentication = config.General && config.General.noAuthentication === true; const noAuthentication = config.General && config.General.noAuthentication === true;
interface AppRequest extends Request {
headers: {
authorization?: string;
"trilium-cred"?: string;
}
session: {
loggedIn: boolean;
}
}
function checkAuth(req: AppRequest, res: Response, next: NextFunction) { function checkAuth(req: AppRequest, res: Response, next: NextFunction) {
if (!sqlInit.isDbInitialized()) { if (!sqlInit.isDbInitialized()) {
res.redirect("setup"); res.redirect("setup");

View File

@ -1,7 +1,11 @@
import { Request, Response } from "express";
import AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity"); import AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity");
import BNote = require("../becca/entities/bnote"); import BNote = require("../becca/entities/bnote");
export interface ApiParams { export interface ApiParams {
startNote?: BNote; startNote?: BNote;
originEntity?: AbstractBeccaEntity<any>; originEntity?: AbstractBeccaEntity<any>;
pathParams?: string[],
req?: Request,
res?: Response
} }

View File

@ -28,7 +28,7 @@ interface OpmlOutline {
outline: OpmlOutline[]; outline: OpmlOutline[];
} }
async function importOpml(taskContext: TaskContext, fileBuffer: Buffer, parentNote: BNote) { async function importOpml(taskContext: TaskContext, 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) {

View File

@ -7,7 +7,7 @@ import log = require('./log');
const sessionSecretPath = `${dataDir.TRILIUM_DATA_DIR}/session_secret.txt`; const sessionSecretPath = `${dataDir.TRILIUM_DATA_DIR}/session_secret.txt`;
let sessionSecret; let sessionSecret: string;
const ENCODING = "ascii"; const ENCODING = "ascii";