mirror of
https://github.com/zadam/trilium.git
synced 2025-11-17 02:30:42 +01:00
added support for trilium-sender
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user