mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
commit
5c5e5603d8
17
db/migrations/0228__fix_blobIds.sql
Normal file
17
db/migrations/0228__fix_blobIds.sql
Normal file
@ -0,0 +1,17 @@
|
||||
-- + is normally replaced by X and / by Y, but this can temporarily cause UNIQUE key exception
|
||||
-- this might create blob duplicates, but cleanup will eventually take care of it
|
||||
|
||||
UPDATE blobs SET blobId = REPLACE(blobId, '+', 'A');
|
||||
UPDATE blobs SET blobId = REPLACE(blobId, '/', 'B');
|
||||
|
||||
UPDATE notes SET blobId = REPLACE(blobId, '+', 'A');
|
||||
UPDATE notes SET blobId = REPLACE(blobId, '/', 'B');
|
||||
|
||||
UPDATE attachments SET blobId = REPLACE(blobId, '+', 'A');
|
||||
UPDATE attachments SET blobId = REPLACE(blobId, '/', 'B');
|
||||
|
||||
UPDATE revisions SET blobId = REPLACE(blobId, '+', 'A');
|
||||
UPDATE revisions SET blobId = REPLACE(blobId, '/', 'B');
|
||||
|
||||
UPDATE entity_changes SET entityId = REPLACE(entityId, '+', 'A') WHERE entityName = 'blobs';
|
||||
UPDATE entity_changes SET entityId = REPLACE(entityId, '/', 'B') WHERE entityName = 'blobs';
|
1761
package-lock.json
generated
1761
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.62.4",
|
||||
"version": "0.63.0-beta",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@ -37,11 +37,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@electron/remote": "2.1.0",
|
||||
"@electron/remote": "2.1.1",
|
||||
"@excalidraw/excalidraw": "0.16.1",
|
||||
"archiver": "6.0.1",
|
||||
"async-mutex": "0.4.0",
|
||||
"axios": "1.6.2",
|
||||
"axios": "1.6.3",
|
||||
"better-sqlite3": "8.4.0",
|
||||
"boxicons": "2.1.4",
|
||||
"chokidar": "3.5.3",
|
||||
@ -59,10 +59,10 @@
|
||||
"escape-html": "1.0.3",
|
||||
"express": "4.18.2",
|
||||
"express-partial-content": "1.0.2",
|
||||
"express-rate-limit": "7.1.4",
|
||||
"express-rate-limit": "7.1.5",
|
||||
"express-session": "1.17.3",
|
||||
"force-graph": "1.43.4",
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-extra": "11.2.0",
|
||||
"helmet": "7.1.0",
|
||||
"html": "1.0.0",
|
||||
"html2plaintext": "2.1.4",
|
||||
@ -76,13 +76,13 @@
|
||||
"joplin-turndown-plugin-gfm": "1.0.12",
|
||||
"jquery": "3.7.1",
|
||||
"jquery-hotkeys": "0.2.2",
|
||||
"jsdom": "22.1.0",
|
||||
"jsdom": "23.0.1",
|
||||
"katex": "0.16.9",
|
||||
"marked": "9.1.6",
|
||||
"mermaid": "10.6.1",
|
||||
"mime-types": "2.1.35",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"node-abi": "3.51.0",
|
||||
"node-abi": "3.52.0",
|
||||
"normalize-strings": "1.1.1",
|
||||
"open": "8.4.1",
|
||||
"panzoom": "9.4.3",
|
||||
@ -106,31 +106,31 @@
|
||||
"tree-kill": "1.2.2",
|
||||
"turndown": "7.1.2",
|
||||
"unescape": "1.0.1",
|
||||
"ws": "8.14.2",
|
||||
"ws": "8.16.0",
|
||||
"xml2js": "0.6.2",
|
||||
"yauzl": "2.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "24.6.4",
|
||||
"electron-builder": "24.9.1",
|
||||
"electron-packager": "17.1.2",
|
||||
"electron-rebuild": "3.2.9",
|
||||
"eslint": "8.54.0",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-jsonc": "2.10.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-jsonc": "2.11.2",
|
||||
"eslint-plugin-prettier": "5.0.1",
|
||||
"esm": "3.2.25",
|
||||
"husky": "8.0.3",
|
||||
"jasmine": "5.1.0",
|
||||
"jsdoc": "4.0.2",
|
||||
"jsonc-eslint-parser": "2.4.0",
|
||||
"lint-staged": "15.1.0",
|
||||
"lint-staged": "15.2.0",
|
||||
"lorem-ipsum": "2.0.8",
|
||||
"nodemon": "3.0.1",
|
||||
"prettier": "3.1.0",
|
||||
"nodemon": "3.0.2",
|
||||
"prettier": "3.1.1",
|
||||
"rcedit": "4.0.1",
|
||||
"webpack": "5.89.0",
|
||||
"webpack-cli": "5.1.4"
|
||||
|
44
src/public/app/menus/image_context_menu.js
Normal file
44
src/public/app/menus/image_context_menu.js
Normal file
@ -0,0 +1,44 @@
|
||||
import utils from "../services/utils.js";
|
||||
import contextMenu from "./context_menu.js";
|
||||
import imageService from "../services/image.js";
|
||||
|
||||
const PROP_NAME = "imageContextMenuInstalled";
|
||||
|
||||
function setupContextMenu($image) {
|
||||
if (!utils.isElectron() || $image.prop(PROP_NAME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image.prop(PROP_NAME, true);
|
||||
$image.on('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
|
||||
contextMenu.show({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
{
|
||||
title: "Copy reference to clipboard",
|
||||
command: "copyImageReferenceToClipboard",
|
||||
uiIcon: "bx bx-empty"
|
||||
},
|
||||
{title: "Copy image to clipboard", command: "copyImageToClipboard", uiIcon: "bx bx-empty"},
|
||||
],
|
||||
selectMenuItemHandler: ({command}) => {
|
||||
if (command === 'copyImageReferenceToClipboard') {
|
||||
imageService.copyImageReferenceToClipboard($image);
|
||||
} else if (command === 'copyImageToClipboard') {
|
||||
const webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents();
|
||||
utils.dynamicRequire('electron');
|
||||
webContents.copyImageAt(e.pageX, e.pageY);
|
||||
} else {
|
||||
throw new Error(`Unrecognized command '${command}'`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
setupContextMenu
|
||||
};
|
@ -9,6 +9,7 @@ import linkService from "./link.js";
|
||||
import treeService from "./tree.js";
|
||||
import FNote from "../entities/fnote.js";
|
||||
import FAttachment from "../entities/fattachment.js";
|
||||
import imageContextMenuService from "../menus/image_context_menu.js";
|
||||
|
||||
let idCounter = 1;
|
||||
|
||||
@ -148,6 +149,8 @@ function renderImage(entity, $renderedContent, options = {}) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
imageContextMenuService.setupContextMenu($img);
|
||||
}
|
||||
|
||||
function renderFile(entity, type, $renderedContent) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import utils from "../../services/utils.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
import libraryLoader from "../../services/library_loader.js";
|
||||
import contextMenu from "../../menus/context_menu.js";
|
||||
import imageContextMenuService from "../../menus/image_context_menu.js";
|
||||
import imageService from "../../services/image.js";
|
||||
|
||||
const TPL = `
|
||||
@ -55,36 +55,7 @@ class ImageTypeWidget extends TypeWidget {
|
||||
});
|
||||
});
|
||||
|
||||
if (utils.isElectron()) {
|
||||
// for browser, we want to let the native menu
|
||||
this.$imageView.on('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
|
||||
contextMenu.show({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
{
|
||||
title: "Copy reference to clipboard",
|
||||
command: "copyImageReferenceToClipboard",
|
||||
uiIcon: "bx bx-empty"
|
||||
},
|
||||
{title: "Copy image to clipboard", command: "copyImageToClipboard", uiIcon: "bx bx-empty"},
|
||||
],
|
||||
selectMenuItemHandler: ({command}) => {
|
||||
if (command === 'copyImageReferenceToClipboard') {
|
||||
imageService.copyImageReferenceToClipboard(this.$imageWrapper);
|
||||
} else if (command === 'copyImageToClipboard') {
|
||||
const webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents();
|
||||
utils.dynamicRequire('electron');
|
||||
webContents.copyImageAt(e.pageX, e.pageY);
|
||||
} else {
|
||||
throw new Error(`Unrecognized command '${command}'`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
imageContextMenuService.setupContextMenu(this.$imageView);
|
||||
|
||||
super.doRender();
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ function register(app) {
|
||||
|
||||
// excalidraw-view mode in shared notes
|
||||
app.use(`/${assetPath}/node_modules/react/umd/react.production.min.js`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/react/umd/react.production.min.js')));
|
||||
app.use(`/${assetPath}/node_modules/react/umd/react.development.js`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/react/umd/react.development.js')));
|
||||
app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.production.min.js`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/react-dom/umd/react-dom.production.min.js')));
|
||||
app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.development.js`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/react-dom/umd/react-dom.development.js')));
|
||||
// expose the whole dist folder since complete assets are needed in edit and share
|
||||
app.use(`/node_modules/@excalidraw/excalidraw/dist/`, express.static(path.join(srcRoot, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
|
||||
app.use(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
|
||||
|
@ -39,7 +39,13 @@ function getLightAnonymizationScript() {
|
||||
SELECT blobId FROM notes WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
|
||||
UNION ALL
|
||||
SELECT blobId FROM revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
|
||||
);`;
|
||||
);
|
||||
|
||||
UPDATE options SET value = 'anonymized' WHERE name IN
|
||||
('documentId', 'documentSecret', 'encryptedDataKey',
|
||||
'passwordVerificationHash', 'passwordVerificationSalt',
|
||||
'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')
|
||||
AND value != '';`;
|
||||
}
|
||||
|
||||
async function createAnonymizedCopy(type) {
|
||||
|
@ -4,8 +4,8 @@ const build = require('./build.js');
|
||||
const packageJson = require('../../package.json');
|
||||
const {TRILIUM_DATA_DIR} = require('./data_dir.js');
|
||||
|
||||
const APP_DB_VERSION = 227;
|
||||
const SYNC_VERSION = 31;
|
||||
const APP_DB_VERSION = 228;
|
||||
const SYNC_VERSION = 32;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
module.exports = {
|
||||
|
@ -5,12 +5,14 @@ const utils = require('./utils.js');
|
||||
|
||||
function getBlobPojo(entityName, entityId) {
|
||||
const entity = becca.getEntity(entityName, entityId);
|
||||
|
||||
if (!entity) {
|
||||
throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`);
|
||||
}
|
||||
|
||||
const blob = becca.getBlob(entity);
|
||||
if (!blob) {
|
||||
throw new NotFoundError(`Blob ${entity.blobId} for ${entityName} '${entityId}' was not found.`);
|
||||
}
|
||||
|
||||
const pojo = blob.getPojo();
|
||||
|
||||
|
@ -1 +1 @@
|
||||
module.exports = { buildDate:"2023-12-07T00:03:59+01:00", buildRevision: "2e23c521c356c2305124f5df0f474532fa5f34ce" };
|
||||
module.exports = { buildDate:"2024-01-04T00:49:06+01:00", buildRevision: "394530921e3f7ddd852fa4ceb1ac4585447df35e" };
|
||||
|
@ -17,6 +17,9 @@ const {sanitizeAttributeName} = require('./sanitize_attribute_name.js');
|
||||
const noteTypes = require('../services/note_types.js').getNoteTypeNames();
|
||||
|
||||
class ConsistencyChecks {
|
||||
/**
|
||||
* @param autoFix - automatically fix all encountered problems. False is only for debugging during development (fail fast)
|
||||
*/
|
||||
constructor(autoFix) {
|
||||
this.autoFix = autoFix;
|
||||
this.unrecoveredConsistencyErrors = false;
|
||||
|
@ -37,6 +37,8 @@ function eraseNotes(noteIdsToErase) {
|
||||
function setEntityChangesAsErased(entityChanges) {
|
||||
for (const ec of entityChanges) {
|
||||
ec.isErased = true;
|
||||
// we're not changing hash here, not sure if good or not
|
||||
// content hash check takes isErased flag into account, though
|
||||
ec.utcDateChanged = dateUtils.utcNowDateTime();
|
||||
|
||||
entityChangesService.putEntityChangeWithForcedChange(ec);
|
||||
|
@ -73,8 +73,11 @@ async function migrate() {
|
||||
}
|
||||
});
|
||||
|
||||
log.info("VACUUMing database, this might take a while ...");
|
||||
sql.execute("VACUUM");
|
||||
if (currentDbVersion === 214) {
|
||||
// special VACUUM after the big migration
|
||||
log.info("VACUUMing database, this might take a while ...");
|
||||
sql.execute("VACUUM");
|
||||
}
|
||||
}
|
||||
|
||||
function executeMigration(mig) {
|
||||
|
@ -63,10 +63,15 @@ function exec(opts) {
|
||||
}
|
||||
|
||||
let responseStr = '';
|
||||
let chunks = [];
|
||||
|
||||
response.on('data', chunk => responseStr += chunk);
|
||||
response.on('data', chunk => chunks.push(chunk));
|
||||
|
||||
response.on('end', () => {
|
||||
// use Buffer instead of string concatenation to avoid implicit decoding for each chunk
|
||||
// decode the entire data chunks explicitly as utf-8
|
||||
responseStr = Buffer.concat(chunks).toString('utf-8')
|
||||
|
||||
if ([200, 201, 204].includes(response.statusCode)) {
|
||||
try {
|
||||
const jsonObj = responseStr.trim() ? JSON.parse(responseStr) : null;
|
||||
|
@ -91,12 +91,16 @@ function updateNormalEntity(remoteEC, remoteEntityRow, instanceId, updateContext
|
||||
updateContext.updated[remoteEC.entityName].push(remoteEC.entityId);
|
||||
}
|
||||
|
||||
if (!localEC || localEC.utcDateChanged < remoteEC.utcDateChanged || localEC.hash !== remoteEC.hash) {
|
||||
if (!localEC || localEC.utcDateChanged < remoteEC.utcDateChanged
|
||||
|| localEC.hash !== remoteEC.hash
|
||||
|| localEC.isErased !== remoteEC.isErased
|
||||
) {
|
||||
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (localEC.hash !== remoteEC.hash && localEC.utcDateChanged > remoteEC.utcDateChanged) {
|
||||
} else if ((localEC.hash !== remoteEC.hash || localEC.isErased !== remoteEC.isErased)
|
||||
&& localEC.utcDateChanged > remoteEC.utcDateChanged) {
|
||||
// the change on our side is newer than on the other side, so the other side should update
|
||||
entityChangesService.putEntityChangeForOtherInstances(localEC);
|
||||
|
||||
@ -148,7 +152,7 @@ function eraseEntity(entityChange) {
|
||||
];
|
||||
|
||||
if (!entityNames.includes(entityName)) {
|
||||
log.error(`Cannot erase entity '${entityName}', id '${entityId}'.`);
|
||||
log.error(`Cannot erase ${entityName} '${entityId}'.`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ function hashedBlobId(content) {
|
||||
|
||||
// we don't want such + and / in the IDs
|
||||
const kindaBase62Hash = base64Hash
|
||||
.replace('+', 'X')
|
||||
.replace('/', 'Y');
|
||||
.replaceAll('+', 'X')
|
||||
.replaceAll('/', 'Y');
|
||||
|
||||
// 20 characters of base62 gives us ~120 bit of entropy which is plenty enough
|
||||
return kindaBase62Hash.substr(0, 20);
|
||||
|
Loading…
x
Reference in New Issue
Block a user