hermod_api/handlers/
qr_code.rs

1use actix_web::{web, HttpRequest, HttpResponse};
2use sqlx::PgPool;
3use uuid::Uuid;
4
5use super::ApplicationResponse;
6use crate::{
7    db::QrCode,
8    handlers::{json_response, ApplicationError},
9    services::auth::AuthenticationError,
10    services::jwt::JwtClient,
11};
12use serde::Deserialize;
13use tracing::field::Empty;
14
15#[derive(Deserialize)]
16pub struct GetQrCodeRequest {
17    pub slug: String,
18}
19
20#[derive(serde::Serialize)]
21pub struct GetQrCodeResponse {
22    pub generation_data: String,
23}
24
25#[tracing::instrument(name = "handlers::qr_code::get", skip(pool, query))]
26/// get(qr_code?slug={SLUG}) runs a sample SQL query and checks if the user is logged in
27pub async fn get_qr_code_data(
28    pool: web::Data<PgPool>,
29    query: web::Query<GetQrCodeRequest>,
30) -> ApplicationResponse {
31    if let Some(qr_code) = sqlx::query!("SELECT * FROM qr_code WHERE slug=$1", &query.slug)
32        .fetch_optional(pool.as_ref())
33        .await?
34    {
35        json_response(&GetQrCodeResponse {
36            generation_data: qr_code.generation_data,
37        })
38    } else {
39        Err(ApplicationError::NotFoundError(format!(
40            "No QR code found with slug {}",
41            &query.slug
42        )))
43    }
44}
45
46#[derive(Deserialize, Clone)]
47pub struct EditQrCodeRequest {
48    pub id: Uuid,
49    pub generation_data: String,
50    pub slug: String,
51}
52
53#[tracing::instrument(name = "handlers::qr_code::edit", skip(pool, query, jwt), fields(username=Empty, user_id=Empty))]
54/// get(/qr_code/edit?id={ID}&generation_data={DATA}&slug={SLUG}) edits a QR code with the relevant information
55pub async fn edit_qr_code(
56    pool: web::Data<PgPool>,
57    query: web::Query<EditQrCodeRequest>,
58    request: HttpRequest,
59    jwt: web::Data<JwtClient>,
60) -> ApplicationResponse {
61    let user = jwt.user_or_403(request).await?;
62    tracing::Span::current().record("username", &tracing::field::display(&user.username));
63    tracing::Span::current().record("user_id", &tracing::field::display(&user.id));
64
65    let query = sqlx::query!(
66        r#"
67            UPDATE qr_code
68            SET generation_data=$2, slug=$3
69            WHERE id=$1 AND account_id=$4
70            RETURNING true
71        "#,
72        query.id,
73        query.generation_data,
74        query.slug,
75        user.id
76    )
77    .fetch_optional(pool.as_ref())
78    .await?;
79
80    if query.is_some() {
81        Ok(HttpResponse::Ok().finish())
82    } else {
83        Err(ApplicationError::AuthError(
84            AuthenticationError::Unauthorized,
85        ))
86    }
87}
88
89#[derive(Deserialize, Clone)]
90pub struct DeleteQrCodeRequest {
91    pub id: Uuid,
92}
93
94#[tracing::instrument(name = "handlers::qr_code::delete", skip(pool, query, jwt), fields(username=Empty, user_id=Empty))]
95/// get(/qr_code/delete?id={ID}) edits a QR code with the relevant information
96pub async fn delete_qr_code(
97    pool: web::Data<PgPool>,
98    query: web::Query<EditQrCodeRequest>,
99    request: HttpRequest,
100    jwt: web::Data<JwtClient>,
101) -> ApplicationResponse {
102    let user = jwt.user_or_403(request).await?;
103    tracing::Span::current().record("username", &tracing::field::display(&user.username));
104    tracing::Span::current().record("user_id", &tracing::field::display(&user.id));
105
106    let query = sqlx::query!(
107        r#"
108            DELETE FROM qr_code
109            WHERE id=$1 AND account_id=$2
110            RETURNING true
111        "#,
112        query.id,
113        user.id
114    )
115    .fetch_optional(pool.as_ref())
116    .await?;
117    if query.is_some() {
118        Ok(HttpResponse::Ok().finish())
119    } else {
120        Err(ApplicationError::AuthError(
121            AuthenticationError::Unauthorized,
122        ))
123    }
124}
125
126#[derive(Deserialize, Clone)]
127pub struct NewQrCodeRequest {
128    pub generation_data: String,
129    pub slug: String,
130}
131
132#[tracing::instrument(name = "hadlers::qr_code::store", skip(pool, query, jwt), fields(username=Empty, user_id=Empty))]
133/// get(/qr_code/store?generation_data={DATA}&slug={SLUG}) stores a QR code with the relevant information
134pub async fn store_qr_code(
135    pool: web::Data<PgPool>,
136    query: web::Query<NewQrCodeRequest>,
137    request: HttpRequest,
138    jwt: web::Data<JwtClient>,
139) -> ApplicationResponse {
140    let user = jwt.user_or_403(request).await?;
141    tracing::Span::current().record("username", &tracing::field::display(&user.username));
142    tracing::Span::current().record("user_id", &tracing::field::display(&user.id));
143
144    sqlx::query!(
145        r#"
146            INSERT INTO qr_code (id, account_id, slug, generation_data)
147            VALUES ($1, $2, $3, $4)"#,
148        Uuid::new_v4(),
149        user.id,
150        query.slug,
151        query.generation_data
152    )
153    .execute(pool.as_ref())
154    .await?;
155    Ok(HttpResponse::Ok().finish())
156}
157
158#[derive(serde::Serialize)]
159pub struct ListQrCodesResponse {
160    pub qr_codes: Vec<QrCode>,
161}
162
163#[tracing::instrument(name = "handlers::qr_code::list", skip(pool, request, jwt))]
164/// get(/qr_codes) lists QR codes assosciated with a given user
165pub async fn list_qr_codes(
166    pool: web::Data<PgPool>,
167    request: HttpRequest,
168    jwt: web::Data<JwtClient>,
169) -> ApplicationResponse {
170    let user = jwt.user_or_403(request).await?;
171
172    let qr_codes = sqlx::query_as!(
173        QrCode,
174        r#"
175            SELECT * FROM qr_code
176            WHERE account_id=$1"#,
177        user.id,
178    )
179    .fetch_all(pool.as_ref())
180    .await?;
181    json_response(&ListQrCodesResponse { qr_codes })
182}