committed by
							
								
								GitHub
							
						
					
				
				 21 changed files with 372 additions and 86 deletions
			
			
		@ -0,0 +1,10 @@ | 
				
			|||||
 | 
					--- | 
				
			||||
 | 
					name: ⚠ Please go to "Discussions" Tab if you want to ask or share something | 
				
			||||
 | 
					about: BUG REPORT ONLY HERE | 
				
			||||
 | 
					title: '' | 
				
			||||
 | 
					labels: '' | 
				
			||||
 | 
					assignees: '' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					--- | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					BUG REPORT ONLY HERE | 
				
			||||
@ -0,0 +1,34 @@ | 
				
			|||||
 | 
					--- | 
				
			||||
 | 
					name: Bug report | 
				
			||||
 | 
					about: Create a report to help us improve | 
				
			||||
 | 
					title: '' | 
				
			||||
 | 
					labels: bug | 
				
			||||
 | 
					assignees: '' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					--- | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**Describe the bug** | 
				
			||||
 | 
					A clear and concise description of what the bug is. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**To Reproduce** | 
				
			||||
 | 
					Steps to reproduce the behavior: | 
				
			||||
 | 
					1. Go to '...' | 
				
			||||
 | 
					2. Click on '....' | 
				
			||||
 | 
					3. Scroll down to '....' | 
				
			||||
 | 
					4. See error | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**Expected behavior** | 
				
			||||
 | 
					A clear and concise description of what you expected to happen. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**Screenshots** | 
				
			||||
 | 
					If applicable, add screenshots to help explain your problem. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**Desktop (please complete the following information):** | 
				
			||||
 | 
					 - Uptime Kuma Version: | 
				
			||||
 | 
					 - Using Docker?: Yes/No | 
				
			||||
 | 
					 - OS:  | 
				
			||||
 | 
					 - Browser: | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					**Additional context** | 
				
			||||
 | 
					Add any other context about the problem here. | 
				
			||||
@ -0,0 +1,37 @@ | 
				
			|||||
 | 
					-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. | 
				
			||||
 | 
					-- Change Monitor.created_date from "TIMESTAMP" to "DATETIME" | 
				
			||||
 | 
					-- SQL Generated by Intellij Idea | 
				
			||||
 | 
					PRAGMA foreign_keys=off; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					BEGIN TRANSACTION; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					create table monitor_dg_tmp | 
				
			||||
 | 
					( | 
				
			||||
 | 
					    id INTEGER not null | 
				
			||||
 | 
					        primary key autoincrement, | 
				
			||||
 | 
					    name VARCHAR(150), | 
				
			||||
 | 
					    active BOOLEAN default 1 not null, | 
				
			||||
 | 
					    user_id INTEGER | 
				
			||||
 | 
					        references user | 
				
			||||
 | 
					                   on update cascade on delete set null, | 
				
			||||
 | 
					    interval INTEGER default 20 not null, | 
				
			||||
 | 
					    url TEXT, | 
				
			||||
 | 
					    type VARCHAR(20), | 
				
			||||
 | 
					    weight INTEGER default 2000, | 
				
			||||
 | 
					    hostname VARCHAR(255), | 
				
			||||
 | 
					    port INTEGER, | 
				
			||||
 | 
					    created_date DATETIME, | 
				
			||||
 | 
					    keyword VARCHAR(255) | 
				
			||||
 | 
					); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword from monitor; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					drop table monitor; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					alter table monitor_dg_tmp rename to monitor; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					create index user_id on monitor (user_id); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					COMMIT; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					PRAGMA foreign_keys=on; | 
				
			||||
