Skip to content

Commit 5d04dae

Browse files
committed
chore: refactoring routing
1 parent 5114d67 commit 5d04dae

File tree

67 files changed

+3100
-1640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+3100
-1640
lines changed

crates/cluster/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ pub async fn create_challenge_env(
239239
("cds/env_id".to_owned(), id.to_string()),
240240
("cds/user_id".to_owned(), format!("{}", user.id)),
241241
(
242-
"cds/team_id".to_owned(),
242+
"cds/profile".to_owned(),
243243
format!("{}", match &team {
244244
Some(team) => team.id,
245245
_ => 0,

crates/db/src/transfer/game_challenge.rs

+12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ pub struct GameChallenge {
1919
pub challenge: Option<Challenge>,
2020
}
2121

22+
impl GameChallenge {
23+
pub fn desensitize(&self) -> Self {
24+
Self {
25+
challenge: self
26+
.to_owned()
27+
.challenge
28+
.map(|challenge| challenge.desensitize()),
29+
..self.to_owned()
30+
}
31+
}
32+
}
33+
2234
impl From<entity::game_challenge::Model> for GameChallenge {
2335
fn from(entity: entity::game_challenge::Model) -> Self {
2436
Self {

crates/db/src/util/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub async fn is_user_in_team(user_id: i64, team_id: i64) -> Result<bool, DbErr>
2222
/// ```sql
2323
/// SELECT u.id AS user_id, t.game_id, t.is_allowed
2424
/// FROM teams t
25-
/// JOIN team_users tu ON t.id = tu.team_id
25+
/// JOIN team_users tu ON t.id = tu.profile
2626
/// WHERE u.id = ? AND t.game_id = ? AND t.is_allowed = true;
2727
/// ```
2828
pub async fn is_user_in_game(

crates/web/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod model;
44
pub mod router;
55
pub mod traits;
66
pub mod util;
7+
mod worker;
78

89
use std::{sync::Arc, time::Duration};
910

@@ -49,6 +50,8 @@ pub async fn init() -> Result<(), anyhow::Error> {
4950
APP.set(router)
5051
.map_err(|_| anyhow::anyhow!("Failed to set router into OnceCell"))?;
5152

53+
worker::init().await?;
54+
5255
Ok(())
5356
}
5457

crates/web/src/middleware/auth.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use axum::{
2+
body::Body,
3+
extract::Request,
4+
http::header::COOKIE,
5+
middleware::Next,
6+
response::{IntoResponse, Response},
7+
};
8+
use cds_db::{entity::user::Group, get_db};
9+
use jsonwebtoken::{DecodingKey, Validation, decode};
10+
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
11+
use serde_json::json;
12+
13+
use crate::{
14+
extract::Extension,
15+
traits::{Ext, WebError},
16+
};
17+
18+
pub async fn extract(mut req: Request<Body>, next: Next) -> Result<Response, WebError> {
19+
let mut ext = req
20+
.extensions()
21+
.get::<Ext>()
22+
.unwrap_or(&Ext::default())
23+
.to_owned();
24+
25+
let cookies = req
26+
.headers()
27+
.get(COOKIE)
28+
.and_then(|header| header.to_str().ok())
29+
.unwrap_or("")
30+
.to_string();
31+
32+
let mut jar = cookie::CookieJar::new();
33+
let cookies: Vec<String> = cookies
34+
.split(";")
35+
.map(|cookie| cookie.trim().to_string())
36+
.collect();
37+
for cookie in cookies {
38+
if let Ok(parsed_cookie) = cookie::Cookie::parse(cookie) {
39+
jar.add(parsed_cookie);
40+
}
41+
}
42+
43+
let token = jar.get("token").map(|cookie| cookie.value()).unwrap_or("");
44+
45+
let decoding_key = DecodingKey::from_secret(cds_config::get_constant().jwt.secret.as_bytes());
46+
let validation = Validation::default();
47+
48+
if let Ok(data) = decode::<crate::util::jwt::Claims>(token, &decoding_key, &validation) {
49+
if let Some(user) = cds_db::entity::user::Entity::find()
50+
.filter(cds_db::entity::user::Column::Id.eq(data.claims.id))
51+
.filter(cds_db::entity::user::Column::DeletedAt.is_null())
52+
.one(get_db())
53+
.await?
54+
.map(|user| cds_db::transfer::User::from(user))
55+
{
56+
if user.group == Group::Banned {
57+
return Err(WebError::Forbidden(json!("forbidden")));
58+
}
59+
60+
ext.operator = Some(user);
61+
}
62+
}
63+
64+
req.extensions_mut().insert(ext);
65+
66+
Ok(next.run(req).await)
67+
}
68+
69+
pub async fn admin_only(
70+
Extension(ext): Extension<Ext>, req: Request, next: Next,
71+
) -> Result<impl IntoResponse, WebError> {
72+
let operator = ext.operator.ok_or(WebError::Unauthorized(json!("")))?;
73+
74+
if operator.group != Group::Admin {
75+
return Err(WebError::Forbidden(json!("forbidden")));
76+
}
77+
78+
Ok(next.run(req).await)
79+
}

crates/web/src/middleware/mod.rs

+1-61
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,3 @@
1+
pub mod auth;
12
pub mod error;
23
pub mod network;
3-
4-
use axum::{
5-
body::Body, extract::Request, http::header::COOKIE, middleware::Next, response::Response,
6-
};
7-
use cds_db::{entity::user::Group, get_db};
8-
use jsonwebtoken::{DecodingKey, Validation, decode};
9-
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
10-
use serde_json::json;
11-
12-
use crate::traits::{Ext, WebError};
13-
14-
pub async fn auth(mut req: Request<Body>, next: Next) -> Result<Response, WebError> {
15-
let mut ext = req
16-
.extensions()
17-
.get::<Ext>()
18-
.unwrap_or(&Ext::default())
19-
.to_owned();
20-
21-
let cookies = req
22-
.headers()
23-
.get(COOKIE)
24-
.and_then(|header| header.to_str().ok())
25-
.unwrap_or("")
26-
.to_string();
27-
28-
let mut jar = cookie::CookieJar::new();
29-
let cookies: Vec<String> = cookies
30-
.split(";")
31-
.map(|cookie| cookie.trim().to_string())
32-
.collect();
33-
for cookie in cookies {
34-
if let Ok(parsed_cookie) = cookie::Cookie::parse(cookie) {
35-
jar.add(parsed_cookie);
36-
}
37-
}
38-
39-
let token = jar.get("token").map(|cookie| cookie.value()).unwrap_or("");
40-
41-
let decoding_key = DecodingKey::from_secret(cds_config::get_constant().jwt.secret.as_bytes());
42-
let validation = Validation::default();
43-
44-
if let Ok(data) = decode::<crate::util::jwt::Claims>(token, &decoding_key, &validation) {
45-
if let Some(user) = cds_db::entity::user::Entity::find()
46-
.filter(cds_db::entity::user::Column::Id.eq(data.claims.id))
47-
.filter(cds_db::entity::user::Column::DeletedAt.is_null())
48-
.one(get_db())
49-
.await?
50-
.map(|user| cds_db::transfer::User::from(user))
51-
{
52-
if user.group == Group::Banned {
53-
return Err(WebError::Forbidden(json!("forbidden")));
54-
}
55-
56-
ext.operator = Some(user);
57-
}
58-
}
59-
60-
req.extensions_mut().insert(ext);
61-
62-
Ok(next.run(req).await)
63-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use axum::{
2+
Router,
3+
extract::{DefaultBodyLimit, Multipart},
4+
http::StatusCode,
5+
};
6+
use cds_db::{entity::user::Group, get_db};
7+
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
8+
use serde_json::json;
9+
10+
use crate::{
11+
extract::{Extension, Path},
12+
traits::{Ext, WebError, WebResponse},
13+
};
14+
15+
pub fn router() -> Router {
16+
Router::new()
17+
.route(
18+
"/",
19+
axum::routing::post(save_challenge_attachment)
20+
.layer(DefaultBodyLimit::max(512 * 1024 * 1024 /* MB */)),
21+
)
22+
.route("/", axum::routing::delete(delete_challenge_attachment))
23+
}
24+
25+
pub async fn save_challenge_attachment(
26+
Extension(ext): Extension<Ext>, Path(challenge_id): Path<uuid::Uuid>, mut multipart: Multipart,
27+
) -> Result<WebResponse<()>, WebError> {
28+
let operator = ext.operator.ok_or(WebError::Unauthorized(json!("")))?;
29+
if operator.group != Group::Admin {
30+
return Err(WebError::Forbidden(json!("")));
31+
}
32+
33+
let challenge = cds_db::entity::challenge::Entity::find_by_id(challenge_id)
34+
.filter(cds_db::entity::challenge::Column::DeletedAt.is_null())
35+
.one(get_db())
36+
.await?
37+
.ok_or(WebError::BadRequest(json!("challenge_not_found")))?;
38+
39+
let path = format!("challenges/{}/attachment", challenge.id);
40+
let mut filename = String::new();
41+
let mut data = Vec::<u8>::new();
42+
while let Some(field) = multipart.next_field().await.unwrap() {
43+
if field.name() == Some("file") {
44+
filename = field.file_name().unwrap().to_string();
45+
data = match field.bytes().await {
46+
Ok(bytes) => bytes.to_vec(),
47+
Err(_err) => {
48+
return Err(WebError::BadRequest(json!("size_too_large")));
49+
}
50+
};
51+
}
52+
}
53+
54+
cds_media::delete_dir(path.clone()).await?;
55+
56+
cds_media::save(path, filename, data)
57+
.await
58+
.map_err(|_| WebError::InternalServerError(json!("")))?;
59+
60+
Ok(WebResponse {
61+
code: StatusCode::OK,
62+
..Default::default()
63+
})
64+
}
65+
66+
pub async fn delete_challenge_attachment(
67+
Extension(ext): Extension<Ext>, Path(challenge_id): Path<uuid::Uuid>,
68+
) -> Result<WebResponse<()>, WebError> {
69+
let operator = ext.operator.ok_or(WebError::Unauthorized(json!("")))?;
70+
if operator.group != Group::Admin {
71+
return Err(WebError::Forbidden(json!("")));
72+
}
73+
74+
let challenge = cds_db::entity::challenge::Entity::find_by_id(challenge_id)
75+
.filter(cds_db::entity::challenge::Column::DeletedAt.is_null())
76+
.one(get_db())
77+
.await?
78+
.ok_or(WebError::BadRequest(json!("challenge_not_found")))?;
79+
80+
let path = format!("challenges/{}/attachment", challenge.id);
81+
82+
cds_media::delete_dir(path)
83+
.await
84+
.map_err(|_| WebError::InternalServerError(json!("")))?;
85+
86+
Ok(WebResponse {
87+
code: StatusCode::OK,
88+
..Default::default()
89+
})
90+
}

0 commit comments

Comments
 (0)