mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
Merge pull request #43 from TriliumNext/feature/typescript_backend_7
Convert backend to TypeScript (71% -> 80%)
This commit is contained in:
commit
98d12901a5
@ -161,6 +161,14 @@ export default class Becca {
|
||||
return row ? new BRevision(row) : null;
|
||||
}
|
||||
|
||||
getRevisionOrThrow(revisionId: string): BRevision {
|
||||
const revision = this.getRevision(revisionId);
|
||||
if (!revision) {
|
||||
throw new NotFoundError(`Revision '${revisionId}' has not been found.`);
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
|
||||
getAttachment(attachmentId: string, opts: AttachmentOpts = {}): BAttachment | null {
|
||||
opts.includeContentLength = !!opts.includeContentLength;
|
||||
|
||||
@ -246,7 +254,7 @@ export default class Becca {
|
||||
return rows.map(row => new BRecentNote(row));
|
||||
}
|
||||
|
||||
getRevisionsFromQuery(query: string, params = []): BRevision[] {
|
||||
getRevisionsFromQuery(query: string, params: string[] = []): BRevision[] {
|
||||
const rows = sql.getRows<RevisionRow>(query, params);
|
||||
|
||||
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
|
||||
@ -288,4 +296,18 @@ export interface ConstructorData<T extends AbstractBeccaEntity<T>> {
|
||||
primaryKeyName: string;
|
||||
entityName: string;
|
||||
hashedProperties: (keyof T)[];
|
||||
}
|
||||
|
||||
export interface NotePojo {
|
||||
noteId: string;
|
||||
title?: string;
|
||||
isProtected?: boolean;
|
||||
type: string;
|
||||
mime: string;
|
||||
blobId?: string;
|
||||
isDeleted: boolean;
|
||||
dateCreated?: string;
|
||||
dateModified?: string;
|
||||
utcDateCreated: string;
|
||||
utcDateModified?: string;
|
||||
}
|
@ -26,8 +26,8 @@ interface ContentOpts {
|
||||
abstract class AbstractBeccaEntity<T extends AbstractBeccaEntity<T>> {
|
||||
|
||||
utcDateModified?: string;
|
||||
protected dateCreated?: string;
|
||||
protected dateModified?: string;
|
||||
dateCreated?: string;
|
||||
dateModified?: string;
|
||||
|
||||
utcDateCreated!: string;
|
||||
|
||||
|
@ -15,6 +15,7 @@ import eventService = require('../../services/events');
|
||||
import { AttachmentRow, NoteRow, NoteType, RevisionRow } from './rows';
|
||||
import BBranch = require('./bbranch');
|
||||
import BAttribute = require('./battribute');
|
||||
import { NotePojo } from '../becca-interface';
|
||||
dayjs.extend(utc);
|
||||
|
||||
const LABEL = 'label';
|
||||
@ -222,7 +223,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
||||
|
||||
/**
|
||||
* @throws Error in case of invalid JSON */
|
||||
getJsonContent(): {} | null {
|
||||
getJsonContent(): any | null {
|
||||
const content = this.getContent();
|
||||
|
||||
if (typeof content !== "string" || !content || !content.trim()) {
|
||||
@ -1679,7 +1680,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
||||
this.utcDateModified = dateUtils.utcNowDateTime();
|
||||
}
|
||||
|
||||
getPojo() {
|
||||
getPojo(): NotePojo {
|
||||
return {
|
||||
noteId: this.noteId,
|
||||
title: this.title || undefined,
|
||||
|
@ -40,7 +40,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
|
||||
utcDateLastEdited?: string;
|
||||
utcDateCreated!: string;
|
||||
contentLength?: number;
|
||||
content?: string;
|
||||
content?: string | Buffer;
|
||||
|
||||
constructor(row: RevisionRow, titleDecrypted = false) {
|
||||
super();
|
||||
@ -91,9 +91,8 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
|
||||
*
|
||||
* This is the same approach as is used for Note's content.
|
||||
*/
|
||||
// TODO: initial declaration included Buffer, but everywhere it's treated as a string.
|
||||
getContent(): string {
|
||||
return this._getContent() as string;
|
||||
getContent(): string | Buffer {
|
||||
return this._getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +100,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
|
||||
getJsonContent(): {} | null {
|
||||
const content = this.getContent();
|
||||
|
||||
if (!content || !content.trim()) {
|
||||
if (!content || typeof content !== "string" || !content.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1,27 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const imageService = require('../../services/image');
|
||||
const becca = require('../../becca/becca');
|
||||
import imageService = require('../../services/image');
|
||||
import becca = require('../../becca/becca');
|
||||
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
|
||||
const fs = require('fs');
|
||||
import fs = require('fs');
|
||||
import { Request, Response } from 'express';
|
||||
import BNote = require('../../becca/entities/bnote');
|
||||
import BRevision = require('../../becca/entities/brevision');
|
||||
|
||||
function returnImageFromNote(req, res) {
|
||||
function returnImageFromNote(req: Request, res: Response) {
|
||||
const image = becca.getNote(req.params.noteId);
|
||||
|
||||
return returnImageInt(image, res);
|
||||
}
|
||||
|
||||
function returnImageFromRevision(req, res) {
|
||||
function returnImageFromRevision(req: Request, res: Response) {
|
||||
const image = becca.getRevision(req.params.revisionId);
|
||||
|
||||
return returnImageInt(image, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BNote|BRevision} image
|
||||
* @param res
|
||||
*/
|
||||
function returnImageInt(image, res) {
|
||||
function returnImageInt(image: BNote | BRevision | null, res: Response) {
|
||||
if (!image) {
|
||||
res.set('Content-Type', 'image/png');
|
||||
return res.send(fs.readFileSync(`${RESOURCE_DIR}/db/image-deleted.png`));
|
||||
@ -40,12 +39,13 @@ function returnImageInt(image, res) {
|
||||
}
|
||||
}
|
||||
|
||||
function renderSvgAttachment(image, res, attachmentName) {
|
||||
function renderSvgAttachment(image: BNote | BRevision, res: Response, attachmentName: string) {
|
||||
let svgString = '<svg/>'
|
||||
const attachment = image.getAttachmentByTitle(attachmentName);
|
||||
|
||||
if (attachment) {
|
||||
svgString = attachment.getContent();
|
||||
const content = attachment.getContent();
|
||||
if (attachment && typeof content === "string") {
|
||||
svgString = content;
|
||||
} else {
|
||||
// backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
|
||||
const contentSvg = image.getJsonContentSafely()?.svg;
|
||||
@ -62,7 +62,7 @@ function renderSvgAttachment(image, res, attachmentName) {
|
||||
}
|
||||
|
||||
|
||||
function returnAttachedImage(req, res) {
|
||||
function returnAttachedImage(req: Request, res: Response) {
|
||||
const attachment = becca.getAttachment(req.params.attachmentId);
|
||||
|
||||
if (!attachment) {
|
||||
@ -81,9 +81,9 @@ function returnAttachedImage(req, res) {
|
||||
res.send(attachment.getContent());
|
||||
}
|
||||
|
||||
function updateImage(req) {
|
||||
function updateImage(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
const {file} = req;
|
||||
const {file} = (req as any);
|
||||
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
@ -99,7 +99,7 @@ function updateImage(req) {
|
||||
return { uploaded: true };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
returnImageFromNote,
|
||||
returnImageFromRevision,
|
||||
returnAttachedImage,
|
@ -1,18 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
const enexImportService = require('../../services/import/enex');
|
||||
const opmlImportService = require('../../services/import/opml');
|
||||
const zipImportService = require('../../services/import/zip');
|
||||
const singleImportService = require('../../services/import/single');
|
||||
const cls = require('../../services/cls');
|
||||
const path = require('path');
|
||||
const becca = require('../../becca/becca');
|
||||
const beccaLoader = require('../../becca/becca_loader');
|
||||
const log = require('../../services/log');
|
||||
const TaskContext = require('../../services/task_context');
|
||||
const ValidationError = require('../../errors/validation_error');
|
||||
import enexImportService = require('../../services/import/enex');
|
||||
import opmlImportService = require('../../services/import/opml');
|
||||
import zipImportService = require('../../services/import/zip');
|
||||
import singleImportService = require('../../services/import/single');
|
||||
import cls = require('../../services/cls');
|
||||
import path = require('path');
|
||||
import becca = require('../../becca/becca');
|
||||
import beccaLoader = require('../../becca/becca_loader');
|
||||
import log = require('../../services/log');
|
||||
import TaskContext = require('../../services/task_context');
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
import { Request } from 'express';
|
||||
import BNote = require('../../becca/entities/bnote');
|
||||
|
||||
async function importNotesToBranch(req) {
|
||||
async function importNotesToBranch(req: Request) {
|
||||
const { parentNoteId } = req.params;
|
||||
const { taskId, last } = req.body;
|
||||
|
||||
@ -25,7 +27,7 @@ async function importNotesToBranch(req) {
|
||||
replaceUnderscoresWithSpaces: req.body.replaceUnderscoresWithSpaces !== 'false'
|
||||
};
|
||||
|
||||
const file = req.file;
|
||||
const file = (req as any).file;
|
||||
|
||||
if (!file) {
|
||||
throw new ValidationError("No file has been uploaded");
|
||||
@ -42,7 +44,7 @@ async function importNotesToBranch(req) {
|
||||
// eliminate flickering during import
|
||||
cls.ignoreEntityChangeIds();
|
||||
|
||||
let note; // typically root of the import - client can show it after finishing the import
|
||||
let note: BNote | null; // typically root of the import - client can show it after finishing the import
|
||||
|
||||
const taskContext = TaskContext.getInstance(taskId, 'importNotes', options);
|
||||
|
||||
@ -50,14 +52,24 @@ async function importNotesToBranch(req) {
|
||||
if (extension === '.zip' && options.explodeArchives) {
|
||||
note = await zipImportService.importZip(taskContext, file.buffer, parentNote);
|
||||
} else if (extension === '.opml' && options.explodeArchives) {
|
||||
note = await opmlImportService.importOpml(taskContext, file.buffer, parentNote);
|
||||
const importResult = await opmlImportService.importOpml(taskContext, file.buffer, parentNote);
|
||||
if (!Array.isArray(importResult)) {
|
||||
note = importResult;
|
||||
} else {
|
||||
return importResult;
|
||||
}
|
||||
} else if (extension === '.enex' && options.explodeArchives) {
|
||||
note = await enexImportService.importEnex(taskContext, file, parentNote);
|
||||
const importResult = await enexImportService.importEnex(taskContext, file, parentNote);
|
||||
if (!Array.isArray(importResult)) {
|
||||
note = importResult;
|
||||
} else {
|
||||
return importResult;
|
||||
}
|
||||
} else {
|
||||
note = await singleImportService.importSingleFile(taskContext, file, parentNote);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`;
|
||||
taskContext.reportError(message);
|
||||
|
||||
@ -66,11 +78,15 @@ async function importNotesToBranch(req) {
|
||||
return [500, message];
|
||||
}
|
||||
|
||||
if (!note) {
|
||||
return [500, "No note was generated as a result of the import."];
|
||||
}
|
||||
|
||||
if (last === "true") {
|
||||
// small timeout to avoid race condition (the message is received before the transaction is committed)
|
||||
setTimeout(() => taskContext.taskSucceeded({
|
||||
parentNoteId: parentNoteId,
|
||||
importedNoteId: note.noteId
|
||||
importedNoteId: note?.noteId
|
||||
}), 1000);
|
||||
}
|
||||
|
||||
@ -80,7 +96,7 @@ async function importNotesToBranch(req) {
|
||||
return note.getPojo();
|
||||
}
|
||||
|
||||
async function importAttachmentsToNote(req) {
|
||||
async function importAttachmentsToNote(req: Request) {
|
||||
const { parentNoteId } = req.params;
|
||||
const { taskId, last } = req.body;
|
||||
|
||||
@ -88,7 +104,7 @@ async function importAttachmentsToNote(req) {
|
||||
shrinkImages: req.body.shrinkImages !== 'false',
|
||||
};
|
||||
|
||||
const file = req.file;
|
||||
const file = (req as any).file;
|
||||
|
||||
if (!file) {
|
||||
throw new ValidationError("No file has been uploaded");
|
||||
@ -102,7 +118,7 @@ async function importAttachmentsToNote(req) {
|
||||
try {
|
||||
await singleImportService.importAttachment(taskContext, file, parentNote);
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`;
|
||||
taskContext.reportError(message);
|
||||
|
||||
@ -119,7 +135,7 @@ async function importAttachmentsToNote(req) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
importNotesToBranch,
|
||||
importAttachmentsToNote
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const keyboardActions = require('../../services/keyboard_actions');
|
||||
const becca = require('../../becca/becca');
|
||||
import keyboardActions = require('../../services/keyboard_actions');
|
||||
import becca = require('../../becca/becca');
|
||||
|
||||
function getKeyboardActions() {
|
||||
return keyboardActions.getKeyboardActions();
|
||||
@ -14,7 +14,7 @@ function getShortcutsForNotes() {
|
||||
return labels.filter(attr => becca.getNote(attr.noteId)?.type !== 'launcher');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getKeyboardActions,
|
||||
getShortcutsForNotes
|
||||
};
|
@ -1,19 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
const options = require('../../services/options');
|
||||
const utils = require('../../services/utils');
|
||||
const dateUtils = require('../../services/date_utils');
|
||||
const instanceId = require('../../services/instance_id');
|
||||
const passwordEncryptionService = require('../../services/encryption/password_encryption');
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const appInfo = require('../../services/app_info');
|
||||
const eventService = require('../../services/events');
|
||||
const sqlInit = require('../../services/sql_init');
|
||||
const sql = require('../../services/sql');
|
||||
const ws = require('../../services/ws');
|
||||
const etapiTokenService = require('../../services/etapi_tokens');
|
||||
import options = require('../../services/options');
|
||||
import utils = require('../../services/utils');
|
||||
import dateUtils = require('../../services/date_utils');
|
||||
import instanceId = require('../../services/instance_id');
|
||||
import passwordEncryptionService = require('../../services/encryption/password_encryption');
|
||||
import protectedSessionService = require('../../services/protected_session');
|
||||
import appInfo = require('../../services/app_info');
|
||||
import eventService = require('../../services/events');
|
||||
import sqlInit = require('../../services/sql_init');
|
||||
import sql = require('../../services/sql');
|
||||
import ws = require('../../services/ws');
|
||||
import etapiTokenService = require('../../services/etapi_tokens');
|
||||
import { Request } from 'express';
|
||||
|
||||
function loginSync(req) {
|
||||
function loginSync(req: Request) {
|
||||
if (!sqlInit.schemaExists()) {
|
||||
return [500, { message: "DB schema does not exist, can't sync." }];
|
||||
}
|
||||
@ -44,7 +45,7 @@ function loginSync(req) {
|
||||
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.session.loggedIn = true;
|
||||
(req as any).session.loggedIn = true;
|
||||
|
||||
return {
|
||||
instanceId: instanceId,
|
||||
@ -52,7 +53,7 @@ function loginSync(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function loginToProtectedSession(req) {
|
||||
function loginToProtectedSession(req: Request) {
|
||||
const password = req.body.password;
|
||||
|
||||
if (!passwordEncryptionService.verifyPassword(password)) {
|
||||
@ -63,6 +64,12 @@ function loginToProtectedSession(req) {
|
||||
}
|
||||
|
||||
const decryptedDataKey = passwordEncryptionService.getDataKey(password);
|
||||
if (!decryptedDataKey) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Unable to obtain data key."
|
||||
}
|
||||
}
|
||||
|
||||
protectedSessionService.setDataKey(decryptedDataKey);
|
||||
|
||||
@ -87,7 +94,7 @@ function touchProtectedSession() {
|
||||
protectedSessionService.touchProtectedSession();
|
||||
}
|
||||
|
||||
function token(req) {
|
||||
function token(req: Request) {
|
||||
const password = req.body.password;
|
||||
|
||||
if (!passwordEncryptionService.verifyPassword(password)) {
|
||||
@ -102,7 +109,7 @@ function token(req) {
|
||||
return { token: authToken };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
loginSync,
|
||||
loginToProtectedSession,
|
||||
logoutFromProtectedSession,
|
@ -1,18 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
const becca = require('../../becca/becca');
|
||||
const { JSDOM } = require("jsdom");
|
||||
import becca = require('../../becca/becca');
|
||||
import { JSDOM } from "jsdom";
|
||||
import BNote = require('../../becca/entities/bnote');
|
||||
import BAttribute = require('../../becca/entities/battribute');
|
||||
import { Request } from 'express';
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
|
||||
function buildDescendantCountMap(noteIdsToCount) {
|
||||
function buildDescendantCountMap(noteIdsToCount: string[]) {
|
||||
if (!Array.isArray(noteIdsToCount)) {
|
||||
throw new Error('noteIdsToCount: type error');
|
||||
}
|
||||
|
||||
const noteIdToCountMap = Object.create(null);
|
||||
|
||||
function getCount(noteId) {
|
||||
function getCount(noteId: string) {
|
||||
if (!(noteId in noteIdToCountMap)) {
|
||||
const note = becca.getNote(noteId);
|
||||
if (!note) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hiddenImageNoteIds = note.getRelations('imageLink').map(rel => rel.value);
|
||||
const childNoteIds = note.children.map(child => child.noteId);
|
||||
@ -33,19 +40,14 @@ function buildDescendantCountMap(noteIdsToCount) {
|
||||
|
||||
return noteIdToCountMap;
|
||||
}
|
||||
/**
|
||||
* @param {BNote} note
|
||||
* @param {int} depth
|
||||
* @returns {string[]} noteIds
|
||||
*/
|
||||
function getNeighbors(note, depth) {
|
||||
function getNeighbors(note: BNote, depth: number): string[] {
|
||||
if (depth === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const retNoteIds = [];
|
||||
|
||||
function isIgnoredRelation(relation) {
|
||||
function isIgnoredRelation(relation: BAttribute) {
|
||||
return ['relationMapLink', 'template', 'inherit', 'image', 'ancestor'].includes(relation.name);
|
||||
}
|
||||
|
||||
@ -90,8 +92,9 @@ function getNeighbors(note, depth) {
|
||||
return retNoteIds;
|
||||
}
|
||||
|
||||
function getLinkMap(req) {
|
||||
const mapRootNote = becca.getNote(req.params.noteId);
|
||||
function getLinkMap(req: Request) {
|
||||
const mapRootNote = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
// if the map root itself has "excludeFromNoteMap" attribute (journal typically) then there wouldn't be anything
|
||||
// to display, so we'll just ignore it
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap');
|
||||
@ -125,7 +128,7 @@ function getLinkMap(req) {
|
||||
const noteIdsArray = Array.from(noteIds)
|
||||
|
||||
const notes = noteIdsArray.map(noteId => {
|
||||
const note = becca.getNote(noteId);
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
return [
|
||||
note.noteId,
|
||||
@ -144,6 +147,9 @@ function getLinkMap(req) {
|
||||
}
|
||||
else if (rel.name === 'imageLink') {
|
||||
const parentNote = becca.getNote(rel.noteId);
|
||||
if (!parentNote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !parentNote.getChildNotes().find(childNote => childNote.noteId === rel.value);
|
||||
}
|
||||
@ -165,8 +171,8 @@ function getLinkMap(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function getTreeMap(req) {
|
||||
const mapRootNote = becca.getNote(req.params.noteId);
|
||||
function getTreeMap(req: Request) {
|
||||
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
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy('excludeFromNoteMap');
|
||||
@ -198,8 +204,8 @@ function getTreeMap(req) {
|
||||
note.getLabelValue('color')
|
||||
]);
|
||||
|
||||
const noteIds = new Set();
|
||||
notes.forEach(([noteId]) => noteIds.add(noteId));
|
||||
const noteIds = new Set<string>();
|
||||
notes.forEach(([noteId]) => noteId && noteIds.add(noteId));
|
||||
|
||||
const links = [];
|
||||
|
||||
@ -225,7 +231,7 @@ function getTreeMap(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function updateDescendantCountMapForSearch(noteIdToDescendantCountMap, relationships) {
|
||||
function updateDescendantCountMapForSearch(noteIdToDescendantCountMap: Record<string, number>, relationships: { parentNoteId: string, childNoteId: string }[]) {
|
||||
for (const {parentNoteId, childNoteId} of relationships) {
|
||||
const parentNote = becca.notes[parentNoteId];
|
||||
if (!parentNote || parentNote.type !== 'search') {
|
||||
@ -237,16 +243,17 @@ function updateDescendantCountMapForSearch(noteIdToDescendantCountMap, relations
|
||||
}
|
||||
}
|
||||
|
||||
function removeImages(document) {
|
||||
function removeImages(document: Document) {
|
||||
const images = document.getElementsByTagName('img');
|
||||
while (images.length > 0) {
|
||||
images[0].parentNode.removeChild(images[0]);
|
||||
while (images && images.length > 0) {
|
||||
images[0]?.parentNode?.removeChild(images[0]);
|
||||
}
|
||||
}
|
||||
|
||||
const EXCERPT_CHAR_LIMIT = 200;
|
||||
type ElementOrText = (Element | Text);
|
||||
|
||||
function findExcerpts(sourceNote, referencedNoteId) {
|
||||
function findExcerpts(sourceNote: BNote, referencedNoteId: string) {
|
||||
const html = sourceNote.getContent();
|
||||
const document = new JSDOM(html).window.document;
|
||||
|
||||
@ -263,25 +270,24 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
||||
|
||||
linkEl.classList.add("backlink-link");
|
||||
|
||||
let centerEl = linkEl;
|
||||
let centerEl: HTMLElement = linkEl;
|
||||
|
||||
while (centerEl.tagName !== 'BODY' && centerEl.parentElement?.textContent?.length <= EXCERPT_CHAR_LIMIT) {
|
||||
while (centerEl.tagName !== 'BODY' && centerEl.parentElement && (centerEl.parentElement?.textContent?.length || 0) <= EXCERPT_CHAR_LIMIT) {
|
||||
centerEl = centerEl.parentElement;
|
||||
}
|
||||
|
||||
/** @var {HTMLElement[]} */
|
||||
const excerptEls = [centerEl];
|
||||
let excerptLength = centerEl.textContent.length;
|
||||
let left = centerEl;
|
||||
let right = centerEl;
|
||||
const excerptEls: ElementOrText[] = [centerEl];
|
||||
let excerptLength = centerEl.textContent?.length || 0;
|
||||
let left: ElementOrText = centerEl;
|
||||
let right: ElementOrText = centerEl;
|
||||
|
||||
while (excerptLength < EXCERPT_CHAR_LIMIT) {
|
||||
let added = false;
|
||||
|
||||
const prev = left.previousElementSibling;
|
||||
const prev: Element | null = left.previousElementSibling;
|
||||
|
||||
if (prev) {
|
||||
const prevText = prev.textContent;
|
||||
const prevText = prev.textContent || "";
|
||||
|
||||
if (prevText.length + excerptLength > EXCERPT_CHAR_LIMIT) {
|
||||
const prefix = prevText.substr(prevText.length - (EXCERPT_CHAR_LIMIT - excerptLength));
|
||||
@ -298,12 +304,12 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
||||
added = true;
|
||||
}
|
||||
|
||||
const next = right.nextElementSibling;
|
||||
const next: Element | null = right.nextElementSibling;
|
||||
|
||||
if (next) {
|
||||
const nextText = next.textContent;
|
||||
|
||||
if (nextText.length + excerptLength > EXCERPT_CHAR_LIMIT) {
|
||||
if (nextText && nextText.length + excerptLength > EXCERPT_CHAR_LIMIT) {
|
||||
const suffix = nextText.substr(nextText.length - (EXCERPT_CHAR_LIMIT - excerptLength));
|
||||
|
||||
const textNode = document.createTextNode(`${suffix}…`);
|
||||
@ -314,7 +320,7 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
||||
|
||||
right = next;
|
||||
excerptEls.push(right);
|
||||
excerptLength += nextText.length;
|
||||
excerptLength += nextText?.length || 0;
|
||||
added = true;
|
||||
}
|
||||
|
||||
@ -336,13 +342,13 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
||||
return excerpts;
|
||||
}
|
||||
|
||||
function getFilteredBacklinks(note) {
|
||||
function getFilteredBacklinks(note: BNote) {
|
||||
return note.getTargetRelations()
|
||||
// search notes have "ancestor" relations which are not interesting
|
||||
.filter(relation => !!relation.getNote() && relation.getNote().type !== 'search');
|
||||
}
|
||||
|
||||
function getBacklinkCount(req) {
|
||||
function getBacklinkCount(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
@ -352,7 +358,7 @@ function getBacklinkCount(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function getBacklinks(req) {
|
||||
function getBacklinks(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
@ -379,7 +385,7 @@ function getBacklinks(req) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getLinkMap,
|
||||
getTreeMap,
|
||||
getBacklinkCount,
|
@ -1,25 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
const noteService = require('../../services/notes');
|
||||
const eraseService = require('../../services/erase');
|
||||
const treeService = require('../../services/tree');
|
||||
const sql = require('../../services/sql');
|
||||
const utils = require('../../services/utils');
|
||||
const log = require('../../services/log');
|
||||
const TaskContext = require('../../services/task_context');
|
||||
const becca = require('../../becca/becca');
|
||||
const ValidationError = require('../../errors/validation_error');
|
||||
const blobService = require('../../services/blob');
|
||||
import noteService = require('../../services/notes');
|
||||
import eraseService = require('../../services/erase');
|
||||
import treeService = require('../../services/tree');
|
||||
import sql = require('../../services/sql');
|
||||
import utils = require('../../services/utils');
|
||||
import log = require('../../services/log');
|
||||
import TaskContext = require('../../services/task_context');
|
||||
import becca = require('../../becca/becca');
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
import blobService = require('../../services/blob');
|
||||
import { Request } from 'express';
|
||||
import BBranch = require('../../becca/entities/bbranch');
|
||||
import { AttributeRow } from '../../becca/entities/rows';
|
||||
|
||||
function getNote(req) {
|
||||
function getNote(req: Request) {
|
||||
return becca.getNoteOrThrow(req.params.noteId);
|
||||
}
|
||||
|
||||
function getNoteBlob(req) {
|
||||
function getNoteBlob(req: Request) {
|
||||
return blobService.getBlobPojo('notes', req.params.noteId);
|
||||
}
|
||||
|
||||
function getNoteMetadata(req) {
|
||||
function getNoteMetadata(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
return {
|
||||
@ -30,12 +33,20 @@ function getNoteMetadata(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function createNote(req) {
|
||||
function createNote(req: Request) {
|
||||
const params = Object.assign({}, req.body); // clone
|
||||
params.parentNoteId = req.params.parentNoteId;
|
||||
|
||||
const { target, targetBranchId } = req.query;
|
||||
|
||||
if (target !== "into" && target !== "after") {
|
||||
throw new ValidationError("Invalid target type.");
|
||||
}
|
||||
|
||||
if (targetBranchId && typeof targetBranchId !== "string") {
|
||||
throw new ValidationError("Missing or incorrect type for target branch ID.");
|
||||
}
|
||||
|
||||
const { note, branch } = noteService.createNewNoteWithTarget(target, targetBranchId, params);
|
||||
|
||||
return {
|
||||
@ -44,14 +55,14 @@ function createNote(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function updateNoteData(req) {
|
||||
function updateNoteData(req: Request) {
|
||||
const {content, attachments} = req.body;
|
||||
const {noteId} = req.params;
|
||||
|
||||
return noteService.updateNoteData(noteId, content, attachments);
|
||||
}
|
||||
|
||||
function deleteNote(req) {
|
||||
function deleteNote(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
const taskId = req.query.taskId;
|
||||
const eraseNotes = req.query.eraseNotes === 'true';
|
||||
@ -60,8 +71,11 @@ function deleteNote(req) {
|
||||
// note how deleteId is separate from taskId - single taskId produces separate deleteId for each "top level" deleted note
|
||||
const deleteId = utils.randomString(10);
|
||||
|
||||
const note = becca.getNote(noteId);
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
if (typeof taskId !== "string") {
|
||||
throw new ValidationError("Missing or incorrect type for task ID.");
|
||||
}
|
||||
const taskContext = TaskContext.getInstance(taskId, 'deleteNotes');
|
||||
|
||||
note.deleteNote(deleteId, taskContext);
|
||||
@ -75,7 +89,7 @@ function deleteNote(req) {
|
||||
}
|
||||
}
|
||||
|
||||
function undeleteNote(req) {
|
||||
function undeleteNote(req: Request) {
|
||||
const taskContext = TaskContext.getInstance(utils.randomString(10), 'undeleteNotes');
|
||||
|
||||
noteService.undeleteNote(req.params.noteId, taskContext);
|
||||
@ -83,7 +97,7 @@ function undeleteNote(req) {
|
||||
taskContext.taskSucceeded();
|
||||
}
|
||||
|
||||
function sortChildNotes(req) {
|
||||
function sortChildNotes(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
const {sortBy, sortDirection, foldersFirst, sortNatural, sortLocale} = req.body;
|
||||
|
||||
@ -94,11 +108,11 @@ function sortChildNotes(req) {
|
||||
treeService.sortNotes(noteId, sortBy, reverse, foldersFirst, sortNatural, sortLocale);
|
||||
}
|
||||
|
||||
function protectNote(req) {
|
||||
function protectNote(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
const note = becca.notes[noteId];
|
||||
const protect = !!parseInt(req.params.isProtected);
|
||||
const includingSubTree = !!parseInt(req.query.subtree);
|
||||
const includingSubTree = !!parseInt(req.query?.subtree as string);
|
||||
|
||||
const taskContext = new TaskContext(utils.randomString(10), 'protectNotes', {protect});
|
||||
|
||||
@ -107,18 +121,18 @@ function protectNote(req) {
|
||||
taskContext.taskSucceeded();
|
||||
}
|
||||
|
||||
function setNoteTypeMime(req) {
|
||||
function setNoteTypeMime(req: Request) {
|
||||
// can't use [] destructuring because req.params is not iterable
|
||||
const {noteId} = req.params;
|
||||
const {type, mime} = req.body;
|
||||
|
||||
const note = becca.getNote(noteId);
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
note.type = type;
|
||||
note.mime = mime;
|
||||
note.save();
|
||||
}
|
||||
|
||||
function changeTitle(req) {
|
||||
function changeTitle(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
const title = req.body.title;
|
||||
|
||||
@ -145,7 +159,7 @@ function changeTitle(req) {
|
||||
return note;
|
||||
}
|
||||
|
||||
function duplicateSubtree(req) {
|
||||
function duplicateSubtree(req: Request) {
|
||||
const {noteId, parentNoteId} = req.params;
|
||||
|
||||
return noteService.duplicateSubtree(noteId, parentNoteId);
|
||||
@ -159,14 +173,14 @@ function eraseUnusedAttachmentsNow() {
|
||||
eraseService.eraseUnusedAttachmentsNow();
|
||||
}
|
||||
|
||||
function getDeleteNotesPreview(req) {
|
||||
function getDeleteNotesPreview(req: Request) {
|
||||
const {branchIdsToDelete, deleteAllClones} = req.body;
|
||||
|
||||
const noteIdsToBeDeleted = new Set();
|
||||
const strongBranchCountToDelete = {}; // noteId => count (integer)
|
||||
const noteIdsToBeDeleted = new Set<string>();
|
||||
const strongBranchCountToDelete: Record<string, number> = {}; // noteId => count
|
||||
|
||||
function branchPreviewDeletion(branch) {
|
||||
if (branch.isWeak) {
|
||||
function branchPreviewDeletion(branch: BBranch) {
|
||||
if (branch.isWeak || !branch.branchId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -196,18 +210,18 @@ function getDeleteNotesPreview(req) {
|
||||
branchPreviewDeletion(branch);
|
||||
}
|
||||
|
||||
let brokenRelations = [];
|
||||
let brokenRelations: AttributeRow[] = [];
|
||||
|
||||
if (noteIdsToBeDeleted.size > 0) {
|
||||
sql.fillParamList(noteIdsToBeDeleted);
|
||||
|
||||
// FIXME: No need to do this in database, can be done with becca data
|
||||
brokenRelations = sql.getRows(`
|
||||
brokenRelations = sql.getRows<AttributeRow>(`
|
||||
SELECT attr.noteId, attr.name, attr.value
|
||||
FROM attributes attr
|
||||
JOIN param_list ON param_list.paramId = attr.value
|
||||
WHERE attr.isDeleted = 0
|
||||
AND attr.type = 'relation'`).filter(attr => !noteIdsToBeDeleted.has(attr.noteId));
|
||||
AND attr.type = 'relation'`).filter(attr => attr.noteId && !noteIdsToBeDeleted.has(attr.noteId));
|
||||
}
|
||||
|
||||
return {
|
||||
@ -216,7 +230,7 @@ function getDeleteNotesPreview(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function forceSaveRevision(req) {
|
||||
function forceSaveRevision(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
@ -227,7 +241,7 @@ function forceSaveRevision(req) {
|
||||
note.saveRevision();
|
||||
}
|
||||
|
||||
function convertNoteToAttachment(req) {
|
||||
function convertNoteToAttachment(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
@ -236,7 +250,7 @@ function convertNoteToAttachment(req) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getNote,
|
||||
getNoteBlob,
|
||||
getNoteMetadata,
|
@ -1,9 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const optionService = require('../../services/options');
|
||||
const log = require('../../services/log');
|
||||
const searchService = require('../../services/search/services/search');
|
||||
const ValidationError = require('../../errors/validation_error');
|
||||
import optionService = require('../../services/options');
|
||||
import log = require('../../services/log');
|
||||
import searchService = require('../../services/search/services/search');
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
import { Request } from 'express';
|
||||
|
||||
// options allowed to be updated directly in the Options dialog
|
||||
const ALLOWED_OPTIONS = new Set([
|
||||
@ -62,7 +63,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
|
||||
function getOptions() {
|
||||
const optionMap = optionService.getOptionMap();
|
||||
const resultMap = {};
|
||||
const resultMap: Record<string, string> = {};
|
||||
|
||||
for (const optionName in optionMap) {
|
||||
if (isAllowed(optionName)) {
|
||||
@ -75,7 +76,7 @@ function getOptions() {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
function updateOption(req) {
|
||||
function updateOption(req: Request) {
|
||||
const {name, value} = req.params;
|
||||
|
||||
if (!update(name, value)) {
|
||||
@ -83,7 +84,7 @@ function updateOption(req) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateOptions(req) {
|
||||
function updateOptions(req: Request) {
|
||||
for (const optionName in req.body) {
|
||||
if (!update(optionName, req.body[optionName])) {
|
||||
// this should be improved
|
||||
@ -93,7 +94,7 @@ function updateOptions(req) {
|
||||
}
|
||||
}
|
||||
|
||||
function update(name, value) {
|
||||
function update(name: string, value: string) {
|
||||
if (!isAllowed(name)) {
|
||||
return false;
|
||||
}
|
||||
@ -128,14 +129,14 @@ function getUserThemes() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function isAllowed(name) {
|
||||
function isAllowed(name: string) {
|
||||
return ALLOWED_OPTIONS.has(name)
|
||||
|| name.startsWith("keyboardShortcuts")
|
||||
|| name.endsWith("Collapsed")
|
||||
|| name.startsWith("hideArchivedNotes");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getOptions,
|
||||
updateOption,
|
||||
updateOptions,
|
@ -1,8 +1,10 @@
|
||||
const becca = require('../../becca/becca');
|
||||
const markdownService = require('../../services/import/markdown');
|
||||
import { Request } from "express";
|
||||
|
||||
import becca = require('../../becca/becca');
|
||||
import markdownService = require('../../services/import/markdown');
|
||||
|
||||
function getIconUsage() {
|
||||
const iconClassToCountMap = {};
|
||||
const iconClassToCountMap: Record<string, number> = {};
|
||||
|
||||
for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) {
|
||||
if (noteId.startsWith("_")) {
|
||||
@ -25,7 +27,7 @@ function getIconUsage() {
|
||||
return { iconClassToCountMap };
|
||||
}
|
||||
|
||||
function renderMarkdown(req) {
|
||||
function renderMarkdown(req: Request) {
|
||||
const { markdownContent } = req.body;
|
||||
|
||||
return {
|
||||
@ -33,7 +35,7 @@ function renderMarkdown(req) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getIconUsage,
|
||||
renderMarkdown
|
||||
};
|
@ -1,9 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const passwordService = require('../../services/encryption/password');
|
||||
const ValidationError = require('../../errors/validation_error');
|
||||
import passwordService = require('../../services/encryption/password');
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
import { Request } from 'express';
|
||||
|
||||
function changePassword(req) {
|
||||
function changePassword(req: Request) {
|
||||
if (passwordService.isPasswordSet()) {
|
||||
return passwordService.changePassword(req.body.current_password, req.body.new_password);
|
||||
}
|
||||
@ -12,7 +13,7 @@ function changePassword(req) {
|
||||
}
|
||||
}
|
||||
|
||||
function resetPassword(req) {
|
||||
function resetPassword(req: Request) {
|
||||
// protection against accidental call (not a security measure)
|
||||
if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") {
|
||||
throw new ValidationError("Incorrect password reset confirmation");
|
||||
@ -21,7 +22,7 @@ function resetPassword(req) {
|
||||
return passwordService.resetPassword();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
changePassword,
|
||||
resetPassword
|
||||
};
|
@ -1,16 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
const sql = require('../../services/sql');
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const noteService = require('../../services/notes');
|
||||
const becca = require('../../becca/becca');
|
||||
import sql = require('../../services/sql');
|
||||
import protectedSessionService = require('../../services/protected_session');
|
||||
import noteService = require('../../services/notes');
|
||||
import becca = require('../../becca/becca');
|
||||
import { Request } from 'express';
|
||||
import { RevisionRow } from '../../becca/entities/rows';
|
||||
|
||||
function getRecentChanges(req) {
|
||||
interface RecentChangeRow {
|
||||
noteId: string;
|
||||
current_isDeleted: boolean;
|
||||
current_deleteId: string;
|
||||
current_title: string;
|
||||
current_isProtected: boolean,
|
||||
title: string;
|
||||
utcDate: string;
|
||||
date: string;
|
||||
canBeUndeleted?: boolean;
|
||||
}
|
||||
|
||||
function getRecentChanges(req: Request) {
|
||||
const {ancestorNoteId} = req.params;
|
||||
|
||||
let recentChanges = [];
|
||||
|
||||
const revisionRows = sql.getRows(`
|
||||
const revisionRows = sql.getRows<RecentChangeRow>(`
|
||||
SELECT
|
||||
notes.noteId,
|
||||
notes.isDeleted AS current_isDeleted,
|
||||
@ -36,7 +50,7 @@ function getRecentChanges(req) {
|
||||
// now we need to also collect date points not represented in note revisions:
|
||||
// 1. creation for all notes (dateCreated)
|
||||
// 2. deletion for deleted notes (dateModified)
|
||||
const noteRows = sql.getRows(`
|
||||
const noteRows = sql.getRows<RecentChangeRow>(`
|
||||
SELECT
|
||||
notes.noteId,
|
||||
notes.isDeleted AS current_isDeleted,
|
||||
@ -76,8 +90,8 @@ function getRecentChanges(req) {
|
||||
for (const change of recentChanges) {
|
||||
if (change.current_isProtected) {
|
||||
if (protectedSessionService.isProtectedSessionAvailable()) {
|
||||
change.title = protectedSessionService.decryptString(change.title);
|
||||
change.current_title = protectedSessionService.decryptString(change.current_title);
|
||||
change.title = protectedSessionService.decryptString(change.title) || "[protected]";
|
||||
change.current_title = protectedSessionService.decryptString(change.current_title) || "[protected]";
|
||||
}
|
||||
else {
|
||||
change.title = change.current_title = "[protected]";
|
||||
@ -97,6 +111,6 @@ function getRecentChanges(req) {
|
||||
return recentChanges;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getRecentChanges
|
||||
};
|
@ -1,10 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
const BRecentNote = require('../../becca/entities/brecent_note');
|
||||
const sql = require('../../services/sql');
|
||||
const dateUtils = require('../../services/date_utils');
|
||||
import BRecentNote = require('../../becca/entities/brecent_note');
|
||||
import sql = require('../../services/sql');
|
||||
import dateUtils = require('../../services/date_utils');
|
||||
import { Request } from 'express';
|
||||
|
||||
function addRecentNote(req) {
|
||||
function addRecentNote(req: Request) {
|
||||
new BRecentNote({
|
||||
noteId: req.body.noteId,
|
||||
notePath: req.body.notePath
|
||||
@ -18,6 +19,6 @@ function addRecentNote(req) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
addRecentNote
|
||||
};
|
@ -1,10 +1,22 @@
|
||||
const becca = require('../../becca/becca');
|
||||
const sql = require('../../services/sql');
|
||||
import { Request } from 'express';
|
||||
import becca = require('../../becca/becca');
|
||||
import sql = require('../../services/sql');
|
||||
|
||||
function getRelationMap(req) {
|
||||
interface ResponseData {
|
||||
noteTitles: Record<string, string>;
|
||||
relations: {
|
||||
attributeId: string,
|
||||
sourceNoteId: string,
|
||||
targetNoteId: string,
|
||||
name: string
|
||||
}[];
|
||||
inverseRelations: Record<string, string>;
|
||||
}
|
||||
|
||||
function getRelationMap(req: Request) {
|
||||
const {relationMapNoteId, noteIds} = req.body;
|
||||
|
||||
const resp = {
|
||||
const resp: ResponseData = {
|
||||
// noteId => title
|
||||
noteTitles: {},
|
||||
relations: [],
|
||||
@ -14,13 +26,13 @@ function getRelationMap(req) {
|
||||
}
|
||||
};
|
||||
|
||||
if (noteIds.length === 0) {
|
||||
if (!Array.isArray(noteIds) || noteIds.length === 0) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
const questionMarks = noteIds.map(noteId => '?').join(',');
|
||||
|
||||
const relationMapNote = becca.getNote(relationMapNoteId);
|
||||
const relationMapNote = becca.getNoteOrThrow(relationMapNoteId);
|
||||
|
||||
const displayRelationsVal = relationMapNote.getLabelValue('displayRelations');
|
||||
const displayRelations = !displayRelationsVal ? [] : displayRelationsVal
|
||||
@ -32,7 +44,7 @@ function getRelationMap(req) {
|
||||
.split(",")
|
||||
.map(token => token.trim());
|
||||
|
||||
const foundNoteIds = sql.getColumn(`SELECT noteId FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds);
|
||||
const foundNoteIds = sql.getColumn<string>(`SELECT noteId FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds);
|
||||
const notes = becca.getNotes(foundNoteIds);
|
||||
|
||||
for (const note of notes) {
|
||||
@ -64,6 +76,6 @@ function getRelationMap(req) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getRelationMap
|
||||
};
|
@ -1,22 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
const beccaService = require('../../becca/becca_service');
|
||||
const revisionService = require('../../services/revisions');
|
||||
const utils = require('../../services/utils');
|
||||
const sql = require('../../services/sql');
|
||||
const cls = require('../../services/cls');
|
||||
const path = require('path');
|
||||
const becca = require('../../becca/becca');
|
||||
const blobService = require('../../services/blob');
|
||||
const eraseService = require("../../services/erase");
|
||||
import beccaService = require('../../becca/becca_service');
|
||||
import revisionService = require('../../services/revisions');
|
||||
import utils = require('../../services/utils');
|
||||
import sql = require('../../services/sql');
|
||||
import cls = require('../../services/cls');
|
||||
import path = require('path');
|
||||
import becca = require('../../becca/becca');
|
||||
import blobService = require('../../services/blob');
|
||||
import eraseService = require("../../services/erase");
|
||||
import { Request, Response } from 'express';
|
||||
import BRevision = require('../../becca/entities/brevision');
|
||||
import BNote = require('../../becca/entities/bnote');
|
||||
import { NotePojo } from '../../becca/becca-interface';
|
||||
|
||||
function getRevisionBlob(req) {
|
||||
interface NotePath {
|
||||
noteId: string;
|
||||
branchId?: string;
|
||||
title: string;
|
||||
notePath: string[];
|
||||
path: string;
|
||||
}
|
||||
|
||||
interface NotePojoWithNotePath extends NotePojo {
|
||||
notePath?: string[] | null;
|
||||
}
|
||||
|
||||
function getRevisionBlob(req: Request) {
|
||||
const preview = req.query.preview === 'true';
|
||||
|
||||
return blobService.getBlobPojo('revisions', req.params.revisionId, { preview });
|
||||
}
|
||||
|
||||
function getRevisions(req) {
|
||||
function getRevisions(req: Request) {
|
||||
return becca.getRevisionsFromQuery(`
|
||||
SELECT revisions.*,
|
||||
LENGTH(blobs.content) AS contentLength
|
||||
@ -26,12 +42,12 @@ function getRevisions(req) {
|
||||
ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]);
|
||||
}
|
||||
|
||||
function getRevision(req) {
|
||||
const revision = becca.getRevision(req.params.revisionId);
|
||||
function getRevision(req: Request) {
|
||||
const revision = becca.getRevisionOrThrow(req.params.revisionId);
|
||||
|
||||
if (revision.type === 'file') {
|
||||
if (revision.hasStringContent()) {
|
||||
revision.content = revision.getContent().substr(0, 10000);
|
||||
revision.content = (revision.getContent() as string).substr(0, 10000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -45,11 +61,7 @@ function getRevision(req) {
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BRevision} revision
|
||||
* @returns {string}
|
||||
*/
|
||||
function getRevisionFilename(revision) {
|
||||
function getRevisionFilename(revision: BRevision) {
|
||||
let filename = utils.formatDownloadTitle(revision.title, revision.type, revision.mime);
|
||||
|
||||
const extension = path.extname(filename);
|
||||
@ -68,8 +80,8 @@ function getRevisionFilename(revision) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
function downloadRevision(req, res) {
|
||||
const revision = becca.getRevision(req.params.revisionId);
|
||||
function downloadRevision(req: Request, res: Response) {
|
||||
const revision = becca.getRevisionOrThrow(req.params.revisionId);
|
||||
|
||||
if (!revision.isContentAvailable()) {
|
||||
return res.setHeader("Content-Type", "text/plain")
|
||||
@ -85,18 +97,18 @@ function downloadRevision(req, res) {
|
||||
res.send(revision.getContent());
|
||||
}
|
||||
|
||||
function eraseAllRevisions(req) {
|
||||
const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?',
|
||||
function eraseAllRevisions(req: Request) {
|
||||
const revisionIdsToErase = sql.getColumn<string>('SELECT revisionId FROM revisions WHERE noteId = ?',
|
||||
[req.params.noteId]);
|
||||
|
||||
eraseService.eraseRevisions(revisionIdsToErase);
|
||||
}
|
||||
|
||||
function eraseRevision(req) {
|
||||
function eraseRevision(req: Request) {
|
||||
eraseService.eraseRevisions([req.params.revisionId]);
|
||||
}
|
||||
|
||||
function restoreRevision(req) {
|
||||
function restoreRevision(req: Request) {
|
||||
const revision = becca.getRevision(req.params.revisionId);
|
||||
|
||||
if (revision) {
|
||||
@ -117,7 +129,9 @@ function restoreRevision(req) {
|
||||
noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true });
|
||||
|
||||
// content is rewritten to point to the restored revision attachments
|
||||
revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`);
|
||||
if (typeof revisionContent === "string") {
|
||||
revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`);
|
||||
}
|
||||
}
|
||||
|
||||
note.title = revision.title;
|
||||
@ -126,8 +140,8 @@ function restoreRevision(req) {
|
||||
}
|
||||
}
|
||||
|
||||
function getEditedNotesOnDate(req) {
|
||||
const noteIds = sql.getColumn(`
|
||||
function getEditedNotesOnDate(req: Request) {
|
||||
const noteIds = sql.getColumn<string>(`
|
||||
SELECT notes.*
|
||||
FROM notes
|
||||
WHERE noteId IN (
|
||||
@ -152,7 +166,7 @@ function getEditedNotesOnDate(req) {
|
||||
return notes.map(note => {
|
||||
const notePath = getNotePathData(note);
|
||||
|
||||
const notePojo = note.getPojo();
|
||||
const notePojo: NotePojoWithNotePath = note.getPojo();
|
||||
notePojo.notePath = notePath ? notePath.notePath : null;
|
||||
|
||||
return notePojo;
|
||||
@ -160,7 +174,7 @@ function getEditedNotesOnDate(req) {
|
||||
|
||||
}
|
||||
|
||||
function getNotePathData(note) {
|
||||
function getNotePathData(note: BNote): NotePath | undefined {
|
||||
const retPath = note.getBestNotePath();
|
||||
|
||||
if (retPath) {
|
||||
@ -173,7 +187,7 @@ function getNotePathData(note) {
|
||||
}
|
||||
else {
|
||||
const parentNote = note.parents[0];
|
||||
branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId).branchId;
|
||||
branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId)?.branchId;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -186,7 +200,7 @@ function getNotePathData(note) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getRevisionBlob,
|
||||
getRevisions,
|
||||
getRevision,
|
@ -1,19 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
const scriptService = require('../../services/script');
|
||||
const attributeService = require('../../services/attributes');
|
||||
const becca = require('../../becca/becca');
|
||||
const syncService = require('../../services/sync');
|
||||
const sql = require('../../services/sql');
|
||||
import scriptService = require('../../services/script');
|
||||
import attributeService = require('../../services/attributes');
|
||||
import becca = require('../../becca/becca');
|
||||
import syncService = require('../../services/sync');
|
||||
import sql = require('../../services/sql');
|
||||
import { Request } from 'express';
|
||||
|
||||
interface ScriptBody {
|
||||
script: string;
|
||||
params: any[];
|
||||
startNoteId: string;
|
||||
currentNoteId: string;
|
||||
originEntityName: string;
|
||||
originEntityId: string;
|
||||
transactional: boolean;
|
||||
}
|
||||
|
||||
// The async/await here is very confusing, because the body.script may, but may not be async. If it is async, then we
|
||||
// need to await it and make the complete response including metadata available in a Promise, so that the route detects
|
||||
// this and does result.then().
|
||||
async function exec(req) {
|
||||
async function exec(req: Request) {
|
||||
try {
|
||||
const { body } = req;
|
||||
const body = (req.body as ScriptBody);
|
||||
|
||||
const execute = body => scriptService.executeScript(
|
||||
const execute = (body: ScriptBody) => scriptService.executeScript(
|
||||
body.script,
|
||||
body.params,
|
||||
body.startNoteId,
|
||||
@ -32,20 +43,20 @@ async function exec(req) {
|
||||
maxEntityChangeId: syncService.getMaxEntityChangeId()
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
function run(req) {
|
||||
const note = becca.getNote(req.params.noteId);
|
||||
function run(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
const result = scriptService.executeNote(note, { originEntity: note });
|
||||
|
||||
return { executionResult: result };
|
||||
}
|
||||
|
||||
function getBundlesWithLabel(label, value) {
|
||||
function getBundlesWithLabel(label: string, value?: string) {
|
||||
const notes = attributeService.getNotesWithLabel(label, value);
|
||||
|
||||
const bundles = [];
|
||||
@ -61,7 +72,7 @@ function getBundlesWithLabel(label, value) {
|
||||
return bundles;
|
||||
}
|
||||
|
||||
function getStartupBundles(req) {
|
||||
function getStartupBundles(req: Request) {
|
||||
if (!process.env.TRILIUM_SAFE_MODE) {
|
||||
if (req.query.mobile === "true") {
|
||||
return getBundlesWithLabel("run", "mobileStartup");
|
||||
@ -84,9 +95,9 @@ function getWidgetBundles() {
|
||||
}
|
||||
}
|
||||
|
||||
function getRelationBundles(req) {
|
||||
function getRelationBundles(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
const note = becca.getNote(noteId);
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
const relationName = req.params.relationName;
|
||||
|
||||
const attributes = note.getAttributes();
|
||||
@ -97,7 +108,7 @@ function getRelationBundles(req) {
|
||||
const bundles = [];
|
||||
|
||||
for (const noteId of uniqueNoteIds) {
|
||||
const note = becca.getNote(noteId);
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
if (!note.isJavaScript() || note.getScriptEnv() !== 'frontend') {
|
||||
continue;
|
||||
@ -113,14 +124,14 @@ function getRelationBundles(req) {
|
||||
return bundles;
|
||||
}
|
||||
|
||||
function getBundle(req) {
|
||||
const note = becca.getNote(req.params.noteId);
|
||||
function getBundle(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
const { script, params } = req.body;
|
||||
|
||||
return scriptService.getScriptBundleForFrontend(note, script, params);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
exec,
|
||||
run,
|
||||
getStartupBundles,
|
@ -1,14 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
const becca = require('../../becca/becca');
|
||||
const SearchContext = require('../../services/search/search_context');
|
||||
const searchService = require('../../services/search/services/search');
|
||||
const bulkActionService = require('../../services/bulk_actions');
|
||||
const cls = require('../../services/cls');
|
||||
const {formatAttrForSearch} = require('../../services/attribute_formatter');
|
||||
const ValidationError = require('../../errors/validation_error');
|
||||
import { Request } from "express";
|
||||
|
||||
function searchFromNote(req) {
|
||||
import becca = require('../../becca/becca');
|
||||
import SearchContext = require('../../services/search/search_context');
|
||||
import searchService = require('../../services/search/services/search');
|
||||
import bulkActionService = require('../../services/bulk_actions');
|
||||
import cls = require('../../services/cls');
|
||||
import attributeFormatter = require('../../services/attribute_formatter');
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
import SearchResult = require("../../services/search/search_result");
|
||||
|
||||
function searchFromNote(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
if (!note) {
|
||||
@ -23,7 +26,7 @@ function searchFromNote(req) {
|
||||
return searchService.searchFromNote(note);
|
||||
}
|
||||
|
||||
function searchAndExecute(req) {
|
||||
function searchAndExecute(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
if (!note) {
|
||||
@ -40,7 +43,7 @@ function searchAndExecute(req) {
|
||||
bulkActionService.executeActions(note, searchResultNoteIds);
|
||||
}
|
||||
|
||||
function quickSearch(req) {
|
||||
function quickSearch(req: Request) {
|
||||
const {searchString} = req.params;
|
||||
|
||||
const searchContext = new SearchContext({
|
||||
@ -58,7 +61,7 @@ function quickSearch(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function search(req) {
|
||||
function search(req: Request) {
|
||||
const {searchString} = req.params;
|
||||
|
||||
const searchContext = new SearchContext({
|
||||
@ -72,7 +75,7 @@ function search(req) {
|
||||
.map(sr => sr.noteId);
|
||||
}
|
||||
|
||||
function getRelatedNotes(req) {
|
||||
function getRelatedNotes(req: Request) {
|
||||
const attr = req.body;
|
||||
|
||||
const searchSettings = {
|
||||
@ -81,10 +84,10 @@ function getRelatedNotes(req) {
|
||||
fuzzyAttributeSearch: false
|
||||
};
|
||||
|
||||
const matchingNameAndValue = searchService.findResultsWithQuery(formatAttrForSearch(attr, true), new SearchContext(searchSettings));
|
||||
const matchingName = searchService.findResultsWithQuery(formatAttrForSearch(attr, false), new SearchContext(searchSettings));
|
||||
const matchingNameAndValue = searchService.findResultsWithQuery(attributeFormatter.formatAttrForSearch(attr, true), new SearchContext(searchSettings));
|
||||
const matchingName = searchService.findResultsWithQuery(attributeFormatter.formatAttrForSearch(attr, false), new SearchContext(searchSettings));
|
||||
|
||||
const results = [];
|
||||
const results: SearchResult[] = [];
|
||||
|
||||
const allResults = matchingNameAndValue.concat(matchingName);
|
||||
|
||||
@ -123,7 +126,7 @@ function searchTemplates() {
|
||||
}).map(note => note.noteId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
searchFromNote,
|
||||
searchAndExecute,
|
||||
getRelatedNotes,
|
@ -1,66 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const imageType = require('image-type');
|
||||
const imageService = require('../../services/image');
|
||||
const noteService = require('../../services/notes');
|
||||
const { sanitizeAttributeName } = require('../../services/sanitize_attribute_name');
|
||||
const specialNotesService = require('../../services/special_notes');
|
||||
|
||||
function uploadImage(req) {
|
||||
const file = req.file;
|
||||
|
||||
if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
|
||||
return [400, `Unknown image type: ${file.mimetype}`];
|
||||
}
|
||||
|
||||
const originalName = `Sender image.${imageType(file.buffer).ext}`;
|
||||
|
||||
const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']);
|
||||
|
||||
const { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true);
|
||||
|
||||
const labelsStr = req.headers['x-labels'];
|
||||
|
||||
if (labelsStr?.trim()) {
|
||||
const labels = JSON.parse(labelsStr);
|
||||
|
||||
for (const { name, value } of labels) {
|
||||
note.setLabel(sanitizeAttributeName(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
note.setLabel("sentFromSender");
|
||||
|
||||
return {
|
||||
noteId: noteId
|
||||
};
|
||||
}
|
||||
|
||||
function saveNote(req) {
|
||||
const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']);
|
||||
|
||||
const { note, branch } = noteService.createNewNote({
|
||||
parentNoteId: parentNote.noteId,
|
||||
title: req.body.title,
|
||||
content: req.body.content,
|
||||
isProtected: false,
|
||||
type: 'text',
|
||||
mime: 'text/html'
|
||||
});
|
||||
|
||||
if (req.body.labels) {
|
||||
for (const { name, value } of req.body.labels) {
|
||||
note.setLabel(sanitizeAttributeName(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
noteId: note.noteId,
|
||||
branchId: branch.branchId
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
uploadImage,
|
||||
saveNote
|
||||
};
|
83
src/routes/api/sender.ts
Normal file
83
src/routes/api/sender.ts
Normal file
@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
|
||||
import imageType = require('image-type');
|
||||
import imageService = require('../../services/image');
|
||||
import noteService = require('../../services/notes');
|
||||
import sanitize_attribute_name = require('../../services/sanitize_attribute_name');
|
||||
import specialNotesService = require('../../services/special_notes');
|
||||
import { Request } from 'express';
|
||||
|
||||
function uploadImage(req: Request) {
|
||||
const file = (req as any).file;
|
||||
|
||||
if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) {
|
||||
return [400, `Unknown image type: ${file.mimetype}`];
|
||||
}
|
||||
|
||||
const uploadedImageType = imageType(file.buffer);
|
||||
if (!uploadedImageType) {
|
||||
return [400, "Unable to determine image type."];
|
||||
}
|
||||
const originalName = `Sender image.${uploadedImageType.ext}`;
|
||||
|
||||
if (!req.headers["x-local-date"] || Array.isArray(req.headers["x-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 { note, noteId } = imageService.saveImage(parentNote.noteId, file.buffer, originalName, true);
|
||||
|
||||
const labelsStr = req.headers['x-labels'];
|
||||
|
||||
if (labelsStr?.trim()) {
|
||||
const labels = JSON.parse(labelsStr);
|
||||
|
||||
for (const { name, value } of labels) {
|
||||
note.setLabel(sanitize_attribute_name.sanitizeAttributeName(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
note.setLabel("sentFromSender");
|
||||
|
||||
return {
|
||||
noteId: noteId
|
||||
};
|
||||
}
|
||||
|
||||
function saveNote(req: Request) {
|
||||
if (!req.headers["x-local-date"] || Array.isArray(req.headers["x-local-date"])) {
|
||||
return [400, "Invalid local date"];
|
||||
}
|
||||
|
||||
const parentNote = specialNotesService.getInboxNote(req.headers['x-local-date']);
|
||||
|
||||
const { note, branch } = noteService.createNewNote({
|
||||
parentNoteId: parentNote.noteId,
|
||||
title: req.body.title,
|
||||
content: req.body.content,
|
||||
isProtected: false,
|
||||
type: 'text',
|
||||
mime: 'text/html'
|
||||
});
|
||||
|
||||
if (req.body.labels) {
|
||||
for (const { name, value } of req.body.labels) {
|
||||
note.setLabel(sanitize_attribute_name.sanitizeAttributeName(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
noteId: note.noteId,
|
||||
branchId: branch.branchId
|
||||
};
|
||||
}
|
||||
|
||||
export = {
|
||||
uploadImage,
|
||||
saveNote
|
||||
};
|
@ -1,9 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const sqlInit = require('../../services/sql_init');
|
||||
const setupService = require('../../services/setup');
|
||||
const log = require('../../services/log');
|
||||
const appInfo = require('../../services/app_info');
|
||||
import sqlInit = require('../../services/sql_init');
|
||||
import setupService = require('../../services/setup');
|
||||
import log = require('../../services/log');
|
||||
import appInfo = require('../../services/app_info');
|
||||
import { Request } from 'express';
|
||||
|
||||
function getStatus() {
|
||||
return {
|
||||
@ -17,13 +18,13 @@ async function setupNewDocument() {
|
||||
await sqlInit.createInitialDatabase();
|
||||
}
|
||||
|
||||
function setupSyncFromServer(req) {
|
||||
function setupSyncFromServer(req: Request) {
|
||||
const { syncServerHost, syncProxy, password } = req.body;
|
||||
|
||||
return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password);
|
||||
}
|
||||
|
||||
function saveSyncSeed(req) {
|
||||
function saveSyncSeed(req: Request) {
|
||||
const { options, syncVersion } = req.body;
|
||||
|
||||
if (appInfo.syncVersion !== syncVersion) {
|
||||
@ -50,7 +51,7 @@ function getSyncSeed() {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getStatus,
|
||||
setupNewDocument,
|
||||
setupSyncFromServer,
|
@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const similarityService = require('../../becca/similarity');
|
||||
const becca = require('../../becca/becca');
|
||||
|
||||
async function getSimilarNotes(req) {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
return await similarityService.findSimilarNotes(noteId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSimilarNotes
|
||||
};
|
18
src/routes/api/similar_notes.ts
Normal file
18
src/routes/api/similar_notes.ts
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
import { Request } from "express";
|
||||
|
||||
import similarityService = require('../../becca/similarity');
|
||||
import becca = require('../../becca/becca');
|
||||
|
||||
async function getSimilarNotes(req: Request) {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
const note = becca.getNoteOrThrow(noteId);
|
||||
|
||||
return await similarityService.findSimilarNotes(noteId);
|
||||
}
|
||||
|
||||
export = {
|
||||
getSimilarNotes
|
||||
};
|
@ -1,32 +1,33 @@
|
||||
"use strict";
|
||||
|
||||
const dateNoteService = require('../../services/date_notes');
|
||||
const sql = require('../../services/sql');
|
||||
const cls = require('../../services/cls');
|
||||
const specialNotesService = require('../../services/special_notes');
|
||||
const becca = require('../../becca/becca');
|
||||
import dateNoteService = require('../../services/date_notes');
|
||||
import sql = require('../../services/sql');
|
||||
import cls = require('../../services/cls');
|
||||
import specialNotesService = require('../../services/special_notes');
|
||||
import becca = require('../../becca/becca');
|
||||
import { Request } from 'express';
|
||||
|
||||
function getInboxNote(req) {
|
||||
function getInboxNote(req: Request) {
|
||||
return specialNotesService.getInboxNote(req.params.date);
|
||||
}
|
||||
|
||||
function getDayNote(req) {
|
||||
function getDayNote(req: Request) {
|
||||
return dateNoteService.getDayNote(req.params.date);
|
||||
}
|
||||
|
||||
function getWeekNote(req) {
|
||||
function getWeekNote(req: Request) {
|
||||
return dateNoteService.getWeekNote(req.params.date);
|
||||
}
|
||||
|
||||
function getMonthNote(req) {
|
||||
function getMonthNote(req: Request) {
|
||||
return dateNoteService.getMonthNote(req.params.month);
|
||||
}
|
||||
|
||||
function getYearNote(req) {
|
||||
function getYearNote(req: Request) {
|
||||
return dateNoteService.getYearNote(req.params.year);
|
||||
}
|
||||
|
||||
function getDayNotesForMonth(req) {
|
||||
function getDayNotesForMonth(req: Request) {
|
||||
const month = req.params.month;
|
||||
|
||||
return sql.getMap(`
|
||||
@ -42,7 +43,7 @@ function getDayNotesForMonth(req) {
|
||||
AND attr.value LIKE '${month}%'`);
|
||||
}
|
||||
|
||||
function saveSqlConsole(req) {
|
||||
function saveSqlConsole(req: Request) {
|
||||
return specialNotesService.saveSqlConsole(req.body.sqlConsoleNoteId);
|
||||
}
|
||||
|
||||
@ -50,14 +51,14 @@ function createSqlConsole() {
|
||||
return specialNotesService.createSqlConsole();
|
||||
}
|
||||
|
||||
function saveSearchNote(req) {
|
||||
function saveSearchNote(req: Request) {
|
||||
return specialNotesService.saveSearchNote(req.body.searchNoteId);
|
||||
}
|
||||
|
||||
function createSearchNote(req) {
|
||||
function createSearchNote(req: Request) {
|
||||
const hoistedNote = getHoistedNote();
|
||||
const searchString = req.body.searchString || "";
|
||||
const ancestorNoteId = req.body.ancestorNoteId || hoistedNote.noteId;
|
||||
const ancestorNoteId = req.body.ancestorNoteId || hoistedNote?.noteId;
|
||||
|
||||
return specialNotesService.createSearchNote(searchString, ancestorNoteId);
|
||||
}
|
||||
@ -66,22 +67,22 @@ function getHoistedNote() {
|
||||
return becca.getNote(cls.getHoistedNoteId());
|
||||
}
|
||||
|
||||
function createLauncher(req) {
|
||||
function createLauncher(req: Request) {
|
||||
return specialNotesService.createLauncher({
|
||||
parentNoteId: req.params.parentNoteId,
|
||||
launcherType: req.params.launcherType
|
||||
});
|
||||
}
|
||||
|
||||
function resetLauncher(req) {
|
||||
function resetLauncher(req: Request) {
|
||||
return specialNotesService.resetLauncher(req.params.noteId);
|
||||
}
|
||||
|
||||
function createOrUpdateScriptLauncherFromApi(req) {
|
||||
function createOrUpdateScriptLauncherFromApi(req: Request) {
|
||||
return specialNotesService.createOrUpdateScriptLauncherFromApi(req.body);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getInboxNote,
|
||||
getDayNote,
|
||||
getWeekNote,
|
@ -1,7 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const sql = require('../../services/sql');
|
||||
const becca = require('../../becca/becca');
|
||||
import sql = require('../../services/sql');
|
||||
import becca = require('../../becca/becca');
|
||||
import { Request } from 'express';
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
|
||||
function getSchema() {
|
||||
const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
|
||||
@ -17,10 +19,15 @@ function getSchema() {
|
||||
return tables;
|
||||
}
|
||||
|
||||
function execute(req) {
|
||||
function execute(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
const queries = note.getContent().split("\n---");
|
||||
const content = note.getContent();
|
||||
if (typeof content !== "string") {
|
||||
throw new ValidationError("Invalid note type.");
|
||||
}
|
||||
|
||||
const queries = content.split("\n---");
|
||||
|
||||
try {
|
||||
const results = [];
|
||||
@ -51,7 +58,7 @@ function execute(req) {
|
||||
results
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: e.message
|
||||
@ -59,7 +66,7 @@ function execute(req) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getSchema,
|
||||
execute
|
||||
};
|
@ -1,10 +1,11 @@
|
||||
const sql = require('../../services/sql');
|
||||
const becca = require('../../becca/becca');
|
||||
import sql = require('../../services/sql');
|
||||
import becca = require('../../becca/becca');
|
||||
import { Request } from 'express';
|
||||
|
||||
function getNoteSize(req) {
|
||||
function getNoteSize(req: Request) {
|
||||
const {noteId} = req.params;
|
||||
|
||||
const blobSizes = sql.getMap(`
|
||||
const blobSizes = sql.getMap<string, number>(`
|
||||
SELECT blobs.blobId, LENGTH(content)
|
||||
FROM blobs
|
||||
LEFT JOIN notes ON notes.blobId = blobs.blobId AND notes.noteId = ? AND notes.isDeleted = 0
|
||||
@ -21,14 +22,14 @@ function getNoteSize(req) {
|
||||
};
|
||||
}
|
||||
|
||||
function getSubtreeSize(req) {
|
||||
function getSubtreeSize(req: Request) {
|
||||
const note = becca.getNoteOrThrow(req.params.noteId);
|
||||
|
||||
const subTreeNoteIds = note.getSubtreeNoteIds();
|
||||
|
||||
sql.fillParamList(subTreeNoteIds);
|
||||
|
||||
const blobSizes = sql.getMap(`
|
||||
const blobSizes = sql.getMap<string, number>(`
|
||||
SELECT blobs.blobId, LENGTH(content)
|
||||
FROM param_list
|
||||
JOIN notes ON notes.noteId = param_list.paramId AND notes.isDeleted = 0
|
||||
@ -44,7 +45,7 @@ function getSubtreeSize(req) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getNoteSize,
|
||||
getSubtreeSize
|
||||
};
|
@ -1,16 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
const syncService = require('../../services/sync');
|
||||
const syncUpdateService = require('../../services/sync_update');
|
||||
const entityChangesService = require('../../services/entity_changes');
|
||||
const sql = require('../../services/sql');
|
||||
const sqlInit = require('../../services/sql_init');
|
||||
const optionService = require('../../services/options');
|
||||
const contentHashService = require('../../services/content_hash');
|
||||
const log = require('../../services/log');
|
||||
const syncOptions = require('../../services/sync_options');
|
||||
const utils = require('../../services/utils');
|
||||
const ws = require('../../services/ws');
|
||||
import syncService = require('../../services/sync');
|
||||
import syncUpdateService = require('../../services/sync_update');
|
||||
import entityChangesService = require('../../services/entity_changes');
|
||||
import sql = require('../../services/sql');
|
||||
import sqlInit = require('../../services/sql_init');
|
||||
import optionService = require('../../services/options');
|
||||
import contentHashService = require('../../services/content_hash');
|
||||
import log = require('../../services/log');
|
||||
import syncOptions = require('../../services/sync_options');
|
||||
import utils = require('../../services/utils');
|
||||
import ws = require('../../services/ws');
|
||||
import { Request } from 'express';
|
||||
import { EntityChange, EntityChangeRecord } from '../../services/entity_changes_interface';
|
||||
import ValidationError = require('../../errors/validation_error');
|
||||
|
||||
async function testSync() {
|
||||
try {
|
||||
@ -26,7 +29,7 @@ async function testSync() {
|
||||
|
||||
return { success: true, message: "Sync server handshake has been successful, sync has been started." };
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
return {
|
||||
success: false,
|
||||
message: e.message
|
||||
@ -82,15 +85,19 @@ function forceFullSync() {
|
||||
syncService.sync();
|
||||
}
|
||||
|
||||
function getChanged(req) {
|
||||
function getChanged(req: Request) {
|
||||
const startTime = Date.now();
|
||||
|
||||
let lastEntityChangeId = parseInt(req.query.lastEntityChangeId);
|
||||
if (typeof req.query.lastEntityChangeId !== "string") {
|
||||
throw new ValidationError("Missing or invalid last entity change ID.");
|
||||
}
|
||||
|
||||
let lastEntityChangeId: number | null | undefined = parseInt(req.query.lastEntityChangeId);
|
||||
const clientInstanceId = req.query.instanceId;
|
||||
let filteredEntityChanges = [];
|
||||
let filteredEntityChanges: EntityChange[] = [];
|
||||
|
||||
do {
|
||||
const entityChanges = sql.getRows(`
|
||||
const entityChanges: EntityChange[] = sql.getRows<EntityChange>(`
|
||||
SELECT *
|
||||
FROM entity_changes
|
||||
WHERE isSynced = 1
|
||||
@ -129,16 +136,22 @@ function getChanged(req) {
|
||||
};
|
||||
}
|
||||
|
||||
const partialRequests = {};
|
||||
const partialRequests: Record<string, {
|
||||
createdAt: number,
|
||||
payload: string
|
||||
}> = {};
|
||||
|
||||
function update(req) {
|
||||
function update(req: Request) {
|
||||
let { body } = req;
|
||||
|
||||
const pageCount = parseInt(req.get('pageCount'));
|
||||
const pageIndex = parseInt(req.get('pageIndex'));
|
||||
const pageCount = parseInt(req.get('pageCount') as string);
|
||||
const pageIndex = parseInt(req.get('pageIndex') as string);
|
||||
|
||||
if (pageCount !== 1) {
|
||||
const requestId = req.get('requestId');
|
||||
if (!requestId) {
|
||||
throw new Error("Missing request ID.");
|
||||
}
|
||||
|
||||
if (pageIndex === 0) {
|
||||
partialRequests[requestId] = {
|
||||
@ -185,7 +198,7 @@ function syncFinished() {
|
||||
sqlInit.setDbAsInitialized();
|
||||
}
|
||||
|
||||
function queueSector(req) {
|
||||
function queueSector(req: Request) {
|
||||
const entityName = utils.sanitizeSqlIdentifier(req.params.entityName);
|
||||
const sector = utils.sanitizeSqlIdentifier(req.params.sector);
|
||||
|
||||
@ -196,7 +209,7 @@ function checkEntityChanges() {
|
||||
require('../../services/consistency_checks').runEntityChangesChecks();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
testSync,
|
||||
checkSync,
|
||||
syncNow,
|
@ -1,16 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
const becca = require('../../becca/becca');
|
||||
const log = require('../../services/log');
|
||||
const NotFoundError = require('../../errors/not_found_error');
|
||||
import becca = require('../../becca/becca');
|
||||
import log = require('../../services/log');
|
||||
import NotFoundError = require('../../errors/not_found_error');
|
||||
import { Request } from 'express';
|
||||
import BNote = require('../../becca/entities/bnote');
|
||||
|
||||
function getNotesAndBranchesAndAttributes(noteIds) {
|
||||
noteIds = new Set(noteIds);
|
||||
const collectedNoteIds = new Set();
|
||||
const collectedAttributeIds = new Set();
|
||||
const collectedBranchIds = new Set();
|
||||
function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set<string>) {
|
||||
const noteIds = new Set(_noteIds);
|
||||
const collectedNoteIds = new Set<string>();
|
||||
const collectedAttributeIds = new Set<string>();
|
||||
const collectedBranchIds = new Set<string>();
|
||||
|
||||
function collectEntityIds(note) {
|
||||
function collectEntityIds(note?: BNote) {
|
||||
if (!note || collectedNoteIds.has(note.noteId)) {
|
||||
return;
|
||||
}
|
||||
@ -18,15 +20,18 @@ function getNotesAndBranchesAndAttributes(noteIds) {
|
||||
collectedNoteIds.add(note.noteId);
|
||||
|
||||
for (const branch of note.getParentBranches()) {
|
||||
collectedBranchIds.add(branch.branchId);
|
||||
if (branch.branchId) {
|
||||
collectedBranchIds.add(branch.branchId);
|
||||
}
|
||||
|
||||
collectEntityIds(branch.parentNote);
|
||||
}
|
||||
|
||||
for (const childNote of note.children) {
|
||||
const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, note.noteId);
|
||||
|
||||
collectedBranchIds.add(childBranch.branchId);
|
||||
if (childBranch && childBranch.branchId) {
|
||||
collectedBranchIds.add(childBranch.branchId);
|
||||
}
|
||||
}
|
||||
|
||||
for (const attr of note.ownedAttributes) {
|
||||
@ -122,11 +127,11 @@ function getNotesAndBranchesAndAttributes(noteIds) {
|
||||
};
|
||||
}
|
||||
|
||||
function getTree(req) {
|
||||
const subTreeNoteId = req.query.subTreeNoteId || 'root';
|
||||
const collectedNoteIds = new Set([subTreeNoteId]);
|
||||
function getTree(req: Request) {
|
||||
const subTreeNoteId = typeof req.query.subTreeNoteId === "string" ? req.query.subTreeNoteId : 'root';
|
||||
const collectedNoteIds = new Set<string>([subTreeNoteId]);
|
||||
|
||||
function collect(parentNote) {
|
||||
function collect(parentNote: BNote) {
|
||||
if (!parentNote) {
|
||||
console.trace(parentNote);
|
||||
}
|
||||
@ -136,7 +141,7 @@ function getTree(req) {
|
||||
|
||||
const childBranch = becca.getBranchFromChildAndParent(childNote.noteId, parentNote.noteId);
|
||||
|
||||
if (childBranch.isExpanded) {
|
||||
if (childBranch?.isExpanded) {
|
||||
collect(childBranch.childNote);
|
||||
}
|
||||
}
|
||||
@ -151,11 +156,11 @@ function getTree(req) {
|
||||
return getNotesAndBranchesAndAttributes(collectedNoteIds);
|
||||
}
|
||||
|
||||
function load(req) {
|
||||
function load(req: Request) {
|
||||
return getNotesAndBranchesAndAttributes(req.body.noteIds);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
getTree,
|
||||
load
|
||||
};
|
@ -17,48 +17,48 @@ const NotFoundError = require('../errors/not_found_error');
|
||||
const ValidationError = require('../errors/validation_error');
|
||||
|
||||
// page routes
|
||||
const setupRoute = require('./setup.js');
|
||||
const setupRoute = require('./setup');
|
||||
const loginRoute = require('./login.js');
|
||||
const indexRoute = require('./index.js');
|
||||
|
||||
// API routes
|
||||
const treeApiRoute = require('./api/tree.js');
|
||||
const notesApiRoute = require('./api/notes.js');
|
||||
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.js');
|
||||
const optionsApiRoute = require('./api/options.js');
|
||||
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.js');
|
||||
const recentNotesRoute = require('./api/recent_notes.js');
|
||||
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.js');
|
||||
const setupApiRoute = require('./api/setup.js');
|
||||
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.js');
|
||||
const senderRoute = require('./api/sender.js');
|
||||
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.js');
|
||||
const noteMapRoute = require('./api/note_map');
|
||||
const clipperRoute = require('./api/clipper');
|
||||
const similarNotesRoute = require('./api/similar_notes.js');
|
||||
const keysRoute = require('./api/keys.js');
|
||||
const similarNotesRoute = require('./api/similar_notes');
|
||||
const keysRoute = require('./api/keys');
|
||||
const backendLogRoute = require('./api/backend_log');
|
||||
const statsRoute = require('./api/stats.js');
|
||||
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.js');
|
||||
const otherRoute = require('./api/other');
|
||||
const shareRoutes = require('../share/routes.js');
|
||||
|
||||
const etapiAuthRoutes = require('../etapi/auth.js');
|
||||
|
@ -44,7 +44,7 @@ function subscribeBeccaLoader(eventTypes: EventType, listener: EventListener) {
|
||||
}
|
||||
}
|
||||
|
||||
function emit(eventType: string, data: any) {
|
||||
function emit(eventType: string, data?: any) {
|
||||
const listeners = eventListeners[eventType];
|
||||
|
||||
if (listeners) {
|
||||
|
@ -55,7 +55,7 @@ interface Note {
|
||||
let note: Partial<Note> = {};
|
||||
let resource: Resource;
|
||||
|
||||
function importEnex(taskContext: TaskContext, file: File, parentNote: BNote) {
|
||||
function importEnex(taskContext: TaskContext, file: File, parentNote: BNote): Promise<BNote> {
|
||||
const saxStream = sax.createStream(true);
|
||||
|
||||
const rootNoteTitle = file.originalname.toLowerCase().endsWith(".enex")
|
||||
|
@ -251,7 +251,7 @@ function createNewNote(params: NoteParams): {
|
||||
});
|
||||
}
|
||||
|
||||
function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: string, params: NoteParams) {
|
||||
function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: string | undefined, params: NoteParams) {
|
||||
if (!params.type) {
|
||||
const parentNote = becca.notes[params.parentNoteId];
|
||||
|
||||
@ -263,7 +263,7 @@ function createNewNoteWithTarget(target: ("into" | "after"), targetBranchId: str
|
||||
if (target === 'into') {
|
||||
return createNewNote(params);
|
||||
}
|
||||
else if (target === 'after') {
|
||||
else if (target === 'after' && targetBranchId) {
|
||||
const afterBranch = becca.branches[targetBranchId];
|
||||
|
||||
// not updating utcDateModified to avoid having to sync whole rows
|
||||
|
@ -106,7 +106,7 @@ function execute(ctx: ScriptContext, script: string) {
|
||||
return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx);
|
||||
}
|
||||
|
||||
function getParams(params: ScriptParams) {
|
||||
function getParams(params?: ScriptParams) {
|
||||
if (!params) {
|
||||
return params;
|
||||
}
|
||||
@ -121,7 +121,7 @@ function getParams(params: ScriptParams) {
|
||||
}).join(",");
|
||||
}
|
||||
|
||||
function getScriptBundleForFrontend(note: BNote, script: string, params: ScriptParams) {
|
||||
function getScriptBundleForFrontend(note: BNote, script?: string, params?: ScriptParams) {
|
||||
let overrideContent = null;
|
||||
|
||||
if (script) {
|
||||
|
@ -110,7 +110,7 @@ function getSyncSeedOptions() {
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export = {
|
||||
hasSyncServerSchemaAndSeed,
|
||||
triggerSync,
|
||||
sendSeedToSyncServer,
|
||||
|
@ -166,7 +166,7 @@ function createScriptLauncher(parentNoteId: string, forceNoteId?: string) {
|
||||
interface LauncherConfig {
|
||||
parentNoteId: string;
|
||||
launcherType: string;
|
||||
noteId: string;
|
||||
noteId?: string;
|
||||
}
|
||||
|
||||
function createLauncher({ parentNoteId, launcherType, noteId }: LauncherConfig) {
|
||||
|
@ -269,8 +269,8 @@ function transactional<T>(func: (statement: Statement) => T) {
|
||||
}
|
||||
}
|
||||
|
||||
function fillParamList(paramIds: string[], truncate = true) {
|
||||
if (paramIds.length === 0) {
|
||||
function fillParamList(paramIds: string[] | Set<string>, truncate = true) {
|
||||
if ("length" in paramIds && paramIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ class TaskContext {
|
||||
});
|
||||
}
|
||||
|
||||
taskSucceeded(result?: string) {
|
||||
taskSucceeded(result?: string | Record<string, string | undefined>) {
|
||||
ws.sendMessageToAllClients({
|
||||
type: 'taskSucceeded',
|
||||
taskId: this.taskId,
|
||||
|
@ -41,7 +41,7 @@ interface Message {
|
||||
taskType?: string | null;
|
||||
message?: string;
|
||||
reason?: string;
|
||||
result?: string;
|
||||
result?: string | Record<string, string | undefined>;
|
||||
|
||||
script?: string;
|
||||
params?: any[];
|
||||
|
Loading…
x
Reference in New Issue
Block a user