feat: v0.3.0 — standardize codebase, centralize infra, merge QR into CMS
- Enforce axum best practices across all 13 workspace crates (max 200 LOC/file, no comments, no unwrap, clean architecture) - Fix domain→infrastructure dependency inversions in imphnen-iam and imphnen-dimentorin - Extract imphnen-storage (MinIO) and imphnen-email (Lettre) as standalone crates - Centralize all config in ENV struct: CDN_URL, CORS_ALLOWED_ORIGINS - Centralize SMTP through imphnen-email; remove dead HackathonConfig - Centralize database: QR crate now shares main DB pool (single DATABASE_URL) - Rename QR users table to qr_users to avoid collision with main users table - Merge imphnen-qr into imphnen-cms/src/qr (13 crates, down from 14) - Restructure imphnen-hackathon flat modules into clean architecture - Remove all stale env vars from .env.example (SurrealDB, QR_JWT, Hackathon infra) - Fix Dockerfile to include all current workspace crates - Bump all crate versions 0.2.0 → 0.3.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2ae43b3bcc
commit
331a4a4e88
@@ -0,0 +1,112 @@
|
||||
use super::super::dto::{
|
||||
BookSessionRequestDto, BookSessionResponseDto, SessionFeedbackRequestDto,
|
||||
SessionFeedbackResponseDto, UpdateSessionStatusRequestDto,
|
||||
UpdateSessionStatusResponseDto,
|
||||
};
|
||||
use crate::sessions::domain::SessionService;
|
||||
use axum::{
|
||||
extract::{Extension, Path},
|
||||
http::HeaderMap,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use imphnen_libs::ValidatedJson;
|
||||
use imphnen_utils::AppError;
|
||||
use imphnen_utils::{ApiSuccess, extract_email};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v1/mentors/{id}/sessions/create",
|
||||
tag = "sessions",
|
||||
security(("Bearer" = [])),
|
||||
params(
|
||||
("id" = String, Path, description = "Mentor ID"),
|
||||
),
|
||||
request_body = BookSessionRequestDto,
|
||||
responses(
|
||||
(status = 201, description = "Session booked successfully", body = BookSessionResponseDto),
|
||||
(status = 400, description = "Invalid request"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 404, description = "Mentor not found"),
|
||||
)
|
||||
)]
|
||||
pub async fn post_book_session(
|
||||
headers: HeaderMap,
|
||||
Extension(service): Extension<Arc<dyn SessionService>>,
|
||||
Path(mentor_id): Path<String>,
|
||||
ValidatedJson(dto): ValidatedJson<BookSessionRequestDto>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let user_email = extract_email(&headers)
|
||||
.ok_or_else(|| AppError::AuthenticationError("Token tidak valid".to_string()))?;
|
||||
let resp = BookSessionResponseDto::from(
|
||||
service
|
||||
.book_session(mentor_id, user_email, dto.into())
|
||||
.await?,
|
||||
);
|
||||
Ok(ApiSuccess(resp))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/v1/sessions/update/{id}/status",
|
||||
tag = "sessions",
|
||||
security(("Bearer" = [])),
|
||||
params(
|
||||
("id" = String, Path, description = "Session ID"),
|
||||
),
|
||||
request_body = UpdateSessionStatusRequestDto,
|
||||
responses(
|
||||
(status = 200, description = "Status updated successfully", body = UpdateSessionStatusResponseDto),
|
||||
(status = 400, description = "Invalid request"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 404, description = "Session not found"),
|
||||
)
|
||||
)]
|
||||
pub async fn put_update_session_status(
|
||||
headers: HeaderMap,
|
||||
Extension(service): Extension<Arc<dyn SessionService>>,
|
||||
Path(session_id): Path<String>,
|
||||
ValidatedJson(dto): ValidatedJson<UpdateSessionStatusRequestDto>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let user_email = extract_email(&headers)
|
||||
.ok_or_else(|| AppError::AuthenticationError("Token tidak valid".to_string()))?;
|
||||
let resp = UpdateSessionStatusResponseDto::from(
|
||||
service
|
||||
.update_session_status(session_id, user_email, dto.into())
|
||||
.await?,
|
||||
);
|
||||
Ok(ApiSuccess(resp))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v1/sessions/{id}/feedback/create",
|
||||
tag = "sessions",
|
||||
security(("Bearer" = [])),
|
||||
params(
|
||||
("id" = String, Path, description = "Session ID"),
|
||||
),
|
||||
request_body = SessionFeedbackRequestDto,
|
||||
responses(
|
||||
(status = 200, description = "Feedback submitted successfully", body = SessionFeedbackResponseDto),
|
||||
(status = 400, description = "Invalid request or session not completed"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 403, description = "Forbidden"),
|
||||
(status = 404, description = "Session not found"),
|
||||
)
|
||||
)]
|
||||
pub async fn post_submit_feedback(
|
||||
headers: HeaderMap,
|
||||
Extension(service): Extension<Arc<dyn SessionService>>,
|
||||
Path(session_id): Path<String>,
|
||||
ValidatedJson(dto): ValidatedJson<SessionFeedbackRequestDto>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let user_email = extract_email(&headers)
|
||||
.ok_or_else(|| AppError::AuthenticationError("Token tidak valid".to_string()))?;
|
||||
let resp = SessionFeedbackResponseDto::from(
|
||||
service
|
||||
.submit_feedback(session_id, user_email, dto.into())
|
||||
.await?,
|
||||
);
|
||||
Ok(ApiSuccess(resp))
|
||||
}
|
||||
Reference in New Issue
Block a user