mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-30 12:13:52 +08: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]; | ||||
|         } | ||||
| 
 | ||||
|         // this IF is to ease the migration from before hashed options, can be later removed
 | ||||
|         if (this.constructor.tableName !== 'options' || this.isSynced) { | ||||
|             this["hash"] = utils.hash(contentToHash).substr(0, 10); | ||||
|         } | ||||
|         this["hash"] = utils.hash(contentToHash).substr(0, 10); | ||||
|     } | ||||
| 
 | ||||
|     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() { | ||||
|     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(); | ||||
|     } | ||||
|     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 loginRoute = require('./login'); | ||||
| const migrationRoute = require('./migration'); | ||||
| const setupRoute = require('./setup'); | ||||
| const multer = require('multer')(); | ||||
| 
 | ||||
| @ -14,7 +13,6 @@ const noteRevisionsApiRoute = require('./api/note_revisions'); | ||||
| const recentChangesApiRoute = require('./api/recent_changes'); | ||||
| const optionsApiRoute = require('./api/options'); | ||||
| const passwordApiRoute = require('./api/password'); | ||||
| const migrationApiRoute = require('./api/migration'); | ||||
| const syncApiRoute = require('./api/sync'); | ||||
| const loginApiRoute = require('./api/login'); | ||||
| const eventLogRoute = require('./api/event_log'); | ||||
| @ -96,7 +94,6 @@ function register(app) { | ||||
|     route(GET, '/login', [], loginRoute.loginPage); | ||||
|     route(POST, '/login', [], loginRoute.login); | ||||
|     route(POST, '/logout', [auth.checkAuth], loginRoute.logout); | ||||
|     route(GET, '/migration', [auth.checkAuthForMigrationPage], migrationRoute.migrationPage); | ||||
|     route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage); | ||||
| 
 | ||||
|     apiRoute(GET, '/api/tree', treeApiRoute.getTree); | ||||
| @ -180,9 +177,6 @@ function register(app) { | ||||
|     apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes); | ||||
|     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); | ||||
|     // 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); | ||||
|  | ||||
| @ -12,18 +12,6 @@ async function checkAuth(req, res, next) { | ||||
|     else if (!req.session.loggedIn && !utils.isElectron()) { | ||||
|         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 { | ||||
|         next(); | ||||
|     } | ||||
| @ -35,27 +23,12 @@ async function checkApiAuthOrElectron(req, res, next) { | ||||
|     if (!req.session.loggedIn && !utils.isElectron()) { | ||||
|         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
 | ||||
|         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) { | ||||
|         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) { | ||||
|         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
 | ||||
|         next(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|     checkAuth, | ||||
|     checkAuthForMigrationPage, | ||||
|     checkApiAuth, | ||||
|     checkApiAuthForMigrationPage, | ||||
|     checkAppNotInitialized, | ||||
|     checkApiAuthOrElectron, | ||||
|     checkSenderToken | ||||
|  | ||||
| @ -2,7 +2,6 @@ const repository = require('./repository'); | ||||
| const utils = require('./utils'); | ||||
| const dateUtils = require('./date_utils'); | ||||
| const appInfo = require('./app_info'); | ||||
| const Option = require('../entities/option'); | ||||
| 
 | ||||
| async function getOption(name) { | ||||
|     const option = await repository.getOption(name); | ||||
| @ -27,6 +26,9 @@ async function setOption(name, value) { | ||||
| } | ||||
| 
 | ||||
| async function createOption(name, value, isSynced) { | ||||
|     // to avoid circular dependency, need to find better solution
 | ||||
|     const Option = require('../entities/option'); | ||||
| 
 | ||||
|     await new Option({ | ||||
|         name: name, | ||||
|         value: value, | ||||
|  | ||||
| @ -42,7 +42,10 @@ const dbReady = new Promise((resolve, reject) => { | ||||
|         } | ||||
| 
 | ||||
|         if (!await isDbUpToDate()) { | ||||
|             return; | ||||
|             // avoiding circular dependency
 | ||||
|             const migrationService = require('./migration'); | ||||
| 
 | ||||
|             await migrationService.migrate(); | ||||
|         } | ||||
| 
 | ||||
|         resolve(db); | ||||
|  | ||||
| @ -22,13 +22,6 @@ let syncServerCertificate = null; | ||||
| async function sync() { | ||||
|     try { | ||||
|         await syncMutexService.doExclusively(async () => { | ||||
|             if (!await sqlInit.isDbUpToDate()) { | ||||
|                 return { | ||||
|                     success: false, | ||||
|                     message: "DB not up to date" | ||||
|                 }; | ||||
|             } | ||||
| 
 | ||||
|             const syncContext = await login(); | ||||
| 
 | ||||
|             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
	 azivner
						azivner