You've already forked AstralRinth
forked from didirus/AstralRinth
fix: added ability for regenerate checksums with issued mr migrations.
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
use std::process::exit;
|
||||
|
||||
use reqwest;
|
||||
use tokio::fs::File as AsyncFile;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
async fn download_file(download_url: &str, local_filename: &str, os_type: &str, auto_update_supported: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let download_dir = dirs::download_dir().ok_or("[download_file] • Failed to determine download directory")?;
|
||||
let full_path = download_dir.join(local_filename);
|
||||
let response = reqwest::get(download_url).await?;
|
||||
let bytes = response.bytes().await?;
|
||||
let mut dest_file = AsyncFile::create(&full_path).await?;
|
||||
dest_file.write_all(&bytes).await?;
|
||||
println!("[download_file] • File downloaded to: {:?}", full_path);
|
||||
if auto_update_supported {
|
||||
let status;
|
||||
if os_type.to_lowercase() == "Windows".to_lowercase() {
|
||||
status = Command::new("explorer")
|
||||
.arg(download_dir.display().to_string())
|
||||
.status()
|
||||
.await
|
||||
.expect("[download_file] • Failed to open downloads folder");
|
||||
} else if os_type.to_lowercase() == "MacOS".to_lowercase() {
|
||||
status = Command::new("open")
|
||||
.arg(full_path.to_str().unwrap_or_default())
|
||||
.status()
|
||||
.await
|
||||
.expect("[download_file] • Failed to execute command");
|
||||
} else {
|
||||
status = Command::new(".")
|
||||
.arg(full_path.to_str().unwrap_or_default())
|
||||
.status()
|
||||
.await
|
||||
.expect("[download_file] • Failed to execute command");
|
||||
}
|
||||
if status.success() {
|
||||
println!("[download_file] • File opened successfully!");
|
||||
} else {
|
||||
eprintln!("[download_file] • Failed to open the file. Exit code: {:?}", status.code());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init_download(download_url: &str, local_filename: &str, os_type: &str, auto_update_supported: bool) {
|
||||
println!("[init_download] • Initialize downloading from • {:?}", download_url);
|
||||
println!("[init_download] • Save local file name • {:?}", local_filename);
|
||||
if let Err(e) = download_file(download_url, local_filename, os_type, auto_update_supported).await {
|
||||
eprintln!("[init_download] • An error occurred! Failed to download the file: {}", e);
|
||||
} else {
|
||||
println!("[init_download] • Code finishes without errors.");
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ pub mod pack;
|
||||
pub mod process;
|
||||
pub mod profile;
|
||||
pub mod settings;
|
||||
pub mod update; // [AR] Feature
|
||||
pub mod tags;
|
||||
pub mod download; // AstralRinth
|
||||
pub mod worlds;
|
||||
|
||||
pub mod data {
|
||||
|
||||
42
packages/app-lib/src/api/update.rs
Normal file
42
packages/app-lib/src/api/update.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use reqwest;
|
||||
use tokio::fs::File as AsyncFile;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
pub(crate) async fn download_file(download_url: &str, local_filename: &str, os_type: &str, auto_update_supported: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let download_dir = dirs::download_dir().ok_or("[AR] • Failed to determine download directory")?;
|
||||
let full_path = download_dir.join(local_filename);
|
||||
let response = reqwest::get(download_url).await?;
|
||||
let bytes = response.bytes().await?;
|
||||
let mut dest_file = AsyncFile::create(&full_path).await?;
|
||||
dest_file.write_all(&bytes).await?;
|
||||
println!("[AR] • File downloaded to: {:?}", full_path);
|
||||
if auto_update_supported {
|
||||
let status;
|
||||
if os_type.to_lowercase() == "Windows".to_lowercase() {
|
||||
status = Command::new("explorer")
|
||||
.arg(download_dir.display().to_string())
|
||||
.status()
|
||||
.await
|
||||
.expect("[AR] • Failed to open downloads folder");
|
||||
} else if os_type.to_lowercase() == "MacOS".to_lowercase() {
|
||||
status = Command::new("open")
|
||||
.arg(full_path.to_str().unwrap_or_default())
|
||||
.status()
|
||||
.await
|
||||
.expect("[AR] • Failed to execute command");
|
||||
} else {
|
||||
status = Command::new(".")
|
||||
.arg(full_path.to_str().unwrap_or_default())
|
||||
.status()
|
||||
.await
|
||||
.expect("[AR] • Failed to execute command");
|
||||
}
|
||||
if status.success() {
|
||||
println!("[AR] • File opened successfully!");
|
||||
} else {
|
||||
eprintln!("[AR] • Failed to open the file. Exit code: {:?}", status.code());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -8,7 +8,7 @@ and launching Modrinth mod packs
|
||||
#![deny(unused_must_use)]
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
pub mod util; // [AR] Refactor
|
||||
|
||||
mod api;
|
||||
mod config;
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
use crate::state::DirectoryInfo;
|
||||
use crate::ErrorKind;
|
||||
use sqlx::sqlite::{
|
||||
SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions,
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use std::env;
|
||||
use tokio::time::Instant;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use tokio::time::Instant;
|
||||
|
||||
pub(crate) async fn connect() -> crate::Result<Pool<Sqlite>> {
|
||||
let pool = connect_without_migrate().await?;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
async fn connect_without_migrate() -> crate::Result<Pool<Sqlite>> {
|
||||
let settings_dir = DirectoryInfo::get_initial_settings_dir().ok_or(
|
||||
crate::ErrorKind::FSError(
|
||||
"Could not find valid config dir".to_string(),
|
||||
),
|
||||
ErrorKind::FSError("Could not find valid config dir".to_string()),
|
||||
)?;
|
||||
|
||||
if !settings_dir.exists() {
|
||||
@@ -20,7 +33,6 @@ pub(crate) async fn connect() -> crate::Result<Pool<Sqlite>> {
|
||||
}
|
||||
|
||||
let db_path = settings_dir.join("app.db");
|
||||
let db_exists = db_path.exists();
|
||||
|
||||
let uri = format!("sqlite:{}", db_path.display());
|
||||
let conn_options = SqliteConnectOptions::from_str(&uri)?
|
||||
@@ -34,22 +46,6 @@ pub(crate) async fn connect() -> crate::Result<Pool<Sqlite>> {
|
||||
.connect_with(conn_options)
|
||||
.await?;
|
||||
|
||||
if db_exists {
|
||||
fix_modrinth_issued_migrations(&pool).await?;
|
||||
}
|
||||
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
|
||||
if !db_exists {
|
||||
fix_modrinth_issued_migrations(&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)
|
||||
}
|
||||
|
||||
@@ -75,72 +71,103 @@ async fn stale_data_cleanup(pool: &Pool<Sqlite>) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/*
|
||||
// Patch by AstralRinth - 08.07.2025
|
||||
Problem files:
|
||||
// Patched by AstralRinth
|
||||
Problem files, view detailed information in .gitattributes:
|
||||
/packages/app-lib/migrations/20240711194701_init.sql !eol
|
||||
CRLF -> 4c47e326f16f2b1efca548076ce638d4c90dd610172fe48c47d6de9bc46ef1c5abeadfdea05041ddd72c3819fa10c040
|
||||
LF -> e973512979feac07e415405291eefafc1ef0bd89454958ad66f5452c381db8679c20ffadab55194ecf6ba8ec4ca2db21
|
||||
/packages/app-lib/migrations/20240813205023_drop-active-unique.sql !eol
|
||||
CRLF -> 10f4a494df6fd791a093cc61401ecf3f9750fa6b97aa304ab06e29671e446586240910ffbf806f6ddc484a756770dde9
|
||||
LF -> 5b53534a7ffd74eebede234222be47e1d37bd0cc5fee4475212491b0c0379c16e3079e08eee0af959b1fa20835eeb206
|
||||
/packages/app-lib/migrations/20240930001852_disable-personalized-ads.sql !eol
|
||||
CRLF -> c8028ec3a2e61d15586e2f69ad6c6be5ac03b95918c2014cefb183ed6c254a52aad6f9ce98cda13ad545da3398574702
|
||||
LF -> c0de804f171b5530010edae087a6e75645c0e90177e28365f935c9fdd9a5c68e24850b8c1498e386a379d525d520bc57
|
||||
/packages/app-lib/migrations/20241222013857_feature-flags.sql !eol
|
||||
CRLF -> f8c55065e2563fa4738976eb13a052ae4c28da8d33143185550f6e1cee394a3243b1dca090b3e8bc50a93a8286a78c09
|
||||
LF -> c17542cb989a0466153e695bfa4717f8970feee185ca186a2caa1f2f6c5d4adb990ab97c26cacfbbe09c39ac81551704
|
||||
*/
|
||||
async fn fix_modrinth_issued_migrations(
|
||||
pool: &Pool<Sqlite>,
|
||||
) -> crate::Result<()> {
|
||||
let arch = env::consts::ARCH;
|
||||
let os = env::consts::OS;
|
||||
pub(crate) async fn fix_version_hash(
|
||||
eol: &str,
|
||||
) -> crate::Result<bool> {
|
||||
let started = Instant::now();
|
||||
|
||||
tracing::info!("Running on OS: {}, ARCH: {}", os, arch);
|
||||
// Create connection to the database without migrations
|
||||
let pool = connect_without_migrate().await?;
|
||||
tracing::info!("⚙️ Patching Modrinth corrupted migration checksums using EOL standard: {eol}");
|
||||
|
||||
if os == "windows" && arch == "x86_64" {
|
||||
tracing::warn!("🛑 Skipping migration checksum fix on Windows x86_64 (runtime-detected)");
|
||||
return Ok(());
|
||||
// validate EOL input
|
||||
if eol != "lf" && eol != "crlf" {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let started = Instant::now();
|
||||
tracing::info!("Fixing modrinth issued migrations");
|
||||
sqlx::query(
|
||||
// [eol][version] -> checksum
|
||||
let checksums: HashMap<(&str, &str), &str> = HashMap::from([
|
||||
(
|
||||
("lf", "20240711194701"),
|
||||
"e973512979feac07e415405291eefafc1ef0bd89454958ad66f5452c381db8679c20ffadab55194ecf6ba8ec4ca2db21",
|
||||
),
|
||||
(
|
||||
("crlf", "20240711194701"),
|
||||
"4c47e326f16f2b1efca548076ce638d4c90dd610172fe48c47d6de9bc46ef1c5abeadfdea05041ddd72c3819fa10c040",
|
||||
),
|
||||
(
|
||||
("lf", "20240813205023"),
|
||||
"5b53534a7ffd74eebede234222be47e1d37bd0cc5fee4475212491b0c0379c16e3079e08eee0af959b1fa20835eeb206",
|
||||
),
|
||||
(
|
||||
("crlf", "20240813205023"),
|
||||
"10f4a494df6fd791a093cc61401ecf3f9750fa6b97aa304ab06e29671e446586240910ffbf806f6ddc484a756770dde9",
|
||||
),
|
||||
(
|
||||
("lf", "20240930001852"),
|
||||
"c0de804f171b5530010edae087a6e75645c0e90177e28365f935c9fdd9a5c68e24850b8c1498e386a379d525d520bc57",
|
||||
),
|
||||
(
|
||||
("crlf", "20240930001852"),
|
||||
"c8028ec3a2e61d15586e2f69ad6c6be5ac03b95918c2014cefb183ed6c254a52aad6f9ce98cda13ad545da3398574702",
|
||||
),
|
||||
(
|
||||
("lf", "20241222013857"),
|
||||
"c17542cb989a0466153e695bfa4717f8970feee185ca186a2caa1f2f6c5d4adb990ab97c26cacfbbe09c39ac81551704",
|
||||
),
|
||||
(
|
||||
("crlf", "20241222013857"),
|
||||
"f8c55065e2563fa4738976eb13a052ae4c28da8d33143185550f6e1cee394a3243b1dca090b3e8bc50a93a8286a78c09",
|
||||
),
|
||||
]);
|
||||
|
||||
let mut changed = false;
|
||||
|
||||
for ((eol_key, version), checksum) in checksums.iter() {
|
||||
if *eol_key != eol {
|
||||
continue;
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"⏳ Patching checksum for migration {version} ({})",
|
||||
eol.to_uppercase()
|
||||
);
|
||||
|
||||
let result = sqlx::query(&format!(
|
||||
r#"
|
||||
UPDATE "_sqlx_migrations"
|
||||
SET checksum = X'e973512979feac07e415405291eefafc1ef0bd89454958ad66f5452c381db8679c20ffadab55194ecf6ba8ec4ca2db21'
|
||||
WHERE version = '20240711194701';
|
||||
"#,
|
||||
)
|
||||
.execute(pool)
|
||||
SET checksum = X'{checksum}'
|
||||
WHERE version = '{version}';
|
||||
"#
|
||||
))
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
tracing::info!("⚙️ Fixed checksum for first migration");
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE "_sqlx_migrations"
|
||||
SET checksum = X'5b53534a7ffd74eebede234222be47e1d37bd0cc5fee4475212491b0c0379c16e3079e08eee0af959b1fa20835eeb206'
|
||||
WHERE version = '20240813205023';
|
||||
"#,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
tracing::info!("⚙️ Fixed checksum for second migration");
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE "_sqlx_migrations"
|
||||
SET checksum = X'c0de804f171b5530010edae087a6e75645c0e90177e28365f935c9fdd9a5c68e24850b8c1498e386a379d525d520bc57'
|
||||
WHERE version = '20240930001852';
|
||||
"#,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
tracing::info!("⚙️ Fixed checksum for third migration");
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE "_sqlx_migrations"
|
||||
SET checksum = X'c17542cb989a0466153e695bfa4717f8970feee185ca186a2caa1f2f6c5d4adb990ab97c26cacfbbe09c39ac81551704'
|
||||
WHERE version = '20241222013857';
|
||||
"#,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
tracing::info!("⚙️ Fixed checksum for fourth migration");
|
||||
let elapsed = started.elapsed();
|
||||
|
||||
if result.rows_affected() > 0 {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"✅ Fixed all known Modrinth checksums for migrations in {:.2?}",
|
||||
elapsed
|
||||
"✅ Checksum patching completed in {:.2?} (changes: {})",
|
||||
started.elapsed(),
|
||||
changed
|
||||
);
|
||||
Ok(())
|
||||
|
||||
Ok(changed)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
///
|
||||
/// [AR] Feature
|
||||
///
|
||||
use crate::Result;
|
||||
use crate::api::update;
|
||||
use crate::state::db;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::process;
|
||||
use tokio::io;
|
||||
|
||||
/*
|
||||
AstralRinth Utils
|
||||
*/
|
||||
const PACKAGE_JSON_CONTENT: &str =
|
||||
// include_str!("../../../../apps/app-frontend/package.json");
|
||||
include_str!("../../../../apps/app/tauri.conf.json");
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Launcher {
|
||||
pub version: String
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
pub fn read_package_json() -> io::Result<Launcher> {
|
||||
@@ -19,3 +23,41 @@ pub fn read_package_json() -> io::Result<Launcher> {
|
||||
|
||||
Ok(launcher)
|
||||
}
|
||||
|
||||
pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
|
||||
tracing::info!("[AR] • Attempting to apply migration fix");
|
||||
let patched = db::fix_version_hash(eol).await?;
|
||||
if patched {
|
||||
tracing::info!("[AR] • Successfully applied migration fix");
|
||||
} else {
|
||||
tracing::error!("[AR] • Failed to apply migration fix");
|
||||
}
|
||||
Ok(patched)
|
||||
}
|
||||
|
||||
pub async fn init_download(
|
||||
download_url: &str,
|
||||
local_filename: &str,
|
||||
os_type: &str,
|
||||
auto_update_supported: bool,
|
||||
) -> Result<()> {
|
||||
println!("[AR] • Initialize downloading from • {:?}", download_url);
|
||||
println!("[AR] • Save local file name • {:?}", local_filename);
|
||||
if let Err(e) = update::download_file(
|
||||
download_url,
|
||||
local_filename,
|
||||
os_type,
|
||||
auto_update_supported,
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"[AR] • An error occurred! Failed to download the file: {}",
|
||||
e
|
||||
);
|
||||
} else {
|
||||
println!("[AR] • Code finishes without errors.");
|
||||
process::exit(0)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user