From 761e1daf23b83a6795e2cdf38e6b851af439ed70 Mon Sep 17 00:00:00 2001 From: Artemis Date: Sat, 8 Feb 2025 18:18:35 +0100 Subject: [PATCH] added qrcode on the tags --- CHANGELOG.md | 18 +++++- Cargo.lock | 99 +++++++++++++++++++++++++++++++ Cargo.toml | 1 + Rocket.toml | 2 + src/config.rs | 7 +++ src/main.rs | 4 ++ src/pages.rs | 8 ++- src/routes/account.rs | 14 ++++- src/routes/error_handlers.rs | 8 +++ templates/account/index.html.tera | 7 ++- 10 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 src/config.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0583a..c09b725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,26 @@ This will try to keep history of all changes and document impacts in deployment / hosting. All versions before v1.1.0 are ignored as the tool was only ready-ish at v1.1.0 -## [next] +## v2.0.0 +breaking change: configuration changes + +- added*: qrcode generation of tags - added: admin prod deployment instructions +the qrcode generation of tags introduces a new required configuration key, `default.public_url`. +This should contain the canonical base URL of your instance (e.g. `https://dolltags.pet/`). + +Example below. + +```toml +[default] +secret_key=... +public_url="https://dolltags.pet/" + +[...] +``` + ## v1.3.0 - 2025-02-07 - added: social tags; sharing in a social network should give a small preview of the base details diff --git a/Cargo.lock b/Cargo.lock index 94c1b9b..8f9bfb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.10.0" @@ -472,6 +478,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.14" @@ -631,6 +646,7 @@ dependencies = [ "chrono", "clap", "orion", + "qrcode-generator", "rand", "regex", "rocket", @@ -704,6 +720,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -736,6 +761,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.1" @@ -1054,6 +1089,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "http" version = "0.2.12" @@ -1310,6 +1354,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -1539,6 +1595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1932,6 +1989,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polyval" version = "0.6.2" @@ -1981,6 +2051,23 @@ dependencies = [ "yansi", ] +[[package]] +name = "qrcode-generator" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0051849b5465059b75f59d388c7318aad6554701b74ecf02afc2573b0306c" +dependencies = [ + "html-escape", + "image", + "qrcodegen", +] + +[[package]] +name = "qrcodegen" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142" + [[package]] name = "quote" version = "1.0.38" @@ -2434,6 +2521,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "siphasher" version = "1.0.1" @@ -3256,6 +3349,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index 2d659d5..687f91b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ anyhow = "1.0" orion = "0.17" uuid = { version = "1.11", features = ["serde"] } clap = { version = "4.5", features = ["derive"] } +qrcode-generator = "5" diff --git a/Rocket.toml b/Rocket.toml index c9f4df8..5daaaa0 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,4 +1,6 @@ [default] secret_key = "8STDFCStGMYGoOq8RJf3JJXsg4p6wZVAph50R3Fbq6U=" +public_url = "http://localhost:8000/" + [default.databases.dolltags] url = "postgres://postgres:woofwoof@localhost/dolltags" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b6b8265 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,7 @@ +use rocket::serde::Deserialize; + +#[derive(Debug, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct Config { + pub public_url: String, +} diff --git a/src/main.rs b/src/main.rs index 20f9913..876853c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use auth::session; use cli::check_cli_invocation; +use config::Config; use db::migrate::run_migrations; use db::schema::DollTags; use rocket::fairing::AdHoc; @@ -16,6 +17,7 @@ use routes::{account, admin, error_handlers, form, public}; pub mod auth; pub mod cli; +pub mod config; pub mod db; pub mod ids; pub mod pages; @@ -49,6 +51,7 @@ fn rocket() -> _ { rocket::build() .attach(pages::init_templates()) .attach(DollTags::init()) + .attach(AdHoc::config::()) .attach(AdHoc::try_on_ignite("SQLx migrations", run_migrations)) .attach(AdHoc::on_liftoff("CLI invocation hack", |rocket| { Box::pin(async move { check_cli_invocation(rocket).await }) @@ -66,6 +69,7 @@ fn rocket() -> _ { "/account", routes![ account::index, + account::qr_profile, account::show_settings, account::change_settings, account::change_password, diff --git a/src/pages.rs b/src/pages.rs index 80ca95c..8f6c8e7 100644 --- a/src/pages.rs +++ b/src/pages.rs @@ -5,12 +5,12 @@ use rocket::{ outcome::try_outcome, request::{FromRequest, Outcome}, serde::Serialize, - Request, + Request, State, }; use rocket_dyn_templates::{tera::try_get_value, Template}; use serde_json::{to_value, Value}; -use crate::{auth::session::Session, db::schema::User, ids}; +use crate::{auth::session::Session, config::Config, db::schema::User, ids}; pub fn init_templates() -> impl Fairing { Template::custom(|engines| { @@ -36,6 +36,8 @@ pub struct CommonTemplateState { pub is_admin: bool, /// feature flag - disables the UI for it since it's not implemeted yet. pub forgot_password_implemented: bool, + /// the website's public base URL (used for canonical URLs) + pub public_url: String, } impl CommonTemplateState { @@ -55,11 +57,13 @@ impl<'r> FromRequest<'r> for CommonTemplateState { async fn from_request(request: &'r Request<'_>) -> Outcome { let session_state = try_outcome!(request.guard::().await); + let config_state = try_outcome!(request.guard::<&State>().await); Outcome::Success(CommonTemplateState { logged_in: session_state.0.is_some(), is_admin: false, forgot_password_implemented: false, + public_url: config_state.public_url.clone(), }) } } diff --git a/src/routes/account.rs b/src/routes/account.rs index 0f5644a..cc2e05f 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -1,17 +1,20 @@ use std::net::IpAddr; +use qrcode_generator::QrCodeEcc; use rocket::{ form::{self, Contextual, Error, Form}, - http::CookieJar, + http::{uri::Absolute, CookieJar}, response::Redirect, serde::{json::Json, Serialize}, tokio::task, + State, }; use rocket_dyn_templates::{context, Template}; use sqlx::Acquire; use crate::{ auth::{pw, session}, + config::Config, db::{ doll, schema::{DollProfile, DollTagsDb, User}, @@ -39,6 +42,15 @@ pub async fn index(mut db: DollTagsDb, user: User, meta: CommonTemplateState) -> .into()) } +#[get("/qr-png/")] +pub fn qr_profile(id: &str, _user: User, config: &State) -> PageResult { + let public_uri = Absolute::parse(&config.public_url)?; + let built_uri = uri!(public_uri, crate::routes::public::short_url(id)); + let image = qrcode_generator::to_png_to_vec(built_uri.to_string(), QrCodeEcc::Low, 400)?; + + Ok(image.into()) +} + #[get("/settings")] pub fn show_settings(user: User, meta: CommonTemplateState) -> Template { Template::render( diff --git a/src/routes/error_handlers.rs b/src/routes/error_handlers.rs index ed5b993..1ae3229 100644 --- a/src/routes/error_handlers.rs +++ b/src/routes/error_handlers.rs @@ -10,6 +10,8 @@ pub async fn not_found() -> Template { pub enum PageResponse { Page(Template), Redirect(Redirect), + #[response(content_type = "image/png")] + Image(Vec), } impl From