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

View file

@ -2,6 +2,10 @@
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
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:
```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
cd speedtest-rs
```
@ -41,29 +45,15 @@ cd speedtest-rs
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.
5. Put `assets` folder under the same directory as your compiled binary.
4. Put `assets` folder under the same directory as your compiled binary.
- 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
(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`
6. Change `.env` according to your environment:
```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
```
5. Launch: ./speedtest-rs
## Differences between Go and PHP implementation
@ -74,6 +64,8 @@ Copyright (C) 2016-2021 Federico Dossena
Copyright (C) 2021 Darcy Robson
Copyright (C) 2022 Emmanuel Garette
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
the Free Software Foundation, either version 3 of the License, or

View file

@ -2,11 +2,11 @@ use ipinfo::{IpDetails, IpInfo, IpInfoConfig};
use regex::Regex;
use rocket::serde::{json::Json, Serialize};
use rocket_client_addr::ClientRealAddr;
use std::env::var;
use rocket::State;
use crate::haversine::Units;
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)]
pub struct GetIPOptions {
@ -27,7 +27,7 @@ pub struct GetIPResponse {
}
#[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 {
processed_string: None,
raw_isp_info: None,
@ -42,7 +42,8 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<Ge
}
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());
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() {
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);
}
@ -73,25 +74,26 @@ pub async fn get_ip(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<Ge
}
#[get("/getIP.php?<opts..>")]
pub async fn get_ip_php(client_addr: &ClientRealAddr, opts: GetIPOptions) -> Json<GetIPResponse> {
get_ip(client_addr, opts).await
pub async fn get_ip_php(client_addr: &ClientRealAddr, opts: GetIPOptions, state: &State<Config>) -> Json<GetIPResponse> {
get_ip(client_addr, opts, state).await
}
#[get("/backend/getIP.php?<opts..>")]
pub async fn get_backend_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
}
pub async fn get_ipinfo(ip: &str) -> IpDetails {
let config = IpInfoConfig {
token: Some(var("IPINFO_TOKEN").unwrap_or(String::new())),
pub async fn get_ipinfo(ip: &str, config: &Config) -> IpDetails {
let ipconfig = IpInfoConfig {
token: Some(config.ipinfo_token.to_string()),
..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 ipinfo = res.get(ip).unwrap();

View file

@ -1,3 +1,4 @@
#![allow(unused_must_use)]
#[macro_use]
extern crate rocket;
@ -11,19 +12,30 @@ pub mod haversine;
pub mod serialized_ip_info;
pub mod util;
use dotenv::dotenv;
use std::error::Error;
use clap::{arg, Command, value_parser};
use rocket::fs::FileServer;
use rocket::http::Method;
use empty::*;
use garbage::*;
use get_ip::*;
use util::Config;
#[rocket::main]
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];
@ -44,17 +56,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
];
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("assets");
if asset_path.exists() {
let fileserver = FileServer::from(asset_path);
rocketship = rocketship.mount("/", fileserver);
}
rocketship.launch().await?;
let asset_path = std::env::current_dir().unwrap().join(args.get_one::<String>("assets").expect("required"));
rocket::custom(figment).mount("/", routes)
.manage(config)
.mount("/", FileServer::from(asset_path))
.launch().await?;
Ok(())
}

View file

@ -1,5 +1,3 @@
use std::env::var;
use crate::haversine::{distance, Location, Units};
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 {
latitude: 0.0,
longitude: 0.0,
});
let server_location = Location {
latitude: var("SERVER_LATITUDE")
.unwrap_or_default()
.parse::<f64>()
.unwrap_or_default(),
longitude: var("SERVER_LONGITUDE")
.unwrap_or_default()
.parse::<f64>()
.unwrap_or_default(),
latitude: config.latitude,
longitude: config.longitude,
};
let distance = distance(client_location, server_location, &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,
}