mirror of
https://github.com/JonasunderscoreJones/jonas_jones-api.git
synced 2025-10-23 00:59:18 +02:00
Compare commits
31 commits
Author | SHA1 | Date | |
---|---|---|---|
c25f2032d7 | |||
9ef64b5b82 | |||
c20e276de2 | |||
2c8a9db949 | |||
350c9f722a | |||
cdcf27b0fa | |||
41fa898a67 | |||
d2a89d5067 | |||
3b5378c6f2 | |||
1cdb34503d | |||
4ecbd7aa98 | |||
a63f933dd9 | |||
48da0d7f95 | |||
5e9ed3738c | |||
7bee1f5bae | |||
bdfd5a74a6 | |||
4c62ba2845 | |||
|
852e184b89 | ||
|
a851b18875 | ||
|
c18ab8a8da | ||
|
462ee68763 | ||
|
f201bc7fda | ||
5f2026af0d | |||
5c6a2ac53f | |||
e8d1b934d7 | |||
8e61e81f06 | |||
5eb6080694 | |||
83e1b85811 | |||
bc09ff5f3c | |||
2188f2acd5 | |||
169be592ab |
20 changed files with 1415 additions and 100 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@
|
||||||
/.vscode
|
/.vscode
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
/resources
|
||||||
|
|
718
Cargo.lock
generated
718
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
10
Cargo.toml
10
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "jonas_jones-api"
|
name = "jonas_jones-api"
|
||||||
version = "0.1.0"
|
version = "0.4.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -11,9 +11,13 @@ parking_lot = "0.12.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tokio = { version = "1.35", features = ["macros"] }
|
tokio = { version = "1.35", features = ["macros"] }
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
lastfm = "0.6.1"
|
lastfm = "0.10.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
reqwest = { version = "0.11.22", features = ["json"] }
|
reqwest = { version = "0.12.5", features = ["json", "blocking"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
|
regex = "1"
|
||||||
|
git2 = "0.19.0"
|
||||||
|
ip2location = "0.5.0"
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
|
47
README.md
47
README.md
|
@ -9,16 +9,16 @@ As of now, the project has no proper production build and unless the proper envi
|
||||||
|
|
||||||
Clone the repository and install the dependencies.
|
Clone the repository and install the dependencies.
|
||||||
```bash
|
```bash
|
||||||
$ git clone git@github.com:J-onasJones/jonas_jones-api.git
|
git clone git@github.com:J-onasJones/jonas_jones-api.git
|
||||||
$ cd jonas_jones-api
|
cd jonas_jones-api
|
||||||
$ cargo build
|
cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To run the API, simply run the following command.
|
To run the API, simply run the following command.
|
||||||
```bash
|
```bash
|
||||||
$ cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to run the API in a production environment, you will need to set the following environment variables.
|
If you want to run the API in a production environment, you will need to set the following environment variables.
|
||||||
|
@ -29,8 +29,39 @@ If you want to run the API in a production environment, you will need to set the
|
||||||
- LASTFM_API_SECRET
|
- LASTFM_API_SECRET
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ export API_PORT={port}
|
export API_PORT={port}
|
||||||
$ export API_IP={ip_address}
|
export API_IP={ip_address}
|
||||||
$ export LASTFM_API_KEY={lastfm_api_key}
|
export LASTFM_API_KEY={lastfm_api_key}
|
||||||
$ export LASTFM_API_SECRET={lastfm_api_secret}
|
export LASTFM_API_SECRET={lastfm_api_secret}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
`docker-compose.yaml` (folder paths need adjusting):
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
arch-linux:
|
||||||
|
image: archlinux:latest
|
||||||
|
container_name: jonas_jones-api
|
||||||
|
ports:
|
||||||
|
- "3030:3030"
|
||||||
|
volumes:
|
||||||
|
- /home/jonas_jones/jonas_jones-api:/home/jonas_jones/jonas_jones-api
|
||||||
|
- /home/jonas_jones/.config/rclone/:/root/.config/rclone/
|
||||||
|
command: ["sh", "-c", "pacman -Syu --noconfirm --needed pkg-config openssl python3 python-pip rclone cargo && python3 -m venv api-venv && source api-venv/bin/activate && cd /home/jonas_jones/jonas_jones-api && pip install -r requirements.txt && /usr/bin/cargo run"]
|
||||||
|
```
|
||||||
|
|
||||||
|
run container:
|
||||||
|
```sh
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- analytics backend. track request origin through IP from header (store IP hash, region and time)
|
||||||
|
- rewrite all scripts in rust
|
||||||
|
- DB implementation for projects, kcomebacks, minecraft mod versions
|
||||||
|
- session backend, auth token system
|
||||||
|
- implementation for dashboard front-end with analytics/config
|
||||||
|
- complete minecraft mod implementation
|
11
docker-compose.yaml
Normal file
11
docker-compose.yaml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
arch-linux:
|
||||||
|
image: archlinux:latest
|
||||||
|
container_name: jonas_jones-api
|
||||||
|
ports:
|
||||||
|
- "3030:3030"
|
||||||
|
volumes:
|
||||||
|
- /home/jonas_jones/jonas_jones-api:/home/jonas_jones/jonas_jones-api
|
||||||
|
- /home/jonas_jones/.config/rclone/:/root/.config/rclone/
|
||||||
|
command: ["sh", "-c", "pacman -Syu --noconfirm --needed pkg-config openssl python3 python-pip rclone cargo && python3 -m venv api-venv && source api-venv/bin/activate && cd /home/jonas_jones/jonas_jones-api && pip install -r requirements.txt && /usr/bin/cargo run"]
|
9
requirements.txt
Normal file
9
requirements.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
requests
|
||||||
|
datetime
|
||||||
|
python-dotenv
|
||||||
|
spotipy
|
||||||
|
praw
|
||||||
|
spotipy
|
||||||
|
pylast
|
||||||
|
typing
|
||||||
|
markdown
|
14
src/iplookup.rs
Normal file
14
src/iplookup.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use ip2location::{DB, Record};
|
||||||
|
|
||||||
|
pub fn ip_lookup(ip: &str) -> String {
|
||||||
|
let database_path = "resources/IP2LOCATION-LITE-DB5.IPV6.BIN/IP2LOCATION-LITE-DB5.IPV6.BIN";
|
||||||
|
|
||||||
|
let mut db = DB::from_file(database_path).unwrap();
|
||||||
|
|
||||||
|
let geo_info = db.ip_lookup(ip.parse().unwrap()).unwrap();
|
||||||
|
|
||||||
|
let record = if let Record::LocationDb(rec) = geo_info {
|
||||||
|
Some(rec)
|
||||||
|
} else { None };
|
||||||
|
return record.unwrap().country.unwrap().short_name.to_string();
|
||||||
|
}
|
35
src/main.rs
35
src/main.rs
|
@ -1,19 +1,50 @@
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
|
use tokio::time::{sleep, Duration};
|
||||||
|
use v1::{run_sync_all_command};
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod error_responses;
|
pub mod error_responses;
|
||||||
|
pub mod iplookup;
|
||||||
|
pub mod request_logger;
|
||||||
|
|
||||||
pub use logger::Logger;
|
pub use logger::Logger;
|
||||||
pub use tools::parse_ip;
|
pub use tools::parse_ip;
|
||||||
|
|
||||||
|
|
||||||
|
async fn periodic_script_runner() {
|
||||||
|
loop {
|
||||||
|
Logger::info("Running periodic scripts...");
|
||||||
|
// Run all Functions
|
||||||
|
let _ = run_sync_all_command();
|
||||||
|
|
||||||
|
// Sleep for 6 hours
|
||||||
|
sleep(Duration::from_secs(6 * 60 * 60)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// load .env file
|
// Load .env file
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
server::serve().await;
|
// Start the api
|
||||||
|
let server_task = tokio::spawn(async {
|
||||||
|
server::serve().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// periodic script runner
|
||||||
|
let second_task = tokio::spawn(async {
|
||||||
|
// check if the local repository exists, if not, clone it
|
||||||
|
if !fs::metadata("./resources/turbo_octo_potato").is_ok() {
|
||||||
|
v1::run_setup().unwrap();
|
||||||
|
};
|
||||||
|
periodic_script_runner().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for both tasks to complete
|
||||||
|
let _ = tokio::try_join!(server_task, second_task);
|
||||||
}
|
}
|
||||||
|
|
71
src/request_logger.rs
Normal file
71
src/request_logger.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use chrono::Utc;
|
||||||
|
use reqwest::Client;
|
||||||
|
use reqwest::blocking::get;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::{BufReader, BufWriter};
|
||||||
|
use warp::http::Method;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct RequestLog {
|
||||||
|
timestamp: String,
|
||||||
|
method: String,
|
||||||
|
pathname: String,
|
||||||
|
ip_country_code: String,
|
||||||
|
ip_hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_ip_country_code(ip: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let url = format!("http://ip-api.com/json/{}", ip);
|
||||||
|
let response: Value = get(&url)?.json()?;
|
||||||
|
if let Some(country_code) = response["countryCode"].as_str() {
|
||||||
|
Ok(country_code.to_string())
|
||||||
|
} else {
|
||||||
|
Err("Could not fetch country code".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hash_ip(ip: &str) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(ip);
|
||||||
|
format!("{:x}", hasher.finalize())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn log_request(client: &Client, ip: &str, pathname: &str, method: &Method, file_path: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let method_str = method.as_str();
|
||||||
|
let timestamp = Utc::now().to_rfc3339();
|
||||||
|
let ip_country_code = get_ip_country_code(ip).await?;
|
||||||
|
let ip_hash = hash_ip(ip).await;
|
||||||
|
|
||||||
|
let log_entry = RequestLog {
|
||||||
|
timestamp,
|
||||||
|
method: method_str.to_string(),
|
||||||
|
pathname: pathname.to_string(),
|
||||||
|
ip_country_code,
|
||||||
|
ip_hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = Path::new(file_path);
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path)?;
|
||||||
|
|
||||||
|
let mut logs: Vec<RequestLog> = if path.exists() {
|
||||||
|
let reader = BufReader::new(&file);
|
||||||
|
serde_json::from_reader(reader)?
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
logs.push(log_entry);
|
||||||
|
|
||||||
|
let writer = BufWriter::new(&file);
|
||||||
|
serde_json::to_writer_pretty(writer, &logs)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,13 +1,19 @@
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use reqwest::StatusCode;
|
use lastfm::reqwest::StatusCode;
|
||||||
|
use warp::filters::path::FullPath;
|
||||||
|
use warp::http::Method;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
use warp::reply::Reply;
|
use warp::reply::Reply;
|
||||||
|
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
use crate::error_responses::{ErrorMessage, InternalServerError, BadRequestError, NotFoundError, NotImplementedError};
|
use crate::error_responses::{ErrorMessage, InternalServerError, BadRequestError, NotFoundError, NotImplementedError};
|
||||||
use crate::v1::get_v1_routes;
|
use crate::v1::get_v1_routes;
|
||||||
use crate::{Logger, parse_ip};
|
use crate::{parse_ip, request_logger, Logger};
|
||||||
|
use crate::iplookup::ip_lookup;
|
||||||
|
|
||||||
|
|
||||||
pub async fn serve() {
|
pub async fn serve() {
|
||||||
|
@ -20,9 +26,38 @@ pub async fn serve() {
|
||||||
|
|
||||||
let favicon = warp::path("favicon.ico").and(warp::fs::file("./src/favicon.png"));
|
let favicon = warp::path("favicon.ico").and(warp::fs::file("./src/favicon.png"));
|
||||||
|
|
||||||
|
// /status => 200 OK
|
||||||
|
let status = warp::path("status")
|
||||||
|
.map(|| warp::reply());
|
||||||
|
|
||||||
|
// Middleware filter to log request details
|
||||||
|
let log_request = warp::any()
|
||||||
|
.and(warp::method())
|
||||||
|
.and(warp::path::full())
|
||||||
|
.and(warp::addr::remote())
|
||||||
|
.and(warp::header::optional::<String>("x-forwarded-for"))
|
||||||
|
.map(|method: Method, path: FullPath, addr: Option<SocketAddr>, fwd_for: Option<String>| {
|
||||||
|
let client_ip = fwd_for.unwrap_or_else(|| addr.map(|a| a.ip().to_string()).unwrap_or_else(|| String::from("unknown")));
|
||||||
|
let path_str = path.as_str().to_string(); // Convert to owned String
|
||||||
|
let method_clone = method.clone();
|
||||||
|
let method_str = method_clone.clone().as_str().to_string();
|
||||||
|
let client_ip_clone = client_ip.clone(); // Clone for use outside the async block
|
||||||
|
let path_str_clone = path_str.clone();
|
||||||
|
|
||||||
|
/*tokio::spawn(async move {
|
||||||
|
let client = Client::new();
|
||||||
|
if let Err(e) = request_logger::log_request(&client, &client_ip, &path_str, &method_clone, "requests.json").await {
|
||||||
|
eprintln!("Failed to log request: {:?}", e);
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
|
||||||
|
Logger::info(&format!("{} {} from {} ({})", method_str, path_str_clone, ip_lookup(&client_ip_clone), client_ip_clone));
|
||||||
|
});
|
||||||
|
|
||||||
// GET (any) => reply with return from handle_path
|
// GET (any) => reply with return from handle_path
|
||||||
let routes = favicon.or(get_v1_routes())
|
let routes = log_request
|
||||||
.recover(handle_rejection);
|
.clone().untuple_one().and(favicon.or(status.or(get_v1_routes())
|
||||||
|
.recover(handle_rejection)));
|
||||||
|
|
||||||
|
|
||||||
async fn handle_rejection(err: warp::Rejection) -> Result<impl Reply, Infallible> {
|
async fn handle_rejection(err: warp::Rejection) -> Result<impl Reply, Infallible> {
|
||||||
|
@ -43,7 +78,7 @@ pub async fn serve() {
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(warp::reply::with_status(json, code))
|
Ok(warp::reply::with_status(json, StatusCode::from_u16(code.as_u16()).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
8
src/v1/analytics/mod.rs
Normal file
8
src/v1/analytics/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use warp::Filter;
|
||||||
|
|
||||||
|
pub fn get_analytics_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
|
warp::path("v1").and(warp::path("analytics"))
|
||||||
|
.and((warp::path("logcdnrequest").map(|| "Please refer to the wiki at https://wiki.jonasjones.dev/Api/"))
|
||||||
|
.or(warp::path("ping").map(|| "pong"))
|
||||||
|
.or(warp::path("version").map(|| warp::reply::json(&[option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")]))))
|
||||||
|
}
|
7
src/v1/auth/mod.rs
Normal file
7
src/v1/auth/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use warp::Filter;
|
||||||
|
|
||||||
|
pub fn get_auth_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
|
warp::path("v1").and(warp::path("auth"))
|
||||||
|
.and((warp::path("requestsession").and(warp))
|
||||||
|
.or(warp::path("login")))
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
@ -10,7 +8,7 @@ mod upcoming;
|
||||||
|
|
||||||
use filter::get_kcomebacks_filter_routes;
|
use filter::get_kcomebacks_filter_routes;
|
||||||
use upcoming::get_kcomebacks_upcoming_routes;
|
use upcoming::get_kcomebacks_upcoming_routes;
|
||||||
use crate::error_responses::{InternalServerError, BadRequestError, NotFoundError};
|
use crate::error_responses::InternalServerError;
|
||||||
|
|
||||||
pub fn get_kcomebacks_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
pub fn get_kcomebacks_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
warp::path("v1").and(warp::path("kcomebacks"))
|
warp::path("v1").and(warp::path("kcomebacks"))
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{collections::HashMap, ops::Add};
|
||||||
|
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
use crate::{v1::kcomebacks::filter::filter_daterange_handler, error_responses::BadRequestError};
|
use crate::v1::kcomebacks::filter::filter_daterange_handler;
|
||||||
|
|
||||||
pub fn get_kcomebacks_upcoming_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
pub fn get_kcomebacks_upcoming_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
warp::path("upcoming")
|
warp::path("upcoming")
|
||||||
|
|
|
@ -2,13 +2,16 @@ mod builtin;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod kcomebacks;
|
mod kcomebacks;
|
||||||
mod projects;
|
mod projects;
|
||||||
mod updates;
|
mod run;
|
||||||
|
|
||||||
|
pub use run::setup as run_setup;
|
||||||
|
pub use run::run_sync_all_command;
|
||||||
|
|
||||||
pub use builtin::get_builtin_routes as get_v1_builtin_routes;
|
pub use builtin::get_builtin_routes as get_v1_builtin_routes;
|
||||||
pub use debug::get_debug_routes as get_v1_debug_routes;
|
pub use debug::get_debug_routes as get_v1_debug_routes;
|
||||||
pub use kcomebacks::get_kcomebacks_routes as get_v1_kcomebacks_routes;
|
pub use kcomebacks::get_kcomebacks_routes as get_v1_kcomebacks_routes;
|
||||||
pub use projects::get_project_routes as get_v1_project_routes;
|
pub use projects::get_project_routes as get_v1_project_routes;
|
||||||
pub use updates::get_updates_routes as get_v1_updates_routes;
|
pub use run::get_run_routes as get_v1_updates_routes;
|
||||||
|
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use std::{collections::{HashMap, HashSet}, vec};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use reqwest::StatusCode;
|
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
use crate::{error_responses::{BadRequestError, InternalServerError, NotImplementedError}, v1::projects::{fetch_data, create_json_response, Project as EntryProject}};
|
use crate::{error_responses::BadRequestError, v1::projects::{fetch_data, create_json_response, Project as EntryProject}};
|
||||||
|
|
||||||
pub fn get_project_filter_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
pub fn get_project_filter_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
warp::path("filter")
|
warp::path("filter")
|
||||||
|
@ -399,8 +398,6 @@ async fn filter_category_handler(params: HashMap<String, String>) -> Result<imp
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn filter_language_handler(params: HashMap<String, String>) -> Result<impl warp::Reply, warp::Rejection> {
|
async fn filter_language_handler(params: HashMap<String, String>) -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
return Err(warp::reject::custom(NotImplementedError));
|
|
||||||
return Ok(warp::reply::html("placeholder for compiler to be happy"));
|
|
||||||
// Access the parameres from the HashMap
|
// Access the parameres from the HashMap
|
||||||
let language = params.get("language").unwrap_or(&"".to_string()).to_string();
|
let language = params.get("language").unwrap_or(&"".to_string()).to_string();
|
||||||
let limit = params.get("limit").unwrap_or(&"".to_string()).to_string();
|
let limit = params.get("limit").unwrap_or(&"".to_string()).to_string();
|
||||||
|
@ -429,7 +426,7 @@ async fn filter_language_handler(params: HashMap<String, String>) -> Result<impl
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|item| serde_json::from_value::<EntryProject>(item.clone()).ok())
|
.filter_map(|item| serde_json::from_value::<EntryProject>(item.clone()).ok())
|
||||||
.filter(|project| project.visible)
|
.filter(|project| project.visible)
|
||||||
//.filter(|project| project.languages.iter().any(|lang| lang.contains_key(language)))
|
.filter(|project| project.languages.contains_key(&language))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
|
@ -442,16 +439,14 @@ async fn filter_language_handler(params: HashMap<String, String>) -> Result<impl
|
||||||
let filtered_data = filtered_data.iter().skip(offset.parse::<usize>().unwrap()).take(limit.parse::<usize>().unwrap()).collect::<Vec<_>>();
|
let filtered_data = filtered_data.iter().skip(offset.parse::<usize>().unwrap()).take(limit.parse::<usize>().unwrap()).collect::<Vec<_>>();
|
||||||
|
|
||||||
// return the data
|
// return the data
|
||||||
//Ok(warp::reply::json(&create_json_response(filtered_data, total_results)))
|
Ok(warp::reply::json(&create_json_response(filtered_data, total_results)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn filter_getlangs_handler() -> Result<impl warp::Reply, warp::Rejection> {
|
async fn filter_getlangs_handler() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
return Err(warp::reject::custom(NotImplementedError));
|
|
||||||
return Ok(warp::reply::html("placeholder for compiler to be happy"));
|
|
||||||
// fetch the data
|
// fetch the data
|
||||||
let data = fetch_data().await.unwrap();
|
let data = fetch_data().await.unwrap();
|
||||||
|
|
||||||
// filter the data
|
//filter the data
|
||||||
let filtered_data: Vec<EntryProject> = match data {
|
let filtered_data: Vec<EntryProject> = match data {
|
||||||
Value::Array(items) => {
|
Value::Array(items) => {
|
||||||
items
|
items
|
||||||
|
@ -464,16 +459,25 @@ async fn filter_getlangs_handler() -> Result<impl warp::Reply, warp::Rejection>
|
||||||
};
|
};
|
||||||
|
|
||||||
// filter the data
|
// filter the data
|
||||||
/*let all_language_keys: Vec<&String> = filtered_data
|
let mut languages_set: HashSet<String> = HashSet::new();
|
||||||
.iter()
|
|
||||||
.flat_map(|project| project.languages.iter().flat_map(|lang_map| lang_map.keys()))
|
for project in filtered_data {
|
||||||
.collect();*/
|
for language in project.languages.keys() {
|
||||||
|
languages_set.insert(language.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the total number of results
|
// get the total number of results
|
||||||
//let total_results = all_language_keys.len();
|
let total_results = languages_set.len();
|
||||||
|
|
||||||
|
// json response
|
||||||
|
let json_response = json!({
|
||||||
|
"results": languages_set,
|
||||||
|
"total_results": total_results,
|
||||||
|
});
|
||||||
|
|
||||||
// return the data
|
// return the data
|
||||||
//Ok(warp::reply::json(&create_json_response(all_language_keys, total_results)))
|
Ok(warp::reply::json(&json_response))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn filter_getstatuses_handler() -> Result<impl warp::Reply, warp::Rejection> {
|
async fn filter_getstatuses_handler() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
|
|
@ -15,7 +15,8 @@ pub fn get_project_routes() -> impl warp::Filter<Extract = impl warp::Reply, Err
|
||||||
|
|
||||||
.and(warp::path("last_update").and(warp::get()).and_then(last_update)
|
.and(warp::path("last_update").and(warp::get()).and_then(last_update)
|
||||||
.or(warp::path("start_update").map(|| "Not implemented yet"))
|
.or(warp::path("start_update").map(|| "Not implemented yet"))
|
||||||
.or(get_project_filter_routes()))
|
.or(get_project_filter_routes())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get json data from https://https://cdn.jonasjones.dev/api/projects/projects.json
|
// get json data from https://https://cdn.jonasjones.dev/api/projects/projects.json
|
||||||
|
@ -73,11 +74,3 @@ pub fn create_json_response(items: Vec<&Project>, total_results: usize) -> Value
|
||||||
|
|
||||||
json_response
|
json_response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_item(item: &Value) -> Project {
|
|
||||||
// Parse the item into a struct
|
|
||||||
let item: Project = serde_json::from_value(item.clone()).unwrap();
|
|
||||||
|
|
||||||
// Return the parsed item
|
|
||||||
item
|
|
||||||
}
|
|
284
src/v1/run/mod.rs
Normal file
284
src/v1/run/mod.rs
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
use std::fs;
|
||||||
|
// use std::io::BufRead;
|
||||||
|
use std::process::{Stdio, Command};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
// use tokio::sync::mpsc;
|
||||||
|
use tokio::task;
|
||||||
|
use warp::Filter;
|
||||||
|
use crate::error_responses::InternalServerError;
|
||||||
|
use crate::Logger;
|
||||||
|
|
||||||
|
// DiscographyQuery
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct DiscographyQuery {
|
||||||
|
artists: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_run_routes() -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||||
|
warp::path("v1").and(warp::path("run"))
|
||||||
|
// update/kcomebacks
|
||||||
|
// update/projects
|
||||||
|
// update/makediscography?artists=artist1,artist2,artist3
|
||||||
|
// update/synclikedsongs
|
||||||
|
.and((warp::path("kcomebacks").and_then(update_kcomebacks))
|
||||||
|
.or(warp::path("projects").and_then(update_projects))
|
||||||
|
.or(warp::path("makediscography").map(||"Not implemented yet"))
|
||||||
|
.or(warp::path("synclikedsongs").and_then(sync_liked_songs))
|
||||||
|
.or(warp::path("sync_all").and_then(sync_all))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_kcomebacks() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
// check if the local repository exists, if not, clone it
|
||||||
|
if !fs::metadata("./resources/turbo_octo_potato").is_ok() {
|
||||||
|
setup().unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = run_kcomebacks_command() {
|
||||||
|
// Handle the error here
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
// Return an appropriate response or error
|
||||||
|
return Err(warp::reject::custom(InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(warp::reply::json(&json!({"status": "updating..."})))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_projects() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
// check if the local repository exists, if not, clone it
|
||||||
|
if !fs::metadata("./resources/turbo_octo_potato").is_ok() {
|
||||||
|
setup().unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = run_projects_command() {
|
||||||
|
// Handle the error here
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
// Return an appropriate response or error
|
||||||
|
return Err(warp::reject::custom(InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(warp::reply::json(&json!({"status": "updating..."})))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sync_liked_songs() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
// check if the local repository exists, if not, clone it
|
||||||
|
if !fs::metadata("./resources/turbo_octo_potato").is_ok() {
|
||||||
|
setup().unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = run_likedsongs_command() {
|
||||||
|
// Handle the error here
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
// Return an appropriate response or error
|
||||||
|
return Err(warp::reject::custom(InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(warp::reply::json(&json!({"status": "updating..."})))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sync_all() -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
|
// check if the local repository exists, if not, clone it
|
||||||
|
if !fs::metadata("./resources/turbo_octo_potato").is_ok() {
|
||||||
|
setup().unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = run_sync_all_command() {
|
||||||
|
// Handle the error here
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
// Return an appropriate response or error
|
||||||
|
return Err(warp::reject::custom(InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(warp::reply::json(&json!({"status": "syncing..."})))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup() -> Result<(), git2::Error> {
|
||||||
|
let repository_url = "https://github.com/JonasunderscoreJones/turbo-octo-potato.git";
|
||||||
|
let local_directory = "resources/turbo_octo_potato";
|
||||||
|
|
||||||
|
git2::Repository::clone(repository_url, local_directory)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn run_command() -> Result<(), std::io::Error> {
|
||||||
|
// let (tx, mut rx) = mpsc::channel(1);
|
||||||
|
|
||||||
|
// task::spawn_blocking(move || {
|
||||||
|
// let mut child = Command::new("python3")
|
||||||
|
// .arg(&py_file)
|
||||||
|
// .arg(&args)
|
||||||
|
// .current_dir("resources/turbo_octo_potato")
|
||||||
|
// .stdout(Stdio::piped())
|
||||||
|
// .spawn()
|
||||||
|
// .expect("failed to execute child");
|
||||||
|
|
||||||
|
// let stdout = child.stdout.as_mut().unwrap();
|
||||||
|
|
||||||
|
// let mut reader = std::io::BufReader::new(stdout);
|
||||||
|
|
||||||
|
// let mut line = String::new();
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// let len = reader.read_line(&mut line).unwrap();
|
||||||
|
// if len == 0 {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// tx.blocking_send(line.clone()).unwrap();
|
||||||
|
// line.clear();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// child.wait().unwrap();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// task::spawn(async move {
|
||||||
|
// while let Some(line) = rx.recv().await {
|
||||||
|
// println!("{}", line);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// run_command with python file and args as parameters
|
||||||
|
|
||||||
|
|
||||||
|
pub fn run_kcomebacks_command() -> Result<(), std::io::Error> {
|
||||||
|
// let (tx, mut rx) = mpsc::channel(1);
|
||||||
|
|
||||||
|
task::spawn_blocking(move || {
|
||||||
|
let mut child = Command::new("python3")
|
||||||
|
.arg("rpopfetch.py")
|
||||||
|
.arg("--cdn")
|
||||||
|
.current_dir("resources/turbo_octo_potato")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute child");
|
||||||
|
|
||||||
|
// let stdout = child.stdout.as_mut().unwrap();
|
||||||
|
|
||||||
|
// let mut reader = std::io::BufReader::new(stdout);
|
||||||
|
|
||||||
|
// let mut line = String::new();
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// let len = reader.read_line(&mut line).unwrap();
|
||||||
|
// if len == 0 {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// tx.blocking_send(line.clone()).unwrap();
|
||||||
|
// line.clear();
|
||||||
|
// }
|
||||||
|
|
||||||
|
child.wait().unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// task::spawn(async move {
|
||||||
|
// while let Some(line) = rx.recv().await {
|
||||||
|
// Logger::info(&format!("[/v1/kcomebacks/update]: {}", line));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
Logger::info("Updating kcomebacks...");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_projects_command() -> Result<(), std::io::Error> {
|
||||||
|
// let (tx, mut rx) = mpsc::channel(1);
|
||||||
|
|
||||||
|
task::spawn_blocking(move || {
|
||||||
|
let mut child = Command::new("python3")
|
||||||
|
.arg("update_projects.py")
|
||||||
|
.arg("--cdn")
|
||||||
|
.current_dir("resources/turbo_octo_potato")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute child");
|
||||||
|
|
||||||
|
// let stdout = child.stdout.as_mut().unwrap();
|
||||||
|
|
||||||
|
// let mut reader = std::io::BufReader::new(stdout);
|
||||||
|
|
||||||
|
// let mut line = String::new();
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// let len = reader.read_line(&mut line).unwrap();
|
||||||
|
// if len == 0 {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// tx.blocking_send(line.clone()).unwrap();
|
||||||
|
// line.clear();
|
||||||
|
// }
|
||||||
|
|
||||||
|
child.wait().unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// task::spawn(async move {
|
||||||
|
// while let Some(line) = rx.recv().await {
|
||||||
|
// Logger::info(&format!("[/v1/projects/update]: {}", line));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
Logger::info("Updating projects...");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_likedsongs_command() -> Result<(), std::io::Error> {
|
||||||
|
// let (tx, mut rx) = mpsc::channel(1);
|
||||||
|
|
||||||
|
task::spawn_blocking(move || {
|
||||||
|
let mut child = Command::new("python3")
|
||||||
|
.arg("likedsongsync2.py")
|
||||||
|
.current_dir("resources/turbo_octo_potato")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute child");
|
||||||
|
|
||||||
|
// let stdout = child.stdout.as_mut().unwrap();
|
||||||
|
|
||||||
|
// let mut reader = std::io::BufReader::new(stdout);
|
||||||
|
|
||||||
|
// let mut line = String::new();
|
||||||
|
|
||||||
|
// loop {
|
||||||
|
// let len = reader.read_line(&mut line).unwrap();
|
||||||
|
// if len == 0 {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// tx.blocking_send(line.clone()).unwrap();
|
||||||
|
// line.clear();
|
||||||
|
// }
|
||||||
|
|
||||||
|
child.wait().unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// task::spawn(async move {
|
||||||
|
// while let Some(line) = rx.recv().await {
|
||||||
|
// Logger::info(&format!("[/v1/synclikedsongs]: {}", line));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
Logger::info("Syncing liked songs...");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_sync_all_command() -> Result<(), std::io::Error> {
|
||||||
|
task::spawn_blocking(move || {
|
||||||
|
let mut child = Command::new("python3")
|
||||||
|
.arg("script_interval_runner.py")
|
||||||
|
.current_dir("resources/turbo_octo_potato")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute child");
|
||||||
|
child.wait().unwrap();
|
||||||
|
});
|
||||||
|
Logger::info("Running all Sync Scripts...");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -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 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 {
|
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
|
// 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"))
|
warp::path("v1").and(warp::path("updates")).and(warp::path("minecraft")).and(warp::path("mods"))
|
||||||
|
|
||||||
.and(warp::path::param())
|
.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))
|
||||||
.and(warp::path::param())
|
|
||||||
.and(warp::path::param())
|
|
||||||
.and(warp::path::end())
|
|
||||||
.and(warp::addr::remote())
|
|
||||||
.map(handle_path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_path(modname: String, loadername: String, version: String, remote_ip: Option<std::net::SocketAddr>) -> String {
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
format!("modname: {}, loadername: {}, version: {}, IP: {}", modname, loadername, version, remote_ip.unwrap_or(std::net::SocketAddr::from(([0, 0, 0, 0], 0))).ip())
|
struct ModData {
|
||||||
|
package: String,
|
||||||
|
name: String,
|
||||||
|
versions: Vec<HashMap<String, HashMap<String, ModVersion>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_with_headers(
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
headers: warp::http::HeaderMap,
|
struct ModVersion {
|
||||||
) -> String {
|
recommended: String,
|
||||||
// Iterate through the headers and print them
|
latest: String,
|
||||||
for (name, value) in headers.iter() {
|
all: Vec<String>,
|
||||||
println!("Header: {}: {}", name, value.to_str().unwrap_or("Invalid UTF-8"));
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond with a message or perform other actions as needed
|
// check if the minecraft version is valid
|
||||||
"Headers received".to_string()
|
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