mirror of
https://github.com/JonasunderscoreJones/jonas_jones-api.git
synced 2025-10-23 00:59:18 +02:00
Added mcmod update branch
This commit is contained in:
parent
e8d1b934d7
commit
5c6a2ac53f
4 changed files with 219 additions and 18 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
@ -17,6 +17,15 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
|
@ -527,6 +536,7 @@ dependencies = [
|
|||
"lastfm",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -886,6 +896,35 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.22"
|
||||
|
|
|
@ -17,3 +17,4 @@ chrono = "0.4.31"
|
|||
toml = "0.8.8"
|
||||
reqwest = { version = "0.11.22", features = ["json"] }
|
||||
serde_json = "1.0.108"
|
||||
regex = "1"
|
||||
|
|
|
@ -1,29 +1,144 @@
|
|||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use reqwest::Error;
|
||||
use serde_json::json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use warp::Filter;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::error_responses::BadRequestError;
|
||||
|
||||
pub fn get_mods_paths() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
// any path that starts with /v1/updates/minecraft/mods/{modname}/{loadername}/{version} calls handle_path
|
||||
warp::path("v1").and(warp::path("updates")).and(warp::path("minecraft")).and(warp::path("mods"))
|
||||
|
||||
.and(warp::path::param())
|
||||
.and(warp::path::param())
|
||||
.and(warp::path::param())
|
||||
.and(warp::path::end())
|
||||
.and(warp::addr::remote())
|
||||
.map(handle_path)
|
||||
.and(warp::get().and(warp::path::param()).and(warp::path::param()).and(warp::path::param()).and(warp::path::param()).and(warp::path::end()).and(warp::filters::header::headers_cloned()).and(warp::query::<HashMap<String, String>>()).and_then(handle_path))
|
||||
}
|
||||
|
||||
fn handle_path(modname: String, loadername: String, version: String, remote_ip: Option<std::net::SocketAddr>) -> String {
|
||||
format!("modname: {}, loadername: {}, version: {}, IP: {}", modname, loadername, version, remote_ip.unwrap_or(std::net::SocketAddr::from(([0, 0, 0, 0], 0))).ip())
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct ModData {
|
||||
package: String,
|
||||
name: String,
|
||||
versions: Vec<HashMap<String, HashMap<String, ModVersion>>>,
|
||||
}
|
||||
|
||||
// fn handle_with_headers(
|
||||
// headers: warp::http::HeaderMap,
|
||||
// ) -> String {
|
||||
// // Iterate through the headers and print them
|
||||
// for (name, value) in headers.iter() {
|
||||
// println!("Header: {}: {}", name, value.to_str().unwrap_or("Invalid UTF-8"));
|
||||
// }
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
struct ModVersion {
|
||||
recommended: String,
|
||||
latest: String,
|
||||
all: Vec<String>,
|
||||
}
|
||||
|
||||
// // Respond with a message or perform other actions as needed
|
||||
// "Headers received".to_string()
|
||||
// }
|
||||
// get json data from https://https://cdn.jonasjones.dev/api/mcmods/mcmod_metadata.json
|
||||
pub async fn fetch_data() -> Result<serde_json::Value, Error> {
|
||||
let url = "https://cdn.jonasjones.dev/api/mcmods/mcmod_metadata.json";
|
||||
let response = reqwest::get(url).await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
// Parse the JSON response
|
||||
let json_data: serde_json::Value = response.json().await?;
|
||||
return Ok(json_data);
|
||||
} else {
|
||||
// Handle non-successful status codes
|
||||
Err(response.error_for_status().unwrap_err())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_ip(ip_str: &str) -> bool {
|
||||
if let Ok(ip) = ip_str.parse::<IpAddr>() {
|
||||
match ip {
|
||||
IpAddr::V4(_) => true,
|
||||
IpAddr::V6(_) => true,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_minecraft_version(version: &str) -> bool {
|
||||
// Define the regex pattern for the Minecraft version
|
||||
let pattern = Regex::new(r"^1\.\d{1,2}(\.\d)?$").unwrap();
|
||||
|
||||
// Check if the provided version matches the pattern
|
||||
pattern.is_match(version)
|
||||
}
|
||||
|
||||
fn get_header_forward_for_ip(headers: warp::http::HeaderMap) -> String {
|
||||
// check if the header X-Forward-For exists and return the ip, if not, return an empty string
|
||||
if let Some(forwarded_for) = headers.get("X-Forwarded-For") {
|
||||
if let Ok(ip) = forwarded_for.to_str() {
|
||||
// Extract the first IP address from the comma-separated list
|
||||
if let Some(first_ip) = ip.split(',').next() {
|
||||
return first_ip.trim().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
|
||||
async fn handle_path(modpackage: String, loadername: String, mcversion: String, modversion: String, headers: warp::http::HeaderMap, params: HashMap<String, String>) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
// Retrieve the IP from the header and check if it's valid
|
||||
let mut client_ip = get_header_forward_for_ip(headers);
|
||||
if !is_valid_ip(&client_ip) {
|
||||
client_ip = params.get("ip").unwrap_or(&"".to_string()).to_string();
|
||||
if !is_valid_ip(&client_ip) {
|
||||
client_ip = "Not valid".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// check if the minecraft version is valid
|
||||
if !is_valid_minecraft_version(&mcversion) {
|
||||
return Err(warp::reject::custom(BadRequestError));
|
||||
}
|
||||
|
||||
// fetch the data
|
||||
let data = fetch_data().await.unwrap();
|
||||
|
||||
// filter the data
|
||||
// convert the raw list of data into a list of ModData and ModVersion
|
||||
let mods_data: Vec<ModData> = serde_json::from_value(data).unwrap();
|
||||
|
||||
// get the mod data from the requested mod
|
||||
let mod_data: ModData = mods_data.into_iter().find(|mod_data| mod_data.package == modpackage).unwrap();
|
||||
|
||||
|
||||
// get the version data from the requested loader and remove the other loaders
|
||||
let version_data: HashMap<std::string::String, ModVersion> = mod_data.versions.into_iter().find(|version_data| version_data.contains_key(&loadername)).unwrap().remove(&loadername).unwrap();
|
||||
|
||||
// turn version_data into an object of String: ModVersion key value pairs
|
||||
let version_data: HashMap<std::string::String, ModVersion> = version_data.into_iter().map(|(key, value)| (key, value)).collect();
|
||||
|
||||
// get the version data for the current minecraft version
|
||||
let version_data: ModVersion = version_data.get(&mcversion).unwrap().clone();
|
||||
|
||||
// get recommended and latest version
|
||||
let recommended_version = version_data.recommended.clone();
|
||||
let latest_version = version_data.latest.clone();
|
||||
|
||||
// determine whether the client is up to date
|
||||
let mut up_to_date = false;
|
||||
if modversion == recommended_version {
|
||||
up_to_date = true;
|
||||
}
|
||||
|
||||
// determine if telemetry is enabled by checking if the client_ip is valid
|
||||
let mut telemetry = false;
|
||||
if is_valid_ip(&client_ip) {
|
||||
telemetry = true;
|
||||
}
|
||||
|
||||
// create the response
|
||||
let response = json!({
|
||||
"promos": {
|
||||
"latest": latest_version,
|
||||
"recommended": recommended_version
|
||||
},
|
||||
"upToDate": up_to_date,
|
||||
"telemetry_enabled": telemetry
|
||||
});
|
||||
|
||||
//TODO: Add way to process telemetry data
|
||||
|
||||
// return the data
|
||||
return Ok(warp::reply::json(&response));
|
||||
}
|
||||
|
|
46
src/v1/updates/minecraft/mods/telemetry/mod.rs
Normal file
46
src/v1/updates/minecraft/mods/telemetry/mod.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use std::fs::OpenOptions;
|
||||
use std::io::{self, Write};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
struct IpInfo {
|
||||
region: String,
|
||||
// Add other fields as needed
|
||||
}
|
||||
|
||||
fn get_ip_hash(ip: &str) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
ip.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn log_ip_info(ip_address: &str, file_path: &str, mod_package: &str) -> io::Result<()> {
|
||||
let ip_hash = get_ip_hash(ip_address);
|
||||
|
||||
let ip_info = match get_ip_info(ip_address) {
|
||||
Ok(info) => info,
|
||||
Err(err) => {
|
||||
IpInfo { region: "Unknown".to_string() } // Default to "Unknown" in case of an error
|
||||
}
|
||||
};
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(file_path)?;
|
||||
|
||||
writeln!(file, "{} {} {}", ip_hash, ip_info.region, mod_package)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_path = "ip_log.txt"; // Replace with your desired file path
|
||||
|
||||
// Example usage
|
||||
match log_ip_info("8.8.8.8", file_path) {
|
||||
Err(err) => eprintln!("Error: {}", err),
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue