Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0d99cbc
refactor: introduce newtyped ids in backend_domain
comavius Feb 13, 2026
8578aa2
refactor: newtyped id instantiation for jwt
comavius Feb 13, 2026
1480245
refactor: change visibility of inner uuid
comavius Feb 13, 2026
8c9c879
refactor: improve order of impl blocks
comavius Feb 13, 2026
a79cfea
refactor: remove unused imports
comavius Feb 13, 2026
51eaa23
refactor: improve session-user abstraction
comavius Feb 13, 2026
72e12f7
refactor: implement get_session_user
comavius Feb 13, 2026
4d5fe96
refactor: use into() conversion
comavius Feb 13, 2026
43fc88c
refactor: update cargo.lock
comavius Feb 13, 2026
9ab3063
refactor: remove unused imports
comavius Feb 13, 2026
d1bac27
refactor: usecase layer no longer depends on uuid crate
comavius Feb 13, 2026
56581ba
refactor: usecase models should use newtyped id types
comavius Feb 13, 2026
dcf1d76
usecase tests depends on uuid
comavius Feb 13, 2026
8c43aba
refactor: temporarily add default for testcase id
comavius Feb 13, 2026
aba4245
refactor: revert wrong changes on icon format
comavius Feb 13, 2026
5db5d93
refactor: newtyped ids for usecase services
comavius Feb 13, 2026
ee12084
add axum-extra coockie feature
comavius Feb 14, 2026
6a2f728
fix: misc forgotten fixes
comavius Feb 14, 2026
53025ff
add customized extractor for coockie
comavius Feb 14, 2026
6699803
customized extractor 2
comavius Feb 14, 2026
2748833
newtyped id conversion for backend_app models
comavius Feb 14, 2026
db4bd1a
session repository injection
comavius Feb 14, 2026
23ef39c
ues newtyped id with customized extractor
comavius Feb 14, 2026
63b80b4
removed unused imports
comavius Feb 14, 2026
bae88fc
newtyped id conversion in tests
comavius Feb 14, 2026
44e2165
Update app/backend_app/src/extractor/session.rs
comavius Feb 14, 2026
bfdc0d8
Update app/backend_app/src/extractor/session.rs
comavius Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ aws-config = { version = "1.5.13", features = ["behavior-version-latest"] }
aws-sdk-ec2 = "1.116.0"
aws-sdk-s3 = { version = "1.68.0", features = ["behavior-version-latest"] }
axum = "0.7"
axum-extra = { version = "0.9", features = ["typed-header"] }
axum-extra = { version = "0.9", features = ["cookie", "typed-header"] }
base64 = "0.22.1"
bcrypt = "0.15"
bollard = "0.18.1"
Expand Down
14 changes: 6 additions & 8 deletions app/backend_app/src/di.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,13 @@ type AuthSvc = AuthenticationService<
type ProblemSvc = ProblemService<
ProblemRepositoryImpl,
UserRepositoryImpl,
SessionRepositoryImpl,
TestcaseRepositoryImpl,
ProcedureRepositoryImpl,
RegistryServerImpl,
DepNameRepositoryImpl,
>;
type UserSvc = UserService<
UserRepositoryImpl,
SessionRepositoryImpl,
AuthRepositoryImpl,
IconRepositoryImpl,
ProblemRepositoryImpl,
Expand All @@ -134,7 +132,6 @@ type UserSvc = UserService<
>;
type IconSvc = IconService<IconRepositoryImpl>;
type SubmissionSvc = SubmissionService<
SessionRepositoryImpl,
UserRepositoryImpl,
SubmissionRepositoryImpl,
ProblemRepositoryImpl,
Expand All @@ -148,7 +145,6 @@ type EditorialSvc =
EditorialService<SessionRepositoryImpl, EditorialRepositoryImpl, ProblemRepositoryImpl>;
type TestcaseSvc = TestcaseService<
ProblemRepositoryImpl,
SessionRepositoryImpl,
TestcaseRepositoryImpl,
ProcedureRepositoryImpl,
RegistryClientImpl,
Expand Down Expand Up @@ -176,6 +172,7 @@ pub struct DiContainer {
google_oauth2_service: GoogleOAuth2Svc,
github_oauth2_service: GitHubOAuth2Svc,
traq_oauth2_service: TraqOAuth2Svc,
session_repository: SessionRepositoryImpl,
}

