Files
AstralRinth/packages/app-lib/src/state/mr_auth.rs
Josiah Glosson de3019e92b Theseus build updates (#3588)
* Add launcher_feature_version to Profile

* Misc fixes

- Add typing to theme and settings stuff
- Push instance route on creation from installing a modpack
- Fixed servers not reloading properly when first added

* Make old instances scan the logs folder for joined servers on launcher startup

* Create AttachedWorldData

* Change AttachedWorldData interface

* Rename WorldType::World to WorldType::Singleplayer

* Implement world display status system

* Fix Minecraft font

* Fix set_world_display_status Tauri error

* Add 'Play instance' option

* Add option to disable worlds showing in Home

* Fixes

- Fix available server filter only showing if there are some available
- Fixed server and singleplayer filters sometimes showing when there are only servers or singleplayer worlds
- Fixed new worlds not being automatically added when detected
- Rephrased Jump back into worlds option description

* Fixed sometimes more than 6 items showing up in Jump back in

* Fix servers.dat issue with instances you haven't played before

* Update a bunch of app dependencies in non-breaking ways

* Update dependencies in app-lib that had breaking updates

* Update dependencies in app that had breaking updates

* Fix too large of bulk requests being made, limit max to 800 #3430

* Also update tauri-plugin-opener

* Update app-lib to Rust 2024

* Non-breaking updates in ariadne

* Breaking updates in ariadne

* Ariadne Rust 2024

* Add hiding from home page, add types to Mods.vue

* Make recent worlds go into grid when display is huge

* Fix lint

* Remove redundant media query

* Fix protocol version on home page, and home page being blocked by pinging servers

* Clippy fix in app-lib

* Clippy fix in app

* Clippy fix

* More Clippy fixes

* Fix Prettier lints

* Undo `from_string` changes

* Update macos dependencies

* Apply updates to app-playground as well

* Update Wry + Tauri

* Update sysinfo

* Update theseus_gui to Rust 2024

* Downgrade rand in ariadne to fix labrinth

Labrinth can't use rand 0.9 due to argon2

* Cargo format

---------

Signed-off-by: Josiah Glosson <soujournme@gmail.com>
Co-authored-by: Prospector <prospectordev@gmail.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
Co-authored-by: Alejandro González <me@alegon.dev>
2025-05-02 09:51:17 +00:00

232 lines
6.1 KiB
Rust

use crate::config::{MODRINTH_API_URL, MODRINTH_URL};
use crate::state::{CacheBehaviour, CachedEntry};
use crate::util::fetch::{FetchSemaphore, fetch_advanced};
use chrono::{DateTime, Duration, TimeZone, Utc};
use dashmap::DashMap;
use futures::TryStreamExt;
use reqwest::Method;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ModrinthCredentials {
pub session: String,
pub expires: DateTime<Utc>,
pub user_id: String,
pub active: bool,
}
impl ModrinthCredentials {
pub async fn get_and_refresh(
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite> + Copy,
semaphore: &FetchSemaphore,
) -> crate::Result<Option<Self>> {
let creds = Self::get_active(exec).await?;
if let Some(mut creds) = creds {
if creds.expires < Utc::now() {
#[derive(Deserialize)]
struct Session {
session: String,
}
let resp = fetch_advanced(
Method::POST,
&format!("{MODRINTH_API_URL}session/refresh"),
None,
None,
Some(("Authorization", &*creds.session)),
None,
semaphore,
exec,
)
.await
.ok()
.and_then(|resp| serde_json::from_slice::<Session>(&resp).ok());
if let Some(value) = resp {
creds.session = value.session;
creds.expires = Utc::now() + Duration::weeks(2);
creds.upsert(exec).await?;
Ok(Some(creds))
} else {
Self::remove(&creds.user_id, exec).await?;
Ok(None)
}
} else {
Ok(Some(creds))
}
} else {
Ok(None)
}
}
pub async fn get_active(
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<Option<Self>> {
let res = sqlx::query!(
"
SELECT
id, active, session_id, expires
FROM modrinth_users
WHERE active = TRUE
"
)
.fetch_optional(exec)
.await?;
Ok(res.map(|x| Self {
session: x.session_id,
expires: Utc
.timestamp_opt(x.expires, 0)
.single()
.unwrap_or_else(Utc::now),
user_id: x.id,
active: x.active == 1,
}))
}
pub async fn get_all(
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<DashMap<String, Self>> {
let res = sqlx::query!(
"
SELECT
id, active, session_id, expires
FROM modrinth_users
"
)
.fetch(exec)
.try_fold(DashMap::new(), |acc, x| {
acc.insert(
x.id.clone(),
Self {
session: x.session_id,
expires: Utc
.timestamp_opt(x.expires, 0)
.single()
.unwrap_or_else(Utc::now),
user_id: x.id,
active: x.active == 1,
},
);
async move { Ok(acc) }
})
.await?;
Ok(res)
}
pub async fn upsert(
&self,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite> + Copy,
) -> crate::Result<()> {
let expires = self.expires.timestamp();
if self.active {
sqlx::query!(
"
UPDATE modrinth_users
SET active = FALSE
"
)
.execute(exec)
.await?;
}
sqlx::query!(
"
INSERT INTO modrinth_users (id, active, session_id, expires)
VALUES ($1, $2, $3, $4)
ON CONFLICT (id) DO UPDATE SET
active = $2,
session_id = $3,
expires = $4
",
self.user_id,
self.active,
self.session,
expires,
)
.execute(exec)
.await?;
Ok(())
}
pub async fn remove(
user_id: &str,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<()> {
sqlx::query!(
"
DELETE FROM modrinth_users WHERE id = $1
",
user_id,
)
.execute(exec)
.await?;
Ok(())
}
pub(crate) async fn refresh_all() -> crate::Result<()> {
let state = crate::State::get().await?;
let all = Self::get_all(&state.pool).await?;
let user_ids = all.into_iter().map(|x| x.0).collect::<Vec<_>>();
CachedEntry::get_user_many(
&user_ids.iter().map(|x| &**x).collect::<Vec<_>>(),
Some(CacheBehaviour::Bypass),
&state.pool,
&state.fetch_semaphore,
)
.await?;
Ok(())
}
}
pub fn get_login_url() -> String {
format!("{MODRINTH_URL}auth/sign-in?launcher=true")
}
pub async fn finish_login_flow(
code: &str,
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<ModrinthCredentials> {
let info = fetch_info(code, semaphore, exec).await?;
Ok(ModrinthCredentials {
session: code.to_string(),
expires: Utc::now() + Duration::weeks(2),
user_id: info.id,
active: true,
})
}
async fn fetch_info(
token: &str,
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
) -> crate::Result<crate::state::cache::User> {
let result = fetch_advanced(
Method::GET,
&format!("{MODRINTH_API_URL}user"),
None,
None,
Some(("Authorization", token)),
None,
semaphore,
exec,
)
.await?;
let value = serde_json::from_slice(&result)?;
Ok(value)
}