- Add crates/cache with client, otp, rate_limit, token, lead, jobs modules
- OTP tokens stored in Redis (15-min TTL, single-use GETDEL on verify)
- Refresh tokens stored in Redis (30-day TTL) — removed DB storage
- Password reset tokens stored in Redis (1-hour TTL, single-use)
- Rate limiting: register (10/hr), login (10/15min), OTP resend (3/hr), lead (5/hr), job post (20/hr)
- Lead request deduplication: 24-hour Redis lock per professional+requirement pair
- Marketplace listings cached in Redis (5-min TTL per profession+page+limit)
- Add ProfessionState{pool, redis} to contracts crate, replacing bare PgPool in all 9 profession apps
- All profession handlers and main.rs updated to use ProfessionState
- REDIS_URL env var (default: redis://127.0.0.1:6379) used across all services
- Fix profession model struct name mangling in 6 handlers (MakeupArtistRepository etc.)
- Add custom_data JSONB migration for all 9 profession profile tables
- Add onboarding_state model and repository (save_progress, complete, is_complete)
- Add onboarding handler accepting roleKey:String (not role_id:UUID) for frontend compat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 lines
1.3 KiB
Rust
28 lines
1.3 KiB
Rust
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
|
use db::models::graphic_designer::{GraphicDesignerRepository, UpsertGraphicDesignerProfilePayload};
|
|
use contracts::{auth_middleware::AuthUser, ProfessionState};
|
|
|
|
pub fn router() -> Router<ProfessionState> {
|
|
Router::new()
|
|
.route("/profile/me", get(get_profile).patch(update_profile))
|
|
.merge(contracts::profession_shared::shared_routes("GRAPHIC_DESIGNER"))
|
|
}
|
|
|
|
async fn get_profile(State(state): State<ProfessionState>, auth: AuthUser) -> impl IntoResponse {
|
|
match GraphicDesignerRepository::get_by_user_id(&state.pool, auth.user_id).await {
|
|
Ok(Some(p)) => (StatusCode::OK, Json(p)).into_response(),
|
|
Ok(None) => (StatusCode::NOT_FOUND, "Profile not found").into_response(),
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
}
|
|
}
|
|
|
|
async fn update_profile(
|
|
State(state): State<ProfessionState>,
|
|
auth: AuthUser,
|
|
Json(payload): Json<UpsertGraphicDesignerProfilePayload>,
|
|
) -> impl IntoResponse {
|
|
match GraphicDesignerRepository::upsert(&state.pool, auth.user_id, payload).await {
|
|
Ok(p) => (StatusCode::OK, Json(p)).into_response(),
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
}
|
|
}
|