diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/listing.rs | 26 | ||||
-rw-r--r-- | src/renderer.rs | 95 |
2 files changed, 66 insertions, 55 deletions
diff --git a/src/listing.rs b/src/listing.rs index bc8dcfb..b000649 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -9,14 +9,13 @@ use actix_web::{HttpMessage, HttpRequest, HttpResponse}; use bytesize::ByteSize; use comrak::{markdown_to_html, ComrakOptions}; use percent_encoding::{percent_decode_str, utf8_percent_encode}; -use qrcode::QrCode; use serde::Deserialize; use strum_macros::{Display, EnumString}; use crate::archive::ArchiveMethod; use crate::auth::CurrentUser; use crate::errors::{self, ContextualError}; -use crate::{consts, renderer}; +use crate::renderer; use self::percent_encode_sets::PATH_SEGMENT; @@ -38,7 +37,6 @@ pub struct QueryParameters { pub order: Option<SortingOrder>, pub raw: Option<bool>, pub mkdir_name: Option<String>, - qrcode: Option<String>, download: Option<ArchiveMethod>, } @@ -166,6 +164,13 @@ pub fn directory_listing( let base = Path::new(serve_path); let random_route_abs = format!("/{}", conf.route_prefix); + let abs_url = format!( + "{}://{}{}", + req.connection_info().scheme(), + req.connection_info().host(), + req.uri() + ); + dbg!(&abs_url); let is_root = base.parent().is_none() || Path::new(&req.path()) == Path::new(&random_route_abs); let encoded_dir = match base.strip_prefix(random_route_abs) { @@ -218,20 +223,6 @@ pub fn directory_listing( let query_params = extract_query_parameters(req); - // If the `qrcode` parameter is included in the url, then should respond to the QR code - if let Some(url) = query_params.qrcode { - let res = match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { - Ok(qr) => HttpResponse::Ok() - .content_type(mime::TEXT_HTML_UTF_8) - .body(renderer::qr_code_page(&breadcrumbs, &qr, conf).into_string()), - Err(err) => { - log::error!("URL is invalid (too long?): {:?}", err); - HttpResponse::UriTooLong().finish() - } - }; - return Ok(ServiceResponse::new(req.clone(), res)); - } - let mut entries: Vec<Entry> = Vec::new(); let mut readme: Option<(String, String)> = None; @@ -384,6 +375,7 @@ pub fn directory_listing( renderer::page( entries, readme, + abs_url, is_root, query_params, &breadcrumbs, diff --git a/src/renderer.rs b/src/renderer.rs index 0154ce0..614672f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,12 +2,15 @@ use actix_web::http::StatusCode; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use clap::{crate_name, crate_version}; +use lazy_static::lazy_static; use maud::{html, Markup, PreEscaped, DOCTYPE}; -use qrcode::QrCode; +use qrcode::{types::QrError, QrCode}; +use regex::Regex; use std::time::SystemTime; use strum::IntoEnumIterator; use crate::auth::CurrentUser; +use crate::consts; use crate::listing::{Breadcrumb, Entry, QueryParameters, SortingMethod, SortingOrder}; use crate::{archive::ArchiveMethod, MiniserveConfig}; @@ -16,6 +19,7 @@ use crate::{archive::ArchiveMethod, MiniserveConfig}; pub fn page( entries: Vec<Entry>, readme: Option<(String, String)>, + abs_url: impl AsRef<str>, is_root: bool, query_params: QueryParameters, breadcrumbs: &[Breadcrumb], @@ -86,7 +90,10 @@ pub fn page( } } } - (color_scheme_selector(conf.show_qrcode, conf.hide_theme_selector)) + nav { + (qr_spoiler(conf.show_qrcode, abs_url)) + (color_scheme_selector(conf.hide_theme_selector)) + } div.container { span #top { } h1.title dir="ltr" { @@ -221,25 +228,30 @@ pub fn raw(entries: Vec<Entry>, is_root: bool) -> Markup { } } -/// Renders the QR code page -pub fn qr_code_page(breadcrumbs: &[Breadcrumb], qr: &QrCode, conf: &MiniserveConfig) -> Markup { +/// Renders the QR code SVG +fn qr_code_svg(url: impl AsRef<str>, no_width_height_attr: bool) -> Result<String, QrError> { use qrcode::render::svg; - - let title = breadcrumbs_to_path_string(breadcrumbs); - - html! { - (DOCTYPE) - html.qr_code_page { - (page_header(&title, false, &conf.favicon_route, &conf.css_route)) - body { - (PreEscaped(qr.render() - .quiet_zone(false) - .dark_color(svg::Color("#000000")) - .light_color(svg::Color("#ffffff")) - .build())) - } + let qr = QrCode::with_error_correction_level(url.as_ref(), consts::QR_EC_LEVEL)?; + let mut svg = qr + .render() + .quiet_zone(false) + .dark_color(svg::Color("#000000")) + .light_color(svg::Color("#ffffff")) + .build(); + + if no_width_height_attr { + // HACK: qrcode crate hard-codes height and width into SVG's attributes. + // This behaviour may be undesirable because we want it to fit its HTML container. + // The proper way to remove them is to use a XML parser, but regex is good enough for a + // simple case like this. + lazy_static! { + static ref RE: Regex = + Regex::new(r#"(?P<front><svg.+? )width=".+?" height=".+?"(?P<aft>.+?>)"#).unwrap(); } + svg = RE.replace(&svg, "$front$aft").to_string(); } + + Ok(svg) } /// Build a path string from a list of breadcrumbs. @@ -317,30 +329,37 @@ const THEME_PICKER_CHOICES: &[(&str, &str)] = &[ pub const THEME_SLUGS: &[&str] = &["squirrel", "archlinux", "zenburn", "monokai"]; -/// Partial: color scheme selector -fn color_scheme_selector(show_qrcode: bool, hide_theme_selector: bool) -> Markup { +/// Partial: qr code spoiler +fn qr_spoiler(show_qrcode: bool, content: impl AsRef<str>) -> Markup { html! { - nav { - @if show_qrcode { - div { - p onmouseover="document.querySelector('#qrcode').src = `?qrcode=${encodeURIComponent(window.location.href)}`" { - "QR code" - } - div.qrcode { - img #qrcode alt="QR code" title="QR code of this page"; + @if show_qrcode { + div { + p { + "QR code" + } + div.qrcode #qrcode { + @match qr_code_svg(content, true) { + Ok(svg) => (PreEscaped(svg)), + Err(err) => (format!("QR generation error: {}", err)), } } } - @if !hide_theme_selector { - div { - p { - "Change theme..." - } - ul.theme { - @for color_scheme in THEME_PICKER_CHOICES { - li.(format!("theme_{}", color_scheme.1)) { - (color_scheme_link(color_scheme)) - } + } + } +} + +/// Partial: color scheme selector +fn color_scheme_selector(hide_theme_selector: bool) -> Markup { + html! { + @if !hide_theme_selector { + div { + p { + "Change theme..." + } + ul.theme { + @for color_scheme in THEME_PICKER_CHOICES { + li.(format!("theme_{}", color_scheme.1)) { + (color_scheme_link(color_scheme)) } } } |