diff --git a/database.py b/database.py index a895989..0217eaf 100644 --- a/database.py +++ b/database.py @@ -1,8 +1,24 @@ from datetime import datetime, timezone from peewee import * +from playhouse.migrate import migrate as migrate_database, SqliteMigrator +latest_version = 2 # increment each time new database migrations are added database = DatabaseProxy() # use a proxy object, as we will init real db client inside web.py +migrator = SqliteMigrator(database) + + +# migrates the database +def migrate(current_version): + + # migrate to version 2 + if current_version < 2: + migrate_database( + migrator.add_column("lxmf_messages", 'delivery_attempts', LxmfMessage.delivery_attempts), + migrator.add_column("lxmf_messages", 'next_delivery_attempt_at', LxmfMessage.next_delivery_attempt_at), + ) + + return latest_version class BaseModel(Model): @@ -49,6 +65,8 @@ class LxmfMessage(BaseModel): state = CharField() # state is converted from internal int to a human friendly string progress = FloatField() # progress is converted from internal float 0.00-1.00 to float between 0.00/100 (2 decimal places) is_incoming = BooleanField() # if true, we should ignore state, it's set to draft by default on incoming messages + delivery_attempts = IntegerField(default=0) # how many times delivery has been attempted for this message + next_delivery_attempt_at = FloatField(null=True) # timestamp of when the message will attempt delivery again title = TextField() content = TextField() fields = TextField() # json string diff --git a/web.py b/web.py index e0cdda2..d9d8cee 100644 --- a/web.py +++ b/web.py @@ -57,6 +57,9 @@ class ReticulumMeshChat: self.database_path = os.path.join(self.storage_path, "database.db") lxmf_router_path = os.path.join(self.storage_path, "lxmf_router") + # check if database already exists, before initialization + database_already_exists = os.path.exists(self.database_path) + # init database sqlite_database = SqliteDatabase(self.database_path) database.database.initialize(sqlite_database) @@ -69,6 +72,22 @@ class ReticulumMeshChat: database.LxmfConversationReadState, ]) + # init config + self.config = Config() + + # if database already existed before init, and we don't have a previous version set, we are on version 1 + if database_already_exists and self.config.database_version.get() is None: + self.config.database_version.set(1) + + # if database didn't already exist, it was just fully migrated when it was created, so set the current version + if not database_already_exists: + self.config.database_version.set(database.latest_version) + + # migrate database + current_database_version = self.config.database_version.get() + migrated_database_version = database.migrate(current_version=current_database_version) + self.config.database_version.set(migrated_database_version) + # vacuum database on start to shrink its file size sqlite_database.execute_sql("VACUUM") @@ -77,9 +96,6 @@ class ReticulumMeshChat: .where(database.LxmfMessage.state == "outbound") .orwhere(database.LxmfMessage.state == "sending").execute()) - # init config - self.config = Config() - # init reticulum self.reticulum = RNS.Reticulum(reticulum_config_dir) self.identity = identity @@ -1298,6 +1314,8 @@ class ReticulumMeshChat: "is_incoming": lxmf_message.incoming, "state": self.convert_lxmf_state_to_string(lxmf_message), "progress": progress_percentage, + "delivery_attempts": lxmf_message.delivery_attempts, + "next_delivery_attempt_at": getattr(lxmf_message, "next_delivery_attempt", None), # attribute may not exist yet "title": lxmf_message.title.decode('utf-8'), "content": lxmf_message.content.decode('utf-8'), "fields": fields, @@ -1386,6 +1404,8 @@ class ReticulumMeshChat: "is_incoming": lxmf_message_dict["is_incoming"], "state": lxmf_message_dict["state"], "progress": lxmf_message_dict["progress"], + "delivery_attempts": lxmf_message_dict["delivery_attempts"], + "next_delivery_attempt_at": lxmf_message_dict["next_delivery_attempt_at"], "title": lxmf_message_dict["title"], "content": lxmf_message_dict["content"], "fields": json.dumps(lxmf_message_dict["fields"]), @@ -1719,6 +1739,7 @@ class Config: Config.set(self.key, str(value)) # all possible config items + database_version = IntConfig("database_version", None) display_name = StringConfig("display_name", "Anonymous Peer") auto_announce_enabled = BoolConfig("auto_announce_enabled", False) auto_announce_interval_seconds = IntConfig("auto_announce_interval_seconds", 0)