diff options
| author | Felix Kaaman <tmtu@tmtu.ee> | 2020-02-22 23:27:01 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-22 23:27:01 +0100 |
| commit | 763b8c6579f3ae571f7287c72b9fb4f8b6e89349 (patch) | |
| tree | bc55a0e79107a93bc3e605a0cae32926dc4c52fc /src/web_server | |
| parent | 2792ba9c8a7120a91b3bd2c6075e737690e73405 (diff) | |
| parent | 326cfa543c6263818aad7dec4a869bc8139ec14c (diff) | |
| download | pokebot-763b8c6579f3ae571f7287c72b9fb4f8b6e89349.tar.gz pokebot-763b8c6579f3ae571f7287c72b9fb4f8b6e89349.zip | |
Merge pull request #33 from Mavulp/webserver
Webserver
Diffstat (limited to 'src/web_server')
| -rw-r--r-- | src/web_server/api.rs | 48 | ||||
| -rw-r--r-- | src/web_server/bot_executor.rs | 63 | ||||
| -rw-r--r-- | src/web_server/default.rs | 24 | ||||
| -rw-r--r-- | src/web_server/front_end_cookie.rs | 60 | ||||
| -rw-r--r-- | src/web_server/tmtu.rs | 41 |
5 files changed, 236 insertions, 0 deletions
diff --git a/src/web_server/api.rs b/src/web_server/api.rs new file mode 100644 index 0000000..4deedad --- /dev/null +++ b/src/web_server/api.rs @@ -0,0 +1,48 @@ +use actix::Addr; +use actix_web::{get, web, HttpResponse, Responder, ResponseError}; +use derive_more::Display; +use serde::Serialize; + +use crate::web_server::{BotDataListRequest, BotDataRequest, BotExecutor}; + +#[get("/bots")] +pub async fn get_bot_list(bot: web::Data<Addr<BotExecutor>>) -> impl Responder { + let bot_datas = match bot.send(BotDataListRequest).await.unwrap() { + Ok(data) => data, + Err(_) => Vec::with_capacity(0), + }; + + web::Json(bot_datas) +} + +#[get("/bots/{name}")] +pub async fn get_bot(bot: web::Data<Addr<BotExecutor>>, name: web::Path<String>) -> impl Responder { + if let Some(bot_data) = bot.send(BotDataRequest(name.into_inner())).await.unwrap() { + Ok(web::Json(bot_data)) + } else { + Err(ApiErrorKind::NotFound) + } +} + +#[derive(Serialize)] +struct ApiError { + error: String, + description: String, +} + +#[derive(Debug, Display)] +enum ApiErrorKind { + #[display(fmt = "Not Found")] + NotFound, +} + +impl ResponseError for ApiErrorKind { + fn error_response(&self) -> HttpResponse { + match *self { + ApiErrorKind::NotFound => HttpResponse::NotFound().json(ApiError { + error: self.to_string(), + description: String::from("The requested resource was not found"), + }), + } + } +} diff --git a/src/web_server/bot_executor.rs b/src/web_server/bot_executor.rs new file mode 100644 index 0000000..fde3c08 --- /dev/null +++ b/src/web_server/bot_executor.rs @@ -0,0 +1,63 @@ +use std::sync::Arc; + +use actix::{Actor, Handler, Message, SyncContext}; + +use crate::bot::MasterBot; +use crate::web_server::BotData; + +pub struct BotExecutor(pub Arc<MasterBot>); + +impl Actor for BotExecutor { + type Context = SyncContext<Self>; +} + +pub struct BotNameListRequest; + +impl Message for BotNameListRequest { + // A plain Vec does not work for some reason + type Result = Result<Vec<String>, ()>; +} + +impl Handler<BotNameListRequest> for BotExecutor { + type Result = Result<Vec<String>, ()>; + + fn handle(&mut self, _: BotNameListRequest, _: &mut Self::Context) -> Self::Result { + let bot = &self.0; + + Ok(bot.bot_names()) + } +} + +pub struct BotDataListRequest; + +impl Message for BotDataListRequest { + // A plain Vec does not work for some reason + type Result = Result<Vec<BotData>, ()>; +} + +impl Handler<BotDataListRequest> for BotExecutor { + type Result = Result<Vec<BotData>, ()>; + + fn handle(&mut self, _: BotDataListRequest, _: &mut Self::Context) -> Self::Result { + let bot = &self.0; + + Ok(bot.bot_datas()) + } +} + +pub struct BotDataRequest(pub String); + +impl Message for BotDataRequest { + type Result = Option<BotData>; +} + +impl Handler<BotDataRequest> for BotExecutor { + type Result = Option<BotData>; + + fn handle(&mut self, r: BotDataRequest, _: &mut Self::Context) -> Self::Result { + let name = r.0; + let bot = &self.0; + + bot.bot_data(name) + } +} diff --git a/src/web_server/default.rs b/src/web_server/default.rs new file mode 100644 index 0000000..b3c8291 --- /dev/null +++ b/src/web_server/default.rs @@ -0,0 +1,24 @@ +use actix::Addr; +use actix_web::{web, Error, HttpResponse}; +use askama::actix_web::TemplateIntoResponse; +use askama::Template; + +use crate::web_server::{filters, BotData, BotDataListRequest, BotExecutor}; + +#[derive(Template)] +#[template(path = "index.htm")] +struct OverviewTemplate<'a> { + bots: &'a [BotData], +} + +pub async fn index(bot: web::Data<Addr<BotExecutor>>) -> Result<HttpResponse, Error> { + let bot_datas = match bot.send(BotDataListRequest).await.unwrap() { + Ok(data) => data, + Err(_) => Vec::with_capacity(0), + }; + + OverviewTemplate { + bots: &bot_datas[..], + } + .into_response() +} diff --git a/src/web_server/front_end_cookie.rs b/src/web_server/front_end_cookie.rs new file mode 100644 index 0000000..4812d0d --- /dev/null +++ b/src/web_server/front_end_cookie.rs @@ -0,0 +1,60 @@ +use futures::future::{ok, Ready}; + +use actix_web::{ + dev::Payload, + http::header::{COOKIE, LOCATION, SET_COOKIE}, + FromRequest, HttpRequest, HttpResponse, +}; +use serde::Deserialize; + +#[derive(PartialEq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum FrontEnd { + Default, + Tmtu, +} + +impl FrontEnd { + const COOKIE_NAME: &'static str = "front-end"; + + fn cookie(&self) -> String { + let name = match self { + FrontEnd::Default => "default", + FrontEnd::Tmtu => "tmtu", + }; + + format!("{}={}", Self::COOKIE_NAME, name) + } +} + +impl FromRequest for FrontEnd { + type Error = (); + type Future = Ready<Result<Self, ()>>; + type Config = (); + + fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { + for header in req.headers().get_all(COOKIE) { + if let Ok(value) = header.to_str() { + for c in value.split(';').map(|s| s.trim()) { + let mut split = c.split('='); + if Some(Self::COOKIE_NAME) == split.next() { + match split.next() { + Some("default") => return ok(FrontEnd::Default), + Some("tmtu") => return ok(FrontEnd::Tmtu), + _ => (), + } + } + } + } + } + + ok(FrontEnd::Default) + } +} + +pub fn set_front_end(front: FrontEnd) -> HttpResponse { + HttpResponse::Found() + .header(SET_COOKIE, front.cookie()) + .header(LOCATION, "/") + .finish() +} diff --git a/src/web_server/tmtu.rs b/src/web_server/tmtu.rs new file mode 100644 index 0000000..0645ee4 --- /dev/null +++ b/src/web_server/tmtu.rs @@ -0,0 +1,41 @@ +use actix::Addr; +use actix_web::{http::header, web, Error, HttpResponse}; +use askama::actix_web::TemplateIntoResponse; +use askama::Template; + +use crate::web_server::{filters, BotData, BotDataRequest, BotExecutor, BotNameListRequest}; + +#[derive(Template)] +#[template(path = "tmtu/index.htm")] +struct TmtuTemplate { + bot_names: Vec<String>, + bot: Option<BotData>, +} + +pub async fn index(bot: web::Data<Addr<BotExecutor>>) -> Result<HttpResponse, Error> { + let bot_names = bot.send(BotNameListRequest).await.unwrap().unwrap(); + + TmtuTemplate { + bot_names, + bot: None, + } + .into_response() +} + +pub async fn get_bot( + bot: web::Data<Addr<BotExecutor>>, + name: String, +) -> Result<HttpResponse, Error> { + let bot_names = bot.send(BotNameListRequest).await.unwrap().unwrap(); + + if let Some(bot) = bot.send(BotDataRequest(name)).await.unwrap() { + TmtuTemplate { + bot_names, + bot: Some(bot), + } + .into_response() + } else { + // TODO to 404 or not to 404 + Ok(HttpResponse::Found().header(header::LOCATION, "/").finish()) + } +} |
