mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
safe import implementation
This commit is contained in:
parent
caa7dd9619
commit
14f7a8b7b9
@ -12,6 +12,7 @@ const $fileUploadInput = $("#import-file-upload-input");
|
|||||||
const $importProgressCountWrapper = $("#import-progress-count-wrapper");
|
const $importProgressCountWrapper = $("#import-progress-count-wrapper");
|
||||||
const $importProgressCount = $("#import-progress-count");
|
const $importProgressCount = $("#import-progress-count");
|
||||||
const $importButton = $("#import-button");
|
const $importButton = $("#import-button");
|
||||||
|
const $safeImport = $("#safe-import");
|
||||||
|
|
||||||
let importId;
|
let importId;
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ async function showDialog() {
|
|||||||
$importProgressCountWrapper.hide();
|
$importProgressCountWrapper.hide();
|
||||||
$importProgressCount.text('0');
|
$importProgressCount.text('0');
|
||||||
$fileUploadInput.val('').change(); // to trigger Import button disabling listener below
|
$fileUploadInput.val('').change(); // to trigger Import button disabling listener below
|
||||||
|
$safeImport.attr("checked", "checked");
|
||||||
|
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
|
|
||||||
@ -49,8 +51,10 @@ function importIntoNote(importNoteId) {
|
|||||||
// dialog (which shouldn't happen, but still ...)
|
// dialog (which shouldn't happen, but still ...)
|
||||||
importId = utils.randomString(10);
|
importId = utils.randomString(10);
|
||||||
|
|
||||||
|
const safeImport = $safeImport.is(":checked") ? 1 : 0;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId,
|
url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport,
|
||||||
headers: server.getHeaders(),
|
headers: server.getHeaders(),
|
||||||
data: formData,
|
data: formData,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
@ -12,10 +12,13 @@ const noteCacheService = require('../../services/note_cache');
|
|||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
|
|
||||||
class ImportContext {
|
class ImportContext {
|
||||||
constructor(importId) {
|
constructor(importId, safeImport) {
|
||||||
// importId is to distinguish between different import events - it is possible (though not recommended)
|
// importId is to distinguish between different import events - it is possible (though not recommended)
|
||||||
// to have multiple imports going at the same time
|
// to have multiple imports going at the same time
|
||||||
this.importId = importId;
|
this.importId = importId;
|
||||||
|
|
||||||
|
this.safeImport = safeImport;
|
||||||
|
|
||||||
// // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
|
// // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
|
||||||
this.progressCount = 0;
|
this.progressCount = 0;
|
||||||
this.lastSentCountTs = Date.now();
|
this.lastSentCountTs = Date.now();
|
||||||
@ -53,7 +56,10 @@ class ImportContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function importToBranch(req) {
|
async function importToBranch(req) {
|
||||||
const {parentNoteId, importId} = req.params;
|
let {parentNoteId, importId, safeImport} = req.params;
|
||||||
|
|
||||||
|
safeImport = safeImport !== '0';
|
||||||
|
|
||||||
const file = req.file;
|
const file = req.file;
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@ -74,7 +80,7 @@ async function importToBranch(req) {
|
|||||||
|
|
||||||
let note; // typically root of the import - client can show it after finishing the import
|
let note; // typically root of the import - client can show it after finishing the import
|
||||||
|
|
||||||
const importContext = new ImportContext(importId);
|
const importContext = new ImportContext(importId, safeImport);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (extension === '.tar') {
|
if (extension === '.tar') {
|
||||||
|
@ -129,7 +129,7 @@ function register(app) {
|
|||||||
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
||||||
|
|
||||||
route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
||||||
route(POST, '/api/notes/:parentNoteId/import/:importId', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
|
route(POST, '/api/notes/:parentNoteId/import/:importId/safe/:safeImport', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
|
||||||
|
|
||||||
route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware],
|
route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware],
|
||||||
filesRoute.uploadFile, apiResultHandler);
|
filesRoute.uploadFile, apiResultHandler);
|
||||||
|
@ -5,13 +5,14 @@ const sql = require('./sql');
|
|||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const Attribute = require('../entities/attribute');
|
const Attribute = require('../entities/attribute');
|
||||||
|
|
||||||
|
const ATTRIBUTE_TYPES = [ 'label', 'label-definition', 'relation', 'relation-definition' ];
|
||||||
|
|
||||||
const BUILTIN_ATTRIBUTES = [
|
const BUILTIN_ATTRIBUTES = [
|
||||||
// label names
|
// label names
|
||||||
{ type: 'label', name: 'disableVersioning' },
|
{ type: 'label', name: 'disableVersioning' },
|
||||||
{ type: 'label', name: 'calendarRoot' },
|
{ type: 'label', name: 'calendarRoot' },
|
||||||
{ type: 'label', name: 'archived' },
|
{ type: 'label', name: 'archived' },
|
||||||
{ type: 'label', name: 'excludeFromExport' },
|
{ type: 'label', name: 'excludeFromExport' },
|
||||||
{ type: 'label', name: 'run' },
|
|
||||||
{ type: 'label', name: 'manualTransactionHandling' },
|
{ type: 'label', name: 'manualTransactionHandling' },
|
||||||
{ type: 'label', name: 'disableInclusion' },
|
{ type: 'label', name: 'disableInclusion' },
|
||||||
{ type: 'label', name: 'appCss' },
|
{ type: 'label', name: 'appCss' },
|
||||||
@ -19,19 +20,20 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'label', name: 'hideChildrenOverview' },
|
{ type: 'label', name: 'hideChildrenOverview' },
|
||||||
{ type: 'label', name: 'hidePromotedAttributes' },
|
{ type: 'label', name: 'hidePromotedAttributes' },
|
||||||
{ type: 'label', name: 'readOnly' },
|
{ type: 'label', name: 'readOnly' },
|
||||||
{ type: 'label', name: 'customRequestHandler' },
|
{ type: 'label', name: 'run', isDangerous: true },
|
||||||
{ type: 'label', name: 'customResourceProvider' },
|
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
|
||||||
|
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
|
||||||
|
|
||||||
// relation names
|
// relation names
|
||||||
{ type: 'relation', name: 'runOnNoteView' },
|
{ type: 'relation', name: 'runOnNoteView', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnNoteCreation' },
|
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnNoteTitleChange' },
|
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnNoteChange' },
|
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnChildNoteCreation' },
|
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnAttributeCreation' },
|
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
|
||||||
{ type: 'relation', name: 'runOnAttributeChange' },
|
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
|
||||||
{ type: 'relation', name: 'template' },
|
{ type: 'relation', name: 'template' },
|
||||||
{ type: 'relation', name: 'renderNote' }
|
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
async function getNotesWithLabel(name, value) {
|
async function getNotesWithLabel(name, value) {
|
||||||
@ -94,11 +96,25 @@ async function getAttributeNames(type, nameLike) {
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAttributeType(type) {
|
||||||
|
return ATTRIBUTE_TYPES.includes(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAttributeDangerous(type, name) {
|
||||||
|
return BUILTIN_ATTRIBUTES.some(attr =>
|
||||||
|
attr.type === attr.type &&
|
||||||
|
attr.name.toLowerCase() === name.trim().toLowerCase() &&
|
||||||
|
attr.isDangerous
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getNotesWithLabel,
|
getNotesWithLabel,
|
||||||
getNotesWithLabels,
|
getNotesWithLabels,
|
||||||
getNoteWithLabel,
|
getNoteWithLabel,
|
||||||
createLabel,
|
createLabel,
|
||||||
createAttribute,
|
createAttribute,
|
||||||
getAttributeNames
|
getAttributeNames,
|
||||||
|
isAttributeType,
|
||||||
|
isAttributeDangerous
|
||||||
};
|
};
|
@ -6,6 +6,7 @@ const utils = require('../../services/utils');
|
|||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
|
const attributeService = require('../../services/attributes');
|
||||||
const Branch = require('../../entities/branch');
|
const Branch = require('../../entities/branch');
|
||||||
const tar = require('tar-stream');
|
const tar = require('tar-stream');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
@ -153,10 +154,19 @@ async function importTar(importContext, fileBuffer, importRootNote) {
|
|||||||
for (const attr of noteMeta.attributes) {
|
for (const attr of noteMeta.attributes) {
|
||||||
attr.noteId = note.noteId;
|
attr.noteId = note.noteId;
|
||||||
|
|
||||||
|
if (!attributeService.isAttributeType(attr.type)) {
|
||||||
|
log.error("Unrecognized attribute type " + attr.type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (attr.type === 'relation') {
|
if (attr.type === 'relation') {
|
||||||
attr.value = getNewNoteId(attr.value);
|
attr.value = getNewNoteId(attr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importContext.safeImport && attributeService.isAttributeDangerous(attr.type, attr.name)) {
|
||||||
|
attr.name = 'disabled-' + attr.name;
|
||||||
|
}
|
||||||
|
|
||||||
attributes.push(attr);
|
attributes.push(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user