added qrcode on the tags

This commit is contained in:
Artemis 2025-02-08 18:18:35 +01:00
parent dd26eed497
commit 761e1daf23
10 changed files with 163 additions and 5 deletions

View file

@ -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

99
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -1,4 +1,6 @@
[default]
secret_key = "8STDFCStGMYGoOq8RJf3JJXsg4p6wZVAph50R3Fbq6U="
public_url = "http://localhost:8000/"
[default.databases.dolltags]
url = "postgres://postgres:woofwoof@localhost/dolltags"

7
src/config.rs Normal file
View file

@ -0,0 +1,7 @@
use rocket::serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Config {
pub public_url: String,
}

View file

@ -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::<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,

View file

@ -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<CommonTemplateState, ()> {
let session_state = try_outcome!(request.guard::<Session>().await);
let config_state = try_outcome!(request.guard::<&State<Config>>().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(),
})
}
}

View file

@ -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/<id>")]
pub fn qr_profile(id: &str, _user: User, config: &State<Config>) -> 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(

View file

@ -10,6 +10,8 @@ pub async fn not_found() -> Template {
pub enum PageResponse {
Page(Template),
Redirect(Redirect),
#[response(content_type = "image/png")]
Image(Vec<u8>),
}
impl From<Template> for PageResponse {
@ -24,6 +26,12 @@ impl From<Redirect> for PageResponse {
}
}
impl From<Vec<u8>> for PageResponse {
fn from(value: Vec<u8>) -> Self {
PageResponse::Image(value)
}
}
#[derive(Responder)]
#[response(status = 500)]
pub struct Fail(Template);

View file

@ -35,6 +35,11 @@
<a href="/account/edit_tag/{{profile.id}}">Edit</a>
<a href="/account/delete/{{profile.id}}">Delete</a>
</div>
<details>
<summary>show the QR code</summary>
<img loading="lazy" src="/account/qr-png/{{profile.id}}" alt="A QrCode containing a direct link to the profile" />
</details>
</article>
{% endfor %}
</section>
@ -52,4 +57,4 @@
</ul>
</section>
{% endif %}
{% endblock main %}
{% endblock main %}