2026-03-17 20:42:51 +01:00
|
|
|
use axum::{
|
|
|
|
|
extract::{Path, Query, State},
|
|
|
|
|
http::StatusCode,
|
|
|
|
|
response::IntoResponse,
|
|
|
|
|
routing::{get, patch, post},
|
|
|
|
|
Json, Router,
|
|
|
|
|
};
|
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
use db::models::company::{CompanyRepository, UpsertCompanyProfilePayload};
|
|
|
|
|
use db::models::job::{JobRepository, CreateJobPayload as DbCreateJobPayload, UpdateJobPayload as DbUpdateJobPayload};
|
|
|
|
|
use db::models::application::ApplicationRepository;
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
use db::models::user::UserRepository;
|
2026-03-17 20:42:51 +01:00
|
|
|
use contracts::auth_middleware::AuthUser;
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
use crate::AppState;
|
2026-03-17 20:42:51 +01:00
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
pub fn router() -> Router<AppState> {
|
2026-03-17 20:42:51 +01:00
|
|
|
Router::new()
|
|
|
|
|
.route("/profile/me", get(get_profile).patch(update_profile))
|
|
|
|
|
.route("/jobs", get(list_jobs).post(create_job))
|
2026-03-25 22:15:07 +01:00
|
|
|
.route("/jobs/{id}", get(get_job).patch(update_job))
|
|
|
|
|
.route("/jobs/{id}/submit", post(submit_job))
|
|
|
|
|
.route("/jobs/{id}/close", post(close_job))
|
|
|
|
|
.route("/jobs/{id}/applications", get(list_applications))
|
|
|
|
|
.route("/applications/{id}/status", patch(update_application_status))
|
|
|
|
|
.route("/applications/{id}/contact", get(view_contact))
|
2026-03-17 20:42:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
pub struct PaginationQuery {
|
|
|
|
|
pub page: Option<i64>,
|
|
|
|
|
pub limit: Option<i64>,
|
|
|
|
|
pub status: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
pub struct CreateJobRequest {
|
|
|
|
|
pub title: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub location: String,
|
|
|
|
|
pub job_type: Option<String>,
|
|
|
|
|
pub salary_min: Option<i32>,
|
|
|
|
|
pub salary_max: Option<i32>,
|
|
|
|
|
pub experience_years: Option<i32>,
|
|
|
|
|
pub skills: Option<Vec<String>>,
|
|
|
|
|
pub category: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
pub struct UpdateApplicationStatusPayload {
|
|
|
|
|
pub status: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_profile(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
auth: AuthUser,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(profile)) => (StatusCode::OK, Json(profile)).into_response(),
|
|
|
|
|
Ok(None) => (StatusCode::NOT_FOUND, "Company profile not found").into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn update_profile(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
auth: AuthUser,
|
|
|
|
|
Json(payload): Json<UpsertCompanyProfilePayload>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match CompanyRepository::upsert(&state.pool, auth.user_id, payload).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(profile) => (StatusCode::OK, Json(profile)).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_jobs(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
auth: AuthUser,
|
|
|
|
|
Query(q): Query<PaginationQuery>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let page = q.page.unwrap_or(1);
|
|
|
|
|
let limit = q.limit.unwrap_or(20);
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::list_by_company_id(&state.pool, company.id, q.status, page, limit).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(jobs) => (StatusCode::OK, Json(serde_json::json!({
|
|
|
|
|
"data": jobs,
|
|
|
|
|
"pagination": { "page": page, "limit": limit }
|
|
|
|
|
}))).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn create_job(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
auth: AuthUser,
|
|
|
|
|
Json(payload): Json<CreateJobRequest>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-19 00:30:23 +01:00
|
|
|
if company.status != "APPROVED" {
|
|
|
|
|
return (StatusCode::FORBIDDEN, "Company profile approval is required before posting jobs").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 20:42:51 +01:00
|
|
|
let db_payload = DbCreateJobPayload {
|
|
|
|
|
company_id: company.id,
|
|
|
|
|
title: payload.title,
|
|
|
|
|
category: payload.category,
|
|
|
|
|
description: payload.description,
|
|
|
|
|
location: payload.location,
|
|
|
|
|
job_type: payload.job_type,
|
|
|
|
|
salary_min: payload.salary_min,
|
|
|
|
|
salary_max: payload.salary_max,
|
|
|
|
|
experience_years: payload.experience_years,
|
|
|
|
|
skills: payload.skills,
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::create(&state.pool, db_payload).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(job) => (StatusCode::CREATED, Json(job)).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_job(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
_auth: AuthUser,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(job)) => (StatusCode::OK, Json(job)).into_response(),
|
|
|
|
|
Ok(None) => (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn update_job(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
Json(payload): Json<DbUpdateJobPayload>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
2026-03-19 00:30:23 +01:00
|
|
|
|
|
|
|
|
if company.status != "APPROVED" {
|
|
|
|
|
return (StatusCode::FORBIDDEN, "Company profile approval is required before submitting jobs").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let job = match JobRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) if j.company_id == company.id => j,
|
|
|
|
|
Ok(Some(_)) => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::update(&state.pool, job.id, payload).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(updated) => (StatusCode::OK, Json(updated)).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn submit_job(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
|
|
|
|
|
let job = match JobRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) if j.company_id == company.id => j,
|
|
|
|
|
Ok(Some(_)) => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if job.status != "DRAFT" {
|
|
|
|
|
return (StatusCode::BAD_REQUEST, "Job already submitted or live").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::update_status(&state.pool, job.id, "PENDING_APPROVAL").await {
|
|
|
|
|
Ok(updated) => {
|
|
|
|
|
// Fire email to company user (ignore failures)
|
|
|
|
|
if let Ok(user) = UserRepository::get_by_id(&state.pool, auth.user_id).await {
|
|
|
|
|
let _ = state.mail.send_job_submitted_email(&user.email, &user.full_name, &updated.title).await;
|
|
|
|
|
}
|
|
|
|
|
(StatusCode::OK, Json(updated)).into_response()
|
|
|
|
|
}
|
2026-03-17 20:42:51 +01:00
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn close_job(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
|
|
|
|
|
let job = match JobRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) if j.company_id == company.id => j,
|
|
|
|
|
Ok(Some(_)) => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match JobRepository::update_status(&state.pool, job.id, "CLOSED").await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(updated) => (StatusCode::OK, Json(updated)).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_applications(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
Query(q): Query<PaginationQuery>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Company not found").into_response(),
|
|
|
|
|
};
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
|
|
|
|
|
let job = match JobRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) if j.company_id == company.id => j,
|
|
|
|
|
Ok(Some(_)) => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Job not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let page = q.page.unwrap_or(1);
|
|
|
|
|
let limit = q.limit.unwrap_or(20);
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match ApplicationRepository::list_by_job_id(&state.pool, job.id, q.status, page, limit).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(apps) => (StatusCode::OK, Json(serde_json::json!({
|
|
|
|
|
"data": apps,
|
|
|
|
|
"pagination": { "page": page, "limit": limit }
|
|
|
|
|
}))).into_response(),
|
|
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn update_application_status(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
Json(payload): Json<UpdateApplicationStatusPayload>,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let app = match ApplicationRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(a)) => a,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Application not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let job = match JobRepository::get_by_id(&state.pool, app.job_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) => j,
|
|
|
|
|
_ => return (StatusCode::INTERNAL_SERVER_ERROR, "Job lost").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if job.company_id != company.id {
|
|
|
|
|
return (StatusCode::FORBIDDEN, "Access denied").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
match ApplicationRepository::update_status(&state.pool, app.id, &payload.status).await {
|
|
|
|
|
Ok(updated) => {
|
|
|
|
|
// Notify applicant of status change (ignore failures)
|
|
|
|
|
let applicant_info = sqlx::query_as::<_, (String, String)>(
|
|
|
|
|
"SELECT u.full_name, u.email FROM users u INNER JOIN job_seekers js ON js.user_id = u.id WHERE js.id = $1",
|
|
|
|
|
)
|
|
|
|
|
.bind(app.job_seeker_id)
|
|
|
|
|
.fetch_optional(&state.pool)
|
|
|
|
|
.await;
|
|
|
|
|
if let Ok(Some((name, email))) = applicant_info {
|
|
|
|
|
let _ = state.mail.send_application_status_email(&email, &name, &job.title, &payload.status).await;
|
|
|
|
|
}
|
|
|
|
|
(StatusCode::OK, Json(updated)).into_response()
|
|
|
|
|
}
|
2026-03-17 20:42:51 +01:00
|
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn view_contact(
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
State(state): State<AppState>,
|
2026-03-17 20:42:51 +01:00
|
|
|
Path(id): Path<Uuid>,
|
|
|
|
|
auth: AuthUser,
|
|
|
|
|
) -> impl IntoResponse {
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let app = match ApplicationRepository::get_by_id(&state.pool, id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(a)) => a,
|
|
|
|
|
_ => return (StatusCode::NOT_FOUND, "Application not found").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let job = match JobRepository::get_by_id(&state.pool, app.job_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(j)) => j,
|
|
|
|
|
_ => return (StatusCode::INTERNAL_SERVER_ERROR, "Job lost").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
let company = match CompanyRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
2026-03-17 20:42:51 +01:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
|
_ => return (StatusCode::FORBIDDEN, "Access denied").into_response(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if job.company_id != company.id {
|
|
|
|
|
return (StatusCode::FORBIDDEN, "Access denied").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
feat(phase1): wire email notifications, shared email crate, AppState for services
- Create crates/email shared Mailer with 18+ templates (auth, approvals, jobs, leads, tracecoins)
- users/mail.rs now re-exports from shared crate (lettre dep removed)
- Wire password changed/reset emails in users auth handlers
- Wire profile approval/rejection emails in users approvals handlers (company, customer, all 9 professional types)
- Wire job approved/rejected emails in users approvals handlers
- Wire requirement approved email in users approvals handlers
- Add AppState (pool + mail) to companies service; wire submit_job and update_application_status emails
- Add AppState (pool + mail) to customers service; wire submit_requirement, approve_request, reject_request emails (incl. contact-exchange on lead acceptance)
- Add AppState (pool + storage) to job_seekers service with resume upload multipart handler
- Wire lead cancellation and accepted-leads handlers in contracts/profession_shared.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:42:48 +02:00
|
|
|
// If contact was already viewed for this application, return info without deducting again
|
|
|
|
|
if !app.contact_viewed {
|
|
|
|
|
let total_remaining = company.free_contact_views + company.purchased_contact_views;
|
|
|
|
|
if total_remaining <= 0 {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::PAYMENT_REQUIRED,
|
|
|
|
|
Json(serde_json::json!({
|
|
|
|
|
"error": "Contact view quota exhausted. Please purchase a package.",
|
|
|
|
|
"code": "QUOTA_EXHAUSTED"
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deduct from free views first, then purchased
|
|
|
|
|
let sql = if company.free_contact_views > 0 {
|
|
|
|
|
"UPDATE companies SET free_contact_views = free_contact_views - 1 WHERE id = $1"
|
|
|
|
|
} else {
|
|
|
|
|
"UPDATE companies SET purchased_contact_views = purchased_contact_views - 1 WHERE id = $1"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Err(e) = sqlx::query(sql).bind(company.id).execute(&state.pool).await {
|
|
|
|
|
tracing::error!("Failed to deduct contact view quota: {}", e);
|
|
|
|
|
return (StatusCode::INTERNAL_SERVER_ERROR, "Failed to deduct quota").into_response();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Err(e) = ApplicationRepository::mark_contact_viewed(&state.pool, app.id).await {
|
|
|
|
|
tracing::error!("Failed to mark contact viewed: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch job seeker contact info via job_seeker_id → job_seekers.user_id → users
|
|
|
|
|
let contact = sqlx::query_as::<_, (Option<String>, String, Option<String>)>(
|
|
|
|
|
r#"
|
|
|
|
|
SELECT u.full_name, u.email, u.phone
|
|
|
|
|
FROM users u
|
|
|
|
|
INNER JOIN job_seekers js ON js.user_id = u.id
|
|
|
|
|
WHERE js.id = $1
|
|
|
|
|
"#,
|
|
|
|
|
)
|
|
|
|
|
.bind(app.job_seeker_id)
|
|
|
|
|
.fetch_optional(&state.pool)
|
|
|
|
|
.await;
|
|
|
|
|
|
|
|
|
|
match contact {
|
|
|
|
|
Ok(Some((full_name, email, phone))) => {
|
|
|
|
|
// Fetch updated quota to return to client
|
|
|
|
|
let updated_company = CompanyRepository::get_by_user_id(&state.pool, auth.user_id)
|
|
|
|
|
.await
|
|
|
|
|
.ok()
|
|
|
|
|
.flatten();
|
|
|
|
|
let (free_remaining, purchased_remaining) = updated_company
|
|
|
|
|
.map(|c| (c.free_contact_views, c.purchased_contact_views))
|
|
|
|
|
.unwrap_or((0, 0));
|
|
|
|
|
|
|
|
|
|
(StatusCode::OK, Json(serde_json::json!({
|
|
|
|
|
"application_id": id,
|
|
|
|
|
"full_name": full_name,
|
|
|
|
|
"email": email,
|
|
|
|
|
"phone": phone,
|
|
|
|
|
"quota": {
|
|
|
|
|
"free_remaining": free_remaining,
|
|
|
|
|
"purchased_remaining": purchased_remaining,
|
|
|
|
|
"total_remaining": free_remaining + purchased_remaining
|
|
|
|
|
}
|
|
|
|
|
})))
|
|
|
|
|
.into_response()
|
|
|
|
|
}
|
|
|
|
|
Ok(None) => (StatusCode::NOT_FOUND, "Applicant not found").into_response(),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
tracing::error!("Failed to fetch applicant contact: {}", e);
|
|
|
|
|
(StatusCode::INTERNAL_SERVER_ERROR, "Failed to fetch contact info").into_response()
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-17 20:42:51 +01:00
|
|
|
}
|