575 lines
18 KiB
Rust
575 lines
18 KiB
Rust
|
|
use crate::AppState;
|
||
|
|
use axum::{
|
||
|
|
extract::{Path, Query, State},
|
||
|
|
http::StatusCode,
|
||
|
|
response::IntoResponse,
|
||
|
|
routing::{get, post},
|
||
|
|
Json, Router,
|
||
|
|
};
|
||
|
|
use contracts::auth_middleware::{require_admin, AuthUser};
|
||
|
|
use db::models::job::JobRepository;
|
||
|
|
use db::models::requirement::RequirementRepository;
|
||
|
|
use serde::Deserialize;
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
pub fn router() -> Router<AppState> {
|
||
|
|
Router::new()
|
||
|
|
.route("/", get(list_pending))
|
||
|
|
.route("/profiles/company/:user_id/approve", post(approve_company_profile))
|
||
|
|
.route("/profiles/company/:user_id/reject", post(reject_company_profile))
|
||
|
|
.route("/profiles/customer/:user_id/approve", post(approve_customer_profile))
|
||
|
|
.route("/profiles/customer/:user_id/reject", post(reject_customer_profile))
|
||
|
|
.route("/profiles/professional/:role_key/:user_id/approve", post(approve_professional_profile))
|
||
|
|
.route("/profiles/professional/:role_key/:user_id/reject", post(reject_professional_profile))
|
||
|
|
.route("/jobs/:id/approve", post(approve_job))
|
||
|
|
.route("/jobs/:id/reject", post(reject_job))
|
||
|
|
.route("/requirements/:id/approve", post(approve_requirement))
|
||
|
|
.route("/requirements/:id/reject", post(reject_requirement))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
pub struct ListQuery {
|
||
|
|
pub page: Option<i64>,
|
||
|
|
pub limit: Option<i64>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize)]
|
||
|
|
pub struct RejectPayload {
|
||
|
|
pub reason: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn list_pending(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Query(q): Query<ListQuery>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
let page = q.page.unwrap_or(1);
|
||
|
|
let limit = q.limit.unwrap_or(20);
|
||
|
|
let offset = (page - 1) * limit;
|
||
|
|
|
||
|
|
let jobs = sqlx::query_as!(
|
||
|
|
db::models::job::Job,
|
||
|
|
r#"
|
||
|
|
SELECT *
|
||
|
|
FROM jobs
|
||
|
|
WHERE status = 'PENDING_APPROVAL'
|
||
|
|
ORDER BY created_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let requirements = sqlx::query_as!(
|
||
|
|
db::models::requirement::Requirement,
|
||
|
|
r#"
|
||
|
|
SELECT *
|
||
|
|
FROM requirements
|
||
|
|
WHERE status = 'PENDING_APPROVAL'
|
||
|
|
ORDER BY created_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let company_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM company_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let customer_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM customer_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let photographer_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM photographer_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let makeup_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM makeup_artist_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let tutor_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM tutor_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let developer_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM developer_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let video_editor_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM video_editor_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let graphic_designer_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM graphic_designer_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let social_media_manager_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM social_media_manager_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let fitness_trainer_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM fitness_trainer_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
let catering_profiles = sqlx::query!(
|
||
|
|
r#"
|
||
|
|
SELECT user_id, status, updated_at
|
||
|
|
FROM catering_service_profiles
|
||
|
|
WHERE status = 'PENDING'
|
||
|
|
ORDER BY updated_at ASC
|
||
|
|
LIMIT $1 OFFSET $2
|
||
|
|
"#,
|
||
|
|
limit,
|
||
|
|
offset
|
||
|
|
)
|
||
|
|
.fetch_all(&state.pool)
|
||
|
|
.await;
|
||
|
|
|
||
|
|
match (
|
||
|
|
jobs,
|
||
|
|
requirements,
|
||
|
|
company_profiles,
|
||
|
|
customer_profiles,
|
||
|
|
photographer_profiles,
|
||
|
|
makeup_profiles,
|
||
|
|
tutor_profiles,
|
||
|
|
developer_profiles,
|
||
|
|
video_editor_profiles,
|
||
|
|
graphic_designer_profiles,
|
||
|
|
social_media_manager_profiles,
|
||
|
|
fitness_trainer_profiles,
|
||
|
|
catering_profiles,
|
||
|
|
) {
|
||
|
|
(
|
||
|
|
Ok(jobs),
|
||
|
|
Ok(requirements),
|
||
|
|
Ok(company_profiles),
|
||
|
|
Ok(customer_profiles),
|
||
|
|
Ok(photographer_profiles),
|
||
|
|
Ok(makeup_profiles),
|
||
|
|
Ok(tutor_profiles),
|
||
|
|
Ok(developer_profiles),
|
||
|
|
Ok(video_editor_profiles),
|
||
|
|
Ok(graphic_designer_profiles),
|
||
|
|
Ok(social_media_manager_profiles),
|
||
|
|
Ok(fitness_trainer_profiles),
|
||
|
|
Ok(catering_profiles),
|
||
|
|
) => (
|
||
|
|
StatusCode::OK,
|
||
|
|
Json(serde_json::json!({
|
||
|
|
"jobs": jobs,
|
||
|
|
"requirements": requirements,
|
||
|
|
"profiles": {
|
||
|
|
"company": company_profiles,
|
||
|
|
"customer": customer_profiles,
|
||
|
|
"photographer": photographer_profiles,
|
||
|
|
"makeup_artist": makeup_profiles,
|
||
|
|
"tutor": tutor_profiles,
|
||
|
|
"developer": developer_profiles,
|
||
|
|
"video_editor": video_editor_profiles,
|
||
|
|
"graphic_designer": graphic_designer_profiles,
|
||
|
|
"social_media_manager": social_media_manager_profiles,
|
||
|
|
"fitness_trainer": fitness_trainer_profiles,
|
||
|
|
"catering_services": catering_profiles
|
||
|
|
},
|
||
|
|
"pagination": { "page": page, "limit": limit }
|
||
|
|
})),
|
||
|
|
)
|
||
|
|
.into_response(),
|
||
|
|
(Err(e), _, _, _, _, _, _, _, _, _, _, _, _)
|
||
|
|
| (_, Err(e), _, _, _, _, _, _, _, _, _, _, _)
|
||
|
|
| (_, _, Err(e), _, _, _, _, _, _, _, _, _, _)
|
||
|
|
| (_, _, _, Err(e), _, _, _, _, _, _, _, _, _)
|
||
|
|
| (_, _, _, _, Err(e), _, _, _, _, _, _, _, _)
|
||
|
|
| (_, _, _, _, _, Err(e), _, _, _, _, _, _, _)
|
||
|
|
| (_, _, _, _, _, _, Err(e), _, _, _, _, _, _)
|
||
|
|
| (_, _, _, _, _, _, _, Err(e), _, _, _, _, _)
|
||
|
|
| (_, _, _, _, _, _, _, _, Err(e), _, _, _, _)
|
||
|
|
| (_, _, _, _, _, _, _, _, _, Err(e), _, _, _)
|
||
|
|
| (_, _, _, _, _, _, _, _, _, _, Err(e), _, _)
|
||
|
|
| (_, _, _, _, _, _, _, _, _, _, _, Err(e), _)
|
||
|
|
| (_, _, _, _, _, _, _, _, _, _, _, _, Err(e)) => {
|
||
|
|
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn approve_company_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(user_id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
match sqlx::query!(
|
||
|
|
"UPDATE company_profiles SET status = 'APPROVED', updated_at = NOW() WHERE user_id = $1",
|
||
|
|
user_id
|
||
|
|
)
|
||
|
|
.execute(&state.pool)
|
||
|
|
.await
|
||
|
|
{
|
||
|
|
Ok(result) if result.rows_affected() > 0 => {
|
||
|
|
(StatusCode::OK, Json(serde_json::json!({ "user_id": user_id, "status": "APPROVED" }))).into_response()
|
||
|
|
}
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Company profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn reject_company_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(user_id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
match sqlx::query!(
|
||
|
|
"UPDATE company_profiles SET status = 'REJECTED', updated_at = NOW() WHERE user_id = $1",
|
||
|
|
user_id
|
||
|
|
)
|
||
|
|
.execute(&state.pool)
|
||
|
|
.await
|
||
|
|
{
|
||
|
|
Ok(result) if result.rows_affected() > 0 => {
|
||
|
|
(StatusCode::OK, Json(serde_json::json!({ "user_id": user_id, "status": "REJECTED" }))).into_response()
|
||
|
|
}
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Company profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn approve_customer_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(user_id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
match sqlx::query!(
|
||
|
|
"UPDATE customer_profiles SET status = 'APPROVED', updated_at = NOW() WHERE user_id = $1",
|
||
|
|
user_id
|
||
|
|
)
|
||
|
|
.execute(&state.pool)
|
||
|
|
.await
|
||
|
|
{
|
||
|
|
Ok(result) if result.rows_affected() > 0 => {
|
||
|
|
(StatusCode::OK, Json(serde_json::json!({ "user_id": user_id, "status": "APPROVED" }))).into_response()
|
||
|
|
}
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Customer profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn reject_customer_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(user_id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
match sqlx::query!(
|
||
|
|
"UPDATE customer_profiles SET status = 'REJECTED', updated_at = NOW() WHERE user_id = $1",
|
||
|
|
user_id
|
||
|
|
)
|
||
|
|
.execute(&state.pool)
|
||
|
|
.await
|
||
|
|
{
|
||
|
|
Ok(result) if result.rows_affected() > 0 => {
|
||
|
|
(StatusCode::OK, Json(serde_json::json!({ "user_id": user_id, "status": "REJECTED" }))).into_response()
|
||
|
|
}
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Customer profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn professional_profile_table(role_key: &str) -> Option<&'static str> {
|
||
|
|
match role_key {
|
||
|
|
"PHOTOGRAPHER" => Some("photographer_profiles"),
|
||
|
|
"MAKEUP_ARTIST" => Some("makeup_artist_profiles"),
|
||
|
|
"TUTOR" => Some("tutor_profiles"),
|
||
|
|
"DEVELOPER" => Some("developer_profiles"),
|
||
|
|
"VIDEO_EDITOR" => Some("video_editor_profiles"),
|
||
|
|
"GRAPHIC_DESIGNER" => Some("graphic_designer_profiles"),
|
||
|
|
"SOCIAL_MEDIA_MANAGER" => Some("social_media_manager_profiles"),
|
||
|
|
"FITNESS_TRAINER" => Some("fitness_trainer_profiles"),
|
||
|
|
"CATERING_SERVICES" => Some("catering_service_profiles"),
|
||
|
|
_ => None,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn approve_professional_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path((role_key, user_id)): Path<(String, Uuid)>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
let role_key = role_key.to_uppercase();
|
||
|
|
let Some(table) = professional_profile_table(&role_key) else {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Unsupported professional role_key").into_response();
|
||
|
|
};
|
||
|
|
let query = format!(
|
||
|
|
"UPDATE {} SET status = 'APPROVED', rejection_reason = NULL, approved_at = NOW(), updated_at = NOW() WHERE user_id = $1",
|
||
|
|
table
|
||
|
|
);
|
||
|
|
match sqlx::query(&query).bind(user_id).execute(&state.pool).await {
|
||
|
|
Ok(result) if result.rows_affected() > 0 => (
|
||
|
|
StatusCode::OK,
|
||
|
|
Json(serde_json::json!({ "user_id": user_id, "role_key": role_key, "status": "APPROVED" })),
|
||
|
|
)
|
||
|
|
.into_response(),
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Professional profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn reject_professional_profile(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path((role_key, user_id)): Path<(String, Uuid)>,
|
||
|
|
Json(payload): Json<RejectPayload>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
let role_key = role_key.to_uppercase();
|
||
|
|
let Some(table) = professional_profile_table(&role_key) else {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Unsupported professional role_key").into_response();
|
||
|
|
};
|
||
|
|
let query = format!(
|
||
|
|
"UPDATE {} SET status = 'REJECTED', rejection_reason = $2, updated_at = NOW() WHERE user_id = $1",
|
||
|
|
table
|
||
|
|
);
|
||
|
|
match sqlx::query(&query)
|
||
|
|
.bind(user_id)
|
||
|
|
.bind(payload.reason.unwrap_or_else(|| "Profile rejected".to_string()))
|
||
|
|
.execute(&state.pool)
|
||
|
|
.await
|
||
|
|
{
|
||
|
|
Ok(result) if result.rows_affected() > 0 => (
|
||
|
|
StatusCode::OK,
|
||
|
|
Json(serde_json::json!({ "user_id": user_id, "role_key": role_key, "status": "REJECTED" })),
|
||
|
|
)
|
||
|
|
.into_response(),
|
||
|
|
Ok(_) => (StatusCode::NOT_FOUND, "Professional profile not found").into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn approve_job(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
let existing = match JobRepository::get_by_id(&state.pool, id).await {
|
||
|
|
Ok(Some(job)) => job,
|
||
|
|
Ok(None) => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
||
|
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
};
|
||
|
|
|
||
|
|
if existing.status != "PENDING_APPROVAL" {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Job is not pending approval").into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
match JobRepository::approve(&state.pool, id, auth.user_id).await {
|
||
|
|
Ok(job) => (StatusCode::OK, Json(job)).into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn reject_job(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(id): Path<Uuid>,
|
||
|
|
Json(payload): Json<RejectPayload>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
let existing = match JobRepository::get_by_id(&state.pool, id).await {
|
||
|
|
Ok(Some(job)) => job,
|
||
|
|
Ok(None) => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
||
|
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
};
|
||
|
|
|
||
|
|
if existing.status != "PENDING_APPROVAL" {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Job is not pending approval").into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
match JobRepository::reject(&state.pool, id, payload.reason).await {
|
||
|
|
Ok(job) => (StatusCode::OK, Json(job)).into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn approve_requirement(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(id): Path<Uuid>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
let existing = match RequirementRepository::get_by_id(&state.pool, id).await {
|
||
|
|
Ok(Some(req)) => req,
|
||
|
|
Ok(None) => return (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
|
||
|
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
};
|
||
|
|
|
||
|
|
if existing.status != "PENDING_APPROVAL" {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Requirement is not pending approval").into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
match RequirementRepository::approve(&state.pool, id, auth.user_id).await {
|
||
|
|
Ok(req) => (StatusCode::OK, Json(req)).into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn reject_requirement(
|
||
|
|
auth: AuthUser,
|
||
|
|
State(state): State<AppState>,
|
||
|
|
Path(id): Path<Uuid>,
|
||
|
|
Json(payload): Json<RejectPayload>,
|
||
|
|
) -> impl IntoResponse {
|
||
|
|
if let Err(e) = require_admin(&auth) {
|
||
|
|
return e.into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
let existing = match RequirementRepository::get_by_id(&state.pool, id).await {
|
||
|
|
Ok(Some(req)) => req,
|
||
|
|
Ok(None) => return (StatusCode::NOT_FOUND, "Requirement not found").into_response(),
|
||
|
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
};
|
||
|
|
|
||
|
|
if existing.status != "PENDING_APPROVAL" {
|
||
|
|
return (StatusCode::BAD_REQUEST, "Requirement is not pending approval").into_response();
|
||
|
|
}
|
||
|
|
|
||
|
|
match RequirementRepository::reject(&state.pool, id, payload.reason).await {
|
||
|
|
Ok(req) => (StatusCode::OK, Json(req)).into_response(),
|
||
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||
|
|
}
|
||
|
|
}
|