fix(app-lib): delay account skin data deletion to next reboot
This gives users an opportunity to not unexpectedly lose skin data in case they log off on accident.
This commit is contained in:
12
packages/app-lib/.sqlx/query-e9449930a74c6a6151c3d868042b878b6789927df5adf50986fe642c8afcb681.json
generated
Normal file
12
packages/app-lib/.sqlx/query-e9449930a74c6a6151c3d868042b878b6789927df5adf50986fe642c8afcb681.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM default_minecraft_capes WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e9449930a74c6a6151c3d868042b878b6789927df5adf50986fe642c8afcb681"
|
||||
}
|
||||
12
packages/app-lib/.sqlx/query-fd494269d944b179ade61876669d1821b46d60f5cb79685c489aaebf13b35d24.json
generated
Normal file
12
packages/app-lib/.sqlx/query-fd494269d944b179ade61876669d1821b46d60f5cb79685c489aaebf13b35d24.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM custom_minecraft_skins WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fd494269d944b179ade61876669d1821b46d60f5cb79685c489aaebf13b35d24"
|
||||
}
|
||||
@@ -2,11 +2,34 @@ CREATE TABLE default_minecraft_capes (
|
||||
minecraft_user_uuid TEXT NOT NULL,
|
||||
id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (minecraft_user_uuid, id),
|
||||
FOREIGN KEY (minecraft_user_uuid) REFERENCES minecraft_users(uuid)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
PRIMARY KEY (minecraft_user_uuid, id)
|
||||
);
|
||||
|
||||
-- Emulate a ON UPDATE CASCADE foreign key constraint for the user UUID on the default_minecraft_capes table,
|
||||
-- but allowing deletion of the user UUID in the minecraft_users table. This allows the application to temporarily
|
||||
-- keep skin state around for logged-out users, allowing them to retain their skins under the right conditions
|
||||
CREATE TRIGGER default_minecraft_capes_user_uuid_insert_check
|
||||
BEFORE INSERT ON default_minecraft_capes FOR EACH ROW
|
||||
BEGIN
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM minecraft_users WHERE uuid = NEW.minecraft_user_uuid
|
||||
) THEN RAISE(ABORT, 'Cannot add a default cape for an unknown Minecraft user UUID') END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER default_minecraft_capes_user_uuid_update_check
|
||||
BEFORE UPDATE ON default_minecraft_capes FOR EACH ROW
|
||||
BEGIN
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM minecraft_users WHERE uuid = NEW.minecraft_user_uuid
|
||||
) THEN RAISE(ABORT, 'Cannot change a default cape to refer to an unknown Minecraft user UUID') END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER default_minecraft_capes_user_uuid_update_cascade
|
||||
AFTER UPDATE OF uuid ON minecraft_users FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE default_minecraft_capes SET minecraft_user_uuid = NEW.uuid WHERE minecraft_user_uuid = OLD.uuid;
|
||||
END;
|
||||
|
||||
CREATE TABLE custom_minecraft_skins (
|
||||
minecraft_user_uuid TEXT NOT NULL,
|
||||
texture_key TEXT NOT NULL,
|
||||
@@ -14,12 +37,33 @@ CREATE TABLE custom_minecraft_skins (
|
||||
cape_id TEXT,
|
||||
|
||||
PRIMARY KEY (minecraft_user_uuid, texture_key, variant, cape_id),
|
||||
FOREIGN KEY (minecraft_user_uuid) REFERENCES minecraft_users(uuid)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (texture_key) REFERENCES custom_minecraft_skin_textures(texture_key)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
|
||||
-- Similar partial foreign key emulation as above
|
||||
CREATE TRIGGER custom_minecraft_skins_user_uuid_insert_check
|
||||
BEFORE INSERT ON custom_minecraft_skins FOR EACH ROW
|
||||
BEGIN
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM minecraft_users WHERE uuid = NEW.minecraft_user_uuid
|
||||
) THEN RAISE(ABORT, 'Cannot add a custom skin for an unknown Minecraft user UUID') END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER custom_minecraft_skins_user_uuid_update_check
|
||||
BEFORE UPDATE ON custom_minecraft_skins FOR EACH ROW
|
||||
BEGIN
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM minecraft_users WHERE uuid = NEW.minecraft_user_uuid
|
||||
) THEN RAISE(ABORT, 'Cannot change a custom skin to refer to an unknown Minecraft user UUID') END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER custom_minecraft_skins_user_uuid_update_cascade
|
||||
AFTER UPDATE OF uuid ON minecraft_users FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE custom_minecraft_skins SET minecraft_user_uuid = NEW.uuid WHERE minecraft_user_uuid = OLD.uuid;
|
||||
END;
|
||||
|
||||
CREATE TABLE custom_minecraft_skin_textures (
|
||||
texture_key TEXT NOT NULL,
|
||||
texture PNG BLOB NOT NULL,
|
||||
|
||||
@@ -24,6 +24,8 @@ pub async fn set(settings: Settings) -> crate::Result<()> {
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn cancel_directory_change() -> crate::Result<()> {
|
||||
// This is called to handle state initialization errors due to folder migrations
|
||||
// failing, so fetching a DB connection pool from `State::get` is not reliable here
|
||||
let pool = crate::state::db::connect().await?;
|
||||
let mut settings = Settings::get(&pool).await?;
|
||||
|
||||
|
||||
@@ -36,5 +36,33 @@ pub(crate) async fn connect() -> crate::Result<Pool<Sqlite>> {
|
||||
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
|
||||
if let Err(err) = stale_data_cleanup(&pool).await {
|
||||
tracing::warn!(
|
||||
"Failed to clean up stale data from state database: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
/// Cleans up data from the database that is no longer referenced, but must be
|
||||
/// kept around for a little while to allow users to recover from accidental
|
||||
/// deletions.
|
||||
async fn stale_data_cleanup(pool: &Pool<Sqlite>) -> crate::Result<()> {
|
||||
let mut tx = pool.begin().await?;
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM default_minecraft_capes WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)"
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"DELETE FROM custom_minecraft_skins WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)"
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user