mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
added support for trilium-sender
This commit is contained in:
parent
660908c54b
commit
7b77e40514
7
db/migrations/0075__add_api_token.sql
Normal file
7
db/migrations/0075__add_api_token.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "api_tokens"
|
||||||
|
(
|
||||||
|
apiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
dateCreated TEXT NOT NULL,
|
||||||
|
isDeleted INT NOT NULL DEFAULT 0
|
||||||
|
);
|
@ -119,3 +119,11 @@ CREATE INDEX IDX_note_images_noteId ON note_images (noteId);
|
|||||||
CREATE INDEX IDX_note_images_imageId ON note_images (imageId);
|
CREATE INDEX IDX_note_images_imageId ON note_images (imageId);
|
||||||
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
|
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
|
||||||
CREATE INDEX IDX_attributes_noteId ON attributes (noteId);
|
CREATE INDEX IDX_attributes_noteId ON attributes (noteId);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "api_tokens"
|
||||||
|
(
|
||||||
|
apiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
dateCreated TEXT NOT NULL,
|
||||||
|
isDeleted INT NOT NULL DEFAULT 0
|
||||||
|
);
|
@ -4,16 +4,8 @@ const express = require('express');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const auth = require('../../services/auth');
|
const auth = require('../../services/auth');
|
||||||
const utils = require('../../services/utils');
|
const image = require('../../services/image');
|
||||||
const sync_table = require('../../services/sync_table');
|
|
||||||
const multer = require('multer')();
|
const multer = require('multer')();
|
||||||
const imagemin = require('imagemin');
|
|
||||||
const imageminMozJpeg = require('imagemin-mozjpeg');
|
|
||||||
const imageminPngQuant = require('imagemin-pngquant');
|
|
||||||
const imageminGifLossy = require('imagemin-giflossy');
|
|
||||||
const jimp = require('jimp');
|
|
||||||
const imageType = require('image-type');
|
|
||||||
const sanitizeFilename = require('sanitize-filename');
|
|
||||||
const wrap = require('express-promise-wrap').wrap;
|
const wrap = require('express-promise-wrap').wrap;
|
||||||
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
|
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@ -49,45 +41,7 @@ router.post('', auth.checkApiAuthOrElectron, multer.single('upload'), wrap(async
|
|||||||
return res.status(400).send("Unknown image type: " + file.mimetype);
|
return res.status(400).send("Unknown image type: " + file.mimetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = utils.nowDate();
|
const {fileName, imageId} = await image.saveImage(file, sourceId, noteId);
|
||||||
|
|
||||||
const resizedImage = await resize(file.buffer);
|
|
||||||
const optimizedImage = await optimize(resizedImage);
|
|
||||||
|
|
||||||
const imageFormat = imageType(optimizedImage);
|
|
||||||
|
|
||||||
const fileNameWithouExtension = file.originalname.replace(/\.[^/.]+$/, "");
|
|
||||||
const fileName = sanitizeFilename(fileNameWithouExtension + "." + imageFormat.ext);
|
|
||||||
|
|
||||||
const imageId = utils.newImageId();
|
|
||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
|
||||||
await sql.insert("images", {
|
|
||||||
imageId: imageId,
|
|
||||||
format: imageFormat.ext,
|
|
||||||
name: fileName,
|
|
||||||
checksum: utils.hash(optimizedImage),
|
|
||||||
data: optimizedImage,
|
|
||||||
isDeleted: 0,
|
|
||||||
dateModified: now,
|
|
||||||
dateCreated: now
|
|
||||||
});
|
|
||||||
|
|
||||||
await sync_table.addImageSync(imageId, sourceId);
|
|
||||||
|
|
||||||
const noteImageId = utils.newNoteImageId();
|
|
||||||
|
|
||||||
await sql.insert("note_images", {
|
|
||||||
noteImageId: noteImageId,
|
|
||||||
noteId: noteId,
|
|
||||||
imageId: imageId,
|
|
||||||
isDeleted: 0,
|
|
||||||
dateModified: now,
|
|
||||||
dateCreated: now
|
|
||||||
});
|
|
||||||
|
|
||||||
await sync_table.addNoteImageSync(noteImageId, sourceId);
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
uploaded: true,
|
uploaded: true,
|
||||||
@ -95,54 +49,4 @@ router.post('', auth.checkApiAuthOrElectron, multer.single('upload'), wrap(async
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MAX_SIZE = 1000;
|
|
||||||
const MAX_BYTE_SIZE = 200000; // images should have under 100 KBs
|
|
||||||
|
|
||||||
async function resize(buffer) {
|
|
||||||
const image = await jimp.read(buffer);
|
|
||||||
|
|
||||||
if (image.bitmap.width > image.bitmap.height && image.bitmap.width > MAX_SIZE) {
|
|
||||||
image.resize(MAX_SIZE, jimp.AUTO);
|
|
||||||
}
|
|
||||||
else if (image.bitmap.height > MAX_SIZE) {
|
|
||||||
image.resize(jimp.AUTO, MAX_SIZE);
|
|
||||||
}
|
|
||||||
else if (buffer.byteLength <= MAX_BYTE_SIZE) {
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do resizing with max quality which will be trimmed during optimization step next
|
|
||||||
image.quality(100);
|
|
||||||
|
|
||||||
// when converting PNG to JPG we lose alpha channel, this is replaced by white to match Trilium white background
|
|
||||||
image.background(0xFFFFFFFF);
|
|
||||||
|
|
||||||
// getBuffer doesn't support promises so this workaround
|
|
||||||
return await new Promise((resolve, reject) => image.getBuffer(jimp.MIME_JPEG, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function optimize(buffer) {
|
|
||||||
return await imagemin.buffer(buffer, {
|
|
||||||
plugins: [
|
|
||||||
imageminMozJpeg({
|
|
||||||
quality: 50
|
|
||||||
}),
|
|
||||||
imageminPngQuant({
|
|
||||||
quality: "0-70"
|
|
||||||
}),
|
|
||||||
imageminGifLossy({
|
|
||||||
lossy: 80,
|
|
||||||
optimize: '3' // needs to be string
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -66,7 +66,7 @@ async function importNotes(dir, parentNoteId) {
|
|||||||
const noteText = fs.readFileSync(path, "utf8");
|
const noteText = fs.readFileSync(path, "utf8");
|
||||||
|
|
||||||
const noteId = utils.newNoteId();
|
const noteId = utils.newNoteId();
|
||||||
const noteTreeId = utils.newnoteRevisionId();
|
const noteTreeId = utils.newNoteRevisionId();
|
||||||
|
|
||||||
const now = utils.nowDate();
|
const now = utils.nowDate();
|
||||||
|
|
||||||
|
91
src/routes/api/sender.js
Normal file
91
src/routes/api/sender.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const image = require('../../services/image');
|
||||||
|
const utils = require('../../services/utils');
|
||||||
|
const date_notes = require('../../services/date_notes');
|
||||||
|
const sql = require('../../services/sql');
|
||||||
|
const wrap = require('express-promise-wrap').wrap;
|
||||||
|
const notes = require('../../services/notes');
|
||||||
|
const multer = require('multer')();
|
||||||
|
const password_encryption = require('../../services/password_encryption');
|
||||||
|
const options = require('../../services/options');
|
||||||
|
const sync_table = require('../../services/sync_table');
|
||||||
|
|
||||||
|
router.post('/login', wrap(async (req, res, next) => {
|
||||||
|
const username = req.body.username;
|
||||||
|
const password = req.body.password;
|
||||||
|
|
||||||
|
const isUsernameValid = username === await options.getOption('username');
|
||||||
|
const isPasswordValid = await password_encryption.verifyPassword(password);
|
||||||
|
|
||||||
|
if (!isUsernameValid || !isPasswordValid) {
|
||||||
|
res.status(401).send("Incorrect username/password");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const token = utils.randomSecureToken();
|
||||||
|
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
const apiTokenId = utils.newApiTokenId();
|
||||||
|
|
||||||
|
await sql.insert("api_tokens", {
|
||||||
|
apiTokenId: apiTokenId,
|
||||||
|
token: token,
|
||||||
|
dateCreated: utils.nowDate(),
|
||||||
|
isDeleted: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await sync_table.addApiTokenSync(apiTokenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
async function checkSenderToken(req, res, next) {
|
||||||
|
const token = req.headers.authorization;
|
||||||
|
|
||||||
|
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
|
||||||
|
res.status(401).send("Not authorized");
|
||||||
|
}
|
||||||
|
else if (await sql.isDbUpToDate()) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(409).send("Mismatched app versions"); // need better response than that
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post('/image', checkSenderToken, multer.single('upload'), wrap(async (req, res, next) => {
|
||||||
|
const file = req.file;
|
||||||
|
|
||||||
|
if (!["image/png", "image/jpeg", "image/gif"].includes(file.mimetype)) {
|
||||||
|
return res.status(400).send("Unknown image type: " + file.mimetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentNoteId = await date_notes.getDateNoteId(utils.nowDate());
|
||||||
|
|
||||||
|
const noteId = (await notes.createNewNote(parentNoteId, {
|
||||||
|
title: "Sender image",
|
||||||
|
content: "",
|
||||||
|
target: 'into',
|
||||||
|
isProtected: false,
|
||||||
|
type: 'text',
|
||||||
|
mime: 'text/html'
|
||||||
|
})).noteId;
|
||||||
|
|
||||||
|
const {fileName, imageId} = await image.saveImage(file, null, noteId);
|
||||||
|
|
||||||
|
const url = `/api/images/${imageId}/${fileName}`;
|
||||||
|
|
||||||
|
const content = `<img src="${url}"/>`;
|
||||||
|
|
||||||
|
await sql.execute("UPDATE notes SET content = ? WHERE noteId = ?", [content, noteId]);
|
||||||
|
|
||||||
|
res.send({});
|
||||||
|
}));
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -147,6 +147,12 @@ router.get('/attributes/:attributeId', auth.checkApiAuth, wrap(async (req, res,
|
|||||||
res.send(await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]));
|
res.send(await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
router.get('/api_tokens/:apiTokenId', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
|
const apiTokenId = req.params.apiTokenId;
|
||||||
|
|
||||||
|
res.send(await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [apiTokenId]));
|
||||||
|
}));
|
||||||
|
|
||||||
router.put('/notes', auth.checkApiAuth, wrap(async (req, res, next) => {
|
router.put('/notes', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
await syncUpdate.updateNote(req.body.entity, req.body.sourceId);
|
await syncUpdate.updateNote(req.body.entity, req.body.sourceId);
|
||||||
|
|
||||||
@ -201,4 +207,10 @@ router.put('/attributes', auth.checkApiAuth, wrap(async (req, res, next) => {
|
|||||||
res.send({});
|
res.send({});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
router.put('/api_tokens', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||||
|
await syncUpdate.updateApiToken(req.body.entity, req.body.sourceId);
|
||||||
|
|
||||||
|
res.send({});
|
||||||
|
}));
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -28,6 +28,7 @@ const cleanupRoute = require('./api/cleanup');
|
|||||||
const imageRoute = require('./api/image');
|
const imageRoute = require('./api/image');
|
||||||
const attributesRoute = require('./api/attributes');
|
const attributesRoute = require('./api/attributes');
|
||||||
const scriptRoute = require('./api/script');
|
const scriptRoute = require('./api/script');
|
||||||
|
const senderRoute = require('./api/sender');
|
||||||
|
|
||||||
function register(app) {
|
function register(app) {
|
||||||
app.use('/', indexRoute);
|
app.use('/', indexRoute);
|
||||||
@ -59,6 +60,7 @@ function register(app) {
|
|||||||
app.use('/api/cleanup', cleanupRoute);
|
app.use('/api/cleanup', cleanupRoute);
|
||||||
app.use('/api/images', imageRoute);
|
app.use('/api/images', imageRoute);
|
||||||
app.use('/api/script', scriptRoute);
|
app.use('/api/script', scriptRoute);
|
||||||
|
app.use('/api/sender', senderRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 74;
|
const APP_DB_VERSION = 75;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
app_version: packageJson.version,
|
app_version: packageJson.version,
|
||||||
|
@ -223,6 +223,8 @@ async function runAllChecks() {
|
|||||||
await runSyncRowChecks("recent_notes", "noteTreeId", errorList);
|
await runSyncRowChecks("recent_notes", "noteTreeId", errorList);
|
||||||
await runSyncRowChecks("images", "imageId", errorList);
|
await runSyncRowChecks("images", "imageId", errorList);
|
||||||
await runSyncRowChecks("note_images", "noteImageId", errorList);
|
await runSyncRowChecks("note_images", "noteImageId", errorList);
|
||||||
|
await runSyncRowChecks("attributes", "attributeId", errorList);
|
||||||
|
await runSyncRowChecks("api_tokens", "apiTokenId", errorList);
|
||||||
|
|
||||||
if (errorList.length === 0) {
|
if (errorList.length === 0) {
|
||||||
// we run this only if basic checks passed since this assumes basic data consistency
|
// we run this only if basic checks passed since this assumes basic data consistency
|
||||||
|
108
src/services/image.js
Normal file
108
src/services/image.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const utils = require('./utils');
|
||||||
|
const sql = require('./sql');
|
||||||
|
const sync_table = require('./sync_table');
|
||||||
|
const imagemin = require('imagemin');
|
||||||
|
const imageminMozJpeg = require('imagemin-mozjpeg');
|
||||||
|
const imageminPngQuant = require('imagemin-pngquant');
|
||||||
|
const imageminGifLossy = require('imagemin-giflossy');
|
||||||
|
const jimp = require('jimp');
|
||||||
|
const imageType = require('image-type');
|
||||||
|
const sanitizeFilename = require('sanitize-filename');
|
||||||
|
|
||||||
|
async function saveImage(file, sourceId, noteId) {
|
||||||
|
const resizedImage = await resize(file.buffer);
|
||||||
|
const optimizedImage = await optimize(resizedImage);
|
||||||
|
|
||||||
|
const imageFormat = imageType(optimizedImage);
|
||||||
|
|
||||||
|
const fileNameWithouExtension = file.originalname.replace(/\.[^/.]+$/, "");
|
||||||
|
const fileName = sanitizeFilename(fileNameWithouExtension + "." + imageFormat.ext);
|
||||||
|
|
||||||
|
const imageId = utils.newImageId();
|
||||||
|
const now = utils.nowDate();
|
||||||
|
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
await sql.insert("images", {
|
||||||
|
imageId: imageId,
|
||||||
|
format: imageFormat.ext,
|
||||||
|
name: fileName,
|
||||||
|
checksum: utils.hash(optimizedImage),
|
||||||
|
data: optimizedImage,
|
||||||
|
isDeleted: 0,
|
||||||
|
dateModified: now,
|
||||||
|
dateCreated: now
|
||||||
|
});
|
||||||
|
|
||||||
|
await sync_table.addImageSync(imageId, sourceId);
|
||||||
|
|
||||||
|
const noteImageId = utils.newNoteImageId();
|
||||||
|
|
||||||
|
await sql.insert("note_images", {
|
||||||
|
noteImageId: noteImageId,
|
||||||
|
noteId: noteId,
|
||||||
|
imageId: imageId,
|
||||||
|
isDeleted: 0,
|
||||||
|
dateModified: now,
|
||||||
|
dateCreated: now
|
||||||
|
});
|
||||||
|
|
||||||
|
await sync_table.addNoteImageSync(noteImageId, sourceId);
|
||||||
|
});
|
||||||
|
return {fileName, imageId};
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_SIZE = 1000;
|
||||||
|
const MAX_BYTE_SIZE = 200000; // images should have under 100 KBs
|
||||||
|
|
||||||
|
async function resize(buffer) {
|
||||||
|
const image = await jimp.read(buffer);
|
||||||
|
|
||||||
|
if (image.bitmap.width > image.bitmap.height && image.bitmap.width > MAX_SIZE) {
|
||||||
|
image.resize(MAX_SIZE, jimp.AUTO);
|
||||||
|
}
|
||||||
|
else if (image.bitmap.height > MAX_SIZE) {
|
||||||
|
image.resize(jimp.AUTO, MAX_SIZE);
|
||||||
|
}
|
||||||
|
else if (buffer.byteLength <= MAX_BYTE_SIZE) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do resizing with max quality which will be trimmed during optimization step next
|
||||||
|
image.quality(100);
|
||||||
|
|
||||||
|
// when converting PNG to JPG we lose alpha channel, this is replaced by white to match Trilium white background
|
||||||
|
image.background(0xFFFFFFFF);
|
||||||
|
|
||||||
|
// getBuffer doesn't support promises so this workaround
|
||||||
|
return await new Promise((resolve, reject) => image.getBuffer(jimp.MIME_JPEG, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function optimize(buffer) {
|
||||||
|
return await imagemin.buffer(buffer, {
|
||||||
|
plugins: [
|
||||||
|
imageminMozJpeg({
|
||||||
|
quality: 50
|
||||||
|
}),
|
||||||
|
imageminPngQuant({
|
||||||
|
quality: "0-70"
|
||||||
|
}),
|
||||||
|
imageminGifLossy({
|
||||||
|
lossy: 80,
|
||||||
|
optimize: '3' // needs to be string
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
saveImage
|
||||||
|
};
|
@ -154,10 +154,10 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) {
|
|||||||
note.isProtected = false;
|
note.isProtected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newnoteRevisionId = utils.newnoteRevisionId();
|
const newNoteRevisionId = utils.newNoteRevisionId();
|
||||||
|
|
||||||
await sql.insert('note_revisions', {
|
await sql.insert('note_revisions', {
|
||||||
noteRevisionId: newnoteRevisionId,
|
noteRevisionId: newNoteRevisionId,
|
||||||
noteId: noteId,
|
noteId: noteId,
|
||||||
// title and text should be decrypted now
|
// title and text should be decrypted now
|
||||||
title: oldNote.title,
|
title: oldNote.title,
|
||||||
@ -167,7 +167,7 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) {
|
|||||||
dateModifiedTo: nowStr
|
dateModifiedTo: nowStr
|
||||||
});
|
});
|
||||||
|
|
||||||
await sync_table.addNoteHistorySync(newnoteRevisionId, sourceId);
|
await sync_table.addNoteHistorySync(newNoteRevisionId, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveNoteImages(noteId, noteText, sourceId) {
|
async function saveNoteImages(noteId, noteText, sourceId) {
|
||||||
|
@ -149,6 +149,9 @@ async function pullSync(syncContext) {
|
|||||||
else if (sync.entityName === 'attributes') {
|
else if (sync.entityName === 'attributes') {
|
||||||
await syncUpdate.updateAttribute(resp, syncContext.sourceId);
|
await syncUpdate.updateAttribute(resp, syncContext.sourceId);
|
||||||
}
|
}
|
||||||
|
else if (sync.entityName === 'api_tokens') {
|
||||||
|
await syncUpdate.updateApiToken(resp, syncContext.sourceId);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error(`Unrecognized entity type ${sync.entityName} in sync #${sync.id}`);
|
throw new Error(`Unrecognized entity type ${sync.entityName} in sync #${sync.id}`);
|
||||||
}
|
}
|
||||||
@ -233,6 +236,9 @@ async function pushEntity(sync, syncContext) {
|
|||||||
else if (sync.entityName === 'attributes') {
|
else if (sync.entityName === 'attributes') {
|
||||||
entity = await sql.getRow('SELECT * FROM attributes WHERE attributeId = ?', [sync.entityId]);
|
entity = await sql.getRow('SELECT * FROM attributes WHERE attributeId = ?', [sync.entityId]);
|
||||||
}
|
}
|
||||||
|
else if (sync.entityName === 'api_tokens') {
|
||||||
|
entity = await sql.getRow('SELECT * FROM api_tokens WHERE apiTokenId = ?', [sync.entityId]);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error(`Unrecognized entity type ${sync.entityName} in sync #${sync.id}`);
|
throw new Error(`Unrecognized entity type ${sync.entityName} in sync #${sync.id}`);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,10 @@ async function addAttributeSync(attributeId, sourceId) {
|
|||||||
await addEntitySync("attributes", attributeId, sourceId);
|
await addEntitySync("attributes", attributeId, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addApiTokenSync(apiTokenId, sourceId) {
|
||||||
|
await addEntitySync("api_tokens", apiTokenId, sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
async function addEntitySync(entityName, entityId, sourceId) {
|
async function addEntitySync(entityName, entityId, sourceId) {
|
||||||
await sql.replace("sync", {
|
await sql.replace("sync", {
|
||||||
entityName: entityName,
|
entityName: entityName,
|
||||||
@ -93,6 +97,7 @@ async function fillAllSyncRows() {
|
|||||||
await fillSyncRows("images", "imageId");
|
await fillSyncRows("images", "imageId");
|
||||||
await fillSyncRows("note_images", "noteImageId");
|
await fillSyncRows("note_images", "noteImageId");
|
||||||
await fillSyncRows("attributes", "attributeId");
|
await fillSyncRows("attributes", "attributeId");
|
||||||
|
await fillSyncRows("api_tokens", "apiTokenId");
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -105,6 +110,7 @@ module.exports = {
|
|||||||
addImageSync,
|
addImageSync,
|
||||||
addNoteImageSync,
|
addNoteImageSync,
|
||||||
addAttributeSync,
|
addAttributeSync,
|
||||||
|
addApiTokenSync,
|
||||||
addEntitySync,
|
addEntitySync,
|
||||||
cleanupSyncRowsForMissingEntities,
|
cleanupSyncRowsForMissingEntities,
|
||||||
fillAllSyncRows
|
fillAllSyncRows
|
||||||
|
@ -137,6 +137,20 @@ async function updateAttribute(entity, sourceId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateApiToken(entity, sourceId) {
|
||||||
|
const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]);
|
||||||
|
|
||||||
|
if (!apiTokenId) {
|
||||||
|
await sql.doInTransaction(async () => {
|
||||||
|
await sql.replace("api_tokens", entity);
|
||||||
|
|
||||||
|
await sync_table.addApiTokenSync(entity.apiTokenId, sourceId);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("Update/sync API token " + entity.apiTokenId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateNote,
|
updateNote,
|
||||||
updateNoteTree,
|
updateNoteTree,
|
||||||
@ -146,5 +160,6 @@ module.exports = {
|
|||||||
updateRecentNotes,
|
updateRecentNotes,
|
||||||
updateImage,
|
updateImage,
|
||||||
updateNoteImage,
|
updateNoteImage,
|
||||||
updateAttribute
|
updateAttribute,
|
||||||
|
updateApiToken
|
||||||
};
|
};
|
@ -11,7 +11,7 @@ function newNoteTreeId() {
|
|||||||
return randomString(12);
|
return randomString(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
function newnoteRevisionId() {
|
function newNoteRevisionId() {
|
||||||
return randomString(12);
|
return randomString(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +27,10 @@ function newAttributeId() {
|
|||||||
return randomString(12);
|
return randomString(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function newApiTokenId() {
|
||||||
|
return randomString(12);
|
||||||
|
}
|
||||||
|
|
||||||
function randomString(length) {
|
function randomString(length) {
|
||||||
return randtoken.generate(length);
|
return randtoken.generate(length);
|
||||||
}
|
}
|
||||||
@ -126,10 +130,11 @@ module.exports = {
|
|||||||
parseDateTime,
|
parseDateTime,
|
||||||
newNoteId,
|
newNoteId,
|
||||||
newNoteTreeId,
|
newNoteTreeId,
|
||||||
newnoteRevisionId,
|
newNoteRevisionId,
|
||||||
newImageId,
|
newImageId,
|
||||||
newNoteImageId,
|
newNoteImageId,
|
||||||
newAttributeId,
|
newAttributeId,
|
||||||
|
newApiTokenId,
|
||||||
toBase64,
|
toBase64,
|
||||||
fromBase64,
|
fromBase64,
|
||||||
hmac,
|
hmac,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user