mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
398 lines
9.4 KiB
Python
398 lines
9.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from functools import lru_cache
|
|
from typing import Dict, Type
|
|
|
|
from backend.base.definitions import Constants, DBMigrator
|
|
from backend.base.logging import LOGGER
|
|
from backend.internals.db import get_db, iter_commit
|
|
|
|
|
|
@lru_cache(1)
|
|
def get_db_migration_map() -> Dict[int, Type[DBMigrator]]:
|
|
"""Get a map of the database version to the migrator class for that version
|
|
to one database version higher. E.g. 2 -> Migrate2To3.
|
|
|
|
Returns:
|
|
Dict[int, Type[DBMigrator]]: The map.
|
|
"""
|
|
return {
|
|
m.start_version: m
|
|
for m in DBMigrator.__subclasses__()
|
|
}
|
|
|
|
|
|
@lru_cache(1)
|
|
def get_latest_db_version() -> int:
|
|
"""Get the latest database version supported.
|
|
|
|
Returns:
|
|
int: The version.
|
|
"""
|
|
return max(get_db_migration_map()) + 1
|
|
|
|
|
|
def migrate_db() -> None:
|
|
"""
|
|
Migrate a MIND database from it's current version
|
|
to the newest version supported by the MIND version installed.
|
|
"""
|
|
from backend.internals.settings import Settings
|
|
|
|
s = Settings()
|
|
current_db_version = s.sv.database_version
|
|
newest_version = get_latest_db_version()
|
|
if current_db_version == newest_version:
|
|
get_db_migration_map.cache_clear()
|
|
return
|
|
|
|
LOGGER.info("Migrating database to newer version...")
|
|
LOGGER.debug(
|
|
"Database migration: %d -> %d",
|
|
current_db_version, newest_version
|
|
)
|
|
|
|
db_migration_map = get_db_migration_map()
|
|
for start_version in iter_commit(range(current_db_version, newest_version)):
|
|
if start_version not in db_migration_map:
|
|
continue
|
|
db_migration_map[start_version]().run()
|
|
s.update({"database_version": start_version + 1})
|
|
|
|
get_db().execute("VACUUM;")
|
|
s.clear_cache()
|
|
get_db_migration_map.cache_clear()
|
|
|
|
return
|
|
|
|
|
|
class MigrateToUTC(DBMigrator):
|
|
start_version = 1
|
|
|
|
def run(self) -> None:
|
|
# V1 -> V2
|
|
|
|
from datetime import datetime
|
|
from time import time
|
|
|
|
cursor = get_db()
|
|
|
|
t = time()
|
|
utc_offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
|
|
|
|
cursor.execute("SELECT time, id FROM reminders;")
|
|
new_reminders = [
|
|
[
|
|
round((
|
|
datetime.fromtimestamp(r["time"]) - utc_offset
|
|
).timestamp()),
|
|
r["id"]
|
|
]
|
|
for r in cursor
|
|
]
|
|
|
|
cursor.executemany(
|
|
"UPDATE reminders SET time = ? WHERE id = ?;",
|
|
new_reminders
|
|
)
|
|
return
|
|
|
|
|
|
class MigrateAddColor(DBMigrator):
|
|
start_version = 2
|
|
|
|
def run(self) -> None:
|
|
# V2 -> V3
|
|
|
|
get_db().executescript("""
|
|
ALTER TABLE reminders
|
|
ADD color VARCHAR(7);
|
|
ALTER TABLE templates
|
|
ADD color VARCHAR(7);
|
|
""")
|
|
|
|
return
|
|
|
|
|
|
class MigrateFixRQ(DBMigrator):
|
|
start_version = 3
|
|
|
|
def run(self) -> None:
|
|
# V3 -> V4
|
|
|
|
get_db().executescript("""
|
|
UPDATE reminders
|
|
SET repeat_quantity = repeat_quantity || 's'
|
|
WHERE repeat_quantity NOT LIKE '%s';
|
|
""")
|
|
|
|
return
|
|
|
|
|
|
class MigrateToReminderServices(DBMigrator):
|
|
start_version = 4
|
|
|
|
def run(self) -> None:
|
|
# V4 -> V5
|
|
|
|
get_db().executescript("""
|
|
BEGIN TRANSACTION;
|
|
PRAGMA defer_foreign_keys = ON;
|
|
|
|
CREATE TEMPORARY TABLE temp_reminder_services(
|
|
reminder_id,
|
|
static_reminder_id,
|
|
template_id,
|
|
notification_service_id
|
|
);
|
|
|
|
-- Reminders
|
|
INSERT INTO temp_reminder_services(
|
|
reminder_id, notification_service_id
|
|
)
|
|
SELECT id, notification_service
|
|
FROM reminders;
|
|
|
|
CREATE TEMPORARY TABLE temp_reminders AS
|
|
SELECT
|
|
id, user_id, title, text,
|
|
time, repeat_quantity, repeat_interval, original_time,
|
|
color
|
|
FROM reminders;
|
|
DROP TABLE reminders;
|
|
CREATE TABLE reminders(
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
text TEXT,
|
|
time INTEGER NOT NULL,
|
|
|
|
repeat_quantity VARCHAR(15),
|
|
repeat_interval INTEGER,
|
|
original_time INTEGER,
|
|
|
|
color VARCHAR(7),
|
|
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
INSERT INTO reminders
|
|
SELECT * FROM temp_reminders;
|
|
|
|
-- Templates
|
|
INSERT INTO temp_reminder_services(
|
|
template_id, notification_service_id
|
|
)
|
|
SELECT id, notification_service
|
|
FROM templates;
|
|
|
|
CREATE TEMPORARY TABLE temp_templates AS
|
|
SELECT id, user_id, title, text, color
|
|
FROM templates;
|
|
DROP TABLE templates;
|
|
CREATE TABLE templates(
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
text TEXT,
|
|
|
|
color VARCHAR(7),
|
|
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
INSERT INTO templates
|
|
SELECT * FROM temp_templates;
|
|
|
|
INSERT INTO reminder_services
|
|
SELECT * FROM temp_reminder_services;
|
|
|
|
COMMIT;
|
|
""")
|
|
|
|
return
|
|
|
|
|
|
class MigrateRemoveUser1(DBMigrator):
|
|
start_version = 5
|
|
|
|
def run(self) -> None:
|
|
# V5 -> V6
|
|
from backend.base.custom_exceptions import (AccessUnauthorized,
|
|
UserNotFound)
|
|
from backend.implementations.users import Users
|
|
|
|
try:
|
|
Users().login('User1', 'Password1').delete()
|
|
|
|
except (UserNotFound, AccessUnauthorized):
|
|
pass
|
|
|
|
return
|
|
|
|
|
|
class MigrateAddWeekdays(DBMigrator):
|
|
start_version = 6
|
|
|
|
def run(self) -> None:
|
|
# V6 -> V7
|
|
|
|
get_db().executescript("""
|
|
ALTER TABLE reminders
|
|
ADD weekdays VARCHAR(13);
|
|
""")
|
|
|
|
return
|
|
|
|
|
|
class MigrateAddAdmin(DBMigrator):
|
|
start_version = 7
|
|
|
|
def run(self) -> None:
|
|
# V7 -> V8
|
|
|
|
from backend.implementations.users import Users
|
|
from backend.internals.settings import Settings
|
|
|
|
cursor = get_db()
|
|
|
|
cursor.executescript("""
|
|
DROP TABLE config;
|
|
CREATE TABLE IF NOT EXISTS config(
|
|
key VARCHAR(255) PRIMARY KEY,
|
|
value BLOB NOT NULL
|
|
);
|
|
"""
|
|
)
|
|
Settings()._insert_missing_settings()
|
|
|
|
cursor.executescript("""
|
|
ALTER TABLE users
|
|
ADD admin BOOL NOT NULL DEFAULT 0;
|
|
"""
|
|
)
|
|
users = Users()
|
|
if 'admin' in users:
|
|
users.get_one(
|
|
users.user_db.username_to_id('admin')
|
|
).update_username('admin_old')
|
|
|
|
users.add(
|
|
Constants.ADMIN_USERNAME, Constants.ADMIN_PASSWORD,
|
|
force=True,
|
|
is_admin=True
|
|
)
|
|
|
|
return
|
|
|
|
|
|
class MigrateHostSettingsToDB(DBMigrator):
|
|
start_version = 8
|
|
|
|
def run(self) -> None:
|
|
# V8 -> V9
|
|
# In newer versions, the variables don't exist anymore, and behaviour
|
|
# was to then set the values to the default values. But that's already
|
|
# taken care of by the settings, so nothing to do here anymore.
|
|
return
|
|
|
|
|
|
class MigrateUpdateManifest(DBMigrator):
|
|
start_version = 9
|
|
|
|
def run(self) -> None:
|
|
# V9 -> V10
|
|
# There used to be a migration here that fixed the manifest file.
|
|
# That has since been replaced by the dynamic endpoint serving the JSON.
|
|
# So the migration doesn't do anything anymore, and a function used
|
|
# doesn't exist anymore, so the whole migration is just removed.
|
|
return
|
|
|
|
|
|
class MigrateAddEnabled(DBMigrator):
|
|
start_version = 10
|
|
|
|
def run(self) -> None:
|
|
# V10 -> V11
|
|
|
|
get_db().execute("""
|
|
ALTER TABLE reminders
|
|
ADD enabled BOOL NOT NULL DEFAULT 1;
|
|
""")
|
|
return
|
|
|
|
|
|
class MigrateSetDBBackupFolder(DBMigrator):
|
|
start_version = 11
|
|
|
|
def run(self) -> None:
|
|
# V11 -> V12
|
|
|
|
from backend.internals.settings import Settings, SettingsValues
|
|
|
|
s = Settings()
|
|
sv = s.get_settings()
|
|
if sv.db_backup_folder == '':
|
|
s.update({"db_backup_folder": SettingsValues.db_backup_folder})
|
|
|
|
return
|
|
|
|
|
|
class MigrateAddCronScheduleColumn(DBMigrator):
|
|
start_version = 12
|
|
|
|
def run(self) -> None:
|
|
# V12 -> V13
|
|
|
|
get_db().executescript("""
|
|
PRAGMA foreign_keys = OFF;
|
|
BEGIN TRANSACTION;
|
|
|
|
CREATE TEMPORARY TABLE temp_reminders_13 AS
|
|
SELECT * FROM reminders;
|
|
DROP TABLE reminders;
|
|
|
|
CREATE TABLE IF NOT EXISTS reminders(
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
text TEXT,
|
|
time INTEGER NOT NULL,
|
|
|
|
repeat_quantity VARCHAR(15),
|
|
repeat_interval INTEGER,
|
|
original_time INTEGER,
|
|
weekdays VARCHAR(13),
|
|
cron_schedule VARCHAR(255),
|
|
|
|
color VARCHAR(7),
|
|
enabled BOOL NOT NULL DEFAULT 1,
|
|
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
INSERT INTO reminders
|
|
SELECT
|
|
id, user_id,
|
|
title, text, time,
|
|
repeat_quantity, repeat_interval,
|
|
original_time, weekdays,
|
|
NULL AS cron_schedule,
|
|
color, enabled
|
|
FROM temp_reminders_13;
|
|
|
|
COMMIT;
|
|
PRAGMA foreign_keys = ON;
|
|
""")
|
|
|
|
|
|
class MigrateAddMFAColumn(DBMigrator):
|
|
start_version = 13
|
|
|
|
def run(self) -> None:
|
|
# V13 -> V14
|
|
|
|
get_db().executescript("""
|
|
ALTER TABLE users
|
|
ADD mfa_apprise_url TEXT;
|
|
""")
|
|
return
|