mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	Merge remote-tracking branch 'origin/stable'
# Conflicts: # libraries/ckeditor/ckeditor.js # libraries/ckeditor/ckeditor.js.map
This commit is contained in:
		
						commit
						d65624d8d2
					
				@ -24,6 +24,13 @@ const TPL = `
 | 
			
		||||
<p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and metadata)
 | 
			
		||||
    for sharing online for debugging purposes without fear of leaking your personal data.</p>
 | 
			
		||||
 | 
			
		||||
<h4>Backup database</h4>
 | 
			
		||||
 | 
			
		||||
<button id="backup-database-button" class="btn">Backup database</button>
 | 
			
		||||
 | 
			
		||||
<br/>
 | 
			
		||||
<br/>
 | 
			
		||||
 | 
			
		||||
<h4>Vacuum database</h4>
 | 
			
		||||
 | 
			
		||||
<p>This will rebuild database which will typically result in smaller database file. No data will be actually changed.</p>
 | 
			
		||||
@ -37,6 +44,7 @@ export default class AdvancedOptions {
 | 
			
		||||
        this.$forceFullSyncButton = $("#force-full-sync-button");
 | 
			
		||||
        this.$fillSyncRowsButton = $("#fill-sync-rows-button");
 | 
			
		||||
        this.$anonymizeButton = $("#anonymize-button");
 | 
			
		||||
        this.$backupDatabaseButton = $("#backup-database-button");
 | 
			
		||||
        this.$vacuumDatabaseButton = $("#vacuum-database-button");
 | 
			
		||||
        this.$findAndFixConsistencyIssuesButton = $("#find-and-fix-consistency-issues-button");
 | 
			
		||||
 | 
			
		||||
@ -58,16 +66,22 @@ export default class AdvancedOptions {
 | 
			
		||||
            toastService.showMessage("Created anonymized database");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$backupDatabaseButton.on('click', async () => {
 | 
			
		||||
            const {backupFile} = await server.post('database/backup-database');
 | 
			
		||||
 | 
			
		||||
            toastService.showMessage("Database has been backed up to " + backupFile, 10000);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$vacuumDatabaseButton.on('click', async () => {
 | 
			
		||||
            await server.post('cleanup/vacuum-database');
 | 
			
		||||
            await server.post('database/vacuum-database');
 | 
			
		||||
 | 
			
		||||
            toastService.showMessage("Database has been vacuumed");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$findAndFixConsistencyIssuesButton.on('click', async () => {
 | 
			
		||||
            await server.post('cleanup/find-and-fix-consistency-issues');
 | 
			
		||||
            await server.post('database/find-and-fix-consistency-issues');
 | 
			
		||||
 | 
			
		||||
            toastService.showMessage("Consistency issues should be fixed.");
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,17 +5,23 @@ import TypeWidget from "./type_widget.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="note-detail-file note-detail-printable">
 | 
			
		||||
    <style>
 | 
			
		||||
        .file-table td {
 | 
			
		||||
            overflow-wrap: anywhere;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <table class="file-table">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th nowrap>Note ID:</th>
 | 
			
		||||
            <th>Note ID:</th>
 | 
			
		||||
            <td class="file-note-id"></td>
 | 
			
		||||
            <th nowrap>Original file name:</th>
 | 
			
		||||
            <th>Original file name:</th>
 | 
			
		||||
            <td class="file-filename"></td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th nowrap>File type:</th>
 | 
			
		||||
            <th>File type:</th>
 | 
			
		||||
            <td class="file-filetype"></td>
 | 
			
		||||
            <th nowrap>File size:</th>
 | 
			
		||||
            <th>File size:</th>
 | 
			
		||||
            <td class="file-filesize"></td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
@ -94,7 +100,7 @@ export default class FileTypeWidget extends TypeWidget {
 | 
			
		||||
                toastService.showError("Upload of a new file revision failed.");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return this.$widget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -130,4 +136,4 @@ export default class FileTypeWidget extends TypeWidget {
 | 
			
		||||
    getFileUrl() {
 | 
			
		||||
        return utils.getUrlForDownload("api/notes/" + this.noteId + "/download");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,15 @@
 | 
			
		||||
 | 
			
		||||
const sql = require('../../services/sql');
 | 
			
		||||
const log = require('../../services/log');
 | 
			
		||||
const backupService = require('../../services/backup');
 | 
			
		||||
const consistencyChecksService = require('../../services/consistency_checks');
 | 
			
		||||
 | 
			
		||||
async function backupDatabase() {
 | 
			
		||||
    return {
 | 
			
		||||
        backupFile: await backupService.backupNow("now")
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function vacuumDatabase() {
 | 
			
		||||
    await sql.execute("VACUUM");
 | 
			
		||||
 | 
			
		||||
@ -15,6 +22,7 @@ async function findAndFixConsistencyIssues() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    backupDatabase,
 | 
			
		||||
    vacuumDatabase,
 | 
			
		||||
    findAndFixConsistencyIssues
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
@ -16,7 +16,7 @@ const ApiToken = require('../../entities/api_token');
 | 
			
		||||
 | 
			
		||||
async function loginSync(req) {
 | 
			
		||||
    if (!await sqlInit.schemaExists()) {
 | 
			
		||||
        return [400, { message: "DB schema does not exist, can't sync." }];
 | 
			
		||||
        return [500, { message: "DB schema does not exist, can't sync." }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const timestampStr = req.body.timestamp;
 | 
			
		||||
@ -27,7 +27,7 @@ async function loginSync(req) {
 | 
			
		||||
 | 
			
		||||
    // login token is valid for 5 minutes
 | 
			
		||||
    if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
 | 
			
		||||
        return [400, { message: 'Auth request time is out of sync, please check that both client and server have correct time.' }];
 | 
			
		||||
        return [401, { message: 'Auth request time is out of sync, please check that both client and server have correct time.' }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const syncVersion = req.body.syncVersion;
 | 
			
		||||
@ -102,4 +102,4 @@ module.exports = {
 | 
			
		||||
    loginSync,
 | 
			
		||||
    loginToProtectedSession,
 | 
			
		||||
    token
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ const importRoute = require('./api/import');
 | 
			
		||||
const setupApiRoute = require('./api/setup');
 | 
			
		||||
const sqlRoute = require('./api/sql');
 | 
			
		||||
const anonymizationRoute = require('./api/anonymization');
 | 
			
		||||
const cleanupRoute = require('./api/cleanup');
 | 
			
		||||
const databaseRoute = require('./api/database');
 | 
			
		||||
const imageRoute = require('./api/image');
 | 
			
		||||
const attributesRoute = require('./api/attributes');
 | 
			
		||||
const scriptRoute = require('./api/script');
 | 
			
		||||
@ -223,10 +223,13 @@ function register(app) {
 | 
			
		||||
    apiRoute(POST, '/api/sql/execute', sqlRoute.execute);
 | 
			
		||||
    apiRoute(POST, '/api/anonymization/anonymize', anonymizationRoute.anonymize);
 | 
			
		||||
 | 
			
		||||
    // VACUUM requires execution outside of transaction
 | 
			
		||||
    route(POST, '/api/cleanup/vacuum-database', [auth.checkApiAuthOrElectron, csrfMiddleware], cleanupRoute.vacuumDatabase, apiResultHandler, false);
 | 
			
		||||
    // backup requires execution outside of transaction
 | 
			
		||||
    route(POST, '/api/database/backup-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.backupDatabase, apiResultHandler, false);
 | 
			
		||||
 | 
			
		||||
    route(POST, '/api/cleanup/find-and-fix-consistency-issues', [auth.checkApiAuthOrElectron, csrfMiddleware], cleanupRoute.findAndFixConsistencyIssues, apiResultHandler, false);
 | 
			
		||||
    // VACUUM requires execution outside of transaction
 | 
			
		||||
    route(POST, '/api/database/vacuum-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.vacuumDatabase, apiResultHandler, false);
 | 
			
		||||
 | 
			
		||||
    route(POST, '/api/database/find-and-fix-consistency-issues', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.findAndFixConsistencyIssues, apiResultHandler, false);
 | 
			
		||||
 | 
			
		||||
    apiRoute(POST, '/api/script/exec', scriptRoute.exec);
 | 
			
		||||
    apiRoute(POST, '/api/script/run/:noteId', scriptRoute.run);
 | 
			
		||||
@ -268,4 +271,4 @@ function register(app) {
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    register
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -29,13 +29,38 @@ async function periodBackup(optionName, fileName, periodInSeconds) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function backupNow(name) {
 | 
			
		||||
    const sql = require('./sql');
 | 
			
		||||
 | 
			
		||||
    // we don't want to backup DB in the middle of sync with potentially inconsistent DB state
 | 
			
		||||
    await syncMutexService.doExclusively(async () => {
 | 
			
		||||
    return await syncMutexService.doExclusively(async () => {
 | 
			
		||||
        const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`;
 | 
			
		||||
 | 
			
		||||
        fs.copySync(dataDir.DOCUMENT_PATH, backupFile);
 | 
			
		||||
        try {
 | 
			
		||||
            fs.unlinkSync(backupFile);
 | 
			
		||||
        }
 | 
			
		||||
        catch (e) {} // unlink throws exception if the file did not exist
 | 
			
		||||
 | 
			
		||||
        log.info("Created backup at " + backupFile);
 | 
			
		||||
        let success = false;
 | 
			
		||||
        let attemptCount = 0
 | 
			
		||||
 | 
			
		||||
        for (; attemptCount < 50 && !success; attemptCount++) {
 | 
			
		||||
            try {
 | 
			
		||||
                await sql.executeNoWrap(`VACUUM INTO '${backupFile}'`);
 | 
			
		||||
                success++;
 | 
			
		||||
            }
 | 
			
		||||
            catch (e) {}
 | 
			
		||||
            // we re-try since VACUUM is very picky and it can't run if there's any other query currently running
 | 
			
		||||
            // which is difficult to guarantee so we just re-try
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (attemptCount === 10) {
 | 
			
		||||
            log.error(`Creating backup ${backupFile} failed`);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            log.info("Created backup at " + backupFile);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return backupFile;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -52,4 +77,4 @@ sqlInit.dbReady.then(() => {
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    backupNow
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -153,6 +153,10 @@ async function execute(query, params = []) {
 | 
			
		||||
    return await wrap(async db => db.run(query, ...params), query);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeNoWrap(query, params = []) {
 | 
			
		||||
    await dbConnection.run(query, ...params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeMany(query, params) {
 | 
			
		||||
    // essentially just alias
 | 
			
		||||
    await getManyRows(query, params);
 | 
			
		||||
@ -264,6 +268,7 @@ module.exports = {
 | 
			
		||||
    getMap,
 | 
			
		||||
    getColumn,
 | 
			
		||||
    execute,
 | 
			
		||||
    executeNoWrap,
 | 
			
		||||
    executeMany,
 | 
			
		||||
    executeScript,
 | 
			
		||||
    transactional,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user