Integrate Vantage live map for guild claims [skip ci]

This commit is contained in:
SCOPEDD committed 2026-09-30 11:02:51 -04:00
1 parent 52de8c3031
commit 602e7fd0f0
17 files changed
+470 -21

No files matched your search

+52 -1
View File
@@ -538,7 +538,58 @@ pub async fn get_public_servers(state: State<'_, AppState>) -> Res<serde_json::V
if !resp.status().is_success() {
return Err("unable to load servers".into());
}
resp.json().await.map_err(err)
let data: serde_json::Value = resp.json().await.map_err(err)?;
let urls = data["servers"].as_array().into_iter().flatten().filter_map(|s| {
Some((s["id"].as_i64()?, s["map_url"].as_str()?.to_owned()))
}).collect();
*state.map_urls.lock().unwrap() = urls;
Ok(data)
}
#[derive(Serialize)]
pub struct MapResource {
status: u16,
data: Option<String>,
etag: Option<String>,
}
/// Native transport for Vantage assets. Browser WebViews cannot fetch a
/// cross-origin Vantage live server, so the launcher uses its HTTP client.
/// Only paths under a map URL supplied by the panel can be fetched.
#[tauri::command]
pub async fn fetch_vantage_resource(
state: State<'_, AppState>, server_id: i64, url: String, etag: Option<String>,
) -> Res<MapResource> {
use base64::Engine;
let configured = state.map_urls.lock().unwrap().get(&server_id).cloned().ok_or("map not configured for this server")?;
let base = reqwest::Url::parse(&configured).map_err(err)?;
let target = reqwest::Url::parse(&url).map_err(err)?;
let root = base.join(".").map_err(err)?;
if !matches!(target.scheme(), "http" | "https") || target.origin() != base.origin()
|| !target.path().starts_with(root.path()) || target.username() != ""
|| target.password().is_some() || target.fragment().is_some() {
return Err("map asset is outside the configured Vantage world".into());
}
if target.as_str().len() > 2048 { return Err("map asset URL is too long".into()); }
let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.timeout(std::time::Duration::from_secs(30))
.build().map_err(err)?;
let mut request = client.get(target);
if let Some(etag) = etag.filter(|e| e.len() < 256 && !e.contains('\n') && !e.contains('\r')) {
request = request.header(reqwest::header::IF_NONE_MATCH, etag);
}
let response = request.send().await.map_err(err)?;
let status = response.status().as_u16();
let etag = response.headers().get(reqwest::header::ETAG).and_then(|v| v.to_str().ok()).map(str::to_owned);
if status == 304 { return Ok(MapResource { status, data: None, etag }); }
if !response.status().is_success() { return Ok(MapResource { status, data: None, etag }); }
if response.content_length().is_some_and(|n| n > 64 * 1024 * 1024) {
return Err("Vantage asset exceeds 64 MiB".into());
}
let bytes = response.bytes().await.map_err(err)?;
if bytes.len() > 64 * 1024 * 1024 { return Err("Vantage asset exceeds 64 MiB".into()); }
Ok(MapResource { status, data: Some(base64::engine::general_purpose::STANDARD.encode(bytes)), etag })
}
// ---------------------------------------------------------------------------