added support for trilium-sender

This commit is contained in:
azivner
2018-02-11 00:18:59 -05:00
parent 660908c54b
commit 7b77e40514
15 changed files with 272 additions and 106 deletions

View File

@@ -3,7 +3,7 @@
const build = require('./build');
const packageJson = require('../../package');
const APP_DB_VERSION = 74;
const APP_DB_VERSION = 75;
module.exports = {
app_version: packageJson.version,

View File

@@ -223,6 +223,8 @@ async function runAllChecks() {
await runSyncRowChecks("recent_notes", "noteTreeId", errorList);
await runSyncRowChecks("images", "imageId", errorList);
await runSyncRowChecks("note_images", "noteImageId", errorList);
await runSyncRowChecks("attributes", "attributeId", errorList);
await runSyncRowChecks("api_tokens", "apiTokenId", errorList);
if (errorList.length === 0) {
// we run this only if basic checks passed since this assumes basic data consistency

108
src/services/image.js Normal file
View 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
};

View File

@@ -154,10 +154,10 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) {
note.isProtected = false;
}
const newnoteRevisionId = utils.newnoteRevisionId();
const newNoteRevisionId = utils.newNoteRevisionId();
await sql.insert('note_revisions', {
noteRevisionId: newnoteRevisionId,
noteRevisionId: newNoteRevisionId,
noteId: noteId,
// title and text should be decrypted now
title: oldNote.title,
@@ -167,7 +167,7 @@ async function saveNoteHistory(noteId, dataKey, sourceId, nowStr) {
dateModifiedTo: nowStr
});
await sync_table.addNoteHistorySync(newnoteRevisionId, sourceId);
await sync_table.addNoteHistorySync(newNoteRevisionId, sourceId);
}
async function saveNoteImages(noteId, noteText, sourceId) {

View File

@@ -149,6 +149,9 @@ async function pullSync(syncContext) {
else if (sync.entityName === 'attributes') {
await syncUpdate.updateAttribute(resp, syncContext.sourceId);
}
else if (sync.entityName === 'api_tokens') {
await syncUpdate.updateApiToken(resp, syncContext.sourceId);
}
else {
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') {
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 {
throw new Error(`Unrecognized entity type ${sync.entityName} in sync #${sync.id}`);
}

View File

@@ -40,6 +40,10 @@ async function addAttributeSync(attributeId, sourceId) {
await addEntitySync("attributes", attributeId, sourceId);
}
async function addApiTokenSync(apiTokenId, sourceId) {
await addEntitySync("api_tokens", apiTokenId, sourceId);
}
async function addEntitySync(entityName, entityId, sourceId) {
await sql.replace("sync", {
entityName: entityName,
@@ -93,6 +97,7 @@ async function fillAllSyncRows() {
await fillSyncRows("images", "imageId");
await fillSyncRows("note_images", "noteImageId");
await fillSyncRows("attributes", "attributeId");
await fillSyncRows("api_tokens", "apiTokenId");
}
module.exports = {
@@ -105,6 +110,7 @@ module.exports = {
addImageSync,
addNoteImageSync,
addAttributeSync,
addApiTokenSync,
addEntitySync,
cleanupSyncRowsForMissingEntities,
fillAllSyncRows

View File

@@ -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 = {
updateNote,
updateNoteTree,
@@ -146,5 +160,6 @@ module.exports = {
updateRecentNotes,
updateImage,
updateNoteImage,
updateAttribute
updateAttribute,
updateApiToken
};

View File

@@ -11,7 +11,7 @@ function newNoteTreeId() {
return randomString(12);
}
function newnoteRevisionId() {
function newNoteRevisionId() {
return randomString(12);
}
@@ -27,6 +27,10 @@ function newAttributeId() {
return randomString(12);
}
function newApiTokenId() {
return randomString(12);
}
function randomString(length) {
return randtoken.generate(length);
}
@@ -126,10 +130,11 @@ module.exports = {
parseDateTime,
newNoteId,
newNoteTreeId,
newnoteRevisionId,
newNoteRevisionId,
newImageId,
newNoteImageId,
newAttributeId,
newApiTokenId,
toBase64,
fromBase64,
hmac,