2017-10-21 21:10:33 -04:00
"use strict" ;
2024-07-18 21:35:17 +03:00
import noteService from "../../services/notes.js" ;
import eraseService from "../../services/erase.js" ;
import treeService from "../../services/tree.js" ;
import sql from "../../services/sql.js" ;
import utils from "../../services/utils.js" ;
import log from "../../services/log.js" ;
import TaskContext from "../../services/task_context.js" ;
import becca from "../../becca/becca.js" ;
import ValidationError from "../../errors/validation_error.js" ;
import blobService from "../../services/blob.js" ;
2025-01-09 18:36:24 +02:00
import type { Request } from "express" ;
2025-01-13 23:18:10 +02:00
import type BBranch from "../../becca/entities/bbranch.js" ;
2025-01-09 18:36:24 +02:00
import type { AttributeRow } from "../../becca/entities/rows.js" ;
2024-04-06 21:55:27 +03:00
2025-02-14 09:40:38 +01:00
/ * *
* @swagger
* / a p i / n o t e s / { n o t e I d } :
* get :
* summary : Retrieve note metadata
* operationId : notes - get
* parameters :
* - name : noteId
* in : path
* required : true
* schema :
* $ref : "#/components/schemas/NoteId"
* responses :
* '200' :
* description : Note metadata
* content :
* application / json :
* schema :
* allOf :
* - $ref : '#/components/schemas/Note'
* - $ref : "#/components/schemas/Timestamps"
* security :
* - session : [ ]
* tags : [ "data" ]
* /
2024-04-06 21:55:27 +03:00
function getNote ( req : Request ) {
2023-05-08 00:02:08 +02:00
return becca . getNoteOrThrow ( req . params . noteId ) ;
2023-05-05 22:21:51 +02:00
}
2021-06-03 21:47:25 +02:00
2025-02-14 09:40:38 +01:00
/ * *
* @swagger
* / a p i / n o t e s / { n o t e I d } / b l o b :
* get :
* summary : Retrieve note content
* operationId : notes - blob
* parameters :
* - name : noteId
* in : path
* required : true
* schema :
* $ref : "#/components/schemas/NoteId"
* responses :
* '304' :
* description : Note content
* content :
* application / json :
* schema :
* $ref : '#/components/schemas/Blob'
* security :
* - session : [ ]
* tags : [ "data" ]
* /
2024-04-06 21:55:27 +03:00
function getNoteBlob ( req : Request ) {
2025-01-09 18:07:02 +02:00
return blobService . getBlobPojo ( "notes" , req . params . noteId ) ;
2023-05-05 22:21:51 +02:00
}
2025-02-14 09:40:38 +01:00
/ * *
* @swagger
* / a p i / n o t e s / { n o t e I d } / m e t a d a t a :
* get :
* summary : Retrieve note metadata ( limited to timestamps )
* operationId : notes - metadata
* parameters :
* - name : noteId
* in : path
* required : true
* schema :
* $ref : "#/components/schemas/NoteId"
* responses :
* '200' :
* description : Note metadata
* content :
* application / json :
* schema :
* $ref : "#/components/schemas/Timestamps"
* security :
* - session : [ ]
* tags : [ "data" ]
* /
2024-04-06 21:55:27 +03:00
function getNoteMetadata ( req : Request ) {
2023-05-08 00:02:08 +02:00
const note = becca . getNoteOrThrow ( req . params . noteId ) ;
2020-08-16 22:57:48 +02:00
2023-05-05 22:21:51 +02:00
return {
dateCreated : note.dateCreated ,
2023-09-06 22:54:31 +02:00
utcDateCreated : note.utcDateCreated ,
dateModified : note.dateModified ,
2025-01-09 18:07:02 +02:00
utcDateModified : note.utcDateModified
2023-05-05 22:21:51 +02:00
} ;
2023-05-05 16:37:39 +02:00
}
2024-04-06 21:55:27 +03:00
function createNote ( req : Request ) {
2019-11-16 11:09:52 +01:00
const params = Object . assign ( { } , req . body ) ; // clone
params . parentNoteId = req . params . parentNoteId ;
2017-10-14 23:31:44 -04:00
2019-11-16 12:28:47 +01:00
const { target , targetBranchId } = req . query ;
2017-10-14 23:31:44 -04:00
2024-04-06 21:55:27 +03:00
if ( target !== "into" && target !== "after" ) {
throw new ValidationError ( "Invalid target type." ) ;
}
2024-04-06 22:17:47 +03:00
if ( targetBranchId && typeof targetBranchId !== "string" ) {
2024-04-06 21:55:27 +03:00
throw new ValidationError ( "Missing or incorrect type for target branch ID." ) ;
}
2020-06-20 12:31:38 +02:00
const { note , branch } = noteService . createNewNoteWithTarget ( target , targetBranchId , params ) ;
2017-10-14 23:31:44 -04:00
2018-03-30 12:57:22 -04:00
return {
2018-04-01 11:42:12 -04:00
note ,
branch
2018-03-30 12:57:22 -04:00
} ;
}
2017-10-14 23:31:44 -04:00
2024-04-06 21:55:27 +03:00
function updateNoteData ( req : Request ) {
2025-01-09 18:07:02 +02:00
const { content , attachments } = req . body ;
const { noteId } = req . params ;
2017-11-05 10:41:54 -05:00
2023-09-08 21:53:57 +02:00
return noteService . updateNoteData ( noteId , content , attachments ) ;
2018-03-30 12:57:22 -04:00
}
2017-10-14 23:31:44 -04:00
2025-02-14 09:40:38 +01:00
/ * *
* @swagger
* / a p i / n o t e s / { n o t e I d } :
* delete :
* summary : Delete note
* operationId : notes - delete
* parameters :
* - name : noteId
* in : path
* required : true
* schema :
* $ref : "#/components/schemas/NoteId"
* - name : taskId
* in : query
* required : true
* schema :
* type : string
* description : Task group identifier
* - name : eraseNotes
* in : query
* schema :
* type : boolean
* required : false
* description : Whether to erase the note immediately
* - name : last
* in : query
* schema :
* type : boolean
* required : true
* description : Whether this is the last request of this task group
* responses :
* '200' :
* description : Note successfully deleted
* security :
* - session : [ ]
* tags : [ "data" ]
* /
2024-04-06 21:55:27 +03:00
function deleteNote ( req : Request ) {
2018-11-14 23:30:28 +01:00
const noteId = req . params . noteId ;
2019-10-19 00:11:07 +02:00
const taskId = req . query . taskId ;
2025-01-09 18:07:02 +02:00
const eraseNotes = req . query . eraseNotes === "true" ;
const last = req . query . last === "true" ;
2018-11-14 23:30:28 +01:00
2020-01-03 10:48:36 +01:00
// note how deleteId is separate from taskId - single taskId produces separate deleteId for each "top level" deleted note
const deleteId = utils . randomString ( 10 ) ;
2024-04-06 21:55:27 +03:00
const note = becca . getNoteOrThrow ( noteId ) ;
2018-11-14 23:30:28 +01:00
2024-04-06 21:55:27 +03:00
if ( typeof taskId !== "string" ) {
throw new ValidationError ( "Missing or incorrect type for task ID." ) ;
}
2025-01-09 18:07:02 +02:00
const taskContext = TaskContext . getInstance ( taskId , "deleteNotes" ) ;
2019-10-18 22:27:38 +02:00
2022-04-19 23:06:46 +02:00
note . deleteNote ( deleteId , taskContext ) ;
2019-10-19 00:11:07 +02:00
2021-09-16 14:38:09 +02:00
if ( eraseNotes ) {
2023-07-27 23:57:12 +02:00
eraseService . eraseNotesWithDeleteId ( deleteId ) ;
2021-09-16 14:38:09 +02:00
}
2019-10-19 00:11:07 +02:00
if ( last ) {
2020-06-20 12:31:38 +02:00
taskContext . taskSucceeded ( ) ;
2019-10-19 00:11:07 +02:00
}
2018-11-14 23:30:28 +01:00
}
2024-04-06 21:55:27 +03:00
function undeleteNote ( req : Request ) {
2025-01-09 18:07:02 +02:00
const taskContext = TaskContext . getInstance ( utils . randomString ( 10 ) , "undeleteNotes" ) ;
2020-01-03 13:14:43 +01:00
2021-05-09 11:12:53 +02:00
noteService . undeleteNote ( req . params . noteId , taskContext ) ;
2020-01-03 13:14:43 +01:00
2020-06-20 12:31:38 +02:00
taskContext . taskSucceeded ( ) ;
2020-01-03 13:14:43 +01:00
}
2024-04-06 21:55:27 +03:00
function sortChildNotes ( req : Request ) {
2018-01-13 17:00:40 -05:00
const noteId = req . params . noteId ;
2025-01-09 18:07:02 +02:00
const { sortBy , sortDirection , foldersFirst , sortNatural , sortLocale } = req . body ;
2018-01-13 17:00:40 -05:00
2023-03-30 11:06:10 +08:00
log . info ( ` Sorting ' ${ noteId } ' children with ${ sortBy } ${ sortDirection } , foldersFirst= ${ foldersFirst } , sortNatural= ${ sortNatural } , sortLocale= ${ sortLocale } ` ) ;
2021-02-28 23:40:15 +01:00
2025-01-09 18:07:02 +02:00
const reverse = sortDirection === "desc" ;
2021-02-28 23:40:15 +01:00
2023-03-30 11:06:10 +08:00
treeService . sortNotes ( noteId , sortBy , reverse , foldersFirst , sortNatural , sortLocale ) ;
2018-03-30 12:57:22 -04:00
}
2018-01-13 17:00:40 -05:00
2024-04-06 21:55:27 +03:00
function protectNote ( req : Request ) {
2018-01-13 20:53:00 -05:00
const noteId = req . params . noteId ;
2021-04-25 22:02:32 +02:00
const note = becca . notes [ noteId ] ;
2018-03-31 10:51:37 -04:00
const protect = ! ! parseInt ( req . params . isProtected ) ;
2024-04-06 21:55:27 +03:00
const includingSubTree = ! ! parseInt ( req . query ? . subtree as string ) ;
2018-01-13 17:00:40 -05:00
2025-01-09 18:07:02 +02:00
const taskContext = new TaskContext ( utils . randomString ( 10 ) , "protectNotes" , { protect } ) ;
2019-10-19 09:58:18 +02:00
2020-06-20 12:31:38 +02:00
noteService . protectNoteRecursively ( note , protect , includingSubTree , taskContext ) ;
2019-10-19 09:58:18 +02:00
taskContext . taskSucceeded ( ) ;
2018-03-30 12:57:22 -04:00
}
2018-01-13 17:00:40 -05:00
2024-04-06 21:55:27 +03:00
function setNoteTypeMime ( req : Request ) {
2018-04-04 23:04:31 -04:00
// can't use [] destructuring because req.params is not iterable
2025-01-09 18:07:02 +02:00
const { noteId } = req . params ;
const { type , mime } = req . body ;
2018-01-20 21:56:03 -05:00
2024-04-06 21:55:27 +03:00
const note = becca . getNoteOrThrow ( noteId ) ;
2018-04-01 17:38:24 -04:00
note . type = type ;
note . mime = mime ;
2020-06-20 12:31:38 +02:00
note . save ( ) ;
2018-03-30 12:57:22 -04:00
}
2018-01-20 21:56:03 -05:00
2024-04-06 21:55:27 +03:00
function changeTitle ( req : Request ) {
2018-10-30 22:18:20 +01:00
const noteId = req . params . noteId ;
const title = req . body . title ;
2023-05-08 00:02:08 +02:00
const note = becca . getNoteOrThrow ( noteId ) ;
2018-10-30 22:18:20 +01:00
2021-05-17 22:35:36 +02:00
if ( ! note . isContentAvailable ( ) ) {
2022-12-09 16:04:13 +01:00
throw new ValidationError ( ` Note ' ${ noteId } ' is not available for change ` ) ;
2018-10-30 22:18:20 +01:00
}
2020-06-23 22:03:01 +02:00
const noteTitleChanged = note . title !== title ;
2021-12-08 21:04:22 +01:00
if ( noteTitleChanged ) {
2023-06-04 23:01:40 +02:00
noteService . saveRevisionIfNeeded ( note ) ;
2021-12-08 21:04:22 +01:00
}
2018-10-30 22:18:20 +01:00
note . title = title ;
2020-06-20 12:31:38 +02:00
note . save ( ) ;
2020-01-19 20:18:02 +01:00
2020-06-23 22:03:01 +02:00
if ( noteTitleChanged ) {
2020-06-24 22:29:53 +02:00
noteService . triggerNoteTitleChanged ( note ) ;
2020-06-23 22:03:01 +02:00
}
2020-01-19 20:18:02 +01:00
return note ;
2018-10-30 22:18:20 +01:00
}
2024-04-06 21:55:27 +03:00
function duplicateSubtree ( req : Request ) {
2025-01-09 18:07:02 +02:00
const { noteId , parentNoteId } = req . params ;
2019-10-19 12:36:16 +02:00
2020-11-19 14:06:32 +01:00
return noteService . duplicateSubtree ( noteId , parentNoteId ) ;
2019-10-19 12:36:16 +02:00
}
2020-12-06 22:11:49 +01:00
function eraseDeletedNotesNow() {
2023-07-27 23:57:12 +02:00
eraseService . eraseDeletedNotesNow ( ) ;
2020-12-06 22:11:49 +01:00
}
2023-04-24 21:22:34 +02:00
function eraseUnusedAttachmentsNow() {
2023-07-27 23:57:12 +02:00
eraseService . eraseUnusedAttachmentsNow ( ) ;
2023-04-24 21:22:34 +02:00
}
2024-04-06 21:55:27 +03:00
function getDeleteNotesPreview ( req : Request ) {
2025-01-09 18:07:02 +02:00
const { branchIdsToDelete , deleteAllClones } = req . body ;
2021-03-14 22:54:39 +01:00
2024-04-06 21:55:27 +03:00
const noteIdsToBeDeleted = new Set < string > ( ) ;
const strongBranchCountToDelete : Record < string , number > = { } ; // noteId => count
2021-03-14 22:54:39 +01:00
2024-04-06 21:55:27 +03:00
function branchPreviewDeletion ( branch : BBranch ) {
if ( branch . isWeak || ! branch . branchId ) {
2022-12-24 13:15:19 +01:00
return ;
}
strongBranchCountToDelete [ branch . branchId ] = strongBranchCountToDelete [ branch . branchId ] || 0 ;
strongBranchCountToDelete [ branch . branchId ] ++ ;
2021-03-14 22:54:39 +01:00
const note = branch . getNote ( ) ;
2022-12-24 13:15:19 +01:00
if ( deleteAllClones || note . getStrongParentBranches ( ) . length <= strongBranchCountToDelete [ branch . branchId ] ) {
2021-03-14 22:54:39 +01:00
noteIdsToBeDeleted . add ( note . noteId ) ;
for ( const childBranch of note . getChildBranches ( ) ) {
branchPreviewDeletion ( childBranch ) ;
}
}
}
for ( const branchId of branchIdsToDelete ) {
2021-05-02 11:23:58 +02:00
const branch = becca . getBranch ( branchId ) ;
2021-03-14 22:54:39 +01:00
if ( ! branch ) {
log . error ( ` Branch ${ branchId } was not found and delete preview can't be calculated for this note. ` ) ;
continue ;
}
branchPreviewDeletion ( branch ) ;
}
2024-04-06 21:55:27 +03:00
let brokenRelations : AttributeRow [ ] = [ ] ;
2021-03-14 22:54:39 +01:00
2021-03-18 23:23:35 +01:00
if ( noteIdsToBeDeleted . size > 0 ) {
2021-03-14 22:54:39 +01:00
sql . fillParamList ( noteIdsToBeDeleted ) ;
2023-04-14 16:49:06 +02:00
// FIXME: No need to do this in database, can be done with becca data
2025-01-09 18:07:02 +02:00
brokenRelations = sql
. getRows < AttributeRow > (
`
2021-03-14 22:54:39 +01:00
SELECT attr . noteId , attr . name , attr . value
FROM attributes attr
2024-12-22 15:42:15 +02:00
JOIN param_list ON param_list . paramId = attr . value
2021-03-14 22:54:39 +01:00
WHERE attr . isDeleted = 0
2025-01-09 18:07:02 +02:00
AND attr . type = 'relation' `
)
. filter ( ( attr ) = > attr . noteId && ! noteIdsToBeDeleted . has ( attr . noteId ) ) ;
2021-03-14 22:54:39 +01:00
}
return {
noteIdsToBeDeleted : Array.from ( noteIdsToBeDeleted ) ,
brokenRelations
} ;
}
2024-04-06 21:55:27 +03:00
function forceSaveRevision ( req : Request ) {
2025-01-09 18:07:02 +02:00
const { noteId } = req . params ;
2023-05-08 00:02:08 +02:00
const note = becca . getNoteOrThrow ( noteId ) ;
2022-11-08 22:36:15 +01:00
if ( ! note . isContentAvailable ( ) ) {
2022-12-09 16:04:13 +01:00
throw new ValidationError ( ` Note revision of a protected note cannot be created outside of a protected session. ` ) ;
2022-11-08 22:36:15 +01:00
}
2023-06-04 23:01:40 +02:00
note . saveRevision ( ) ;
2022-11-08 22:36:15 +01:00
}
2024-04-06 21:55:27 +03:00
function convertNoteToAttachment ( req : Request ) {
2025-01-09 18:07:02 +02:00
const { noteId } = req . params ;
2023-05-08 00:02:08 +02:00
const note = becca . getNoteOrThrow ( noteId ) ;
2023-05-02 22:46:39 +02:00
return {
2023-07-14 18:24:15 +02:00
attachment : note.convertToParentAttachment ( )
2023-05-02 22:46:39 +02:00
} ;
}
2024-07-18 21:47:30 +03:00
export default {
2018-03-30 12:57:22 -04:00
getNote ,
2023-05-05 16:37:39 +02:00
getNoteBlob ,
2023-05-05 22:21:51 +02:00
getNoteMetadata ,
2023-01-24 09:19:49 +01:00
updateNoteData ,
2018-11-14 23:30:28 +01:00
deleteNote ,
2020-01-03 13:14:43 +01:00
undeleteNote ,
2018-03-30 12:57:22 -04:00
createNote ,
2021-02-28 23:40:15 +01:00
sortChildNotes ,
2020-02-26 16:37:17 +01:00
protectNote ,
2018-10-21 10:26:14 +02:00
setNoteTypeMime ,
2019-10-19 12:36:16 +02:00
changeTitle ,
2020-12-06 22:11:49 +01:00
duplicateSubtree ,
2021-03-14 22:54:39 +01:00
eraseDeletedNotesNow ,
2023-04-24 21:22:34 +02:00
eraseUnusedAttachmentsNow ,
2021-04-24 11:39:44 +02:00
getDeleteNotesPreview ,
2023-06-04 23:01:40 +02:00
forceSaveRevision ,
2023-05-02 22:46:39 +02:00
convertNoteToAttachment
2020-06-20 12:31:38 +02:00
} ;