refactor(auth): migrate to httpOnly cookies and update vendor listings

Migrated JWT authentication from localStorage to httpOnly cookies using axum-extra. Refactored vendor listing and edit pages to use the centralized API client. Updated schema and data models to support these changes.
This commit is contained in:
2026-02-09 16:25:38 -05:00
parent feb1a173ec
commit caa318508b
11 changed files with 612 additions and 195 deletions

View File

@@ -1,12 +1,11 @@
use axum::{
http::{header, Method},
Router,
};
use std::env;
use tower_http::cors::{CorsLayer, Any};
use tower_http::cors::CorsLayer;
use crate::auth::structs::AppState;
use crate::auth::auth::{auth_middleware, login, register};
use crate::auth::auth::{auth_middleware, login, register, logout};
use crate::data::data::get_user;
use crate::state::data::{get_counties_by_state, get_county_by_id};
use crate::listing::data::{get_listings, get_listing_by_id, get_listings_by_county, create_listing, update_listing, delete_listing};
@@ -22,17 +21,32 @@ mod listing;
#[tokio::main]
async fn main() {
// Initialize tracing first (before any logging)
// RUST_LOG env var controls log level, e.g. RUST_LOG=debug or RUST_LOG=api_rust=debug
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::from_default_env()
.add_directive("api_rust=info".parse().unwrap())
)
.init();
tracing::info!("Starting NewEnglandBio API server...");
// Load environment variables
dotenv::dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let _jwt_secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set");
let frontend_origin = env::var("FRONTEND_ORIGIN").unwrap_or_else(|_| "http://localhost:9551".to_string());
tracing::info!(frontend_origin = %frontend_origin, "Configuration loaded");
// Connect to PostgreSQL
tracing::info!("Connecting to PostgreSQL database...");
let db_pool = PgPool::connect(&database_url)
.await
.expect("Failed to connect to database");
tracing::info!("Database connection established");
let db = Arc::new(db_pool);
// Create app state
@@ -41,11 +55,14 @@ async fn main() {
jwt_secret: env::var("JWT_SECRET").expect("JWT_SECRET must be set"),
};
// Configure CORS
// Configure CORS with credentials support for cookie auth
let cors = CorsLayer::new()
.allow_origin(tower_http::cors::AllowOrigin::exact(frontend_origin.parse::<header::HeaderValue>().unwrap()))
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers(Any);
.allow_headers([header::CONTENT_TYPE, header::AUTHORIZATION, header::ACCEPT])
.allow_credentials(true);
tracing::debug!("CORS configured for origin: {} with credentials", frontend_origin);
// Build router with separated public and protected routes
let protected_routes = Router::new()
@@ -61,15 +78,20 @@ async fn main() {
let public_routes = Router::new()
.route("/auth/register", axum::routing::post(register))
.route("/auth/login", axum::routing::post(login))
.route("/auth/logout", axum::routing::post(logout))
.route("/categories", axum::routing::get(crate::company::category::get_all_categories))
.route("/state/:state_abbr", axum::routing::get(get_counties_by_state))
.route("/state/:state_abbr/:county_id", axum::routing::get(get_county_by_id))
.route("/listings/county/:county_id", axum::routing::get(get_listings_by_county));
let app = public_routes.merge(protected_routes).with_state(state).layer(cors);
let app = public_routes
.merge(protected_routes)
.with_state(state)
.layer(cors);
// Print server status
println!("Server is running on http://0.0.0.0:9552");
tracing::info!("Routes configured");
tracing::info!("Server is running on http://0.0.0.0:9552");
tracing::info!("Press Ctrl+C to stop");
// Run server
axum::Server::bind(&"0.0.0.0:9552".parse().unwrap())