diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bot/master.rs | 1 | ||||
| -rw-r--r-- | src/web_server.rs | 185 | ||||
| -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 | 8 | ||||
| -rw-r--r-- | src/web_server/tmtu.rs | 41 | ||||
| -rw-r--r-- | src/youtube_dl.rs | 11 |
8 files changed, 203 insertions, 178 deletions
diff --git a/src/bot/master.rs b/src/bot/master.rs index 9b33744..755aaa1 100644 --- a/src/bot/master.rs +++ b/src/bot/master.rs @@ -215,7 +215,6 @@ impl MasterBot { pub fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> { let music_bots = self.music_bots.read().unwrap(); - let bot = music_bots.connected_bots.get(&name)?; Some(crate::web_server::BotData { diff --git a/src/web_server.rs b/src/web_server.rs index 80c914c..0342a38 100644 --- a/src/web_server.rs +++ b/src/web_server.rs @@ -1,20 +1,21 @@ use std::sync::Arc; use std::time::Duration; -use actix::{Actor, Addr, Handler, Message, SyncArbiter, SyncContext}; +use actix::{Addr, SyncArbiter}; use actix_web::{ - get, http::header, middleware::Logger, post, web, App, Error, HttpResponse, HttpServer, - Responder, ResponseError, + get, http::header, middleware::Logger, post, web, App, HttpResponse, HttpServer, Responder, }; -use askama::actix_web::TemplateIntoResponse; -use askama::Template; -use derive_more::Display; use serde::{Deserialize, Serialize}; use crate::bot::MasterBot; use crate::youtube_dl::AudioMetadata; +mod api; +mod bot_executor; +mod default; mod front_end_cookie; +mod tmtu; +pub use bot_executor::*; use front_end_cookie::FrontEnd; pub struct WebServerArgs { @@ -33,9 +34,13 @@ pub async fn start(args: WebServerArgs) -> std::io::Result<()> { .data(bot_addr.clone()) .wrap(Logger::default()) .service(index) - .service(tmtu_bot) + .service(get_bot) .service(post_front_end) - .service(web::scope("/api").service(get_bot_list).service(get_bot)) + .service( + web::scope("/api") + .service(api::get_bot_list) + .service(api::get_bot), + ) .service(actix_files::Files::new("/static", "static/")) }) .bind(args.bind_address)? @@ -47,12 +52,6 @@ pub async fn start(args: WebServerArgs) -> std::io::Result<()> { Ok(()) } -pub struct BotExecutor(pub Arc<MasterBot>); - -impl Actor for BotExecutor { - type Context = SyncContext<Self>; -} - #[derive(Deserialize)] #[serde(rename_all = "kebab-case")] struct FrontEndForm { @@ -60,74 +59,10 @@ struct FrontEndForm { } #[post("/front-end")] -async fn post_front_end(form: web::Form<FrontEndForm>) -> Result<HttpResponse, Error> { +async fn post_front_end(form: web::Form<FrontEndForm>) -> impl Responder { front_end_cookie::set_front_end(form.into_inner().front_end).await } -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()) - } -} - -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()) - } -} - -struct BotDataRequest(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) - } -} - -#[derive(Template)] -#[template(path = "index.htm")] -struct OverviewTemplate<'a> { - bots: &'a [BotData], -} - -#[derive(Template)] -#[template(path = "tmtu/index.htm")] -struct TmtuTemplate { - bot_names: Vec<String>, - bot: Option<BotData>, -} - #[derive(Debug, Serialize)] pub struct BotData { pub name: String, @@ -139,96 +74,22 @@ pub struct BotData { } #[get("/")] -async fn index(bot: web::Data<Addr<BotExecutor>>, front: FrontEnd) -> Result<HttpResponse, Error> { +async fn index(bot: web::Data<Addr<BotExecutor>>, front: FrontEnd) -> impl Responder { match front { - FrontEnd::Lazy => lazy_index(bot).await, - FrontEnd::Tmtu => tmtu_index(bot).await, - } -} - -async fn lazy_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() -} - -async fn tmtu_index(bot: web::Data<Addr<BotExecutor>>) -> Result<HttpResponse, Error> { - let bot_names = bot.send(BotNameListRequest).await.unwrap().unwrap(); - - TmtuTemplate { - bot_names, - bot: None, + FrontEnd::Default => default::index(bot).await, + FrontEnd::Tmtu => tmtu::index(bot).await, } - .into_response() } -#[get("/tmtu/{name}")] -async fn tmtu_bot( +#[get("/bot/{name}")] +async fn get_bot( bot: web::Data<Addr<BotExecutor>>, name: web::Path<String>, front: FrontEnd, -) -> Result<HttpResponse, Error> { - if front != FrontEnd::Tmtu { - return Ok(HttpResponse::Found().header(header::LOCATION, "/").finish()); - } - - let bot_names = bot.send(BotNameListRequest).await.unwrap().unwrap(); - if let Some(bot) = bot.send(BotDataRequest(name.into_inner())).await.unwrap() { - TmtuTemplate { - bot_names, - bot: Some(bot), - } - .into_response() - } else { - Ok(HttpResponse::Found().header(header::LOCATION, "/").finish()) - } -} - -#[get("/bots")] -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) -} - -#[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"), - }), - } - } -} - -#[get("/bots/{name}")] -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) +) -> impl Responder { + match front { + FrontEnd::Tmtu => tmtu::get_bot(bot, name.into_inner()).await, + FrontEnd::Default => Ok(HttpResponse::Found().header(header::LOCATION, "/").finish()), } } 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 index 0207933..4812d0d 100644 --- a/src/web_server/front_end_cookie.rs +++ b/src/web_server/front_end_cookie.rs @@ -10,7 +10,7 @@ use serde::Deserialize; #[derive(PartialEq, Deserialize)] #[serde(rename_all = "lowercase")] pub enum FrontEnd { - Lazy, + Default, Tmtu, } @@ -19,7 +19,7 @@ impl FrontEnd { fn cookie(&self) -> String { let name = match self { - FrontEnd::Lazy => "lazy", + FrontEnd::Default => "default", FrontEnd::Tmtu => "tmtu", }; @@ -39,7 +39,7 @@ impl FromRequest for FrontEnd { let mut split = c.split('='); if Some(Self::COOKIE_NAME) == split.next() { match split.next() { - Some("lazy") => return ok(FrontEnd::Lazy), + Some("default") => return ok(FrontEnd::Default), Some("tmtu") => return ok(FrontEnd::Tmtu), _ => (), } @@ -48,7 +48,7 @@ impl FromRequest for FrontEnd { } } - ok(FrontEnd::Lazy) + ok(FrontEnd::Default) } } 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()) + } +} diff --git a/src/youtube_dl.rs b/src/youtube_dl.rs index 99e50e7..89b1477 100644 --- a/src/youtube_dl.rs +++ b/src/youtube_dl.rs @@ -15,22 +15,11 @@ pub struct AudioMetadata { pub title: String, pub thumbnail: Option<String>, #[serde(default, deserialize_with = "duration_deserialize")] - #[serde(serialize_with = "duration_serialize")] pub duration: Option<Duration>, #[serde(skip)] pub added_by: String, } -fn duration_serialize<S>(d: &Option<Duration>, s: S) -> Result<S::Ok, S::Error> -where - S: serde::Serializer, -{ - match d { - Some(d) => s.serialize_some(&d.as_secs_f64()), - None => s.serialize_none(), - } -} - fn duration_deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error> where D: serde::Deserializer<'de>, |