impl DiContainer {
Expand Down Expand Up @@ -213,15 +210,13 @@ impl DiContainer {
problem_service: ProblemService::new(
provider.provide_problem_repository(),
provider.provide_user_repository(),
provider.provide_session_repository(),
provider.provide_testcase_repository(),
provider.provide_procedure_repository(),
pr_server.clone(),
provider.provide_dep_name_repository(),
),
user_service: UserService::new(
provider.provide_user_repository(),
provider.provide_session_repository(),
provider.provide_auth_repository(),
provider.provide_icon_repository(),
provider.provide_problem_repository(),
Expand All @@ -230,7 +225,6 @@ impl DiContainer {
),
icon_service: IconService::new(provider.provide_icon_repository()),
submission_service: std::sync::Arc::new(SubmissionService::new(
provider.provide_session_repository(),
provider.provide_user_repository(),
provider.provide_submission_repository(),
provider.provide_problem_repository(),
Expand All @@ -247,7 +241,6 @@ impl DiContainer {
),
testcase_service: TestcaseService::new(
provider.provide_problem_repository(),
provider.provide_session_repository(),
provider.provide_testcase_repository(),
provider.provide_procedure_repository(),
pr_client,
Expand All @@ -270,6 +263,7 @@ impl DiContainer {
provider.provide_session_repository(),
provider.provide_user_repository(),
),
session_repository: provider.provide_session_repository(),
}
}

Expand Down Expand Up @@ -316,4 +310,8 @@ impl DiContainer {
pub fn traq_oauth2_service(&self) -> &TraqOAuth2Svc {
&self.traq_oauth2_service
}

pub fn session_repository(&self) -> &SessionRepositoryImpl {
&self.session_repository
}
}
1 change: 1 addition & 0 deletions app/backend_app/src/extractor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod session;
35 changes: 35 additions & 0 deletions app/backend_app/src/extractor/session.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::di::DiContainer;
use axum::{
async_trait,
extract::FromRequestParts,
http::{StatusCode, request::Parts},
};
use axum_extra::extract::cookie::CookieJar;
use domain::{model::session::SessionUser, repository::session::SessionRepository};

pub struct ExtractedSessionUser(pub Option<SessionUser>);

#[async_trait]
impl FromRequestParts<DiContainer> for ExtractedSessionUser {
type Rejection = (StatusCode, &'static str);

async fn from_request_parts(
parts: &mut Parts,
di_container: &DiContainer,
) -> Result<Self, Self::Rejection> {
let jar = CookieJar::from_request_parts(parts, di_container)
.await
.map_err(|_| (StatusCode::BAD_REQUEST, "Invalid Cookie header"))?;

if let Some(session_id) = jar.get("session_id") {
let session_user = di_container
.session_repository()
.get_session_user(session_id.value())
.await
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Session retrieval error"))?;
Ok(ExtractedSessionUser(session_user))
} else {
Ok(ExtractedSessionUser(None))
}
}
}
61 changes: 40 additions & 21 deletions app/backend_app/src/handler/editorial.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::extractor::session::ExtractedSessionUser;
use crate::model::editorials::{CreateEditorial, EditorialResponse, UpdateEditorial};
use crate::model::error::AppError;
use crate::{di::DiContainer, model::editorials::EditorialSummaryResponse};
Expand All @@ -7,18 +8,20 @@ use axum::{
http::StatusCode,
response::IntoResponse,
};
use axum_extra::{TypedHeader, headers::Cookie};

pub async fn get_editorial(
State(di_container): State<DiContainer>,
Path(editorial_id): Path<String>,
TypedHeader(cookie): TypedHeader<Cookie>,
ExtractedSessionUser(session_user): ExtractedSessionUser,
) -> Result<impl IntoResponse, StatusCode> {
let session_id = cookie.get("session_id");

match di_container
.editorial_service()
.get_editorial(session_id, editorial_id)
.get_editorial(
session_user,
uuid::Uuid::parse_str(&editorial_id)
.map_err(|_| StatusCode::BAD_REQUEST)?
.into(),
)
.await
{
Ok(editorial) => {
Expand All @@ -32,13 +35,17 @@ pub async fn get_editorial(
pub async fn get_editorials(
State(di_container): State<DiContainer>,
Path(problem_id): Path<String>,
TypedHeader(cookie): TypedHeader<Cookie>,
ExtractedSessionUser(session_user): ExtractedSessionUser,
) -> Result<impl IntoResponse, StatusCode> {
let session_id = cookie.get("session_id");

match di_container
.editorial_service()
.get_editorials(session_id, problem_id)
.get_editorials(
session_user,
problem_id
.parse::<i64>()
.map_err(|_| StatusCode::BAD_REQUEST)?
.into(),
)
.await
{
Ok(editorials) => {
Expand All @@ -52,15 +59,20 @@ pub async fn get_editorials(

pub async fn post_editorial(
State(di_container): State<DiContainer>,
TypedHeader(cookie): TypedHeader<Cookie>,
ExtractedSessionUser(session_user): ExtractedSessionUser,
Path(problem_id): Path<String>,
Json(query): Json<CreateEditorial>,
) -> Result<impl IntoResponse, StatusCode> {
let session_id = cookie.get("session_id");

match di_container
.editorial_service()
.post_editorial(session_id, problem_id, query.into())
.post_editorial(
session_user,
problem_id
.parse::<i64>()
.map_err(|_| StatusCode::BAD_REQUEST)?
.into(),
query.into(),
)
.await
{
Ok(editorial) => {
Expand All @@ -74,14 +86,18 @@ pub async fn post_editorial(
pub async fn put_editorial(
State(di_container): State<DiContainer>,
Path(editorial_id): Path<String>,
TypedHeader(cookie): TypedHeader<Cookie>,
ExtractedSessionUser(session_user): ExtractedSessionUser,
Json(query): Json<UpdateEditorial>,
) -> Result<impl IntoResponse, StatusCode> {
let session_id = cookie.get("session_id");

match di_container
.editorial_service()
.put_editorial(session_id, editorial_id, query.into())
.put_editorial(
session_user,
uuid::Uuid::parse_str(&editorial_id)
.map_err(|_| StatusCode::BAD_REQUEST)?
.into(),
query.into(),
)
.await
{
Ok(_) => Ok(StatusCode::NO_CONTENT),
Expand All @@ -92,13 +108,16 @@ pub async fn put_editorial(
pub async fn delete_editorial(
State(di_container): State<DiContainer>,
Path(editorial_id): Path<String>,
TypedHeader(cookie): TypedHeader<Cookie>,
ExtractedSessionUser(session_user): ExtractedSessionUser,
) -> Result<impl IntoResponse, StatusCode> {
let session_id = cookie.get("session_id");

match di_container
.editorial_service()
.delete_editorial(session_id, editorial_id)
.delete_editorial(
session_user,
uuid::Uuid::parse_str(&editorial_id)
.map_err(|_| StatusCode::BAD_REQUEST)?
.into(),
)
.await
{
Ok(_) => Ok(StatusCode::NO_CONTENT),
Expand Down
9 changes: 7 additions & 2 deletions app/backend_app/src/handler/icon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ use crate::{di::DiContainer, model::error::AppError};

pub async fn get_icon(
State(di_container): State<DiContainer>,
Path(icon_uuid): Path<String>,
Path(icon_uuid_str): Path<String>,
) -> Result<impl IntoResponse, StatusCode> {
match di_container.icon_service().get_icon(icon_uuid).await {
let icon_uuid = match uuid::Uuid::parse_str(&icon_uuid_str) {
Ok(u) => u,
Err(_) => return Err(StatusCode::BAD_REQUEST),
};
let icon_id = icon_uuid.into();
match di_container.icon_service().get_icon(icon_id).await {
Ok(icon) => {
let mut headers = HeaderMap::new();
headers.insert("Content-Type", icon.content_type.parse().unwrap());
Expand Down
Loading