Skip to content

Commit

Permalink
Added hostinfo route and route utils
Browse files Browse the repository at this point in the history
  • Loading branch information
Stridsvagn69420 committed Nov 15, 2022
1 parent a6fdf19 commit 4bafa1e
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/album.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{Artist, Metadata, add_vec, remove_vec};
/// Album
///
/// A struct representing an album of the Cyrkensia repository.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Album {
/// Name
///
Expand Down
2 changes: 1 addition & 1 deletion src/artist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::{Owner, add_vec, remove_vec};
/// Artist
///
/// A struct representing an author or artist of a song.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Artist {
pub name: String,

Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ pub struct Config {

/// Maximum Age
///
/// The maximum age of the [Hostinfo] in milliseconds as a [u32]. If [None], the Hostinfo will always be regenerated when its route is accessed.
/// The maximum age of the [Hostinfo] in milliseconds as a [u64]. If [None], the Hostinfo will always be regenerated when its route is accessed.
/// This basically activates caching.
pub max_age: Option<u32>
pub max_age: Option<u64>
}

impl Config {
Expand Down
6 changes: 3 additions & 3 deletions src/hostinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{Owner, Album, Config, Metadata};
/// Hostinfo
///
/// A struct representing the metadata or hostinfo and index of a Cyrkensia repository.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Hostinfo {
/// Name
///
Expand Down Expand Up @@ -80,13 +80,13 @@ impl Hostinfo {
/// Generate Hostinfo
///
/// Generates a Hostinfo based on a [Config].
pub fn generate(cfg: Config) -> io::Result<Hostinfo> {
pub fn generate(cfg: &Config) -> io::Result<Hostinfo> {
let mut albums: Vec<Album> = Vec::new();
for rootpath in &cfg.root {
let album_slice = Hostinfo::read_albums(rootpath)?;
albums.extend(album_slice);
}
let mut hostinfo = Hostinfo::from(cfg);
let mut hostinfo = Hostinfo::from(cfg.clone());
hostinfo.size = albums.iter().map(|x| x.size).sum();
hostinfo.albums = albums;
Ok(hostinfo)
Expand Down
43 changes: 35 additions & 8 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use actix_web::http::Uri;
use crate::{Hostinfo, Config};
use std::fmt::Write;
use std::sync::Mutex;
use std::time::Instant;
use std::io;
Expand All @@ -19,6 +21,11 @@ pub mod redirect;
/// Submodule containing Cyrkensia routes.
pub mod routes;

/// Responses
///
/// Submodue containing Cyrkensia responses.
pub mod responses;

/// Cyrkensia State
///
/// State for the Actix-Web server. Used in [routes].
Expand All @@ -32,36 +39,56 @@ pub struct CyrkensiaState {
///
/// The latest generated [Hostinfo].
/// Only used if caching is activated.
pub hostinfo: Mutex<Option<Hostinfo>>,
pub hostinfo: Mutex<Hostinfo>,

/// Last Hostinfo Update
///
/// The [timestamp](Instant) when the [hostinfo] was last updated.
/// `.elapsed().as_secs()` will be used to compare it with the `max_age` in the [Config].
/// Only used if caching is activated.
pub last_updated: Mutex<Option<Instant>>
pub last_updated: Mutex<Instant>
}

impl CyrkensiaState {
/// Constructur
///
/// Creates a new [CyrkensiaState] with given [Config].
pub fn new(cfg: Config) -> io::Result<CyrkensiaState> {
// State with caching
if cfg.max_age.is_some() {
// State with caching
let hostinfo = Hostinfo::generate(cfg.clone())?;
let hostinfo = Hostinfo::generate(&cfg)?;
return Ok(CyrkensiaState {
last_updated: Mutex::new(Some(Instant::now())),
hostinfo: Mutex::new(Some(hostinfo)),
last_updated: Mutex::new(Instant::now()),
hostinfo: Mutex::new(hostinfo),
config: cfg
});
}

// State without caching
Ok(CyrkensiaState {
hostinfo: Mutex::new(None),
last_updated: Mutex::new(None),
hostinfo: Mutex::new(Hostinfo::empty()),
last_updated: Mutex::new(Instant::now()),
config: cfg,
})
}
}

/// Uri Display without Query
///
/// Displays a Uri without the query parameters
pub fn uri_noquery(uri: &Uri) -> String {
let mut f = String::new();

// Protocol
if let Some(scheme) = uri.scheme() {
let _ = write!(&mut f, "{}://", scheme);
}
// Server
if let Some(authority) = uri.authority() {
let _ = write!(&mut f, "{}", authority);
}
// Path
let _ = write!(&mut f, "{}", uri.path());

f
}
41 changes: 41 additions & 0 deletions src/server/responses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::io;
use actix_web::{HttpResponse, body::MessageBody};
use actix_web::http::header::{ContentType, CONTENT_LENGTH};
use crate::{Config, Hostinfo};

/// Hostinfo from Config
///
/// Attempts to create a [HttpResponse] from a generated [Hostinfo] by a [Config].
pub fn hostinfo_json(cfg: &Config) -> io::Result<HttpResponse> {
// Generate Hostinfo
let hostinfo = Hostinfo::generate(cfg)?;
hostinfo_data(&hostinfo)
}

/// Hostinfo from Data
///
/// Attempts to create a [HttpResponse] from a [Hostinfo] struct.
pub fn hostinfo_data(hstinfo: &Hostinfo) -> io::Result<HttpResponse> {
// Convert to String
let raw_json = serde_json::to_string(hstinfo)?;

// Return HttpReponse
Ok(HttpResponse::Ok()
.content_type(ContentType::json())
.append_header(
(CONTENT_LENGTH, raw_json.len())
)
.body(raw_json))
}

/// HTTP Status 500
///
/// Returns a 500 Error with an optional body message
pub fn server_500(msg: Option<impl MessageBody + 'static>) -> HttpResponse {
if let Some(message) = msg {
return HttpResponse::InternalServerError()
.body(message)
}
HttpResponse::InternalServerError()
.finish()
}
47 changes: 47 additions & 0 deletions src/server/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::time::Instant;
use actix_web::{web, Responder, HttpRequest};
use super::{CyrkensiaState, responses, uri_noquery};
use crate::Hostinfo;

/// Hostinfo Route
///
/// Route for serving a [Hostinfo]. Server needs [CyrkensiaState] in `.app_data()` for this.
pub async fn hostinfo(req: HttpRequest, data: web::Data<CyrkensiaState>) -> impl Responder {
// Get config
let Some(delay) = data.config.max_age else {
// Ad hoch Hostinfo
let Ok(resp) = responses::hostinfo_json(&data.config) else {
return responses::server_500(Some("Failed to generate hostinfo"));
};
return resp;
};

// Get last update timestamp and cached hostinfo
let Ok(mut last_updated) = data.last_updated.lock() else {
return responses::server_500(None::<String>);
};
let Ok(mut hostinfo) = data.hostinfo.lock() else {
return responses::server_500(None::<String>);
};

if last_updated.elapsed().as_secs() >= delay {
// Generate new Hostinfo if expired
let Ok(new_hostinfo) = Hostinfo::generate(&data.config) else {
return responses::server_500(Some("Failed to update hostinfo"));
};

// Update Hostinfo and Timestamp
*hostinfo = new_hostinfo;
*last_updated = Instant::now();
}

// Set Origin URL
let mut final_hostinfo = hostinfo.clone();
final_hostinfo.set_origin(uri_noquery(req.uri()));

// Return final result
let Ok(finalres) = responses::hostinfo_data(&final_hostinfo) else {
return responses::server_500(Some("Failed to generate hostinfo"));
};
finalres
}

0 comments on commit 4bafa1e

Please sign in to comment.