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