add argument to commandline
This commit is contained in:
parent
927219667d
commit
b2c8afcd69
6 changed files with 679 additions and 382 deletions
921
Cargo.lock
generated
921
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
17
Cargo.toml
17
Cargo.toml
|
@ -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"
|
|
||||||
|
|
28
README.md
28
README.md
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
23
src/util.rs
23
src/util.rs
|
@ -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,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue