Geoffrey-rs/geoffrey_api/src/main.rs

174 lines
4.5 KiB
Rust

use std::convert::TryFrom;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use warp::Filter;
use structopt::StructOpt;
use tokio::net::UnixListener;
use geoffrey_models::logging::LogLevel;
use geoffrey_models::models::parameters::add_token_params::AddTokenParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::token::Permissions;
use crate::commands::add_token::AddToken;
use crate::commands::{command_filter, Command};
use crate::config::GeoffreyAPIConfig;
use crate::context::Context;
use crate::logging::init_logging;
use crate::model::{local_model_filter, remote_model_filter};
use tokio_stream::wrappers::UnixListenerStream;
mod api_endpoint;
mod commands;
mod config;
mod context;
mod helper;
mod logging;
mod model;
pub type Result<T> = std::result::Result<T, GeoffreyAPIError>;
#[derive(Debug, StructOpt, Clone, Default)]
#[structopt(name = "GeoffreyAPI", about = "Geoffrey Central API")]
pub struct Args {
#[structopt(env = "GEOFFREY_CONFIG", parse(from_os_str))]
config: PathBuf,
#[structopt(
short,
long,
env = "GEOFFREY_LOG_LEVEL",
parse(from_str),
default_value = "Info"
)]
log_level: LogLevel,
#[structopt(short, long)]
force_migration: bool,
#[structopt(subcommand)]
command: GeoffreyApiCommand,
}
#[derive(Debug, PartialEq, StructOpt, Clone)]
pub enum GeoffreyApiCommand {
Run,
CreateAdminToken(CreateTokenCommand),
}
impl Default for GeoffreyApiCommand {
fn default() -> Self {
Self::Run
}
}
#[derive(Debug, StructOpt, PartialEq, Clone)]
pub struct CreateTokenCommand {
#[structopt(parse(try_from_str = Permissions::try_from))]
pub permissions: Vec<Permissions>,
}
async fn run_local_server(ctx: &Arc<Context>) {
let socket_path = ctx
.cfg
.server_config
.local_socket
.as_ref()
.expect("Expected local_socket to be defined");
if socket_path.exists() {
if let Err(e) = std::fs::remove_file(&socket_path) {
log::error!("Unable to cleanup local socket: {}", e);
return;
}
}
let listener = match UnixListener::bind(socket_path) {
Ok(l) => l,
Err(e) => {
log::error!("Unable to open local socket: {}", e);
return;
}
};
let unix_listener_stream = UnixListenerStream::new(listener);
let local_api = local_model_filter(ctx.clone());
warp::serve(local_api.clone())
.run_incoming(unix_listener_stream)
.await;
}
async fn run_remote_server(ctx: &Arc<Context>) {
let socket_addr = match SocketAddr::from_str(ctx.cfg.server_config.host.as_str()) {
Ok(socket_addr) => socket_addr,
Err(e) => {
log::warn!(
"Error parsing {} as address: {}",
ctx.cfg.server_config.host,
e
);
return;
}
};
let api = command_filter(ctx.clone()).or(remote_model_filter(ctx.clone()));
warp::serve(api).run(socket_addr).await;
}
async fn run_server(ctx: Arc<Context>) {
tokio::select! {
// Start local api
_ = run_local_server(&ctx), if ctx.cfg.server_config.local_socket.is_some() => {
log::warn!("Socket API exited")
}
// Start HTTP api
_ = run_remote_server(&ctx) => {
log::warn!("HTTP API exited")
}
}
}
fn create_token(ctx: Arc<Context>, perms: Vec<Permissions>) {
match AddToken::run_command(ctx, &AddTokenParams { permissions: perms }, None) {
Ok(token) => {
// Don't log this to keep tokens out of the log
println!("Added admin token with secret: {}", token.secret)
}
Err(e) => {
log::warn!("Unable to create admin token: {}", e)
}
}
}
#[tokio::main]
async fn main() {
let args: Args = Args::from_args();
if let Err(e) = init_logging(args.log_level) {
println!("Unable to initialize logger: {}", e);
return;
}
let cfg = match GeoffreyAPIConfig::new(args.config.as_path()) {
Ok(cfg) => cfg,
Err(e) => {
log::warn!("Error opening config: {}", e);
return;
}
};
let ctx = Context::new(cfg, args.clone()).unwrap();
match args.command {
GeoffreyApiCommand::Run => run_server(ctx).await,
GeoffreyApiCommand::CreateAdminToken(perms) => create_token(ctx, perms.permissions),
};
}