diff --git a/src/public/javascripts/services/cloning.js b/src/public/javascripts/services/cloning.js index d7e1700b0..025ab5d3f 100644 --- a/src/public/javascripts/services/cloning.js +++ b/src/public/javascripts/services/cloning.js @@ -1,4 +1,5 @@ import treeService from './tree.js'; +import server from './server.js'; async function cloneNoteTo(childNoteId, parentNoteId, prefix) { const resp = await server.put('notes/' + childNoteId + '/clone-to/' + parentNoteId, { diff --git a/src/routes/api/cloning.js b/src/routes/api/cloning.js index af8be046e..926005dfc 100644 --- a/src/routes/api/cloning.js +++ b/src/routes/api/cloning.js @@ -1,84 +1,83 @@ "use strict"; -const express = require('express'); -const router = express.Router(); const sql = require('../../services/sql'); -const auth = require('../../services/auth'); const utils = require('../../services/utils'); const sync_table = require('../../services/sync_table'); -const wrap = require('express-promise-wrap').wrap; const tree = require('../../services/tree'); -router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, wrap(async (req, res, next) => { +async function cloneNoteToParent(req) { const parentNoteId = req.params.parentNoteId; const childNoteId = req.params.childNoteId; const prefix = req.body.prefix; const sourceId = req.headers.source_id; - if (!await tree.validateParentChild(res, parentNoteId, childNoteId)) { - return; + const validationResult = await tree.validateParentChild(parentNoteId, childNoteId); + + if (!validationResult.success) { + return validationResult; } const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]); const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1; - await sql.doInTransaction(async () => { - const branch = { - branchId: utils.newBranchId(), - noteId: childNoteId, - parentNoteId: parentNoteId, - prefix: prefix, - notePosition: newNotePos, - isExpanded: 0, - dateModified: utils.nowDate(), - isDeleted: 0 - }; + const branch = { + branchId: utils.newBranchId(), + noteId: childNoteId, + parentNoteId: parentNoteId, + prefix: prefix, + notePosition: newNotePos, + isExpanded: 0, + dateModified: utils.nowDate(), + isDeleted: 0 + }; - await sql.replace("branches", branch); + await sql.replace("branches", branch); - await sync_table.addBranchSync(branch.branchId, sourceId); + await sync_table.addBranchSync(branch.branchId, sourceId); - await sql.execute("UPDATE branches SET isExpanded = 1 WHERE noteId = ?", [parentNoteId]); - }); + await sql.execute("UPDATE branches SET isExpanded = 1 WHERE noteId = ?", [parentNoteId]); - res.send({ success: true }); -})); + return { success: true }; +} -router.put('/:noteId/clone-after/:afterBranchId', auth.checkApiAuth, wrap(async (req, res, next) => { +async function cloneNoteAfter(req) { const noteId = req.params.noteId; const afterBranchId = req.params.afterBranchId; const sourceId = req.headers.source_id; const afterNote = await tree.getBranch(afterBranchId); - if (!await tree.validateParentChild(res, afterNote.parentNoteId, noteId)) { - return; + const validationResult = await tree.validateParentChild(afterNote.parentNoteId, noteId); + + if (!validationResult.result) { + return validationResult; } - await sql.doInTransaction(async () => { - // we don't change dateModified so other changes are prioritized in case of conflict - // also we would have to sync all those modified note trees otherwise hash checks would fail - await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", - [afterNote.parentNoteId, afterNote.notePosition]); + // we don't change dateModified so other changes are prioritized in case of conflict + // also we would have to sync all those modified note trees otherwise hash checks would fail + await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", + [afterNote.parentNoteId, afterNote.notePosition]); - await sync_table.addNoteReorderingSync(afterNote.parentNoteId, sourceId); + await sync_table.addNoteReorderingSync(afterNote.parentNoteId, sourceId); - const branch = { - branchId: utils.newBranchId(), - noteId: noteId, - parentNoteId: afterNote.parentNoteId, - notePosition: afterNote.notePosition + 1, - isExpanded: 0, - dateModified: utils.nowDate(), - isDeleted: 0 - }; + const branch = { + branchId: utils.newBranchId(), + noteId: noteId, + parentNoteId: afterNote.parentNoteId, + notePosition: afterNote.notePosition + 1, + isExpanded: 0, + dateModified: utils.nowDate(), + isDeleted: 0 + }; - await sql.replace("branches", branch); + await sql.replace("branches", branch); - await sync_table.addBranchSync(branch.branchId, sourceId); - }); + await sync_table.addBranchSync(branch.branchId, sourceId); - res.send({ success: true }); -})); + return { success: true }; +} -module.exports = router; \ No newline at end of file +module.exports = { + cloneNoteToParent, + cloneNoteAfter +}; \ No newline at end of file diff --git a/src/routes/api/labels.js b/src/routes/api/labels.js index 5c796e940..d7c5712c7 100644 --- a/src/routes/api/labels.js +++ b/src/routes/api/labels.js @@ -1,59 +1,53 @@ "use strict"; -const express = require('express'); -const router = express.Router(); const sql = require('../../services/sql'); -const auth = require('../../services/auth'); const sync_table = require('../../services/sync_table'); const utils = require('../../services/utils'); -const wrap = require('express-promise-wrap').wrap; const labels = require('../../services/labels'); -router.get('/notes/:noteId/labels', auth.checkApiAuth, wrap(async (req, res, next) => { +async function getNoteLabels(req) { const noteId = req.params.noteId; - res.send(await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); -})); + return await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]); +} -router.put('/notes/:noteId/labels', auth.checkApiAuth, wrap(async (req, res, next) => { +async function updateNoteLabels(req, res, next) { const noteId = req.params.noteId; const labels = req.body; const now = utils.nowDate(); - await sql.doInTransaction(async () => { - for (const attr of labels) { - if (attr.labelId) { - await sql.execute("UPDATE labels SET name = ?, value = ?, dateModified = ?, isDeleted = ?, position = ? WHERE labelId = ?", - [attr.name, attr.value, now, attr.isDeleted, attr.position, attr.labelId]); - } - else { - // if it was "created" and then immediatelly deleted, we just don't create it at all - if (attr.isDeleted) { - continue; - } - - attr.labelId = utils.newLabelId(); - - await sql.insert("labels", { - labelId: attr.labelId, - noteId: noteId, - name: attr.name, - value: attr.value, - position: attr.position, - dateCreated: now, - dateModified: now, - isDeleted: false - }); - } - - await sync_table.addLabelSync(attr.labelId); + for (const attr of labels) { + if (attr.labelId) { + await sql.execute("UPDATE labels SET name = ?, value = ?, dateModified = ?, isDeleted = ?, position = ? WHERE labelId = ?", + [attr.name, attr.value, now, attr.isDeleted, attr.position, attr.labelId]); } - }); + else { + // if it was "created" and then immediatelly deleted, we just don't create it at all + if (attr.isDeleted) { + continue; + } - res.send(await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); -})); + attr.labelId = utils.newLabelId(); -router.get('/labels/names', auth.checkApiAuth, wrap(async (req, res, next) => { + await sql.insert("labels", { + labelId: attr.labelId, + noteId: noteId, + name: attr.name, + value: attr.value, + position: attr.position, + dateCreated: now, + dateModified: now, + isDeleted: false + }); + } + + await sync_table.addLabelSync(attr.labelId); + } + + return await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]); +} + +async function getAllLabelNames(req) { const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0"); for (const attr of labels.BUILTIN_LABELS) { @@ -64,15 +58,18 @@ router.get('/labels/names', auth.checkApiAuth, wrap(async (req, res, next) => { names.sort(); - res.send(names); -})); + return names; +} -router.get('/labels/values/:labelName', auth.checkApiAuth, wrap(async (req, res, next) => { +async function getValuesForLabel(req) { const labelName = req.params.labelName; - const values = await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]); + return await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]); +} - res.send(values); -})); - -module.exports = router; \ No newline at end of file +module.exports = { + getNoteLabels, + updateNoteLabels, + getAllLabelNames, + getValuesForLabel +}; \ No newline at end of file diff --git a/src/routes/api/tree_changes.js b/src/routes/api/tree_changes.js index 743743e81..a61143c20 100644 --- a/src/routes/api/tree_changes.js +++ b/src/routes/api/tree_changes.js @@ -1,14 +1,10 @@ "use strict"; -const express = require('express'); -const router = express.Router(); const sql = require('../../services/sql'); -const auth = require('../../services/auth'); const utils = require('../../services/utils'); const sync_table = require('../../services/sync_table'); const tree = require('../../services/tree'); const notes = require('../../services/notes'); -const wrap = require('express-promise-wrap').wrap; /** * Code in this file deals with moving and cloning note tree rows. Relationship between note and parent note is unique diff --git a/src/routes/routes.js b/src/routes/routes.js index 764e54e23..f704e36a5 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -32,6 +32,7 @@ const senderRoute = require('./api/sender'); const filesRoute = require('./api/file_upload'); const searchRoute = require('./api/search'); +const log = require('../services/log'); const express = require('express'); const router = express.Router(); const auth = require('../services/auth'); @@ -41,20 +42,29 @@ const sql = require('../services/sql'); function apiRoute(method, path, handler) { router[method](path, auth.checkApiAuth, async (req, res, next) => { try { - const resp = await cls.init(async () => { + const result = await cls.init(async () => { + cls.namespace.set('sourceId', req.headers.source_id); + return await sql.doInTransaction(async () => { return await handler(req, res, next); }); }); - if (Array.isArray(resp)) { - res.status(resp[0]).send(resp[1]); + // if it's an array and first element is integer then we consider this to be [statusCode, response] format + if (Array.isArray(result) && result.length > 0 && Number.isInteger(result[0])) { + const [statusCode, response] = result; + + res.status(statusCode).send(response); + + if (statusCode !== 200) { + log.info(`${method} ${path} returned ${statusCode} with response ${JSON.stringify(response)}`); + } } - else if (resp === undefined) { + else if (result === undefined) { res.status(200); } else { - res.status(200).send(resp); + res.status(200).send(result); } } catch (e) { @@ -88,8 +98,14 @@ function register(app) { apiRoute(PUT, '/api/notes/:noteId/protect-sub-tree/:isProtected', notesApiRoute.protectBranch); apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); - app.use('/api/notes', cloningApiRoute); - app.use('/api', labelsRoute); + apiRoute(PUT, '/api/notes/:childNoteId/clone-to/:parentNoteId', cloningApiRoute.cloneNoteToParent); + apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); + + apiRoute(GET, '/api/notes/:noteId/labels', labelsRoute.getNoteLabels); + apiRoute(PUT, '/api/notes/:noteId/labels', labelsRoute.updateNoteLabels); + apiRoute(GET, '/api/labels/names', labelsRoute.getAllLabelNames); + apiRoute(GET, '/api/labels/values/:labelName', labelsRoute.getValuesForLabel); + app.use('/api/notes-revisions', noteRevisionsApiRoute); app.use('/api/recent-changes', recentChangesApiRoute); app.use('/api/settings', settingsApiRoute); diff --git a/src/services/cls.js b/src/services/cls.js index 5bcb7b2bb..258b711dd 100644 --- a/src/services/cls.js +++ b/src/services/cls.js @@ -9,8 +9,13 @@ function wrap(callback) { return async () => await init(callback); } +function getSourceId() { + return namespace.get('sourceId'); +} + module.exports = { init, wrap, - namespace + namespace, + getSourceId }; \ No newline at end of file