aboutsummaryrefslogtreecommitdiff
path: root/src/routers
diff options
context:
space:
mode:
Diffstat (limited to 'src/routers')
-rw-r--r--src/routers/account.rs30
-rw-r--r--src/routers/queue/access.rs257
-rw-r--r--src/routers/queue/manage.rs78
-rw-r--r--src/routers/queue/mod.rs2
4 files changed, 310 insertions, 57 deletions
diff --git a/src/routers/account.rs b/src/routers/account.rs
index 51ce911..e39327a 100644
--- a/src/routers/account.rs
+++ b/src/routers/account.rs
@@ -2,30 +2,23 @@ use axum::extract::State;
2use chrono::{DateTime, Duration, Utc}; 2use chrono::{DateTime, Duration, Utc};
3use entity::users::{self}; 3use entity::users::{self};
4use sea_orm::{ 4use sea_orm::{
5 ActiveModelTrait, ActiveValue::Set, ColumnTrait, DatabaseConnection, EntityTrait, 5 ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, IntoActiveModel, ModelTrait,
6 IntoActiveModel, ModelTrait, QueryFilter, 6 QueryFilter,
7}; 7};
8use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
9use utoipa::ToSchema; 9use utoipa::ToSchema;
10use utoipa_axum::{router::OpenApiRouter, routes}; 10use utoipa_axum::{router::OpenApiRouter, routes};
11 11
12use crate::{ 12use crate::{
13 ApiError, ApiResult, AppState, ClientError, GlobalResponses, JwtClaims, ServerError, 13 ApiResult, AppState, ClientError, GlobalResponses, JwtClaims, ServerError, SuccessResponse,
14 SuccessResponse, create_jwt, create_password, 14 create_jwt, create_password,
15 extract::{ApiJson, Auth}, 15 extract::{ApiJson, Auth},
16 models::Account, 16 models::Account,
17 tags::ACCOUNT, 17 tags::ACCOUNT,
18 util::username_exists,
18 validate_password, 19 validate_password,
19}; 20};
20 21
21async fn username_exists(username: &str, db: &DatabaseConnection) -> Result<bool, ApiError> {
22 Ok(users::Entity::find()
23 .filter(users::Column::Username.eq(username))
24 .one(db)
25 .await?
26 .is_some())
27}
28
29#[derive(Serialize, ToSchema)] 22#[derive(Serialize, ToSchema)]
30#[schema(description = "Authorization token information")] 23#[schema(description = "Authorization token information")]
31struct Token { 24struct Token {
@@ -205,7 +198,7 @@ async fn login(
205} 198}
206 199
207#[utoipa::path( 200#[utoipa::path(
208 put, 201 patch,
209 path = "/update/password", 202 path = "/update/password",
210 tag = ACCOUNT, 203 tag = ACCOUNT,
211 summary = "Change password", 204 summary = "Change password",
@@ -214,7 +207,7 @@ async fn login(
214 responses( 207 responses(
215 ( 208 (
216 status = 200, body = SuccessResponse<Account>, 209 status = 200, body = SuccessResponse<Account>,
217 description = "Success response with the updated account data" 210 description = "Success response with the changed account data"
218 ), 211 ),
219 GlobalResponses 212 GlobalResponses
220 ), 213 ),
@@ -238,7 +231,7 @@ async fn update_password(
238} 231}
239 232
240#[utoipa::path( 233#[utoipa::path(
241 put, 234 patch,
242 path = "/update/username", 235 path = "/update/username",
243 tag = ACCOUNT, 236 tag = ACCOUNT,
244 summary = "Change username", 237 summary = "Change username",
@@ -247,7 +240,7 @@ async fn update_password(
247 responses( 240 responses(
248 ( 241 (
249 status = 200, body = SuccessResponse<Account>, 242 status = 200, body = SuccessResponse<Account>,
250 description = "Success response with the updated account data" 243 description = "Success response with the changed account data"
251 ), 244 ),
252 GlobalResponses 245 GlobalResponses
253 ), 246 ),
@@ -273,7 +266,7 @@ async fn update_username(
273} 266}
274 267
275#[utoipa::path( 268#[utoipa::path(
276 put, 269 patch,
277 path = "/update/name", 270 path = "/update/name",
278 tag = ACCOUNT, 271 tag = ACCOUNT,
279 summary = "Change name", 272 summary = "Change name",
@@ -282,7 +275,7 @@ async fn update_username(
282 responses( 275 responses(
283 ( 276 (
284 status = 200, body = SuccessResponse<Account>, 277 status = 200, body = SuccessResponse<Account>,
285 description = "Success response with the updated account data" 278 description = "Success response with the changed account data"
286 ), 279 ),
287 GlobalResponses 280 GlobalResponses
288 ), 281 ),
@@ -332,7 +325,6 @@ async fn delete(
332 } 325 }
333 326
334 user.clone().delete(&state.db).await?; 327 user.clone().delete(&state.db).await?;
335
336 Ok(SuccessResponse::ok(user.into())) 328 Ok(SuccessResponse::ok(user.into()))
337} 329}
338 330
diff --git a/src/routers/queue/access.rs b/src/routers/queue/access.rs
index 1cba0b1..1aee8da 100644
--- a/src/routers/queue/access.rs
+++ b/src/routers/queue/access.rs
@@ -1,7 +1,260 @@
1use utoipa_axum::router::OpenApiRouter; 1use axum::extract::State;
2use chrono::{DateTime, Utc};
3use entity::invite_tokens;
4use sea_orm::{ActiveModelTrait, ActiveValue::Set, IntoActiveModel, ModelTrait};
5use serde::Deserialize;
6use utoipa::{IntoParams, ToSchema};
7use utoipa_axum::{router::OpenApiRouter, routes};
2 8
3use crate::AppState; 9use crate::{
10 ApiResult, AppState, GlobalResponses, SuccessResponse,
11 extract::{ApiJson, ApiQuery, Auth},
12 models::InviteToken,
13 tags::INVITE_TOKEN,
14 util::{get_owned_invite_token, get_owned_queue},
15};
16
17#[derive(Deserialize, IntoParams)]
18#[into_params(parameter_in = Query)]
19struct GetInviteTokenByIdQuery {
20 #[param(example = 1)]
21 id: i64,
22}
23
24#[derive(Deserialize, IntoParams)]
25#[into_params(parameter_in = Query)]
26struct GetInviteTokenByQueueIdQuery {
27 #[param(example = 1)]
28 queue_id: i64,
29}
30
31#[derive(Deserialize, ToSchema)]
32#[schema(description = "Body of the create invite token request")]
33struct CreateInviteTokenRequest {
34 #[schema(examples(1))]
35 queue_id: i64,
36 #[schema(examples("For classmates", "Для однокурсников"))]
37 name: String,
38 expiration_date: Option<DateTime<Utc>>,
39}
40
41#[derive(Deserialize, ToSchema)]
42#[schema(description = "Body of the expire invite token request")]
43struct ExpireInviteTokenRequest {
44 #[schema(examples(1))]
45 id: i64,
46}
47
48#[derive(Deserialize, ToSchema)]
49#[schema(description = "Body of the change invite token expiration date request")]
50struct ChangeInviteTokenExpirationDateRequest {
51 #[schema(examples(1))]
52 id: i64,
53 #[schema(examples("2000-01-01 00:00:00Z", "2000-01-01 03:00:00+03:00", json!(null)))]
54 expiration_date: Option<DateTime<Utc>>,
55}
56
57#[derive(Deserialize, ToSchema)]
58#[schema(description = "Body of the delete invite token request")]
59struct DeleteInviteTokenRequest {
60 #[schema(examples(1))]
61 id: i64,
62}
63
64#[utoipa::path(
65 get,
66 path = "/get/by_id",
67 tag = INVITE_TOKEN,
68 summary = "Get by id",
69 description = "Get the invite token by id",
70 params(GetInviteTokenByIdQuery),
71 responses(
72 (
73 status = 200, body = SuccessResponse<InviteToken>,
74 description = "Success response with the requested invite token"
75 ),
76 GlobalResponses
77 ),
78 security(("auth" = [])),
79)]
80async fn get_by_id(
81 State(state): State<AppState>,
82 Auth(user): Auth,
83 ApiQuery(req): ApiQuery<GetInviteTokenByIdQuery>,
84) -> ApiResult<InviteToken> {
85 Ok(SuccessResponse::ok(
86 get_owned_invite_token(req.id, user.id, &state.db)
87 .await?
88 .into(),
89 ))
90}
91
92#[utoipa::path(
93 get,
94 path = "/get/by_queue_id",
95 tag = INVITE_TOKEN,
96 summary = "Get by queue id",
97 description = "Get the invite token by the queue id",
98 params(GetInviteTokenByQueueIdQuery),
99 responses(
100 (
101 status = 200, body = SuccessResponse<Vec<InviteToken>>,
102 description = "Success response with the requested invite tokens"
103 ),
104 GlobalResponses
105 ),
106 security(("auth" = [])),
107)]
108async fn get_by_queue_id(
109 State(state): State<AppState>,
110 Auth(user): Auth,
111 ApiQuery(req): ApiQuery<GetInviteTokenByQueueIdQuery>,
112) -> ApiResult<Vec<InviteToken>> {
113 let queue = get_owned_queue(req.queue_id, user.id, &state.db).await?;
114
115 Ok(SuccessResponse::ok(
116 queue
117 .find_related(invite_tokens::Entity)
118 .all(&state.db)
119 .await?
120 .into_iter()
121 .map(Into::into)
122 .collect(),
123 ))
124}
125
126#[utoipa::path(
127 post,
128 path = "/create",
129 tag = INVITE_TOKEN,
130 summary = "Create",
131 description = "Create a new invite token",
132 request_body = CreateInviteTokenRequest,
133 responses(
134 (
135 status = 200, body = SuccessResponse<InviteToken>,
136 description = "Success response with the created invite token"
137 ),
138 GlobalResponses
139 ),
140 security(("auth" = [])),
141)]
142async fn create(
143 State(state): State<AppState>,
144 Auth(user): Auth,
145 ApiJson(req): ApiJson<CreateInviteTokenRequest>,
146) -> ApiResult<InviteToken> {
147 let queue = get_owned_queue(req.queue_id, user.id, &state.db).await?;
148
149 Ok(SuccessResponse::ok(
150 invite_tokens::ActiveModel {
151 token: Set(uuid::Uuid::new_v4()),
152 queue_id: Set(queue.id),
153 name: Set(req.name),
154 expiration_date: Set(req.expiration_date.as_ref().map(DateTime::naive_utc)),
155 ..Default::default()
156 }
157 .insert(&state.db)
158 .await?
159 .into(),
160 ))
161}
162
163#[utoipa::path(
164 patch,
165 path = "/expire",
166 tag = INVITE_TOKEN,
167 summary = "Expire",
168 description = "Expire the invite token",
169 request_body = ExpireInviteTokenRequest,
170 responses(
171 (
172 status = 200, body = SuccessResponse<InviteToken>,
173 description = "Success response with the changed invite token data"
174 ),
175 GlobalResponses
176 ),
177 security(("auth" = [])),
178)]
179async fn expire(
180 State(state): State<AppState>,
181 Auth(user): Auth,
182 ApiJson(req): ApiJson<ExpireInviteTokenRequest>,
183) -> ApiResult<InviteToken> {
184 let mut active_invite_token = get_owned_invite_token(req.id, user.id, &state.db)
185 .await?
186 .into_active_model();
187
188 active_invite_token.expiration_date = Set(Some(Utc::now().naive_utc()));
189
190 let invite_token = active_invite_token.update(&state.db).await?;
191 Ok(SuccessResponse::ok(invite_token.into()))
192}
193
194#[utoipa::path(
195 patch,
196 path = "/update/expiration_date",
197 tag = INVITE_TOKEN,
198 summary = "Change expiration date",
199 description = "Change invite token expiration date",
200 request_body = ChangeInviteTokenExpirationDateRequest,
201 responses(
202 (
203 status = 200, body = SuccessResponse<InviteToken>,
204 description = "Success response with the changed invite token data"
205 ),
206 GlobalResponses
207 ),
208 security(("auth" = [])),
209)]
210async fn update_expiration_date(
211 State(state): State<AppState>,
212 Auth(user): Auth,
213 ApiJson(req): ApiJson<ChangeInviteTokenExpirationDateRequest>,
214) -> ApiResult<InviteToken> {
215 let mut active_invite_token = get_owned_invite_token(req.id, user.id, &state.db)
216 .await?
217 .into_active_model();
218
219 active_invite_token.expiration_date =
220 Set(req.expiration_date.as_ref().map(DateTime::naive_utc));
221
222 let invite_token = active_invite_token.update(&state.db).await?;
223 Ok(SuccessResponse::ok(invite_token.into()))
224}
225
226#[utoipa::path(
227 delete,
228 path = "/delete",
229 tag = INVITE_TOKEN,
230 summary = "Delete",
231 description = "Delete the invite token",
232 request_body = DeleteInviteTokenRequest,
233 responses(
234 (
235 status = 200, body = SuccessResponse<InviteToken>,
236 description = "Success response with the deleted invite token data"
237 ),
238 GlobalResponses
239 ),
240 security(("auth" = [])),
241)]
242async fn delete(
243 State(state): State<AppState>,
244 Auth(user): Auth,
245 ApiJson(req): ApiJson<DeleteInviteTokenRequest>,
246) -> ApiResult<InviteToken> {
247 let invite_token = get_owned_invite_token(req.id, user.id, &state.db).await?;
248 invite_token.clone().delete(&state.db).await?;
249 Ok(SuccessResponse::ok(invite_token.into()))
250}
4 251
5pub fn router() -> OpenApiRouter<AppState> { 252pub fn router() -> OpenApiRouter<AppState> {
6 OpenApiRouter::new() 253 OpenApiRouter::new()
254 .routes(routes!(get_by_id))
255 .routes(routes!(get_by_queue_id))
256 .routes(routes!(create))
257 .routes(routes!(expire))
258 .routes(routes!(update_expiration_date))
259 .routes(routes!(delete))
7} 260}
diff --git a/src/routers/queue/manage.rs b/src/routers/queue/manage.rs
index 8f42e07..b799458 100644
--- a/src/routers/queue/manage.rs
+++ b/src/routers/queue/manage.rs
@@ -1,50 +1,32 @@
1use axum::extract::State; 1use axum::extract::State;
2use entity::{queues, users}; 2use entity::queues;
3use sea_orm::{ 3use sea_orm::{
4 ActiveModelTrait, ActiveValue::Set, ColumnTrait, DatabaseConnection, EntityTrait, 4 ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, IntoActiveModel, ModelTrait,
5 IntoActiveModel, ModelTrait, QueryFilter, 5 QueryFilter,
6}; 6};
7use serde::Deserialize; 7use serde::Deserialize;
8use utoipa::{IntoParams, ToSchema}; 8use utoipa::{IntoParams, ToSchema};
9use utoipa_axum::{router::OpenApiRouter, routes}; 9use utoipa_axum::{router::OpenApiRouter, routes};
10 10
11use crate::{ 11use crate::{
12 ApiError, ApiResult, AppState, ClientError, GlobalResponses, SuccessResponse, 12 ApiResult, AppState, ClientError, GlobalResponses, SuccessResponse,
13 extract::{ApiJson, ApiQuery, Auth}, 13 extract::{ApiJson, ApiQuery, Auth},
14 models::Queue, 14 models::Queue,
15 tags::QUEUE, 15 tags::QUEUE,
16 util::{get_owned_queue, user_exists},
16}; 17};
17 18
18async fn user_exists(id: i64, db: &DatabaseConnection) -> Result<bool, ApiError> {
19 Ok(users::Entity::find_by_id(id).one(db).await?.is_some())
20}
21
22async fn get_owned_queue(
23 id: i64,
24 owner_id: i64,
25 db: &DatabaseConnection,
26) -> Result<queues::Model, ApiError> {
27 let queue = queues::Entity::find_by_id(id)
28 .one(db)
29 .await?
30 .ok_or(ClientError::QueueNotFound { id })?;
31
32 if queue.owner_id != owner_id {
33 return Err(ClientError::NotQueueOwner { id: queue.id }.into());
34 }
35
36 Ok(queue)
37}
38
39#[derive(Deserialize, IntoParams)] 19#[derive(Deserialize, IntoParams)]
40#[into_params(parameter_in = Query)] 20#[into_params(parameter_in = Query)]
41struct GetByIdQueueQuery { 21struct GetQueueByIdQuery {
22 #[param(example = 1)]
42 id: i64, 23 id: i64,
43} 24}
44 25
45#[derive(Deserialize, IntoParams)] 26#[derive(Deserialize, IntoParams)]
46#[into_params(parameter_in = Query)] 27#[into_params(parameter_in = Query)]
47struct GetByOwnerIdQuery { 28struct GetByOwnerIdQuery {
29 #[param(example = 1)]
48 owner_id: i64, 30 owner_id: i64,
49} 31}
50 32
@@ -82,11 +64,11 @@ struct DeleteQueueRequest {
82 64
83#[utoipa::path( 65#[utoipa::path(
84 get, 66 get,
85 path = "/get/id", 67 path = "/get/by_id",
86 tag = QUEUE, 68 tag = QUEUE,
87 summary = "Get by id", 69 summary = "Get by id",
88 description = "Get the queue by id", 70 description = "Get the queue by id",
89 params(GetByIdQueueQuery), 71 params(GetQueueByIdQuery),
90 responses( 72 responses(
91 ( 73 (
92 status = 200, body = SuccessResponse<Option<Queue>>, 74 status = 200, body = SuccessResponse<Option<Queue>>,
@@ -97,7 +79,7 @@ struct DeleteQueueRequest {
97)] 79)]
98async fn get_by_id( 80async fn get_by_id(
99 State(state): State<AppState>, 81 State(state): State<AppState>,
100 ApiQuery(req): ApiQuery<GetByIdQueueQuery>, 82 ApiQuery(req): ApiQuery<GetQueueByIdQuery>,
101) -> ApiResult<Option<Queue>> { 83) -> ApiResult<Option<Queue>> {
102 Ok(SuccessResponse::ok( 84 Ok(SuccessResponse::ok(
103 queues::Entity::find_by_id(req.id) 85 queues::Entity::find_by_id(req.id)
@@ -109,10 +91,10 @@ async fn get_by_id(
109 91
110#[utoipa::path( 92#[utoipa::path(
111 get, 93 get,
112 path = "/get/owner", 94 path = "/get/by_owner",
113 tag = QUEUE, 95 tag = QUEUE,
114 summary = "Get by owner", 96 summary = "Get by owner",
115 description = "Get queues for a given owner", 97 description = "Get queues by the owner id",
116 params(GetByOwnerIdQuery), 98 params(GetByOwnerIdQuery),
117 responses( 99 responses(
118 ( 100 (
@@ -126,7 +108,7 @@ async fn get_by_owner(
126 State(state): State<AppState>, 108 State(state): State<AppState>,
127 ApiQuery(req): ApiQuery<GetByOwnerIdQuery>, 109 ApiQuery(req): ApiQuery<GetByOwnerIdQuery>,
128) -> ApiResult<Vec<Queue>> { 110) -> ApiResult<Vec<Queue>> {
129 return Ok(SuccessResponse::ok( 111 Ok(SuccessResponse::ok(
130 queues::Entity::find() 112 queues::Entity::find()
131 .filter(queues::Column::OwnerId.eq(req.owner_id)) 113 .filter(queues::Column::OwnerId.eq(req.owner_id))
132 .all(&state.db) 114 .all(&state.db)
@@ -134,7 +116,32 @@ async fn get_by_owner(
134 .into_iter() 116 .into_iter()
135 .map(Into::into) 117 .map(Into::into)
136 .collect(), 118 .collect(),
137 )); 119 ))
120}
121
122#[utoipa::path(
123 get,
124 path = "/get/owned",
125 tag = QUEUE,
126 summary = "Get owned",
127 description = "Get your queues",
128 responses(
129 (
130 status = 200, body = SuccessResponse<Vec<Queue>>,
131 description = "Success response with queues owned by you"
132 ),
133 GlobalResponses
134 ),
135)]
136async fn get_owned(State(state): State<AppState>, Auth(user): Auth) -> ApiResult<Vec<Queue>> {
137 Ok(SuccessResponse::ok(
138 user.find_related(queues::Entity)
139 .all(&state.db)
140 .await?
141 .into_iter()
142 .map(Into::into)
143 .collect(),
144 ))
138} 145}
139 146
140#[utoipa::path( 147#[utoipa::path(
@@ -171,7 +178,7 @@ async fn create(
171} 178}
172 179
173#[utoipa::path( 180#[utoipa::path(
174 put, 181 patch,
175 path = "/update/name", 182 path = "/update/name",
176 tag = QUEUE, 183 tag = QUEUE,
177 summary = "Change name", 184 summary = "Change name",
@@ -202,7 +209,7 @@ async fn update_name(
202} 209}
203 210
204#[utoipa::path( 211#[utoipa::path(
205 put, 212 patch,
206 path = "/update/owner", 213 path = "/update/owner",
207 tag = QUEUE, 214 tag = QUEUE,
208 summary = "Change owner", 215 summary = "Change owner",
@@ -269,6 +276,7 @@ pub fn router() -> OpenApiRouter<AppState> {
269 OpenApiRouter::new() 276 OpenApiRouter::new()
270 .routes(routes!(get_by_id)) 277 .routes(routes!(get_by_id))
271 .routes(routes!(get_by_owner)) 278 .routes(routes!(get_by_owner))
279 .routes(routes!(get_owned))
272 .routes(routes!(create)) 280 .routes(routes!(create))
273 .routes(routes!(update_name)) 281 .routes(routes!(update_name))
274 .routes(routes!(update_owner)) 282 .routes(routes!(update_owner))
diff --git a/src/routers/queue/mod.rs b/src/routers/queue/mod.rs
index dd03956..9af1f01 100644
--- a/src/routers/queue/mod.rs
+++ b/src/routers/queue/mod.rs
@@ -8,5 +8,5 @@ use crate::AppState;
8pub fn router() -> OpenApiRouter<AppState> { 8pub fn router() -> OpenApiRouter<AppState> {
9 OpenApiRouter::new() 9 OpenApiRouter::new()
10 .merge(manage::router()) 10 .merge(manage::router())
11 .merge(access::router()) 11 .nest("/access", access::router())
12} 12}