diff --git a/_check_ts_progress.sh b/_check_ts_progress.sh index ac74a7205..c7b66ce23 100755 --- a/_check_ts_progress.sh +++ b/_check_ts_progress.sh @@ -4,7 +4,7 @@ cloc HEAD \ --git --md \ --include-lang=javascript,typescript \ --found=filelist.txt \ - --exclude-dir=public,libraries + --exclude-dir=public,libraries,views,docs grep -R \.js$ filelist.txt rm filelist.txt \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a0fdabf82..2dd4a639d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,6 +91,8 @@ "@types/archiver": "^6.0.2", "@types/better-sqlite3": "^7.6.9", "@types/cls-hooked": "^4.3.8", + "@types/compression": "^1.7.5", + "@types/cookie-parser": "^1.4.7", "@types/csurf": "^1.11.5", "@types/ejs": "^3.1.5", "@types/escape-html": "^1.0.4", @@ -105,6 +107,8 @@ "@types/safe-compare": "^1.1.2", "@types/sanitize-html": "^2.11.0", "@types/sax": "^1.2.7", + "@types/semver": "^7.5.8", + "@types/serve-favicon": "^2.5.7", "@types/stream-throttle": "^0.1.4", "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.4", @@ -1229,6 +1233,15 @@ "@types/node": "*" } }, + "node_modules/@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -1238,6 +1251,15 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/csurf": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/@types/csurf/-/csurf-1.11.5.tgz", @@ -1643,6 +1665,12 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -1653,6 +1681,15 @@ "@types/node": "*" } }, + "node_modules/@types/serve-favicon": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/serve-favicon/-/serve-favicon-2.5.7.tgz", + "integrity": "sha512-z9TNUQXdQ+W/OJMP1e3KOYUZ99qJS4+ZfFOIrPGImcayqKoyifbJSEFkVq1MCKBbqjMZpjPj3B5ilrQAR2+TOw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/serve-static": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", @@ -14246,6 +14283,15 @@ "@types/node": "*" } }, + "@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -14255,6 +14301,15 @@ "@types/node": "*" } }, + "@types/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/csurf": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/@types/csurf/-/csurf-1.11.5.tgz", @@ -14630,6 +14685,12 @@ "@types/node": "*" } }, + "@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -14640,6 +14701,15 @@ "@types/node": "*" } }, + "@types/serve-favicon": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/serve-favicon/-/serve-favicon-2.5.7.tgz", + "integrity": "sha512-z9TNUQXdQ+W/OJMP1e3KOYUZ99qJS4+ZfFOIrPGImcayqKoyifbJSEFkVq1MCKBbqjMZpjPj3B5ilrQAR2+TOw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/serve-static": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", diff --git a/package.json b/package.json index 8cc9cf947..cbfcd7e32 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "url": "https://github.com/zadam/trilium.git" }, "scripts": { - "start-server": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.js", - "start-server-no-dir": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.js", - "qstart-server": "npm run qswitch-server && TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.js", + "start-server": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts", + "start-server-no-dir": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts", + "qstart-server": "npm run qswitch-server && TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/www.ts", "start-electron": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron --inspect=5858 .", "start-electron-no-dir": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 electron --inspect=5858 .", "qstart-electron": "npm run qswitch-electron && TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron --inspect=5858 .", @@ -112,6 +112,8 @@ "@types/archiver": "^6.0.2", "@types/better-sqlite3": "^7.6.9", "@types/cls-hooked": "^4.3.8", + "@types/compression": "^1.7.5", + "@types/cookie-parser": "^1.4.7", "@types/csurf": "^1.11.5", "@types/ejs": "^3.1.5", "@types/escape-html": "^1.0.4", @@ -126,6 +128,8 @@ "@types/safe-compare": "^1.1.2", "@types/sanitize-html": "^2.11.0", "@types/sax": "^1.2.7", + "@types/semver": "^7.5.8", + "@types/serve-favicon": "^2.5.7", "@types/stream-throttle": "^0.1.4", "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.4", @@ -151,4 +155,4 @@ "optionalDependencies": { "electron-installer-debian": "3.2.0" } -} +} \ No newline at end of file diff --git a/src/app.js b/src/app.ts similarity index 76% rename from src/app.js rename to src/app.ts index 30daf58ea..ceabe33f9 100644 --- a/src/app.js +++ b/src/app.ts @@ -1,11 +1,11 @@ -const express = require('express'); -const path = require('path'); -const favicon = require('serve-favicon'); -const cookieParser = require('cookie-parser'); -const helmet = require('helmet'); -const compression = require('compression'); -const sessionParser = require('./routes/session_parser'); -const utils = require('./services/utils'); +import express = require('express'); +import path = require('path'); +import favicon = require('serve-favicon'); +import cookieParser = require('cookie-parser'); +import helmet = require('helmet'); +import compression = require('compression'); +import sessionParser = require('./routes/session_parser'); +import utils = require('./services/utils'); require('./services/handlers'); require('./becca/becca_loader'); @@ -20,7 +20,7 @@ if (!utils.isElectron()) { app.use(compression()); // HTTP compression } -app.use(helmet({ +app.use(helmet.default({ hidePoweredBy: false, // errors out in electron contentSecurityPolicy: false, crossOriginEmbedderPolicy: false @@ -38,7 +38,7 @@ app.use(sessionParser); app.use(favicon(`${__dirname}/../images/app-icons/win/icon.ico`)); require('./routes/assets').register(app); -require('./routes/routes.js').register(app); +require('./routes/routes').register(app); require('./routes/custom').register(app); require('./routes/error_handlers').register(app); @@ -57,4 +57,4 @@ if (utils.isElectron()) { require('@electron/remote/main').initialize(); } -module.exports = app; +export = app; diff --git a/src/etapi/attachments.ts b/src/etapi/attachments.ts index 258b9a186..8e76d27e8 100644 --- a/src/etapi/attachments.ts +++ b/src/etapi/attachments.ts @@ -103,6 +103,6 @@ function register(router: Router) { }); } -module.exports = { +export = { register }; diff --git a/src/routes/api/clipper.ts b/src/routes/api/clipper.ts index 070a6c7e9..e7f523cec 100644 --- a/src/routes/api/clipper.ts +++ b/src/routes/api/clipper.ts @@ -229,7 +229,7 @@ function findNotesByUrl(req: Request){ } } -module.exports = { +export = { createNote, addClipping, openNote, diff --git a/src/routes/api/export.ts b/src/routes/api/export.ts index 499198aef..7d6b3f683 100644 --- a/src/routes/api/export.ts +++ b/src/routes/api/export.ts @@ -55,6 +55,6 @@ function exportBranch(req: Request, res: Response) { } } -module.exports = { +export = { exportBranch }; diff --git a/src/routes/api/files.ts b/src/routes/api/files.ts index 682bc94e4..df8f14b58 100644 --- a/src/routes/api/files.ts +++ b/src/routes/api/files.ts @@ -124,7 +124,7 @@ function attachmentContentProvider(req: Request) { return streamContent(attachment.getContent(), attachment.getFileName(), attachment.mime); } -function streamContent(content: string | Buffer, fileName: string, mimeType: string) { +async function streamContent(content: string | Buffer, fileName: string, mimeType: string) { if (typeof content === "string") { content = Buffer.from(content, 'utf8'); } diff --git a/src/routes/routes.js b/src/routes/routes.ts similarity index 81% rename from src/routes/routes.js rename to src/routes/routes.ts index 7aa6a64ef..55cad4cd5 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.ts @@ -1,87 +1,94 @@ "use strict"; -const utils = require('../services/utils'); -const multer = require('multer'); -const log = require('../services/log'); -const express = require('express'); +import utils = require('../services/utils'); +import multer = require('multer'); +import log = require('../services/log'); +import express = require('express'); const router = express.Router(); -const auth = require('../services/auth'); -const cls = require('../services/cls'); -const sql = require('../services/sql'); -const entityChangesService = require('../services/entity_changes'); -const csurf = require('csurf'); -const { createPartialContentHandler } = require("express-partial-content"); -const rateLimit = require("express-rate-limit"); -const AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity'); -const NotFoundError = require('../errors/not_found_error'); -const ValidationError = require('../errors/validation_error'); +import auth = require('../services/auth'); +import cls = require('../services/cls'); +import sql = require('../services/sql'); +import entityChangesService = require('../services/entity_changes'); +import csurf = require('csurf'); +import { createPartialContentHandler } from "express-partial-content"; +import rateLimit = require("express-rate-limit"); +import AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity'); +import NotFoundError = require('../errors/not_found_error'); +import ValidationError = require('../errors/validation_error'); // page routes -const setupRoute = require('./setup'); -const loginRoute = require('./login'); -const indexRoute = require('./index'); +import setupRoute = require('./setup'); +import loginRoute = require('./login'); +import indexRoute = require('./index'); // API routes -const treeApiRoute = require('./api/tree'); -const notesApiRoute = require('./api/notes'); -const branchesApiRoute = require('./api/branches'); -const attachmentsApiRoute = require('./api/attachments'); -const autocompleteApiRoute = require('./api/autocomplete'); -const cloningApiRoute = require('./api/cloning'); -const revisionsApiRoute = require('./api/revisions'); -const recentChangesApiRoute = require('./api/recent_changes'); -const optionsApiRoute = require('./api/options'); -const passwordApiRoute = require('./api/password'); -const syncApiRoute = require('./api/sync'); -const loginApiRoute = require('./api/login'); -const recentNotesRoute = require('./api/recent_notes'); -const appInfoRoute = require('./api/app_info'); -const exportRoute = require('./api/export'); -const importRoute = require('./api/import'); -const setupApiRoute = require('./api/setup'); -const sqlRoute = require('./api/sql'); -const databaseRoute = require('./api/database'); -const imageRoute = require('./api/image'); -const attributesRoute = require('./api/attributes'); -const scriptRoute = require('./api/script'); -const senderRoute = require('./api/sender'); -const filesRoute = require('./api/files'); -const searchRoute = require('./api/search'); -const bulkActionRoute = require('./api/bulk_action'); -const specialNotesRoute = require('./api/special_notes'); -const noteMapRoute = require('./api/note_map'); -const clipperRoute = require('./api/clipper'); -const similarNotesRoute = require('./api/similar_notes'); -const keysRoute = require('./api/keys'); -const backendLogRoute = require('./api/backend_log'); -const statsRoute = require('./api/stats'); -const fontsRoute = require('./api/fonts'); -const etapiTokensApiRoutes = require('./api/etapi_tokens'); -const relationMapApiRoute = require('./api/relation-map'); -const otherRoute = require('./api/other'); -const shareRoutes = require('../share/routes'); +import treeApiRoute = require('./api/tree'); +import notesApiRoute = require('./api/notes'); +import branchesApiRoute = require('./api/branches'); +import attachmentsApiRoute = require('./api/attachments'); +import autocompleteApiRoute = require('./api/autocomplete'); +import cloningApiRoute = require('./api/cloning'); +import revisionsApiRoute = require('./api/revisions'); +import recentChangesApiRoute = require('./api/recent_changes'); +import optionsApiRoute = require('./api/options'); +import passwordApiRoute = require('./api/password'); +import syncApiRoute = require('./api/sync'); +import loginApiRoute = require('./api/login'); +import recentNotesRoute = require('./api/recent_notes'); +import appInfoRoute = require('./api/app_info'); +import exportRoute = require('./api/export'); +import importRoute = require('./api/import'); +import setupApiRoute = require('./api/setup'); +import sqlRoute = require('./api/sql'); +import databaseRoute = require('./api/database'); +import imageRoute = require('./api/image'); +import attributesRoute = require('./api/attributes'); +import scriptRoute = require('./api/script'); +import senderRoute = require('./api/sender'); +import filesRoute = require('./api/files'); +import searchRoute = require('./api/search'); +import bulkActionRoute = require('./api/bulk_action'); +import specialNotesRoute = require('./api/special_notes'); +import noteMapRoute = require('./api/note_map'); +import clipperRoute = require('./api/clipper'); +import similarNotesRoute = require('./api/similar_notes'); +import keysRoute = require('./api/keys'); +import backendLogRoute = require('./api/backend_log'); +import statsRoute = require('./api/stats'); +import fontsRoute = require('./api/fonts'); +import etapiTokensApiRoutes = require('./api/etapi_tokens'); +import relationMapApiRoute = require('./api/relation-map'); +import otherRoute = require('./api/other'); +import shareRoutes = require('../share/routes'); -const etapiAuthRoutes = require('../etapi/auth'); -const etapiAppInfoRoutes = require('../etapi/app_info'); -const etapiAttachmentRoutes = require('../etapi/attachments'); -const etapiAttributeRoutes = require('../etapi/attributes'); -const etapiBranchRoutes = require('../etapi/branches'); -const etapiNoteRoutes = require('../etapi/notes'); -const etapiSpecialNoteRoutes = require('../etapi/special_notes'); -const etapiSpecRoute = require('../etapi/spec'); -const etapiBackupRoute = require('../etapi/backup'); +import etapiAuthRoutes = require('../etapi/auth'); +import etapiAppInfoRoutes = require('../etapi/app_info'); +import etapiAttachmentRoutes = require('../etapi/attachments'); +import etapiAttributeRoutes = require('../etapi/attributes'); +import etapiBranchRoutes = require('../etapi/branches'); +import etapiNoteRoutes = require('../etapi/notes'); +import etapiSpecialNoteRoutes = require('../etapi/special_notes'); +import etapiSpecRoute = require('../etapi/spec'); +import etapiBackupRoute = require('../etapi/backup'); +import { AppRequest, AppRequestHandler } from './route-interface'; const csrfMiddleware = csurf({ - cookie: true, - path: '' // empty, so cookie is valid only for the current path + cookie: { + path: "" // empty, so cookie is valid only for the current path + } }); const MAX_ALLOWED_FILE_SIZE_MB = 250; const GET = 'get', PST = 'post', PUT = 'put', PATCH = 'patch', DEL = 'delete'; +type ApiResultHandler = (req: express.Request, res: express.Response, result: unknown) => number; + +// TODO: Deduplicate with etapi_utils.ts afterwards. +type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; + const uploadMiddleware = createUploadMiddleware(); -const uploadMiddlewareWithErrorHandling = function (req, res, next) { +const uploadMiddlewareWithErrorHandling = function (req: express.Request, res: express.Response, next: express.NextFunction) { uploadMiddleware(req, res, function (err) { if (err?.code === 'LIMIT_FILE_SIZE') { res.setHeader("Content-Type", "text/plain") @@ -94,12 +101,12 @@ const uploadMiddlewareWithErrorHandling = function (req, res, next) { }); }; -function register(app) { +function register(app: express.Application) { route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index); route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage); route(GET, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPasswordPage); - const loginRateLimiter = rateLimit({ + const loginRateLimiter = rateLimit.rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 10, // limit each IP to 10 requests per windowMs skipSuccessfulRequests: true // successful auth to rate-limited ETAPI routes isn't counted. However, successful auth to /login is still counted! @@ -353,7 +360,7 @@ function register(app) { } /** Handling common patterns. If entity is not caught, serialization to JSON will fail */ -function convertEntitiesToPojo(result) { +function convertEntitiesToPojo(result: unknown) { if (result instanceof AbstractBeccaEntity) { result = result.getPojo(); } @@ -364,24 +371,24 @@ function convertEntitiesToPojo(result) { } } } - else { - if (result && result.note instanceof AbstractBeccaEntity) { + else if (result && typeof result === "object") { + if ("note" in result && result.note instanceof AbstractBeccaEntity) { result.note = result.note.getPojo(); } - if (result && result.branch instanceof AbstractBeccaEntity) { + if ("branch" in result && result.branch instanceof AbstractBeccaEntity) { result.branch = result.branch.getPojo(); } } - if (result && result.executionResult) { // from runOnBackend() + if (result && typeof result === "object" && "executionResult" in result) { // from runOnBackend() result.executionResult = convertEntitiesToPojo(result.executionResult); } return result; } -function apiResultHandler(req, res, result) { +function apiResultHandler(req: express.Request, res: express.Response, result: unknown) { res.setHeader('trilium-max-entity-change-id', entityChangesService.getMaxEntityChangeId()); result = convertEntitiesToPojo(result); @@ -404,7 +411,7 @@ function apiResultHandler(req, res, result) { } } -function send(res, statusCode, response) { +function send(res: express.Response, statusCode: number, response: unknown) { if (typeof response === 'string') { if (statusCode >= 400) { res.setHeader("Content-Type", "text/plain"); @@ -424,12 +431,12 @@ function send(res, statusCode, response) { } } -function apiRoute(method, path, routeHandler) { +function apiRoute(method: HttpMethod, path: string, routeHandler: express.Handler) { route(method, path, [auth.checkApiAuth, csrfMiddleware], routeHandler, apiResultHandler); } -function route(method, path, middleware, routeHandler, resultHandler = null, transactional = true) { - router[method](path, ...middleware, (req, res, next) => { +function route(method: HttpMethod, path: string, middleware: (express.Handler | AppRequestHandler)[], routeHandler: AppRequestHandler, resultHandler: ApiResultHandler | null = null, transactional = true) { + router[method](path, ...(middleware as express.Handler[]), (req: express.Request, res: express.Response, next: express.NextFunction) => { const start = Date.now(); try { @@ -441,7 +448,7 @@ function route(method, path, middleware, routeHandler, resultHandler = null, tra cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']); cls.set('hoistedNoteId', req.headers['trilium-hoisted-note-id'] || 'root'); - const cb = () => routeHandler(req, res, next); + const cb = () => routeHandler(req as AppRequest, res, next); return transactional ? sql.transactional(cb) : cb(); }); @@ -452,8 +459,8 @@ function route(method, path, middleware, routeHandler, resultHandler = null, tra if (result?.then) { // promise result - .then(promiseResult => handleResponse(resultHandler, req, res, promiseResult, start)) - .catch(e => handleException(e, method, path, res)); + .then((promiseResult: unknown) => handleResponse(resultHandler, req, res, promiseResult, start)) + .catch((e: any) => handleException(e, method, path, res)); } else { handleResponse(resultHandler, req, res, result, start) } @@ -464,13 +471,13 @@ function route(method, path, middleware, routeHandler, resultHandler = null, tra }); } -function handleResponse(resultHandler, req, res, result, start) { +function handleResponse(resultHandler: ApiResultHandler, req: express.Request, res: express.Response, result: unknown, start: number) { const responseLength = resultHandler(req, res, result); log.request(req, res, Date.now() - start, responseLength); } -function handleException(e, method, path, res) { +function handleException(e: any, method: HttpMethod, path: string, res: express.Response) { log.error(`${method} ${path} threw exception: '${e.message}', stack: ${e.stack}`); if (e instanceof ValidationError) { @@ -492,8 +499,8 @@ function handleException(e, method, path, res) { } function createUploadMiddleware() { - const multerOptions = { - fileFilter: (req, file, cb) => { + const multerOptions: multer.Options = { + fileFilter: (req: express.Request, file, cb) => { // UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side. // See https://github.com/expressjs/multer/pull/1102. file.originalname = Buffer.from(file.originalname, "latin1").toString("utf-8"); @@ -510,6 +517,6 @@ function createUploadMiddleware() { return multer(multerOptions).single('upload'); } -module.exports = { +export = { register }; diff --git a/src/services/log.ts b/src/services/log.ts index 742fdcdb1..1e8d0b9e1 100644 --- a/src/services/log.ts +++ b/src/services/log.ts @@ -76,7 +76,7 @@ function error(message: string) { const requestBlacklist = [ "/libraries", "/app", "/images", "/stylesheets", "/api/recent-notes" ]; -function request(req: Request, res: Response, timeMs: number, responseLength = "?") { +function request(req: Request, res: Response, timeMs: number, responseLength: number | string = "?") { for (const bl of requestBlacklist) { if (req.url.startsWith(bl)) { return; diff --git a/src/services/window.ts b/src/services/window.ts index 80f7f7e83..a901d79a8 100644 --- a/src/services/window.ts +++ b/src/services/window.ts @@ -81,7 +81,7 @@ async function createMainWindow(app: App) { app.on('second-instance', () => { // Someone tried to run a second instance, we should focus our window. - // see www.js "requestSingleInstanceLock" for the rest of this logic with explanation + // see www.ts "requestSingleInstanceLock" for the rest of this logic with explanation if (mainWindow) { if (mainWindow.isMinimized()) { mainWindow.restore(); diff --git a/src/www.js b/src/www.ts similarity index 78% rename from src/www.js rename to src/www.ts index 2059a3a7c..3f9aa752e 100644 --- a/src/www.js +++ b/src/www.ts @@ -18,19 +18,19 @@ function exit() { process.on('SIGINT', exit); process.on('SIGTERM', exit); -const app = require('./app'); -const sessionParser = require('./routes/session_parser'); -const fs = require('fs'); -const http = require('http'); -const https = require('https'); -const config = require('./services/config'); -const log = require('./services/log'); -const appInfo = require('./services/app_info'); -const ws = require('./services/ws'); -const utils = require('./services/utils'); -const port = require('./services/port'); -const host = require('./services/host'); -const semver = require('semver'); +import app = require('./app'); +import sessionParser = require('./routes/session_parser'); +import fs = require('fs'); +import http = require('http'); +import https = require('https'); +import config = require('./services/config'); +import log = require('./services/log'); +import appInfo = require('./services/app_info'); +import ws = require('./services/ws'); +import utils = require('./services/utils'); +import port = require('./services/port'); +import host = require('./services/host'); +import semver = require('semver'); if (!semver.satisfies(process.version, ">=10.5.0")) { console.error("Trilium only supports node.js 10.5 and later"); @@ -66,7 +66,7 @@ function startTrilium() { const httpServer = startHttpServer(); - ws.init(httpServer, sessionParser); + ws.init(httpServer, sessionParser as any); // TODO: Not sure why session parser is incompatible. if (utils.isElectron()) { const electronRouting = require('./routes/electron'); @@ -126,25 +126,23 @@ function startHttpServer() { } httpServer.on('error', error => { - if (!listenOnTcp || error.syscall !== 'listen') { + if (!listenOnTcp || ("syscall" in error && error.syscall !== 'listen')) { throw error; } // handle specific listen errors with friendly messages - switch (error.code) { - case 'EACCES': - console.error(`Port ${port} requires elevated privileges. It's recommended to use port above 1024.`); - process.exit(1); - break; - - case 'EADDRINUSE': - console.error(`Port ${port} is already in use. Most likely, another Trilium process is already running. You might try to find it, kill it, and try again.`); - process.exit(1); - break; - - default: - throw error; + if ("code" in error) { + switch (error.code) { + case 'EACCES': + console.error(`Port ${port} requires elevated privileges. It's recommended to use port above 1024.`); + process.exit(1); + case 'EADDRINUSE': + console.error(`Port ${port} is already in use. Most likely, another Trilium process is already running. You might try to find it, kill it, and try again.`); + process.exit(1); + } } + + throw error; } )