From a555b6319cc5f74bf0a79ef7934822b75484d4fe Mon Sep 17 00:00:00 2001 From: azivner Date: Sat, 24 Feb 2018 14:42:52 -0500 Subject: [PATCH] support for backend jobs and other script API changes --- src/entities/note.js | 42 +++++++++++++++++++++++++++ src/plugins/reddit.js | 2 +- src/public/javascripts/note_editor.js | 2 ++ src/public/javascripts/server.js | 12 ++++++++ src/routes/api/script.js | 6 ++++ src/services/date_notes.js | 6 ++-- src/services/script.js | 36 ++++++++++++++++++++++- src/services/script_context.js | 11 ++++--- 8 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/entities/note.js b/src/entities/note.js index f8787011d..3014b15b1 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -43,6 +43,48 @@ class Note extends Entity { return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); } + async getChild(name) { + return this.repository.getEntity(` + SELECT notes.* + FROM note_tree + JOIN notes USING(noteId) + WHERE notes.isDeleted = 0 + AND note_tree.isDeleted = 0 + AND note_tree.parentNoteId = ? + AND notes.title = ?`, [this.noteId, name]); + } + + async getChildren() { + return this.repository.getEntities(` + SELECT notes.* + FROM note_tree + JOIN notes USING(noteId) + WHERE notes.isDeleted = 0 + AND note_tree.isDeleted = 0 + AND note_tree.parentNoteId = ?`, [this.noteId]); + } + + async getParents() { + return this.repository.getEntities(` + SELECT parent_notes.* + FROM + note_tree AS child_tree + JOIN notes AS parent_notes ON parent_notes.noteId = child_tree.parentNoteId + WHERE child_tree.noteId = ? + AND child_tree.isDeleted = 0 + AND parent_notes.isDeleted = 0`, [this.noteId]); + } + + async getNoteTree() { + return this.repository.getEntities(` + SELECT note_tree.* + FROM note_tree + JOIN notes USING(noteId) + WHERE notes.isDeleted = 0 + AND note_tree.isDeleted = 0 + AND note_tree.noteId = ?`, [this.noteId]); + } + beforeSaving() { this.content = JSON.stringify(this.jsonContent, null, '\t'); diff --git a/src/plugins/reddit.js b/src/plugins/reddit.js index cf3d93e0c..18237eb88 100644 --- a/src/plugins/reddit.js +++ b/src/plugins/reddit.js @@ -109,7 +109,7 @@ karma: ${comment.score}, created at ${dateTimeStr}

` let redditAccounts = []; async function runImport() { - const rootNoteId = await date_notes.getRootNoteId(); + const rootNoteId = await date_notes.getRootCalendarNoteId(); // technically mutex shouldn't be necessary but we want to avoid doing potentially expensive import // concurrently with sync diff --git a/src/public/javascripts/note_editor.js b/src/public/javascripts/note_editor.js index 216ba3e48..616acd7ae 100644 --- a/src/public/javascripts/note_editor.js +++ b/src/public/javascripts/note_editor.js @@ -174,6 +174,8 @@ const noteEditor = (function() { codeEditor.setOption("mode", info.mime); CodeMirror.autoLoadMode(codeEditor, info.mode); } + + codeEditor.refresh(); } } diff --git a/src/public/javascripts/server.js b/src/public/javascripts/server.js index bbdab12f4..b6f2e335b 100644 --- a/src/public/javascripts/server.js +++ b/src/public/javascripts/server.js @@ -32,6 +32,10 @@ const server = (function() { } function prepareParams(params) { + if (!params) { + return params; + } + return params.map(p => { if (typeof p === "function") { return "!@#Function: " + p.toString(); @@ -52,6 +56,13 @@ const server = (function() { return ret.executionResult; } + async function setJob(opts) { + opts.job = opts.job.toString(); + opts.params = prepareParams(opts.params); + + await post('script/job', opts); + } + let i = 1; const reqResolves = {}; @@ -116,6 +127,7 @@ const server = (function() { put, remove, exec, + setJob, ajax, // don't remove, used from CKEditor image upload! getHeaders diff --git a/src/routes/api/script.js b/src/routes/api/script.js index ad09dfe12..b1674d8e6 100644 --- a/src/routes/api/script.js +++ b/src/routes/api/script.js @@ -17,6 +17,12 @@ router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => { }); })); +router.post('/job', auth.checkApiAuth, wrap(async (req, res, next) => { + await script.setJob(req.body); + + res.send({}); +})); + router.get('/startup', auth.checkApiAuth, wrap(async (req, res, next) => { const noteIds = await attributes.getNoteIdsWithAttribute("run_on_startup"); const repository = new Repository(req); diff --git a/src/services/date_notes.js b/src/services/date_notes.js index d93c868fd..a5e483ced 100644 --- a/src/services/date_notes.js +++ b/src/services/date_notes.js @@ -29,7 +29,7 @@ async function getNoteStartingWith(parentNoteId, startsWith) { AND note_tree.isDeleted = 0`, [parentNoteId]); } -async function getRootNoteId() { +async function getRootCalendarNoteId() { let rootNoteId = await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId) WHERE attributes.name = '${CALENDAR_ROOT_ATTRIBUTE}' AND notes.isDeleted = 0`); @@ -91,7 +91,7 @@ async function getMonthNoteId(dateTimeStr, rootNoteId) { async function getDateNoteId(dateTimeStr, rootNoteId = null) { if (!rootNoteId) { - rootNoteId = await getRootNoteId(); + rootNoteId = await getRootCalendarNoteId(); } const dateStr = dateTimeStr.substr(0, 10); @@ -119,7 +119,7 @@ async function getDateNoteId(dateTimeStr, rootNoteId = null) { } module.exports = { - getRootNoteId, + getRootCalendarNoteId, getYearNoteId, getMonthNoteId, getDateNoteId diff --git a/src/services/script.js b/src/services/script.js index 9b860d49d..00db4c1d0 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -18,7 +18,40 @@ async function executeScript(dataKey, script, params) { return ret; } +const timeouts = {}; +const intervals = {}; + +function clearExistingJob(name) { + if (timeouts[name]) { + clearTimeout(timeouts[name]); + + delete timeouts[name]; + } + + if (intervals[name]) { + clearInterval(intervals[name]); + + delete intervals[name]; + } +} + +async function setJob(opts) { + clearExistingJob(opts.name); + + if (opts.runEveryMs && opts.runEveryMs > 0) { + intervals[opts.name] = setInterval(() => executeScript(null, opts.job, opts.params), opts.runEveryMs); + } + + if (opts.initialRunAfterMs && opts.initialRunAfterMs > 0) { + timeouts[opts.name] = setTimeout(() => executeScript(null, opts.job, opts.params), opts.initialRunAfterMs); + } +} + function getParams(params) { + if (!params) { + return params; + } + return params.map(p => { if (typeof p === "string" && p.startsWith("!@#Function: ")) { return p.substr(13); @@ -30,5 +63,6 @@ function getParams(params) { } module.exports = { - executeScript + executeScript, + setJob }; \ No newline at end of file diff --git a/src/services/script_context.js b/src/services/script_context.js index a4da9b55c..ce508e612 100644 --- a/src/services/script_context.js +++ b/src/services/script_context.js @@ -23,10 +23,10 @@ function ScriptContext(noteId, dataKey) { return notes.length > 0 ? notes[0] : null; }; - this.createNote = async function (parentNoteId, name, jsonContent, extraOptions = {}) { + this.createNote = async function (parentNoteId, title, content = "", extraOptions = {}) { const note = { - title: name, - content: extraOptions.json ? JSON.stringify(jsonContent, null, '\t') : jsonContent, + title: title, + content: extraOptions.json ? JSON.stringify(content, null, '\t') : content, target: 'into', isProtected: extraOptions.isProtected !== undefined ? extraOptions.isProtected : false, type: extraOptions.type, @@ -58,10 +58,9 @@ function ScriptContext(noteId, dataKey) { this.updateEntity = this.repository.updateEntity; - this.log = function(message) { - log.info(`Script: ${message}`); - }; + this.log = message => log.info(`Script: ${message}`); + this.getRootCalendarNoteId = date_notes.getRootCalendarNoteId; this.getDateNoteId = date_notes.getDateNoteId; }