add argument to commandline

This commit is contained in:
Emmanuel Garette 2022-10-10 21:20:08 +02:00
parent 927219667d
commit b2c8afcd69
6 changed files with 679 additions and 382 deletions

921
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,14 @@
[package] [package]
name = "speedtest-rs" name = "speedtest-rs"
version = "0.1.0" version = "1.0.0"
edition = "2021" edition = "2021"
license = "LGPL-3.0+" license = "LGPL-3.0+"
[dependencies] [dependencies]
rocket = { version = "0.5.0-rc.1", features = ["json"] } rocket = { version = "0.5.0-rc.2", features = ["json"] }
rand = { version = "0.8.4" } rocket-client-addr = "0.5.2"
rocket-client-addr = "0.5.0" rand = { version = "0.8.5" }
regex = "1.5.4" regex = "1"
ipinfo = "0.2.0" ipinfo = "0.3.1"
serde_with = { version = "1.10.0", features = ["json"] } serde_with = { version = "2.0.1", features = ["json"] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "a062933" } clap = "4.0.11"
dotenv = "0.15.0"

View file

@ -2,6 +2,10 @@
This is a lightweight backend written in Rust for [Librespeed](https://github.com/librespeed/speedtest). This is a lightweight backend written in Rust for [Librespeed](https://github.com/librespeed/speedtest).
Fork from: https://github.com/camilohe/speedtest-rs.git
Original repos: https://github.com/drobson03/speedtest-rs.git
## Compatibility ## Compatibility
Supported by all Librespeed frontends, though some features are missing (see below). Supported by all Librespeed frontends, though some features are missing (see below).
@ -30,7 +34,7 @@ You need Rust 1.55+ to compile the binary.
1. Clone this repository: 1. Clone this repository:
```bash ```bash
git clone github.com/drobson03/speedtest-rs git clone https://cloud.silique.fr/gitea/Silique/speedtest-rs.git
# Change current working directory to the repository # Change current working directory to the repository
cd speedtest-rs cd speedtest-rs
``` ```
@ -41,29 +45,15 @@ cd speedtest-rs
cargo build --release cargo build --release
``` ```
3. Copy the `assets` directory and the compiled `speedtest-rs` binary into a single directory along with a copy of `.env.example` named `.env` with your preferred port, listen address and [IPinfo.io](https://ipinfo.io/) API token. 3. Copy the `assets` directory and the compiled `speedtest-rs` binary into a single directory.
4. Put `assets` folder under the same directory as your compiled binary.
5. Put `assets` folder under the same directory as your compiled binary.
- Make sure font files and JavaScript files are in the `assets` directory - Make sure font files and JavaScript files are in the `assets` directory
- You can have multiple HTML pages under `assets` directory. They can be access directly under the server root - You can have multiple HTML pages under `assets` directory. They can be access directly under the server root
(e.g. `/example-singleServer-full.html`) (e.g. `/example-singleServer-full.html`)
- It's possible to have a default page mapped to `/`, simply put a file named `index.html` under `assets` - It's possible to have a default page mapped to `/`, simply put a file named `index.html` under `assets`
6. Change `.env` according to your environment: 5. Launch: ./speedtest-rs
```sh
# your ipinfo.io API token
IPINFO_TOKEN=
# your server's latitude
SERVER_LATITUDE=1
# your server's longitude
SERVER_LONGITUDE=1
# the port to bind to
ROCKET_PORT=8000
# the bind address (0.0.0.0 is all interfaces)
ROCKET_ADDRESS=0.0.0.0
```
## Differences between Go and PHP implementation ## Differences between Go and PHP implementation
@ -74,6 +64,8 @@ Copyright (C) 2016-2021 Federico Dossena
Copyright (C) 2021 Darcy Robson Copyright (C) 2021 Darcy Robson
Copyright (C) 2022 Emmanuel Garette
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
@ -85,4 +77,4 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/lgpl>. along with this program. If not, see <https://www.gnu.org/licenses/lgpl>.

View file

@ -2,11 +2,11 @@ use ipinfo::{IpDetails, IpInfo, IpInfoConfig};
use regex::Regex; use regex::Regex;
use rocket::serde::{json::Json, Serialize}; use rocket::serde::{json::Json, Serialize};
use rocket_client_addr::ClientRealAddr; use rocket_client_addr::ClientRealAddr;
use std::env::var; use rocket::State;
use crate::haversine::Units; use crate::haversine::Units;
use crate::serialized_ip_info::IpDetailsDef; use crate::serialized_ip_info::IpDetailsDef;
use crate::util::{get_client_server_distance_string, get_ip_type}; use crate::util::{get_client_server_distance_string, get_ip_type, Config};
#[derive(FromForm)] #[derive(FromForm)]
pub struct GetIPOptions { pub struct GetIPOptions {
@ -27,7 +27,7 @@ pub struct GetIPResponse {
} }
#[get("/getIP?<opts..>")] #[get("/getIP?<opts..>")]
pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<GetIPResponse> { pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions, state: &State<Config>) -> Json<GetIPResponse> {
let mut result = GetIPResponse { let mut result = GetIPResponse {
processed_string: None, processed_string: None,
raw_isp_info: None, raw_isp_info: None,
@ -42,7 +42,8 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<Ge
} }
if opts.isp { if opts.isp {
let ipinfo = get_ipinfo(&ip).await; let config = state.inner();
let ipinfo = get_ipinfo(&ip, &config).await;
result.raw_isp_info = Some(ipinfo.to_owned()); result.raw_isp_info = Some(ipinfo.to_owned());
let org_regex = Regex::new(r"AS\d+\s").unwrap(); let org_regex = Regex::new(r"AS\d+\s").unwrap();
@ -62,7 +63,7 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<Ge
} }
if !ipinfo.loc.is_empty() { if !ipinfo.loc.is_empty() {
let distance = get_client_server_distance_string(ipinfo.loc, opts.distance); let distance = get_client_server_distance_string(ipinfo.loc, opts.distance, config);
isp = format!("{} ({})", &isp, &distance); isp = format!("{} ({})", &isp, &distance);
} }
@ -73,25 +74,26 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<Ge
} }
#[get("/getIP.php?<opts..>")] #[get("/getIP.php?<opts..>")]
pub async fn get_ip_php(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<GetIPResponse> { pub async fn get_ip_php(client_addr: &ClientRealAddr, opts: GetIPOptions, state: &State<Config>) -> Json<GetIPResponse> {
get_ip(client_addr, opts).await get_ip(client_addr, opts, state).await
} }
#[get("/backend/getIP.php?<opts..>")] #[get("/backend/getIP.php?<opts..>")]
pub async fn get_backend_ip_php( pub async fn get_backend_ip_php(
client_addr: &ClientRealAddr, client_addr: &ClientRealAddr,
opts: GetIPOptions, opts: GetIPOptions,
state: &State<Config>,
) -> Json<GetIPResponse> { ) -> Json<GetIPResponse> {
get_ip(client_addr, opts).await get_ip(client_addr, opts, state).await
} }
pub async fn get_ipinfo(ip: &str) -> IpDetails { pub async fn get_ipinfo(ip: &str, config: &Config) -> IpDetails {
let config = IpInfoConfig { let ipconfig = IpInfoConfig {
token: Some(var("IPINFO_TOKEN").unwrap_or(String::new())), token: Some(config.ipinfo_token.to_string()),
..Default::default() ..Default::default()
}; };
let mut ipinfo_client = IpInfo::new(config).expect("should construct"); let mut ipinfo_client = IpInfo::new(ipconfig).expect("should construct");
let res = ipinfo_client.lookup(&[ip]).unwrap(); let res = ipinfo_client.lookup(&[ip]).unwrap();
let ipinfo = res.get(ip).unwrap(); let ipinfo = res.get(ip).unwrap();

View file

@ -1,3 +1,4 @@
#![allow(unused_must_use)]
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
@ -11,19 +12,30 @@ pub mod haversine;
pub mod serialized_ip_info; pub mod serialized_ip_info;
pub mod util; pub mod util;
use dotenv::dotenv;
use std::error::Error; use std::error::Error;
use clap::{arg, Command, value_parser};
use rocket::fs::FileServer; use rocket::fs::FileServer;
use rocket::http::Method;
use empty::*; use empty::*;
use garbage::*; use garbage::*;
use get_ip::*; use get_ip::*;
use util::Config;
#[rocket::main] #[rocket::main]
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
dotenv().ok();
let args = Command::new("speedtest-rs")
.version("1.0.0")
.author("Emmanuel Garette <egarette@silique.fr>")
.about("Alternative implemention of the Librespeed server API")
.arg(arg!(-i --ip <VALUE>).default_value("127.0.0.1"))
.arg(arg!(-p --port <VALUE>).value_parser(value_parser!(u16)).default_value("8000"))
.arg(arg!(-a --assets <VALUE>).default_value("assets"))
.arg(arg!(-t --ipinfo_token <VALUE>).default_value(""))
.arg(arg!(-l --latitude <VALUE>).value_parser(value_parser!(f64)).default_value("0.0"))
.arg(arg!(-o --longitude <VALUE>).value_parser(value_parser!(f64)).default_value("0.0"))
.get_matches();
let routes = routes![get_ip::get_ip, get_backend_ip_php]; let routes = routes![get_ip::get_ip, get_backend_ip_php];
@ -44,17 +56,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
]; ];
let routes = vec![routes, garbage_routes, empty_routes].concat(); let routes = vec![routes, garbage_routes, empty_routes].concat();
let config = Config {
ip: args.get_one::<String>("ip").expect("required").to_string(),
port: *args.get_one::<u16>("port").expect("required"),
assets: args.get_one::<String>("assets").expect("required").to_string(),
ipinfo_token: args.get_one::<String>("ipinfo_token").expect("required").to_string(),
latitude: *args.get_one::<f64>("latitude").expect("required"),
longitude: *args.get_one::<f64>("longitude").expect("required"),
};
let figment = rocket::Config::figment()
.merge(("address", &config.ip))
.merge(("port", &config.port));
let mut rocketship = rocket::build().mount("/", routes); let asset_path = std::env::current_dir().unwrap().join(args.get_one::<String>("assets").expect("required"));
rocket::custom(figment).mount("/", routes)
let asset_path = std::env::current_dir().unwrap().join("assets"); .manage(config)
if asset_path.exists() { .mount("/", FileServer::from(asset_path))
let fileserver = FileServer::from(asset_path); .launch().await?;
rocketship = rocketship.mount("/", fileserver);
}
rocketship.launch().await?;
Ok(()) Ok(())
} }

View file

@ -1,5 +1,3 @@
use std::env::var;
use crate::haversine::{distance, Location, Units}; use crate::haversine::{distance, Location, Units};
use regex::Regex; use regex::Regex;
@ -43,24 +41,27 @@ pub fn parse_location_string(location: String) -> Result<Location, String> {
}) })
} }
pub fn get_client_server_distance_string(client_location: String, units: Units) -> String { pub fn get_client_server_distance_string(client_location: String, units: Units, config: &Config) -> String {
let client_location = parse_location_string(client_location).unwrap_or(Location { let client_location = parse_location_string(client_location).unwrap_or(Location {
latitude: 0.0, latitude: 0.0,
longitude: 0.0, longitude: 0.0,
}); });
let server_location = Location { let server_location = Location {
latitude: var("SERVER_LATITUDE") latitude: config.latitude,
.unwrap_or_default() longitude: config.longitude,
.parse::<f64>()
.unwrap_or_default(),
longitude: var("SERVER_LONGITUDE")
.unwrap_or_default()
.parse::<f64>()
.unwrap_or_default(),
}; };
let distance = distance(client_location, server_location, &units); let distance = distance(client_location, server_location, &units);
format!("{:.2} {}", distance, units) format!("{:.2} {}", distance, units)
} }
pub struct Config {
pub ip: String,
pub port: u16,
pub assets: String,
pub ipinfo_token: String,
pub latitude: f64,
pub longitude: f64,
}