@ -0,0 +1,119 @@ | 
				
			|||||
 | 
					const fs = require("fs"); | 
				
			||||
 | 
					const {sleep} = require("./util"); | 
				
			||||
 | 
					const {R} = require("redbean-node"); | 
				
			||||
 | 
					const {setSetting, setting} = require("./util-server"); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class Database { | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    static templatePath = "./db/kuma.db" | 
				
			||||
 | 
					    static path =  './data/kuma.db'; | 
				
			||||
 | 
					    static latestVersion = 1; | 
				
			||||
 | 
					    static noReject = true; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    static async patch() { | 
				
			||||
 | 
					        let version = parseInt(await setting("database_version")); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        if (! version) { | 
				
			||||
 | 
					            version = 0; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        console.info("Your database version: " + version); | 
				
			||||
 | 
					        console.info("Latest database version: " + this.latestVersion); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        if (version === this.latestVersion) { | 
				
			||||
 | 
					            console.info("Database no need to patch"); | 
				
			||||
 | 
					        } else { | 
				
			||||
 | 
					            console.info("Database patch is needed") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            console.info("Backup the db") | 
				
			||||
 | 
					            const backupPath = "./data/kuma.db.bak" + version; | 
				
			||||
 | 
					            fs.copyFileSync(Database.path, backupPath); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            // Try catch anything here, if gone wrong, restore the backup
 | 
				
			||||
 | 
					            try { | 
				
			||||
 | 
					                for (let i = version + 1; i <= this.latestVersion; i++) { | 
				
			||||
 | 
					                    const sqlFile = `./db/patch${i}.sql`; | 
				
			||||
 | 
					                    console.info(`Patching ${sqlFile}`); | 
				
			||||
 | 
					                    await Database.importSQLFile(sqlFile); | 
				
			||||
 | 
					                    console.info(`Patched ${sqlFile}`); | 
				
			||||
 | 
					                    await setSetting("database_version", i); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                console.log("Database Patched Successfully"); | 
				
			||||
 | 
					            } catch (ex) { | 
				
			||||
 | 
					                await Database.close(); | 
				
			||||
 | 
					                console.error("Patch db failed!!! Restoring the backup") | 
				
			||||
 | 
					                fs.copyFileSync(backupPath, Database.path); | 
				
			||||
 | 
					                console.error(ex) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					                console.error("Start Uptime-Kuma failed due to patch db failed") | 
				
			||||
 | 
					                console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues") | 
				
			||||
 | 
					                process.exit(1); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** | 
				
			||||
 | 
					     * Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself | 
				
			||||
 | 
					     * @param filename | 
				
			||||
 | 
					     * @returns {Promise<void>} | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    static async importSQLFile(filename) { | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        await R.getCell("SELECT 1"); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        let text = fs.readFileSync(filename).toString(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        // Remove all comments (--)
 | 
				
			||||
 | 
					        let lines = text.split("\n"); | 
				
			||||
 | 
					        lines = lines.filter((line) => { | 
				
			||||
 | 
					            return ! line.startsWith("--") | 
				
			||||
 | 
					        }); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        // Split statements by semicolon
 | 
				
			||||
 | 
					        // Filter out empty line
 | 
				
			||||
 | 
					        text = lines.join("\n") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        let statements = text.split(";") | 
				
			||||
 | 
					            .map((statement) => { | 
				
			||||
 | 
					                return statement.trim(); | 
				
			||||
 | 
					            }) | 
				
			||||
 | 
					            .filter((statement) => { | 
				
			||||
 | 
					                return statement !== ""; | 
				
			||||
 | 
					            }) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        for (let statement of statements) { | 
				
			||||
 | 
					            await R.exec(statement); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** | 
				
			||||
 | 
					     * Special handle, because tarn.js throw a promise reject that cannot be caught | 
				
			||||
 | 
					     * @returns {Promise<void>} | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    static async close() { | 
				
			||||
 | 
					        const listener = (reason, p) => { | 
				
			||||
 | 
					            Database.noReject = false; | 
				
			||||
 | 
					        }; | 
				
			||||
 | 
					        process.addListener('unhandledRejection', listener); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        console.log("Closing DB") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        while (true) { | 
				
			||||
 | 
					            Database.noReject = true; | 
				
			||||
 | 
					            await R.close() | 
				
			||||
 | 
					            await sleep(2000) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            if (Database.noReject) { | 
				
			||||
 | 
					                break; | 
				
			||||
 | 
					            } else { | 
				
			||||
 | 
					                console.log("Waiting to close the db") | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					        console.log("SQLite closed") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        process.removeListener('unhandledRejection', listener); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					module.exports = Database; | 
				
			||||
					Loading…
					
					
				
		Reference in new issue