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))]
26pub 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))]
54pub 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))]
95pub 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))]
133pub 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))]
164pub 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}