use async_std::{
fs,
path::{Path, PathBuf},
};
use data::LicenseDatabase;
use tide::{http::mime, log, Response, Result, Server, StatusCode};
use crate::{
config::{RunningConfig, ServerConfig},
data::Queryable,
errors::ErrorMsg,
};
mod config;
mod data;
mod errors;
#[async_std::main]
async fn main() -> Result<()> {
log::start();
match <ServerConfig as jealousy::FromEnv>::from_env() {
Ok(config) => {
let run_config = RunningConfig {
config: config.clone(),
database: LicenseDatabase::load(config.database.clone()).unwrap_or_default(),
};
let mut app = tide::with_state(run_config);
create_routes(&mut app);
app.listen(format!("{}:{}", config.server_ip, config.server_port))
.await?;
}
Err(_) => (),
};
Ok(())
}
fn create_routes(server: &mut Server<RunningConfig>) {
let url = server.clone().state().config.base_url.clone();
let mut base_route = server.at(&url);
base_route.at(":license").get(get_requested_license);
BASE_URL/list
BASE_URL/full
BASE_URL/list/
BASE_URL/full/
base_route.at("list").get(get_license_list);
base_route.at("list/full").get(get_detailed_license_list);
let mut fallback_routes = base_route.at("");
fallback_routes.get(get_welcome_message);
fallback_routes.at("list/").get(get_license_list);
fallback_routes
.at("list/full/")
.get(get_detailed_license_list);
fallback_routes.at("list/*").get(get_fallback_response);
fallback_routes.at("list/full/*").get(get_fallback_response);
}
fn build_response(status: StatusCode, data: String) -> Response {
let response = Response::builder(status)
.body(format!("{}\n", data))
.content_type(mime::PLAIN)
.build();
response
}
async fn get_welcome_message(req: tide::Request<RunningConfig>) -> tide::Result<Response> {
match &req.state().config.welcome_message {
Some(file) => match async_std::fs::read_to_string(file).await {
Ok(data) => return Ok(build_response(StatusCode::Ok, data)),
Err(_) => {
log::error!("Unable to read welcome message file.");
return Ok(build_response(StatusCode::Ok, build_welcome_message(req)));
}
},
None => return Ok(build_response(StatusCode::Ok, build_welcome_message(req))),
}
}
fn build_welcome_message(req: tide::Request<RunningConfig>) -> String {
let base_url = req.state().config.base_url.clone();
let header = String::from("Available endpoints:");
let base = format!("{}", base_url);
let list = format!("{}list", base_url);
let all = format!("{}list/full", base_url);
let license = format!("{}<license>", base_url);
format!(
"{}\n {}\n {}\n {}\n {}",
header, base, list, all, license
)
}
async fn get_fallback_response(req: tide::Request<RunningConfig>) -> tide::Result<Response> {
Ok(error_response(req.url().path()))
}
async fn get_license_list(req: tide::Request<RunningConfig>) -> tide::Result<Response> {
match req.state().database.get_licenses(5) {
Some(licenses) => Ok(build_response(StatusCode::Ok, format!("{}", licenses))),
None => Ok(build_response(
StatusCode::NotFound,
String::from(ErrorMsg::NoLicensesAvailable.as_str()),
)),
}
}
async fn get_detailed_license_list(req: tide::Request<RunningConfig>) -> tide::Result<Response> {
match req.state().database.get_detailed_licenses() {
Some(licenses) => Ok(build_response(StatusCode::Ok, format!("{}", licenses))),
None => Ok(build_response(
StatusCode::NotFound,
String::from(ErrorMsg::NoLicensesAvailable.as_str()),
)),
}
}
async fn get_requested_license(req: tide::Request<RunningConfig>) -> tide::Result<Response> {
let request = req.url().path().strip_prefix("/").unwrap_or("");
let response = match req.state().database.get_license(request) {
Some(license) => {
let source_dir = PathBuf::from(req.state().config.database.clone());
let source_dir = source_dir.parent().unwrap_or(Path::new(""));
let mut license_file_path = PathBuf::from(source_dir);
license_file_path.push(license.filename);
match fs::read_to_string(&license_file_path).await {
Ok(contents) => {
build_response(StatusCode::Accepted, contents.trim_end().to_string())
}
Err(_) => {
log::error!(
"{} '{}'.",
ErrorMsg::LicenseReadError.as_str(),
license_file_path.to_string_lossy().to_string()
);
error_response(request)
}
}
}
None => {
log::error!("{} '{}'", ErrorMsg::InvalidLicense.as_str(), request);
error_response(request)
}
};
Ok(response)
}
fn error_response(_req: &str) -> Response {
build_response(
StatusCode::NotFound,
format!("{}", ErrorMsg::InvalidLicenseRequest.as_str()).to_string(),
)
}