chore: cleanup — remove stub professionals app, empty crates, unused import
- Delete apps/professionals (all stub handlers returning mock data, shadowed by individual profession services on ports 8085-8093) - Delete empty crate dirs: crates/config, crates/errors, crates/observability (already removed from Cargo.toml workspace members, stale directories) - Delete empty apps/jobseekers dir (duplicate of apps/job_seekers) - Remove unused Uri import from gateway main.rs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e1ea3f5ffe
commit
d49a29b965
3 changed files with 1 additions and 253 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::{Request, State},
|
extract::{Request, State},
|
||||||
http::{HeaderValue, Method, StatusCode, Uri},
|
http::{HeaderValue, Method, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::any,
|
routing::any,
|
||||||
Router,
|
Router,
|
||||||
|
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
use axum::{
|
|
||||||
extract::{Path, Query, State},
|
|
||||||
http::StatusCode,
|
|
||||||
response::IntoResponse,
|
|
||||||
routing::{delete, get, patch, post},
|
|
||||||
Json, Router,
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::PgPool;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
/// Shared professional router — all 9 profession apps mount this.
|
|
||||||
/// Each profession app wraps this under its own /api/<profession_key> path.
|
|
||||||
pub fn router() -> Router<PgPool> {
|
|
||||||
Router::new()
|
|
||||||
// Professional profile
|
|
||||||
.route("/profile/me", get(get_profile).patch(update_profile))
|
|
||||||
// Marketplace (requirements feed)
|
|
||||||
.route("/marketplace", get(browse_marketplace))
|
|
||||||
.route("/marketplace/{id}", get(get_requirement_detail))
|
|
||||||
// Lead requests
|
|
||||||
.route("/leads/request", post(send_lead_request))
|
|
||||||
.route("/leads/requests/me", get(my_requests))
|
|
||||||
.route("/leads/requests/{id}", delete(cancel_request))
|
|
||||||
.route("/leads/accepted/me", get(accepted_leads))
|
|
||||||
.route("/leads/accepted/{id}", get(accepted_lead_detail))
|
|
||||||
// Portfolio
|
|
||||||
.route("/portfolio/me", get(list_portfolio))
|
|
||||||
.route("/portfolio", post(create_portfolio_item))
|
|
||||||
.route("/portfolio/{id}", patch(update_portfolio_item).delete(delete_portfolio_item))
|
|
||||||
// Services
|
|
||||||
.route("/services/me", get(list_services))
|
|
||||||
.route("/services", post(create_service))
|
|
||||||
.route("/services/{id}", patch(update_service).delete(delete_service))
|
|
||||||
// Wallet
|
|
||||||
.route("/wallet/balance", get(wallet_balance))
|
|
||||||
.route("/wallet/ledger", get(wallet_ledger))
|
|
||||||
.route("/wallet/invoices", get(wallet_invoices))
|
|
||||||
.route("/wallet/invoices/{id}", get(wallet_invoice_detail))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct PaginationQuery {
|
|
||||||
pub page: Option<i64>,
|
|
||||||
pub limit: Option<i64>,
|
|
||||||
pub profession_key: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct LeadRequestPayload {
|
|
||||||
pub requirement_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct CreatePortfolioPayload {
|
|
||||||
pub title: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub tags: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct CreateServicePayload {
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub price: i32, // in paise
|
|
||||||
pub duration_minutes: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_profile(State(pool): State<PgPool>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": null, "display_name": null, "status": "ACTIVE" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_profile(State(pool): State<PgPool>, Json(_p): Json<serde_json::Value>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "message": "Profile updated" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn browse_marketplace(
|
|
||||||
State(pool): State<PgPool>,
|
|
||||||
Query(q): Query<PaginationQuery>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
// Returns OPEN + non-expired requirements for this profession_key
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({
|
|
||||||
"data": [],
|
|
||||||
"pagination": { "page": q.page.unwrap_or(1), "limit": q.limit.unwrap_or(20), "total": 0 }
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_requirement_detail(
|
|
||||||
State(pool): State<PgPool>,
|
|
||||||
Path(id): Path<Uuid>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
// Customer contact details NOT included — only revealed after acceptance
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "status": "OPEN" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_lead_request(
|
|
||||||
State(pool): State<PgPool>,
|
|
||||||
Json(payload): Json<LeadRequestPayload>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
// Server enforces: 25 Tracecoins reserved, requirement OPEN, max 20 requests, no duplicate
|
|
||||||
let req_id = payload.requirement_id.clone();
|
|
||||||
(StatusCode::CREATED, Json(serde_json::json!({
|
|
||||||
"id": Uuid::new_v4().to_string(),
|
|
||||||
"requirement_id": req_id,
|
|
||||||
"status": "PENDING",
|
|
||||||
"tracecoins_reserved": 25,
|
|
||||||
"expires_at": (chrono::Utc::now() + chrono::Duration::days(1)).to_rfc3339()
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn my_requests(State(pool): State<PgPool>, Query(q): Query<PaginationQuery>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [], "pagination": { "page": q.page.unwrap_or(1), "limit": 20 } })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn cancel_request(State(pool): State<PgPool>, Path(id): Path<Uuid>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "message": "Request cancelled" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn accepted_leads(State(pool): State<PgPool>, Query(q): Query<PaginationQuery>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
// Returns leads where status = ACCEPTED — includes customer contact info
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [], "pagination": { "page": q.page.unwrap_or(1), "limit": 20 } })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn accepted_lead_detail(State(pool): State<PgPool>, Path(id): Path<Uuid>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string() })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_portfolio(State(pool): State<PgPool>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [] })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_portfolio_item(
|
|
||||||
State(pool): State<PgPool>,
|
|
||||||
Json(p): Json<CreatePortfolioPayload>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::CREATED, Json(serde_json::json!({ "id": Uuid::new_v4().to_string(), "title": p.title })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_portfolio_item(State(pool): State<PgPool>, Path(id): Path<Uuid>, Json(_p): Json<serde_json::Value>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "message": "Updated" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_portfolio_item(State(pool): State<PgPool>, Path(id): Path<Uuid>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "message": "Deleted" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_services(State(pool): State<PgPool>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [] })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_service(
|
|
||||||
State(pool): State<PgPool>,
|
|
||||||
Json(p): Json<CreateServicePayload>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::CREATED, Json(serde_json::json!({ "id": Uuid::new_v4().to_string(), "name": p.name })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_service(State(pool): State<PgPool>, Path(id): Path<Uuid>, Json(_p): Json<serde_json::Value>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "message": "Updated" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_service(State(pool): State<PgPool>, Path(id): Path<Uuid>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string(), "message": "Deleted" })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wallet_balance(State(pool): State<PgPool>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
// Read-only — no write on client
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "balance": 0, "reserved": 0, "available": 0 })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wallet_ledger(State(pool): State<PgPool>, Query(q): Query<PaginationQuery>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [], "pagination": { "page": q.page.unwrap_or(1), "limit": 20 } })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wallet_invoices(State(pool): State<PgPool>, Query(q): Query<PaginationQuery>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "data": [], "pagination": { "page": q.page.unwrap_or(1), "limit": 20 } })))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wallet_invoice_detail(State(pool): State<PgPool>, Path(id): Path<Uuid>) -> impl IntoResponse {
|
|
||||||
let _ = pool;
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({ "id": id.to_string() })))
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
mod handlers;
|
|
||||||
|
|
||||||
use axum::{Router, http::Method};
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(tracing_subscriber::EnvFilter::new(
|
|
||||||
std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
|
|
||||||
))
|
|
||||||
.with(tracing_subscriber::fmt::layer())
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let database_url =
|
|
||||||
std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
|
||||||
|
|
||||||
let pool = sqlx::postgres::PgPoolOptions::new()
|
|
||||||
.max_connections(5)
|
|
||||||
.connect(&database_url)
|
|
||||||
.await
|
|
||||||
.expect("Failed to connect to postgres");
|
|
||||||
|
|
||||||
tracing::info!("Professionals service — connected to database");
|
|
||||||
|
|
||||||
let cors = CorsLayer::new()
|
|
||||||
.allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE, Method::OPTIONS])
|
|
||||||
.allow_origin(Any)
|
|
||||||
.allow_headers(Any);
|
|
||||||
|
|
||||||
let app = Router::new()
|
|
||||||
.route("/health", axum::routing::get(|| async { "Professionals Service OK" }))
|
|
||||||
// All 9 profession types share the same router under /api/professionals
|
|
||||||
.nest("/api/professionals", handlers::router())
|
|
||||||
.layer(cors)
|
|
||||||
.with_state(pool);
|
|
||||||
|
|
||||||
let port: u16 = std::env::var("PORT")
|
|
||||||
.unwrap_or_else(|_| "8084".to_string())
|
|
||||||
.parse()
|
|
||||||
.expect("PORT must be a number");
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
|
||||||
|
|
||||||
tracing::info!("Professionals service listening on {}", addr);
|
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue