m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-14 08:03:48 -05:00

Changed frontend from Liquid to React, fixed bug where server would check for updates twice

This commit is contained in:
Sergio
2024-09-01 19:52:20 +03:00
parent e7673c04db
commit 2f195f611c
34 changed files with 623 additions and 515 deletions

View File

@@ -2,11 +2,10 @@ use std::sync::Arc;
use chrono::Local;
use json::JsonValue;
use liquid::{object, Object};
use tokio::sync::Mutex;
use xitca_web::{
body::ResponseBody,
handler::{handler_service, state::StateRef},
handler::{handler_service, path::PathRef, state::StateRef},
http::WebResponse,
middleware::Logger,
route::get,
@@ -19,23 +18,22 @@ use crate::{
utils::{sort_update_vec, to_json},
};
const RAW_TEMPLATE: &str = include_str!("static/template.liquid");
const STYLE: &str = include_str!("static/index.css");
const HTML: &str = include_str!("static/index.html");
const JS: &str = include_str!("static/assets/index.js");
const CSS: &str = include_str!("static/assets/index.css");
const FAVICON_ICO: &[u8] = include_bytes!("static/favicon.ico");
const FAVICON_SVG: &[u8] = include_bytes!("static/favicon.svg");
const APPLE_TOUCH_ICON: &[u8] = include_bytes!("static/apple-touch-icon.png");
pub async fn serve(port: &u16, socket: Option<String>, config: JsonValue) -> std::io::Result<()> {
let mut data = ServerData::new(socket, config).await;
data.refresh().await;
println!("Starting server, please wait...");
let data = ServerData::new(socket, config).await;
App::new()
.with_state(Arc::new(Mutex::new(data)))
.at("/", get(handler_service(home)))
.at("/", get(handler_service(_static)))
.at("/json", get(handler_service(json)))
.at("/refresh", get(handler_service(refresh)))
.at("/favicon.ico", handler_service(favicon_ico)) // These aren't pretty but this is xitca-web...
.at("/favicon.svg", handler_service(favicon_svg))
.at("/apple-touch-icon.png", handler_service(apple_touch_icon))
.at("/*", get(handler_service(_static)))
.enclosed(Logger::new())
.serve()
.bind(format!("0.0.0.0:{}", port))?
@@ -43,8 +41,16 @@ pub async fn serve(port: &u16, socket: Option<String>, config: JsonValue) -> std
.wait()
}
async fn home(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
WebResponse::new(ResponseBody::from(data.lock().await.template.clone()))
async fn _static(data: StateRef<'_, Arc<Mutex<ServerData>>>, path: PathRef<'_>) -> WebResponse {
match path.0 {
"/" => WebResponse::builder().header("Content-Type", "text/html").body(ResponseBody::from(HTML)).unwrap(),
"/assets/index.js" => WebResponse::builder().header("Content-Type", "text/javascript").body(ResponseBody::from(JS.replace("=\"neutral\"", &format!("=\"{}\"", data.lock().await.theme)))).unwrap(),
"/assets/index.css" => WebResponse::builder().header("Content-Type", "text/css").body(ResponseBody::from(CSS)).unwrap(),
"/favicon.ico" => WebResponse::builder().header("Content-Type", "image/vnd.microsoft.icon").body(ResponseBody::from(FAVICON_ICO)).unwrap(),
"/favicon.svg" => WebResponse::builder().header("Content-Type", "image/svg+xml").body(ResponseBody::from(FAVICON_SVG)).unwrap(),
"/apple-touch-icon.png" => WebResponse::builder().header("Content-Type", "image/png").body(ResponseBody::from(APPLE_TOUCH_ICON)).unwrap(),
_ => WebResponse::builder().status(404).body(ResponseBody::from("Not found")).unwrap()
}
}
async fn json(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
@@ -58,37 +64,25 @@ async fn refresh(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
WebResponse::new(ResponseBody::from("OK"))
}
async fn favicon_ico() -> WebResponse {
WebResponse::new(ResponseBody::from(FAVICON_ICO))
}
async fn favicon_svg() -> WebResponse {
WebResponse::new(ResponseBody::from(FAVICON_SVG))
}
async fn apple_touch_icon() -> WebResponse {
WebResponse::new(ResponseBody::from(APPLE_TOUCH_ICON))
}
struct ServerData {
template: String,
raw_updates: Vec<(String, Option<bool>)>,
json: JsonValue,
socket: Option<String>,
config: JsonValue,
theme: &'static str
}
impl ServerData {
async fn new(socket: Option<String>, config: JsonValue) -> Self {
let mut s = Self {
socket,
template: String::new(),
json: json::object! {
metrics: json::object! {},
images: json::object! {},
},
raw_updates: Vec::new(),
config,
theme: "neutral"
};
s.refresh().await;
s
@@ -96,22 +90,10 @@ impl ServerData {
async fn refresh(&mut self) {
let updates = sort_update_vec(&get_all_updates(self.socket.clone(), &self.config["authentication"]).await);
self.raw_updates = updates;
let template = liquid::ParserBuilder::with_stdlib()
.build()
.unwrap()
.parse(RAW_TEMPLATE)
.unwrap();
let images = self
.raw_updates
.iter()
.map(|(name, has_update)| match has_update {
Some(v) => object!({"name": name, "has_update": v.to_string()}), // Liquid kinda thinks false == nil, so we'll be comparing strings from now on
None => object!({"name": name, "has_update": "null"}),
})
.collect::<Vec<Object>>();
self.json = to_json(&self.raw_updates);
let last_updated = Local::now().format("%Y-%m-%d %H:%M:%S");
let theme = match &self.config["theme"].as_str() {
let last_updated = Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
self.json["last_updated"] = last_updated.to_string().into();
self.theme = match &self.config["theme"].as_str() {
Some(t) => match *t {
"default" => "neutral",
"blue" => "gray",
@@ -122,13 +104,5 @@ impl ServerData {
},
None => "neutral",
};
let globals = object!({
"metrics": [{"name": "Monitored images", "value": self.json["metrics"]["monitored_images"].as_usize()}, {"name": "Up to date", "value": self.json["metrics"]["up_to_date"].as_usize()}, {"name": "Updates available", "value": self.json["metrics"]["update_available"].as_usize()}, {"name": "Unknown", "value": self.json["metrics"]["unknown"].as_usize()}],
"images": images,
"style": STYLE,
"last_updated": last_updated.to_string(),
"theme": theme
});
self.template = template.render(&globals).unwrap();
}
}