Files
SwaggerUIServer/src/main.rs

118 lines
3.4 KiB
Rust

use actix_web::{App, HttpResponse, HttpServer, Responder, web};
use clap::Parser;
const SCHEMA_FILE: &str = "schema.yaml";
/// Simple swagger UI server - serves OpenAPI file
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Path to schema to serve
#[clap(short, long)]
schema: String,
/// Listen address
#[clap(short, long, default_value = "0.0.0.0:8000")]
listen_address: String,
/// Access token, optional
#[clap(short, long)]
token: Option<String>,
}
#[derive(serde::Deserialize)]
struct HandlerPath {
filename: Option<String>,
}
#[derive(serde::Deserialize)]
struct HandlerQuery {
token: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct Schema(String);
#[derive(Debug, Clone, Eq, PartialEq)]
struct AccessToken(Option<String>);
async fn handler(path: web::Path<HandlerPath>,
query: web::Query<HandlerQuery>,
schema: web::Data<Schema>,
access_token: web::Data<AccessToken>) -> impl Responder {
let filename: &str = path.filename.as_deref().unwrap_or("index.html");
if filename.eq("index.html") || filename.eq(SCHEMA_FILE) {
if let Some(tok) = access_token.0.as_ref() {
if !query.token.as_deref().unwrap_or_default().eq(tok) {
return HttpResponse::Unauthorized()
.body("Please indicate as query parameter the 'token'");
}
}
}
let ext = filename
.rsplit_once('.')
.map(|r| r.1).unwrap_or_default();
let content_type = match ext {
"js" => "application/javascript",
"css" => "text/css",
"html" => "text/html",
"png" => "image/png",
"json" => "application/json",
"yml" | "yaml" => "application/x-yaml",
_ => "text/plain"
};
let filebytes = if filename.eq(SCHEMA_FILE) {
Some(schema.0.as_bytes().to_vec())
} else {
swagger_ui::Assets::get(filename).map(|f| f.to_vec())
};
match filebytes {
Some(f) => {
let mut bytes = f.to_vec();
if filename.eq("index.html") {
bytes = String::from_utf8_lossy(&bytes).to_string()
.replace(
"https://petstore.swagger.io/v2/swagger.json",
&format!("/{}?token={}", SCHEMA_FILE, access_token.0.as_deref().unwrap_or_default()),
)
.as_bytes().to_vec();
}
HttpResponse::Ok()
.insert_header(("Content-Type", content_type))
.body(bytes)
}
None => HttpResponse::NotFound().body("404 not found"),
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let args: Args = Args::parse();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
log::info!("Will listen on http://{}/", args.listen_address);
let schema = Schema(std::fs::read_to_string(args.schema)
.expect("Failed to read schema!"));
let access_token = AccessToken(args.token);
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(schema.clone()))
.app_data(web::Data::new(access_token.clone()))
.route("/", web::get().to(handler))
.route("/{filename}", web::get().to(handler))
})
.bind(args.listen_address)?
.run()
.await
}