From 1bd0b68e9110d9f19921d570522e453c9d503842 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Sat, 1 Aug 2026 12:06:22 +0200 Subject: [PATCH] feat(db): browser package update cache and per-profile update mode --- ...8665f6be44e7937deffb921ddb6a5450805ab.json | 26 ++ ...3ddef96835a67aa8affe3453845a646e5ba61.json | 158 +++++++++++ ...1e6be22264a2ca3e91155cb01f2debd39b319.json | 146 ++++++++++ ...9bb0c0dcb880fcff67a096fa94b236fbe6107.json | 146 ++++++++++ ...f338462888518de3307bdab3db0b419a82be9.json | 12 + ...7ffe26960fdc25855f6cc993d6ab5deb84c02.json | 146 ++++++++++ ...a35888bb3b6a5a4fda5df3c662ca16b1d801e.json | 12 + ...3703ba78d72f39b3090734bb3e69c2b72d50c.json | 12 + ...aaa4f0b079ccbd757e5db1a8229ea58d2e700.json | 16 +- ...b55f9beddb045bc43eaef851da89b3e547933.json | 12 + ...5a90e1e0902bde48eea1ced6d89ea6dbb4555.json | 12 + ...79235e2470eeec5b10f1e768fcf54d37d57fe.json | 62 +++++ ...30ba45423c0116c911b9da16576290ed9f3c2.json | 18 +- ...0c758fc8787e982a2587b0a6f3c91abdcb3ec.json | 12 + ...960bf2968c4b75705e1a0e6800e97c3f4628.json} | 16 +- packages/oneclient_cluster/src/lib.rs | 2 +- packages/oneclient_cluster/src/profile.rs | 17 ++ packages/oneclient_cluster/src/profiles.rs | 4 + packages/oneclient_common/src/domain.rs | 67 +++++ packages/oneclient_common/src/lib.rs | 4 +- packages/oneclient_core/src/settings/mod.rs | 4 +- ...1e6be22264a2ca3e91155cb01f2debd39b319.json | 146 ++++++++++ ...9bb0c0dcb880fcff67a096fa94b236fbe6107.json | 146 ++++++++++ ...f338462888518de3307bdab3db0b419a82be9.json | 12 + ...7ffe26960fdc25855f6cc993d6ab5deb84c02.json | 146 ++++++++++ ...aaa4f0b079ccbd757e5db1a8229ea58d2e700.json | 16 +- ...5a90e1e0902bde48eea1ced6d89ea6dbb4555.json | 12 + ...30ba45423c0116c911b9da16576290ed9f3c2.json | 18 +- ...0c758fc8787e982a2587b0a6f3c91abdcb3ec.json | 12 + ...960bf2968c4b75705e1a0e6800e97c3f4628.json} | 16 +- ...20260801120000_browser_package_updates.sql | 37 +++ .../src/dao/browser_package_update.rs | 254 ++++++++++++++++++ packages/oneclient_db/src/dao/mod.rs | 1 + .../oneclient_db/src/dao/setting_profile.rs | 14 +- .../src/models/browser_package_update.rs | 21 ++ packages/oneclient_db/src/models/mod.rs | 2 + .../src/models/setting_profile.rs | 2 + 37 files changed, 1736 insertions(+), 23 deletions(-) create mode 100644 .sqlx/query-07450861d48b3bff106b7c06e958665f6be44e7937deffb921ddb6a5450805ab.json create mode 100644 .sqlx/query-1a83654d854901219ca4be57d793ddef96835a67aa8affe3453845a646e5ba61.json create mode 100644 .sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json create mode 100644 .sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json create mode 100644 .sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json create mode 100644 .sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json create mode 100644 .sqlx/query-71fbe674b85a1fd6dc07a062e96a35888bb3b6a5a4fda5df3c662ca16b1d801e.json create mode 100644 .sqlx/query-836429d5d4054d1605d7de4cbcf3703ba78d72f39b3090734bb3e69c2b72d50c.json rename packages/oneclient_db/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json => .sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json (86%) create mode 100644 .sqlx/query-b42a04c7b073b6cceff8fbd48a7b55f9beddb045bc43eaef851da89b3e547933.json create mode 100644 .sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json create mode 100644 .sqlx/query-c49ab599ef0aa31813f2a84d86f79235e2470eeec5b10f1e768fcf54d37d57fe.json rename packages/oneclient_db/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json => .sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json (71%) create mode 100644 .sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json rename .sqlx/{query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json => query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json} (86%) create mode 100644 packages/oneclient_db/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json create mode 100644 packages/oneclient_db/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json create mode 100644 packages/oneclient_db/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json create mode 100644 packages/oneclient_db/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json rename .sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json => packages/oneclient_db/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json (86%) create mode 100644 packages/oneclient_db/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json rename .sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json => packages/oneclient_db/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json (71%) create mode 100644 packages/oneclient_db/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json rename packages/oneclient_db/.sqlx/{query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json => query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json} (86%) create mode 100644 packages/oneclient_db/migrations/20260801120000_browser_package_updates.sql create mode 100644 packages/oneclient_db/src/dao/browser_package_update.rs create mode 100644 packages/oneclient_db/src/models/browser_package_update.rs diff --git a/.sqlx/query-07450861d48b3bff106b7c06e958665f6be44e7937deffb921ddb6a5450805ab.json b/.sqlx/query-07450861d48b3bff106b7c06e958665f6be44e7937deffb921ddb6a5450805ab.json new file mode 100644 index 00000000..8a0fed68 --- /dev/null +++ b/.sqlx/query-07450861d48b3bff106b7c06e958665f6be44e7937deffb921ddb6a5450805ab.json @@ -0,0 +1,26 @@ +{ + "db_name": "SQLite", + "query": "SELECT id FROM applied_migrations WHERE id = ?", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text", + "origin": { + "Table": { + "table": "applied_migrations", + "name": "id" + } + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false + ] + }, + "hash": "07450861d48b3bff106b7c06e958665f6be44e7937deffb921ddb6a5450805ab" +} diff --git a/.sqlx/query-1a83654d854901219ca4be57d793ddef96835a67aa8affe3453845a646e5ba61.json b/.sqlx/query-1a83654d854901219ca4be57d793ddef96835a67aa8affe3453845a646e5ba61.json new file mode 100644 index 00000000..e2104f28 --- /dev/null +++ b/.sqlx/query-1a83654d854901219ca4be57d793ddef96835a67aa8affe3453845a646e5ba61.json @@ -0,0 +1,158 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tUPDATE clusters\n\t\tSET mc_version = ?,\n\t\t name = ?,\n\t\t folder_name = ?\n\t\tWHERE id = ?\n\t\tRETURNING\n\t\t\tid, name, folder_name, setting_profile_name, mc_version, mc_loader,\n\t\t\tstage, mc_loader_version, created_at, last_played, overall_played, linked_modpack_hash\n\t\t", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "clusters", + "name": "id" + } + } + }, + { + "name": "name", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "name" + } + } + }, + { + "name": "folder_name", + "ordinal": 2, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "folder_name" + } + } + }, + { + "name": "setting_profile_name", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "setting_profile_name" + } + } + }, + { + "name": "mc_version", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "mc_version" + } + } + }, + { + "name": "mc_loader", + "ordinal": 5, + "type_info": "Integer", + "origin": { + "Table": { + "table": "clusters", + "name": "mc_loader" + } + } + }, + { + "name": "stage", + "ordinal": 6, + "type_info": "Integer", + "origin": { + "Table": { + "table": "clusters", + "name": "stage" + } + } + }, + { + "name": "mc_loader_version", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "mc_loader_version" + } + } + }, + { + "name": "created_at", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "created_at" + } + } + }, + { + "name": "last_played", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "last_played" + } + } + }, + { + "name": "overall_played", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "clusters", + "name": "overall_played" + } + } + }, + { + "name": "linked_modpack_hash", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "clusters", + "name": "linked_modpack_hash" + } + } + } + ], + "parameters": { + "Right": 4 + }, + "nullable": [ + false, + false, + false, + true, + false, + false, + false, + true, + true, + true, + true, + true + ] + }, + "hash": "1a83654d854901219ca4be57d793ddef96835a67aa8affe3453845a646e5ba61" +} diff --git a/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json b/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json new file mode 100644 index 00000000..c822cc59 --- /dev/null +++ b/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tSELECT\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\tFROM browser_package_updates\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319" +} diff --git a/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json b/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json new file mode 100644 index 00000000..1d517c25 --- /dev/null +++ b/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tSELECT\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\tFROM browser_package_updates\n\t\tWHERE cluster_id = ?\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107" +} diff --git a/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json b/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json new file mode 100644 index 00000000..cfc5f2d7 --- /dev/null +++ b/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tUPDATE browser_package_updates\n\t\tSET skipped = ?\n\t\tWHERE cluster_id = ? AND hash = ?\n\t\t", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9" +} diff --git a/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json b/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json new file mode 100644 index 00000000..ea400fff --- /dev/null +++ b/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tINSERT INTO browser_package_updates (\n\t\t\tcluster_id, hash, provider, project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)\n\t\tON CONFLICT(cluster_id, hash) DO UPDATE SET\n\t\t\tprovider = excluded.provider,\n\t\t\tproject_id = excluded.project_id,\n\t\t\tinstalled_version_id = excluded.installed_version_id,\n\t\t\tinstalled_version_name = excluded.installed_version_name,\n\t\t\tlatest_version_name = excluded.latest_version_name,\n\t\t\tdisplay_name = excluded.display_name,\n\t\t\tchecked_at = excluded.checked_at,\n\t\t\tskipped = CASE\n\t\t\t\tWHEN browser_package_updates.latest_version_id = excluded.latest_version_id\n\t\t\t\tTHEN browser_package_updates.skipped\n\t\t\t\tELSE 0\n\t\t\tEND,\n\t\t\tlatest_version_id = excluded.latest_version_id\n\t\tRETURNING\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 10 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02" +} diff --git a/.sqlx/query-71fbe674b85a1fd6dc07a062e96a35888bb3b6a5a4fda5df3c662ca16b1d801e.json b/.sqlx/query-71fbe674b85a1fd6dc07a062e96a35888bb3b6a5a4fda5df3c662ca16b1d801e.json new file mode 100644 index 00000000..cb59124e --- /dev/null +++ b/.sqlx/query-71fbe674b85a1fd6dc07a062e96a35888bb3b6a5a4fda5df3c662ca16b1d801e.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tUPDATE game_sessions\n\t\tSET pid = ?, pid_started_at = ?\n\t\tWHERE started_at = ?\n\t\t", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "71fbe674b85a1fd6dc07a062e96a35888bb3b6a5a4fda5df3c662ca16b1d801e" +} diff --git a/.sqlx/query-836429d5d4054d1605d7de4cbcf3703ba78d72f39b3090734bb3e69c2b72d50c.json b/.sqlx/query-836429d5d4054d1605d7de4cbcf3703ba78d72f39b3090734bb3e69c2b72d50c.json new file mode 100644 index 00000000..b445c9cb --- /dev/null +++ b/.sqlx/query-836429d5d4054d1605d7de4cbcf3703ba78d72f39b3090734bb3e69c2b72d50c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT OR IGNORE INTO applied_migrations (id, applied_at) VALUES (?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "836429d5d4054d1605d7de4cbcf3703ba78d72f39b3090734bb3e69c2b72d50c" +} diff --git a/packages/oneclient_db/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json b/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json similarity index 86% rename from packages/oneclient_db/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json rename to .sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json index 93a67f21..e08f44bc 100644 --- a/packages/oneclient_db/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json +++ b/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra\n\t\tFROM setting_profiles\n\t\tWHERE name = ?\n\t\t", + "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\tFROM setting_profiles\n\t\tWHERE name = ?\n\t\t", "describe": { "columns": [ { @@ -123,6 +123,17 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27" + "hash": "98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700" } diff --git a/.sqlx/query-b42a04c7b073b6cceff8fbd48a7b55f9beddb045bc43eaef851da89b3e547933.json b/.sqlx/query-b42a04c7b073b6cceff8fbd48a7b55f9beddb045bc43eaef851da89b3e547933.json new file mode 100644 index 00000000..ebc1b8fd --- /dev/null +++ b/.sqlx/query-b42a04c7b073b6cceff8fbd48a7b55f9beddb045bc43eaef851da89b3e547933.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tDELETE FROM game_session_servers\n\t\tWHERE session_started_at = ?\n\t\t", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "b42a04c7b073b6cceff8fbd48a7b55f9beddb045bc43eaef851da89b3e547933" +} diff --git a/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json b/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json new file mode 100644 index 00000000..584a28cf --- /dev/null +++ b/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM browser_package_updates WHERE cluster_id = ? AND hash = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555" +} diff --git a/.sqlx/query-c49ab599ef0aa31813f2a84d86f79235e2470eeec5b10f1e768fcf54d37d57fe.json b/.sqlx/query-c49ab599ef0aa31813f2a84d86f79235e2470eeec5b10f1e768fcf54d37d57fe.json new file mode 100644 index 00000000..9d2eae4f --- /dev/null +++ b/.sqlx/query-c49ab599ef0aa31813f2a84d86f79235e2470eeec5b10f1e768fcf54d37d57fe.json @@ -0,0 +1,62 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tSELECT cluster_id, started_at, pid, pid_started_at\n\t\tFROM game_sessions\n\t\tWHERE ended_at IS NULL\n\t\tORDER BY started_at ASC\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "game_sessions", + "name": "cluster_id" + } + } + }, + { + "name": "started_at", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "game_sessions", + "name": "started_at" + } + } + }, + { + "name": "pid", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "game_sessions", + "name": "pid" + } + } + }, + { + "name": "pid_started_at", + "ordinal": 3, + "type_info": "Integer", + "origin": { + "Table": { + "table": "game_sessions", + "name": "pid_started_at" + } + } + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + true, + true + ] + }, + "hash": "c49ab599ef0aa31813f2a84d86f79235e2470eeec5b10f1e768fcf54d37d57fe" +} diff --git a/packages/oneclient_db/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json b/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json similarity index 71% rename from packages/oneclient_db/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json rename to .sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json index 9649dd57..72745260 100644 --- a/packages/oneclient_db/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json +++ b/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tINSERT INTO setting_profiles (\n\t\t\tname, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t\thook_pre, hook_wrapper, hook_post, os_extra\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(name) DO UPDATE SET\n\t\t\tjava_path = excluded.java_path,\n\t\t\tresolution = excluded.resolution,\n\t\t\tforce_fullscreen = excluded.force_fullscreen,\n\t\t\tmem_max = excluded.mem_max,\n\t\t\tlaunch_args = excluded.launch_args,\n\t\t\tlaunch_env = excluded.launch_env,\n\t\t\thook_pre = excluded.hook_pre,\n\t\t\thook_wrapper = excluded.hook_wrapper,\n\t\t\thook_post = excluded.hook_post,\n\t\t\tos_extra = excluded.os_extra\n\t\tRETURNING name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n hook_pre, hook_wrapper, hook_post, os_extra\n\t\t", + "query": "\n\t\tINSERT INTO setting_profiles (\n\t\t\tname, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t\thook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(name) DO UPDATE SET\n\t\t\tjava_path = excluded.java_path,\n\t\t\tresolution = excluded.resolution,\n\t\t\tforce_fullscreen = excluded.force_fullscreen,\n\t\t\tmem_max = excluded.mem_max,\n\t\t\tlaunch_args = excluded.launch_args,\n\t\t\tlaunch_env = excluded.launch_env,\n\t\t\thook_pre = excluded.hook_pre,\n\t\t\thook_wrapper = excluded.hook_wrapper,\n\t\t\thook_post = excluded.hook_post,\n\t\t\tos_extra = excluded.os_extra,\n\t\t\tbrowser_update_mode = excluded.browser_update_mode\n\t\tRETURNING name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\t", "describe": { "columns": [ { @@ -123,10 +123,21 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { - "Right": 11 + "Right": 12 }, "nullable": [ false, @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9" + "hash": "c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2" } diff --git a/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json b/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json new file mode 100644 index 00000000..b3eb3cf9 --- /dev/null +++ b/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM browser_package_updates WHERE cluster_id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec" +} diff --git a/.sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json b/.sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json similarity index 86% rename from .sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json rename to .sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json index 58a0b670..c1c2145d 100644 --- a/.sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json +++ b/.sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra\n\t\tFROM setting_profiles\n\t\tORDER BY name ASC\n\t\t", + "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\tFROM setting_profiles\n\t\tORDER BY name ASC\n\t\t", "describe": { "columns": [ { @@ -123,6 +123,17 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80" + "hash": "f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628" } diff --git a/packages/oneclient_cluster/src/lib.rs b/packages/oneclient_cluster/src/lib.rs index 5dfbf5e5..404a1430 100644 --- a/packages/oneclient_cluster/src/lib.rs +++ b/packages/oneclient_cluster/src/lib.rs @@ -25,6 +25,6 @@ pub use cluster::{Cluster, ClusterLinkTarget}; pub use error::{ClusterError, ClusterResult}; pub use manager::ClusterManager; pub use options::{ClusterUpdate, CreateClusterOptions}; -pub use profile::{GameSettingsProfile, SettingsOsExtra}; +pub use profile::{GameSettingsProfile, PackageUpdateMode, SettingsOsExtra}; pub use profiles::ProfileUpdate; pub use stage::ClusterStage; diff --git a/packages/oneclient_cluster/src/profile.rs b/packages/oneclient_cluster/src/profile.rs index ecaa5fa8..d476f201 100644 --- a/packages/oneclient_cluster/src/profile.rs +++ b/packages/oneclient_cluster/src/profile.rs @@ -17,11 +17,15 @@ pub struct GameSettingsProfile { pub hook_wrapper: Option, pub hook_post: Option, pub os_extra: Option, + /// What to do when a package installed from the browser has a newer + /// version. Bundle content is never touched by this. + pub browser_update_mode: Option, } // `Resolution` moved to oneclient_common: the launch-argument builder needs it // too, and that crate cannot depend on the launcher settings. pub use oneclient_common::Resolution; +pub use oneclient_common::domain::PackageUpdateMode; cfg_select! { target_os = "windows" => { @@ -62,6 +66,7 @@ impl GameSettingsProfile { hook_wrapper: None, hook_post: None, os_extra: Some(SettingsOsExtra::default()), + browser_update_mode: Some(PackageUpdateMode::default()), } } @@ -100,6 +105,9 @@ impl GameSettingsProfile { if self.os_extra.is_none() { self.os_extra = global.os_extra.clone(); } + if self.browser_update_mode.is_none() { + self.browser_update_mode = global.browser_update_mode; + } } pub fn from_row(row: SettingProfileRow) -> crate::ClusterResult { @@ -121,6 +129,12 @@ impl GameSettingsProfile { .os_extra .map(|json| serde_json::from_str(&json)) .transpose()?, + // An unrecognised value inherits rather than errors: a profile + // written by a newer build must not make the cluster unloadable. + browser_update_mode: row + .browser_update_mode + .as_deref() + .and_then(PackageUpdateMode::parse), }) } @@ -144,6 +158,9 @@ impl GameSettingsProfile { .as_ref() .map(serde_json::to_string) .transpose()?, + browser_update_mode: self + .browser_update_mode + .map(|mode| mode.as_str().to_string()), }) } } diff --git a/packages/oneclient_cluster/src/profiles.rs b/packages/oneclient_cluster/src/profiles.rs index 4e6cbdd2..24ec19ad 100644 --- a/packages/oneclient_cluster/src/profiles.rs +++ b/packages/oneclient_cluster/src/profiles.rs @@ -15,6 +15,7 @@ use oneclient_db::DbPool; use crate::error::{ClusterError, ClusterResult}; use crate::profile::{GameSettingsProfile, SettingsOsExtra}; use oneclient_common::Resolution; +use oneclient_common::domain::PackageUpdateMode; #[tracing::instrument(level = "debug", skip(pool, global))] pub async fn get_profile_or_default( @@ -132,6 +133,7 @@ pub struct ProfileUpdate { pub hook_wrapper: Patch, pub hook_post: Patch, pub os_extra: Patch, + pub browser_update_mode: Patch, } impl ProfileUpdate { @@ -151,6 +153,8 @@ impl ProfileUpdate { self.hook_post .apply_to_command_option(&mut profile.hook_post); self.os_extra.apply_to_option(&mut profile.os_extra); + self.browser_update_mode + .apply_to_option(&mut profile.browser_update_mode); } } diff --git a/packages/oneclient_common/src/domain.rs b/packages/oneclient_common/src/domain.rs index dcef8f39..402967ea 100644 --- a/packages/oneclient_common/src/domain.rs +++ b/packages/oneclient_common/src/domain.rs @@ -125,6 +125,73 @@ pub enum HashAlgorithm { Sha1, } +/// What the launcher does when a package the user installed from the browser +/// has a newer version at launch. +/// +/// Bundle-provided content is not covered by this: bundles carry their own +/// update flow, and the two must not fight over the same file. +/// +/// The check itself always runs; this only decides what happens with the +/// result, so the "out of date" markers in the package manager are populated +/// no matter which variant is selected. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + Default, + EnumIter, + StrumDisplay, +)] +#[serde(rename_all = "snake_case")] +pub enum PackageUpdateMode { + /// Apply every update as soon as it is found, with no modal. + Automatic, + /// Never apply and never ask. + Skip, + /// Ask, listing each package so the user decides one at a time. + #[default] + Prompt, +} + +impl PackageUpdateMode { + /// Display order, defaulting variant first. + pub const ALL: &[Self] = &[Self::Prompt, Self::Automatic, Self::Skip]; + + #[must_use] + pub const fn label(self) -> &'static str { + match self { + Self::Automatic => "Automatically update", + Self::Skip => "Always skip auto updates", + Self::Prompt => "Show the modal", + } + } + + /// The value stored in the `setting_profiles.browser_update_mode` column. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Automatic => "automatic", + Self::Skip => "skip", + Self::Prompt => "prompt", + } + } + + #[must_use] + pub fn parse(value: &str) -> Option { + match value { + "automatic" => Some(Self::Automatic), + "skip" => Some(Self::Skip), + "prompt" => Some(Self::Prompt), + _ => None, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default, FromRepr)] #[repr(u8)] pub enum GameLoader { diff --git a/packages/oneclient_common/src/lib.rs b/packages/oneclient_common/src/lib.rs index 65924480..b5cf097a 100644 --- a/packages/oneclient_common/src/lib.rs +++ b/packages/oneclient_common/src/lib.rs @@ -15,7 +15,9 @@ pub mod version; mod error; -pub use domain::{ContentType, GameLoader, HashAlgorithm, ProviderId, Resolution}; +pub use domain::{ + ContentType, GameLoader, HashAlgorithm, PackageUpdateMode, ProviderId, Resolution, +}; pub use error::{PathsError, PathsResult}; pub use os_ext::OsExt; pub use patch::Patch; diff --git a/packages/oneclient_core/src/settings/mod.rs b/packages/oneclient_core/src/settings/mod.rs index 1d75139c..417ca8d4 100644 --- a/packages/oneclient_core/src/settings/mod.rs +++ b/packages/oneclient_core/src/settings/mod.rs @@ -2,7 +2,9 @@ mod launcher; pub use launcher::{LauncherSettings, ViewLayout, ViewState}; // Profiles moved to `oneclient_cluster`; re-exported for existing paths. -pub use oneclient_cluster::{GameSettingsProfile, ProfileUpdate, SettingsOsExtra}; +pub use oneclient_cluster::{ + GameSettingsProfile, PackageUpdateMode, ProfileUpdate, SettingsOsExtra, +}; pub use oneclient_common::Resolution; pub mod store; diff --git a/packages/oneclient_db/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json b/packages/oneclient_db/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json new file mode 100644 index 00000000..c822cc59 --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tSELECT\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\tFROM browser_package_updates\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "2005ac6f15d4c5fe36d5bfa87191e6be22264a2ca3e91155cb01f2debd39b319" +} diff --git a/packages/oneclient_db/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json b/packages/oneclient_db/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json new file mode 100644 index 00000000..1d517c25 --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tSELECT\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\tFROM browser_package_updates\n\t\tWHERE cluster_id = ?\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "271e5a3e03861f3699e3b32dc4a9bb0c0dcb880fcff67a096fa94b236fbe6107" +} diff --git a/packages/oneclient_db/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json b/packages/oneclient_db/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json new file mode 100644 index 00000000..cfc5f2d7 --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tUPDATE browser_package_updates\n\t\tSET skipped = ?\n\t\tWHERE cluster_id = ? AND hash = ?\n\t\t", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "472a614b1f1484e839f373fcfecf338462888518de3307bdab3db0b419a82be9" +} diff --git a/packages/oneclient_db/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json b/packages/oneclient_db/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json new file mode 100644 index 00000000..ea400fff --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02.json @@ -0,0 +1,146 @@ +{ + "db_name": "SQLite", + "query": "\n\t\tINSERT INTO browser_package_updates (\n\t\t\tcluster_id, hash, provider, project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)\n\t\tON CONFLICT(cluster_id, hash) DO UPDATE SET\n\t\t\tprovider = excluded.provider,\n\t\t\tproject_id = excluded.project_id,\n\t\t\tinstalled_version_id = excluded.installed_version_id,\n\t\t\tinstalled_version_name = excluded.installed_version_name,\n\t\t\tlatest_version_name = excluded.latest_version_name,\n\t\t\tdisplay_name = excluded.display_name,\n\t\t\tchecked_at = excluded.checked_at,\n\t\t\tskipped = CASE\n\t\t\t\tWHEN browser_package_updates.latest_version_id = excluded.latest_version_id\n\t\t\t\tTHEN browser_package_updates.skipped\n\t\t\t\tELSE 0\n\t\t\tEND,\n\t\t\tlatest_version_id = excluded.latest_version_id\n\t\tRETURNING\n\t\t\tcluster_id, hash, provider as \"provider!: i64\", project_id,\n\t\t\tinstalled_version_id, installed_version_name,\n\t\t\tlatest_version_id, latest_version_name,\n\t\t\tdisplay_name, checked_at, skipped\n\t\t", + "describe": { + "columns": [ + { + "name": "cluster_id", + "ordinal": 0, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "cluster_id" + } + } + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "hash" + } + } + }, + { + "name": "provider!: i64", + "ordinal": 2, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "provider" + } + } + }, + { + "name": "project_id", + "ordinal": 3, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "project_id" + } + } + }, + { + "name": "installed_version_id", + "ordinal": 4, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_id" + } + } + }, + { + "name": "installed_version_name", + "ordinal": 5, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "installed_version_name" + } + } + }, + { + "name": "latest_version_id", + "ordinal": 6, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_id" + } + } + }, + { + "name": "latest_version_name", + "ordinal": 7, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "latest_version_name" + } + } + }, + { + "name": "display_name", + "ordinal": 8, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "display_name" + } + } + }, + { + "name": "checked_at", + "ordinal": 9, + "type_info": "Text", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "checked_at" + } + } + }, + { + "name": "skipped", + "ordinal": 10, + "type_info": "Integer", + "origin": { + "Table": { + "table": "browser_package_updates", + "name": "skipped" + } + } + } + ], + "parameters": { + "Right": 10 + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "5183d452802d8bbec7f2a52ceba7ffe26960fdc25855f6cc993d6ab5deb84c02" +} diff --git a/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json b/packages/oneclient_db/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json similarity index 86% rename from .sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json rename to packages/oneclient_db/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json index 93a67f21..e08f44bc 100644 --- a/.sqlx/query-de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27.json +++ b/packages/oneclient_db/.sqlx/query-98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra\n\t\tFROM setting_profiles\n\t\tWHERE name = ?\n\t\t", + "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\tFROM setting_profiles\n\t\tWHERE name = ?\n\t\t", "describe": { "columns": [ { @@ -123,6 +123,17 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "de1b40c927c9819f675b8e3e8e1f583c41450ae9deda20739d82d95e8ca70b27" + "hash": "98500a40f7e1ca7df79dd0ca90eaaa4f0b079ccbd757e5db1a8229ea58d2e700" } diff --git a/packages/oneclient_db/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json b/packages/oneclient_db/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json new file mode 100644 index 00000000..584a28cf --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM browser_package_updates WHERE cluster_id = ? AND hash = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "c3f6c0105273939636dd0a7c7895a90e1e0902bde48eea1ced6d89ea6dbb4555" +} diff --git a/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json b/packages/oneclient_db/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json similarity index 71% rename from .sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json rename to packages/oneclient_db/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json index 9649dd57..72745260 100644 --- a/.sqlx/query-b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9.json +++ b/packages/oneclient_db/.sqlx/query-c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tINSERT INTO setting_profiles (\n\t\t\tname, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t\thook_pre, hook_wrapper, hook_post, os_extra\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(name) DO UPDATE SET\n\t\t\tjava_path = excluded.java_path,\n\t\t\tresolution = excluded.resolution,\n\t\t\tforce_fullscreen = excluded.force_fullscreen,\n\t\t\tmem_max = excluded.mem_max,\n\t\t\tlaunch_args = excluded.launch_args,\n\t\t\tlaunch_env = excluded.launch_env,\n\t\t\thook_pre = excluded.hook_pre,\n\t\t\thook_wrapper = excluded.hook_wrapper,\n\t\t\thook_post = excluded.hook_post,\n\t\t\tos_extra = excluded.os_extra\n\t\tRETURNING name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n hook_pre, hook_wrapper, hook_post, os_extra\n\t\t", + "query": "\n\t\tINSERT INTO setting_profiles (\n\t\t\tname, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t\thook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\t)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\tON CONFLICT(name) DO UPDATE SET\n\t\t\tjava_path = excluded.java_path,\n\t\t\tresolution = excluded.resolution,\n\t\t\tforce_fullscreen = excluded.force_fullscreen,\n\t\t\tmem_max = excluded.mem_max,\n\t\t\tlaunch_args = excluded.launch_args,\n\t\t\tlaunch_env = excluded.launch_env,\n\t\t\thook_pre = excluded.hook_pre,\n\t\t\thook_wrapper = excluded.hook_wrapper,\n\t\t\thook_post = excluded.hook_post,\n\t\t\tos_extra = excluded.os_extra,\n\t\t\tbrowser_update_mode = excluded.browser_update_mode\n\t\tRETURNING name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\t", "describe": { "columns": [ { @@ -123,10 +123,21 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { - "Right": 11 + "Right": 12 }, "nullable": [ false, @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "b41126ba17cbc3005b446b23cebc29ffe6fc366294191e546808e9f591b723a9" + "hash": "c939cef4b51d506d5b73b4c2e0330ba45423c0116c911b9da16576290ed9f3c2" } diff --git a/packages/oneclient_db/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json b/packages/oneclient_db/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json new file mode 100644 index 00000000..b3eb3cf9 --- /dev/null +++ b/packages/oneclient_db/.sqlx/query-f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM browser_package_updates WHERE cluster_id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "f050a2d799f838b8fffca63c8ca0c758fc8787e982a2587b0a6f3c91abdcb3ec" +} diff --git a/packages/oneclient_db/.sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json b/packages/oneclient_db/.sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json similarity index 86% rename from packages/oneclient_db/.sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json rename to packages/oneclient_db/.sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json index 58a0b670..c1c2145d 100644 --- a/packages/oneclient_db/.sqlx/query-4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80.json +++ b/packages/oneclient_db/.sqlx/query-f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra\n\t\tFROM setting_profiles\n\t\tORDER BY name ASC\n\t\t", + "query": "\n\t\tSELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env,\n\t\t hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode\n\t\tFROM setting_profiles\n\t\tORDER BY name ASC\n\t\t", "describe": { "columns": [ { @@ -123,6 +123,17 @@ "name": "os_extra" } } + }, + { + "name": "browser_update_mode", + "ordinal": 11, + "type_info": "Text", + "origin": { + "Table": { + "table": "setting_profiles", + "name": "browser_update_mode" + } + } } ], "parameters": { @@ -139,8 +150,9 @@ true, true, true, + true, true ] }, - "hash": "4ab22fa481720e3bae5befee821fe1c0689d666f8bbcde98890e2d4715e9bc80" + "hash": "f68b9ade92feb989828ca81582c5960bf2968c4b75705e1a0e6800e97c3f4628" } diff --git a/packages/oneclient_db/migrations/20260801120000_browser_package_updates.sql b/packages/oneclient_db/migrations/20260801120000_browser_package_updates.sql new file mode 100644 index 00000000..0d329e24 --- /dev/null +++ b/packages/oneclient_db/migrations/20260801120000_browser_package_updates.sql @@ -0,0 +1,37 @@ +-- Auto-updates for packages the user installed from the browser, i.e. remote +-- provider content that no bundle provides. Bundles keep their own update flow +-- and are deliberately out of scope here. +-- +-- `browser_update_mode` is the per-profile override of the launcher-wide +-- setting; NULL means "inherit the global value", like every other column on +-- this table. Values are the strings in `PackageUpdateMode::as_str`. +ALTER TABLE setting_profiles ADD COLUMN browser_update_mode TEXT; + +-- Cache of the launch-time update check, so the package manager can mark a +-- package out of date without going back to the provider on every render, and +-- still knows about it after a restart. +-- +-- Keyed by the artifact hash actually installed in the cluster: that is what +-- the package rows are keyed by, and it changes when the update is applied, +-- which is what makes a stale row impossible to mistake for a live one. +-- +-- `skipped` records that the user was asked about *this* newer version and said +-- no. The row stays either way, because a skipped package is still out of date +-- and should still say so in the package manager; it just stops re-opening the +-- modal for the same version. +CREATE TABLE browser_package_updates ( + cluster_id INTEGER NOT NULL, + hash TEXT NOT NULL, + provider INTEGER NOT NULL, + project_id TEXT NOT NULL, + installed_version_id TEXT NOT NULL, + installed_version_name TEXT NOT NULL DEFAULT '', + latest_version_id TEXT NOT NULL, + latest_version_name TEXT NOT NULL DEFAULT '', + display_name TEXT NOT NULL DEFAULT '', + checked_at TEXT NOT NULL, + skipped INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (cluster_id, hash), + FOREIGN KEY (cluster_id) REFERENCES clusters (id) ON DELETE CASCADE +); +CREATE INDEX browser_package_updates_cluster_id_idx ON browser_package_updates (cluster_id); diff --git a/packages/oneclient_db/src/dao/browser_package_update.rs b/packages/oneclient_db/src/dao/browser_package_update.rs new file mode 100644 index 00000000..62b4dff8 --- /dev/null +++ b/packages/oneclient_db/src/dao/browser_package_update.rs @@ -0,0 +1,254 @@ +use chrono::Utc; +use sqlx::{QueryBuilder, Sqlite, SqlitePool}; + +use crate::models::BrowserPackageUpdateRow; + +pub async fn list_for_cluster( + pool: &SqlitePool, + cluster_id: i64, +) -> Result, sqlx::Error> { + sqlx::query_as!( + BrowserPackageUpdateRow, + r#" + SELECT + cluster_id, hash, provider as "provider!: i64", project_id, + installed_version_id, installed_version_name, + latest_version_id, latest_version_name, + display_name, checked_at, skipped + FROM browser_package_updates + WHERE cluster_id = ? + "#, + cluster_id + ) + .fetch_all(pool) + .await +} + +pub async fn list_all( + pool: &SqlitePool, +) -> Result, sqlx::Error> { + sqlx::query_as!( + BrowserPackageUpdateRow, + r#" + SELECT + cluster_id, hash, provider as "provider!: i64", project_id, + installed_version_id, installed_version_name, + latest_version_id, latest_version_name, + display_name, checked_at, skipped + FROM browser_package_updates + "#, + ) + .fetch_all(pool) + .await +} + +/// Records a pending update. +/// +/// A row that already names the same `latest_version_id` keeps its `skipped` +/// flag: the user has answered for that version and re-running the check is not +/// a reason to ask again. A different `latest_version_id` is a new question, so +/// the flag resets. +#[allow(clippy::too_many_arguments)] +pub async fn upsert( + pool: &SqlitePool, + cluster_id: i64, + hash: &str, + provider: i64, + project_id: &str, + installed_version_id: &str, + installed_version_name: &str, + latest_version_id: &str, + latest_version_name: &str, + display_name: &str, +) -> Result { + let checked_at = Utc::now().to_rfc3339(); + + sqlx::query_as!( + BrowserPackageUpdateRow, + r#" + INSERT INTO browser_package_updates ( + cluster_id, hash, provider, project_id, + installed_version_id, installed_version_name, + latest_version_id, latest_version_name, + display_name, checked_at, skipped + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0) + ON CONFLICT(cluster_id, hash) DO UPDATE SET + provider = excluded.provider, + project_id = excluded.project_id, + installed_version_id = excluded.installed_version_id, + installed_version_name = excluded.installed_version_name, + latest_version_name = excluded.latest_version_name, + display_name = excluded.display_name, + checked_at = excluded.checked_at, + skipped = CASE + WHEN browser_package_updates.latest_version_id = excluded.latest_version_id + THEN browser_package_updates.skipped + ELSE 0 + END, + latest_version_id = excluded.latest_version_id + RETURNING + cluster_id, hash, provider as "provider!: i64", project_id, + installed_version_id, installed_version_name, + latest_version_id, latest_version_name, + display_name, checked_at, skipped + "#, + cluster_id, + hash, + provider, + project_id, + installed_version_id, + installed_version_name, + latest_version_id, + latest_version_name, + display_name, + checked_at + ) + .fetch_one(pool) + .await +} + +pub async fn set_skipped( + pool: &SqlitePool, + cluster_id: i64, + hash: &str, + skipped: bool, +) -> Result<(), sqlx::Error> { + let skipped = i64::from(skipped); + sqlx::query!( + r#" + UPDATE browser_package_updates + SET skipped = ? + WHERE cluster_id = ? AND hash = ? + "#, + skipped, + cluster_id, + hash + ) + .execute(pool) + .await?; + + Ok(()) +} + +pub async fn delete(pool: &SqlitePool, cluster_id: i64, hash: &str) -> Result<(), sqlx::Error> { + sqlx::query!( + "DELETE FROM browser_package_updates WHERE cluster_id = ? AND hash = ?", + cluster_id, + hash + ) + .execute(pool) + .await?; + + Ok(()) +} + +/// Drops rows for hashes the latest check did not report. +/// +/// A package that was updated, removed, or turned out to be current again must +/// stop being marked out of date, and the check only tells us what *is* stale. +/// Passing an empty list clears the cluster. +pub async fn retain_hashes( + pool: &SqlitePool, + cluster_id: i64, + hashes: &[String], +) -> Result<(), sqlx::Error> { + if hashes.is_empty() { + sqlx::query!( + "DELETE FROM browser_package_updates WHERE cluster_id = ?", + cluster_id + ) + .execute(pool) + .await?; + return Ok(()); + } + + let mut builder: QueryBuilder = + QueryBuilder::new("DELETE FROM browser_package_updates WHERE cluster_id = "); + builder.push_bind(cluster_id); + builder.push(" AND hash NOT IN ("); + let mut separated = builder.separated(", "); + for hash in hashes { + separated.push_bind(hash); + } + separated.push_unseparated(")"); + + builder.build().execute(pool).await?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + async fn pool() -> SqlitePool { + let pool = SqlitePool::connect("sqlite::memory:") + .await + .expect("in-memory sqlite"); + sqlx::migrate!().run(&pool).await.expect("migrations run"); + // Runtime query, not the macro: the offline cache is generated for the + // library only, and a fixture is not worth a `--all-targets` prepare. + sqlx::query( + r#" + INSERT INTO clusters (id, name, folder_name, mc_version) + VALUES (1, 'test', 'test', '1.21.4') + "#, + ) + .execute(&pool) + .await + .expect("cluster row"); + pool + } + + async fn record(pool: &SqlitePool, hash: &str, latest: &str) -> BrowserPackageUpdateRow { + upsert(pool, 1, hash, 0, "proj", "v1", "1.0.0", latest, "2.0.0", "Mod") + .await + .expect("upsert") + } + + #[tokio::test] + async fn skip_survives_a_recheck_of_the_same_version() { + let pool = pool().await; + record(&pool, "abc", "v2").await; + set_skipped(&pool, 1, "abc", true).await.expect("skip"); + + let row = record(&pool, "abc", "v2").await; + assert_eq!(row.skipped, 1, "re-running the check must not re-ask"); + } + + #[tokio::test] + async fn a_newer_version_asks_again() { + let pool = pool().await; + record(&pool, "abc", "v2").await; + set_skipped(&pool, 1, "abc", true).await.expect("skip"); + + let row = record(&pool, "abc", "v3").await; + assert_eq!(row.skipped, 0, "a different version is a new question"); + } + + #[tokio::test] + async fn retain_drops_hashes_the_check_no_longer_reports() { + let pool = pool().await; + record(&pool, "abc", "v2").await; + record(&pool, "def", "v2").await; + + retain_hashes(&pool, 1, &["abc".to_string()]) + .await + .expect("retain"); + + let rows = list_for_cluster(&pool, 1).await.expect("list"); + assert_eq!(rows.len(), 1); + assert_eq!(rows[0].hash, "abc"); + } + + #[tokio::test] + async fn retain_with_nothing_clears_the_cluster() { + let pool = pool().await; + record(&pool, "abc", "v2").await; + + retain_hashes(&pool, 1, &[]).await.expect("retain"); + + assert!(list_for_cluster(&pool, 1).await.expect("list").is_empty()); + } +} diff --git a/packages/oneclient_db/src/dao/mod.rs b/packages/oneclient_db/src/dao/mod.rs index 986fee8f..63874833 100644 --- a/packages/oneclient_db/src/dao/mod.rs +++ b/packages/oneclient_db/src/dao/mod.rs @@ -1,5 +1,6 @@ pub mod applied_migration; pub mod artifact; +pub mod browser_package_update; pub mod bundle; pub mod cluster; pub mod cluster_bundle; diff --git a/packages/oneclient_db/src/dao/setting_profile.rs b/packages/oneclient_db/src/dao/setting_profile.rs index c82441c2..0511ece1 100644 --- a/packages/oneclient_db/src/dao/setting_profile.rs +++ b/packages/oneclient_db/src/dao/setting_profile.rs @@ -15,7 +15,7 @@ pub async fn list_all(pool: &SqlitePool) -> DbResult> { SettingProfileRow, r#" SELECT name, java_path, resolution, force_fullscreen, mem_max, launch_args, launch_env, - hook_pre, hook_wrapper, hook_post, os_extra + hook_pre, hook_wrapper, hook_post, os_extra, browser_update_mode FROM setting_profiles ORDER BY name ASC "#, @@ -31,7 +31,7 @@ pub async fn get_by_name(pool: &SqlitePool, name: &str) -> DbResult, pub hook_post: Option, pub os_extra: Option, + /// `PackageUpdateMode::as_str`; NULL inherits the global setting. + pub browser_update_mode: Option, }