mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
db upgrades are now handled transparently in the background without bothering the user, closes #119
This commit is contained in:
parent
4c8eeb2e6f
commit
14c704d6db
@ -21,10 +21,7 @@ class Entity {
|
|||||||
contentToHash += "|" + this[propertyName];
|
contentToHash += "|" + this[propertyName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// this IF is to ease the migration from before hashed options, can be later removed
|
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
||||||
if (this.constructor.tableName !== 'options' || this.isSynced) {
|
|
||||||
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import server from './services/server.js';
|
|
||||||
|
|
||||||
$(document).ready(async () => {
|
|
||||||
const {appDbVersion, dbVersion} = await server.get('migration');
|
|
||||||
|
|
||||||
console.log("HI", {appDbVersion, dbVersion});
|
|
||||||
|
|
||||||
if (appDbVersion === dbVersion) {
|
|
||||||
$("#up-to-date").show();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$("#need-to-migrate").show();
|
|
||||||
|
|
||||||
$("#app-db-version").html(appDbVersion);
|
|
||||||
$("#db-version").html(dbVersion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#run-migration").click(async () => {
|
|
||||||
$("#run-migration").prop("disabled", true);
|
|
||||||
|
|
||||||
$("#migration-result").show();
|
|
||||||
|
|
||||||
const result = await server.post('migration');
|
|
||||||
|
|
||||||
for (const migration of result.migrations) {
|
|
||||||
const row = $('<tr>')
|
|
||||||
.append($('<td>').html(migration.dbVersion))
|
|
||||||
.append($('<td>').html(migration.name))
|
|
||||||
.append($('<td>').html(migration.success ? 'Yes' : 'No'))
|
|
||||||
.append($('<td>').html(migration.success ? 'N/A' : migration.error));
|
|
||||||
|
|
||||||
if (!migration.success) {
|
|
||||||
row.addClass("danger");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#migration-table").append(row);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// copy of this shortcut to be able to debug migration problems
|
|
||||||
$(document).bind('keydown', 'ctrl+shift+i', () => {
|
|
||||||
require('electron').remote.getCurrentWindow().toggleDevTools();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
@ -5,7 +5,7 @@ import infoService from "./info.js";
|
|||||||
function getHeaders() {
|
function getHeaders() {
|
||||||
let protectedSessionId = null;
|
let protectedSessionId = null;
|
||||||
|
|
||||||
try { // this is because protected session might not be declared in some cases - like when it's included in migration page
|
try { // this is because protected session might not be declared in some cases
|
||||||
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
|
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
|
||||||
}
|
}
|
||||||
catch(e) {}
|
catch(e) {}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const optionService = require('../../services/options');
|
|
||||||
const migrationService = require('../../services/migration');
|
|
||||||
const appInfo = require('../../services/app_info');
|
|
||||||
|
|
||||||
async function getMigrationInfo() {
|
|
||||||
return {
|
|
||||||
dbVersion: parseInt(await optionService.getOption('dbVersion')),
|
|
||||||
appDbVersion: appInfo.dbVersion
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function executeMigration() {
|
|
||||||
const migrations = await migrationService.migrate();
|
|
||||||
|
|
||||||
return {
|
|
||||||
migrations: migrations
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getMigrationInfo,
|
|
||||||
executeMigration
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
function migrationPage(req, res) {
|
|
||||||
res.render('migration', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
migrationPage
|
|
||||||
};
|
|
@ -1,6 +1,5 @@
|
|||||||
const indexRoute = require('./index');
|
const indexRoute = require('./index');
|
||||||
const loginRoute = require('./login');
|
const loginRoute = require('./login');
|
||||||
const migrationRoute = require('./migration');
|
|
||||||
const setupRoute = require('./setup');
|
const setupRoute = require('./setup');
|
||||||
const multer = require('multer')();
|
const multer = require('multer')();
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ const noteRevisionsApiRoute = require('./api/note_revisions');
|
|||||||
const recentChangesApiRoute = require('./api/recent_changes');
|
const recentChangesApiRoute = require('./api/recent_changes');
|
||||||
const optionsApiRoute = require('./api/options');
|
const optionsApiRoute = require('./api/options');
|
||||||
const passwordApiRoute = require('./api/password');
|
const passwordApiRoute = require('./api/password');
|
||||||
const migrationApiRoute = require('./api/migration');
|
|
||||||
const syncApiRoute = require('./api/sync');
|
const syncApiRoute = require('./api/sync');
|
||||||
const loginApiRoute = require('./api/login');
|
const loginApiRoute = require('./api/login');
|
||||||
const eventLogRoute = require('./api/event_log');
|
const eventLogRoute = require('./api/event_log');
|
||||||
@ -96,7 +94,6 @@ function register(app) {
|
|||||||
route(GET, '/login', [], loginRoute.loginPage);
|
route(GET, '/login', [], loginRoute.loginPage);
|
||||||
route(POST, '/login', [], loginRoute.login);
|
route(POST, '/login', [], loginRoute.login);
|
||||||
route(POST, '/logout', [auth.checkAuth], loginRoute.logout);
|
route(POST, '/logout', [auth.checkAuth], loginRoute.logout);
|
||||||
route(GET, '/migration', [auth.checkAuthForMigrationPage], migrationRoute.migrationPage);
|
|
||||||
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
|
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
|
||||||
|
|
||||||
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
|
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
|
||||||
@ -180,9 +177,6 @@ function register(app) {
|
|||||||
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
|
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
|
||||||
apiRoute(POST, '/api/search/:searchString', searchRoute.saveSearchToNote);
|
apiRoute(POST, '/api/search/:searchString', searchRoute.saveSearchToNote);
|
||||||
|
|
||||||
route(GET, '/api/migration', [auth.checkApiAuthForMigrationPage], migrationApiRoute.getMigrationInfo, apiResultHandler);
|
|
||||||
route(POST, '/api/migration', [auth.checkApiAuthForMigrationPage], migrationApiRoute.executeMigration, apiResultHandler);
|
|
||||||
|
|
||||||
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
|
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
|
||||||
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|
||||||
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);
|
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);
|
||||||
|
@ -12,18 +12,6 @@ async function checkAuth(req, res, next) {
|
|||||||
else if (!req.session.loggedIn && !utils.isElectron()) {
|
else if (!req.session.loggedIn && !utils.isElectron()) {
|
||||||
res.redirect("login");
|
res.redirect("login");
|
||||||
}
|
}
|
||||||
else if (!await sqlInit.isDbUpToDate()) {
|
|
||||||
res.redirect("migration");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkAuthForMigrationPage(req, res, next) {
|
|
||||||
if (!req.session.loggedIn && !utils.isElectron()) {
|
|
||||||
res.redirect("login");
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@ -35,27 +23,12 @@ async function checkApiAuthOrElectron(req, res, next) {
|
|||||||
if (!req.session.loggedIn && !utils.isElectron()) {
|
if (!req.session.loggedIn && !utils.isElectron()) {
|
||||||
res.status(401).send("Not authorized");
|
res.status(401).send("Not authorized");
|
||||||
}
|
}
|
||||||
else if (await sqlInit.isDbUpToDate()) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
res.status(409).send("Mismatched app versions"); // need better response than that
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkApiAuth(req, res, next) {
|
async function checkApiAuth(req, res, next) {
|
||||||
if (!req.session.loggedIn) {
|
|
||||||
res.status(401).send("Not authorized");
|
|
||||||
}
|
|
||||||
else if (await sqlInit.isDbUpToDate()) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.status(409).send("Mismatched app versions"); // need better response than that
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkApiAuthForMigrationPage(req, res, next) {
|
|
||||||
if (!req.session.loggedIn) {
|
if (!req.session.loggedIn) {
|
||||||
res.status(401).send("Not authorized");
|
res.status(401).send("Not authorized");
|
||||||
}
|
}
|
||||||
@ -79,19 +52,14 @@ async function checkSenderToken(req, res, next) {
|
|||||||
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
|
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
|
||||||
res.status(401).send("Not authorized");
|
res.status(401).send("Not authorized");
|
||||||
}
|
}
|
||||||
else if (await sqlInit.isDbUpToDate()) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
res.status(409).send("Mismatched app versions"); // need better response than that
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
checkAuth,
|
checkAuth,
|
||||||
checkAuthForMigrationPage,
|
|
||||||
checkApiAuth,
|
checkApiAuth,
|
||||||
checkApiAuthForMigrationPage,
|
|
||||||
checkAppNotInitialized,
|
checkAppNotInitialized,
|
||||||
checkApiAuthOrElectron,
|
checkApiAuthOrElectron,
|
||||||
checkSenderToken
|
checkSenderToken
|
||||||
|
@ -2,7 +2,6 @@ const repository = require('./repository');
|
|||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const appInfo = require('./app_info');
|
const appInfo = require('./app_info');
|
||||||
const Option = require('../entities/option');
|
|
||||||
|
|
||||||
async function getOption(name) {
|
async function getOption(name) {
|
||||||
const option = await repository.getOption(name);
|
const option = await repository.getOption(name);
|
||||||
@ -27,6 +26,9 @@ async function setOption(name, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createOption(name, value, isSynced) {
|
async function createOption(name, value, isSynced) {
|
||||||
|
// to avoid circular dependency, need to find better solution
|
||||||
|
const Option = require('../entities/option');
|
||||||
|
|
||||||
await new Option({
|
await new Option({
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
|
@ -42,7 +42,10 @@ const dbReady = new Promise((resolve, reject) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!await isDbUpToDate()) {
|
if (!await isDbUpToDate()) {
|
||||||
return;
|
// avoiding circular dependency
|
||||||
|
const migrationService = require('./migration');
|
||||||
|
|
||||||
|
await migrationService.migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(db);
|
resolve(db);
|
||||||
|
@ -22,13 +22,6 @@ let syncServerCertificate = null;
|
|||||||
async function sync() {
|
async function sync() {
|
||||||
try {
|
try {
|
||||||
await syncMutexService.doExclusively(async () => {
|
await syncMutexService.doExclusively(async () => {
|
||||||
if (!await sqlInit.isDbUpToDate()) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "DB not up to date"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncContext = await login();
|
const syncContext = await login();
|
||||||
|
|
||||||
await pushSync(syncContext);
|
await pushSync(syncContext);
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Migration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div style="width: 800px; margin: auto;">
|
|
||||||
<h1>Migration</h1>
|
|
||||||
|
|
||||||
<div id="up-to-date" style="display:none;">
|
|
||||||
<p>Your database is up-to-date with the application.</p>
|
|
||||||
|
|
||||||
<a href="/" class="btn btn-success">Continue to app</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="need-to-migrate" style="display:none;">
|
|
||||||
<p>Your database needs to be migrated to new version before you can use the application again.
|
|
||||||
Database will be backed up before migration in case of something going wrong.</p>
|
|
||||||
|
|
||||||
<table class="table table-bordered" style="width: 200px;">
|
|
||||||
<tr>
|
|
||||||
<th>Application version:</th>
|
|
||||||
<td id="app-db-version" style="text-align: right;"></td>
|
|
||||||
<tr>
|
|
||||||
<th>Database version:</th>
|
|
||||||
<td id="db-version" style="text-align: right;"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<button class="btn btn-warning" id="run-migration">Run migration</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="migration-result" style="display:none;">
|
|
||||||
<h2>Migration result</h2>
|
|
||||||
|
|
||||||
<table id="migration-table" class="table">
|
|
||||||
<tr>
|
|
||||||
<th>Database version</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Success</th>
|
|
||||||
<th>Error</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<a href="/" class="btn btn-success">Continue to app</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
const baseApiUrl = 'api/';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Required for correct loading of scripts in Electron -->
|
|
||||||
<script>
|
|
||||||
if (typeof module === 'object') {
|
|
||||||
window.module = module; module = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const glob = {
|
|
||||||
sourceId: ''
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="libraries/jquery.min.js"></script>
|
|
||||||
|
|
||||||
<link href="libraries/bootstrap/css/bootstrap.css" rel="stylesheet">
|
|
||||||
<script src="libraries/bootstrap/js/bootstrap.js"></script>
|
|
||||||
|
|
||||||
<script src="javascripts/migration.js" type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
x
Reference in New Issue
Block a user