fix: return JSON errors from create_job and add admin job-slot grant endpoint
All checks were successful
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (catering-services) (push) Successful in 11s
build-and-release / build (fitness-trainers) (push) Successful in 7s
build-and-release / build (developers) (push) Successful in 12s
build-and-release / build (cron) (push) Successful in 18s
build-and-release / build (employees) (push) Successful in 16s
build-and-release / build (gateway) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 7s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (users) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (companies) (push) Successful in 1m21s
All checks were successful
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (catering-services) (push) Successful in 11s
build-and-release / build (fitness-trainers) (push) Successful in 7s
build-and-release / build (developers) (push) Successful in 12s
build-and-release / build (cron) (push) Successful in 18s
build-and-release / build (employees) (push) Successful in 16s
build-and-release / build (gateway) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 7s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (users) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (companies) (push) Successful in 1m21s
create_job returned plain-text bodies on every failure branch except
quota-exhausted, so the frontend's res.json() silently failed and always
showed a generic "Failed to create job" message regardless of the real
cause (company not approved, quota exhausted, DB error).
Also adds PATCH /api/admin/companies/{id}/job-slots as a manual top-up
for purchased_job_slots, since no self-serve purchase flow exists yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
b281eb5278
commit
ddb2110e86
2 changed files with 43 additions and 5 deletions
|
|
@ -21,6 +21,7 @@ pub fn router() -> Router<AppState> {
|
|||
.route("/{id}/approve", patch(approve_company))
|
||||
.route("/{id}/reject", patch(reject_company))
|
||||
.route("/{id}/suspend", patch(suspend_company))
|
||||
.route("/{id}/job-slots", patch(grant_job_slots))
|
||||
.route("/jobs", get(list_jobs))
|
||||
.route("/jobs/{id}/approve", post(approve_job))
|
||||
.route("/jobs/{id}/reject", post(reject_job))
|
||||
|
|
@ -224,6 +225,36 @@ async fn suspend_company(
|
|||
Ok(Json(serde_json::json!({ "status": "SUSPENDED" })))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GrantJobSlotsPayload {
|
||||
pub slots: i32,
|
||||
}
|
||||
|
||||
/// Manual top-up for `purchased_job_slots` while the self-serve purchase flow
|
||||
/// (TraceCoin/PayU) doesn't exist yet. Support-only unblock path.
|
||||
async fn grant_job_slots(
|
||||
_auth: AuthUser,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(payload): Json<GrantJobSlotsPayload>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
if payload.slots <= 0 {
|
||||
return Err((StatusCode::BAD_REQUEST, "slots must be a positive integer".to_string()));
|
||||
}
|
||||
|
||||
let new_total: i32 = sqlx::query_scalar(
|
||||
"UPDATE company_profiles SET purchased_job_slots = purchased_job_slots + $1, updated_at = NOW() WHERE id = $2 RETURNING purchased_job_slots"
|
||||
)
|
||||
.bind(payload.slots)
|
||||
.bind(id)
|
||||
.fetch_optional(&state.pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?
|
||||
.ok_or((StatusCode::NOT_FOUND, "Company not found".to_string()))?;
|
||||
|
||||
Ok(Json(serde_json::json!({ "purchased_job_slots": new_total })))
|
||||
}
|
||||
|
||||
async fn list_jobs(
|
||||
_auth: AuthUser,
|
||||
State(state): State<AppState>,
|
||||
|
|
|
|||
|
|
@ -185,17 +185,24 @@ async fn create_job(
|
|||
) -> impl IntoResponse {
|
||||
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
||||
Ok(Some(c)) => c,
|
||||
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
||||
Ok(None) => return (StatusCode::NOT_FOUND, Json(serde_json::json!({ "error": "Company not found" }))).into_response(),
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() }))).into_response(),
|
||||
};
|
||||
|
||||
if company.status != "APPROVED" {
|
||||
return (StatusCode::FORBIDDEN, "Company profile approval is required before posting jobs").into_response();
|
||||
return (
|
||||
StatusCode::FORBIDDEN,
|
||||
Json(serde_json::json!({
|
||||
"error": "Company profile approval is required before posting jobs",
|
||||
"code": "COMPANY_NOT_APPROVED"
|
||||
})),
|
||||
).into_response();
|
||||
}
|
||||
|
||||
// --- New Quota Logic ---
|
||||
let jobs_this_month = match JobRepository::count_by_company_id_this_month(&state.pool, company.id).await {
|
||||
Ok(count) => count,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() }))).into_response(),
|
||||
};
|
||||
|
||||
if jobs_this_month >= 1 {
|
||||
|
|
@ -221,7 +228,7 @@ async fn create_job(
|
|||
|
||||
if let Err(e) = deduct_result {
|
||||
tracing::error!("Failed to deduct job slot: {}", e);
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "Failed to deduct quota").into_response();
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "Failed to deduct quota" }))).into_response();
|
||||
}
|
||||
}
|
||||
// -----------------------
|
||||
|
|
@ -251,7 +258,7 @@ async fn create_job(
|
|||
}
|
||||
(StatusCode::CREATED, Json(job)).into_response()
|
||||
}
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": e.to_string() }))).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue