src/server/hx/mod.rs
Rama main · réplica de lectura del repositorio original.
use askama::Template;
use axum::{Router, routing::get};
use chrono::{DateTime, Utc};
use render::{RenderResult, TemplatedResponse};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::server::AppState;
use crate::{PathFinder, db::Filter, db::MediaRow, db::Order};
use std::path::Path;
mod actions;
mod bar;
mod gallery;
mod preview;
mod render;
mod sse;
#[derive(Clone)]
pub struct Media {
pub uuid: Uuid,
pub url: String,
pub name: String,
pub thumbnail: String,
pub path: String,
pub favorite: bool,
pub video: bool,
pub taken_at: DateTime<Utc>,
}
impl From<(MediaRow, &PathFinder)> for Media {
fn from(value: (MediaRow, &PathFinder)) -> Self {
let name = Path::new(&value.0.path)
.file_name()
.and_then(|s| s.to_str())
.unwrap_or(&value.0.path)
.to_string();
Self {
uuid: value.0.id,
url: value.1.media(&value.0.path),
name,
thumbnail: value.1.thumbnail(&value.0.id),
path: value.0.path,
favorite: value.0.favorite,
video: value.0.media_type == 1,
taken_at: value.0.taken_at,
}
}
}
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub(super) struct GalleryState {
favorite: Option<bool>,
collection: Option<String>,
year: Option<usize>,
month: Option<u8>,
new_to_old: Option<bool>,
}
impl GalleryState {
pub fn as_query(&self) -> String {
serde_urlencoded::to_string(self).unwrap_or_default()
}
}
fn to_timestamp(s: &str) -> DateTime<Utc> {
DateTime::parse_from_rfc3339(s)
.expect("malformed timestamp")
.with_timezone(&Utc)
}
impl From<&GalleryState> for Filter {
fn from(value: &GalleryState) -> Self {
let (start, end) = value.year.map_or((None, None), |year| {
let start = to_timestamp(&format!(
"{}-{:02}-01T00:00:00-00:00",
year,
value.month.unwrap_or(1)
));
let next_year = format!("{}-01-01T00:00:00-00:00", year + 1);
let end = to_timestamp(&match value.month {
None => next_year,
Some(month) => {
if month == 12 {
next_year
} else {
format!("{}-{:02}-01T00:00:00-00:00", year, month + 1)
}
}
});
(Some(start), Some(end))
});
let favorite = if matches!(value.favorite, Some(true)) {
Some(true)
} else {
None
};
Self {
archived: false,
favorite,
taken_after: start,
taken_before: end,
collection: value.collection.clone(),
}
}
}
impl From<&GalleryState> for Order {
fn from(value: &GalleryState) -> Self {
match value.new_to_old {
Some(true) | None => Self::Desc,
Some(false) => Self::Asc,
}
}
}
#[derive(Deserialize, Serialize)]
struct Cursor {
cursor: DateTime<Utc>,
}
impl From<(&GalleryState, &Cursor)> for Filter {
fn from(value: (&GalleryState, &Cursor)) -> Self {
let mut filter: Self = value.0.into();
let order: Order = value.0.into();
match order {
Order::Asc => filter.taken_after = Some(value.1.cursor),
Order::Desc => filter.taken_before = Some(value.1.cursor),
}
filter
}
}
const fn dev_enabled() -> bool {
#[cfg(feature = "dev")]
let dev = true;
#[cfg(not(feature = "dev"))]
let dev = false;
dev
}
#[derive(Template)]
#[template(path = "web/index.html")]
struct HxIndex {}
async fn index() -> RenderResult {
HxIndex {}.render_response()
}
pub fn create_router() -> Router<AppState> {
Router::new()
.route("/", get(index))
.nest("/action", actions::create_router())
.nest("/bar", bar::create_router())
.nest("/gallery", gallery::create_router())
.nest("/preview", preview::create_router())
.nest("/sse", sse::create_router())
}