2025-12-26 20:01:08 -05:00
2026-02-09 09:28:45 -05:00
2025-06-10 16:54:56 -04:00
2025-06-10 16:54:56 -04:00
2025-06-08 13:27:50 -04:00
2025-12-26 20:01:08 -05:00

NewEnglandBio Rust API

RESTful API for heating oil/biofuel price comparison built with Axum 0.6. Serves the SvelteKit frontend and manages users, companies, listings, and scraped oil prices.

Tech Stack

  • Framework: Axum 0.6 (Tokio async runtime)
  • Database: PostgreSQL via sqlx 0.6
  • Auth: JWT (jsonwebtoken) + Argon2 password hashing
  • CORS: tower-http
  • Logging: tracing + tracing-subscriber

Project Structure

src/
├── main.rs              # Server startup, route definitions, CORS config
├── auth/
│   ├── auth.rs          # Register, login, logout, auth middleware
│   └── structs.rs       # User, Claims, LoginRequest, RegisterRequest
├── data/
│   └── data.rs          # get_user endpoint
├── company/
│   ├── company.rs       # Company CRUD (single handler, method dispatch)
│   ├── category.rs      # Service categories endpoint
│   └── structs.rs       # Company, ServiceCategory
├── listing/
│   ├── data.rs          # Listing CRUD + public county listings
│   └── structs.rs       # Listing, CreateListingRequest, UpdateListingRequest
├── state/
│   ├── data.rs          # County lookup endpoints
│   └── structs.rs       # County, ErrorResponse
└── oil_prices/
    ├── data.rs          # Oil prices by county
    └── structs.rs       # OilPrice

API Endpoints

Server listens on 0.0.0.0:9552.

Public Endpoints

Method Path Description
POST /auth/register Register a new user
POST /auth/login Login, returns JWT in httpOnly cookie
POST /auth/logout Clear auth cookie
GET /state/:state_abbr List counties in a state
GET /state/:state_abbr/:county_id Get a specific county
GET /categories List all service categories
GET /oil-prices/county/:county_id Oil prices for a county (sorted by price)
GET /listings/county/:county_id Active listings in a county

Protected Endpoints (require JWT)

Method Path Description
GET /user Get authenticated user info
GET /company Get user's active company
POST /company Create company
PUT /company Update company (or create if none)
DELETE /company Soft-delete company (sets active=false)
GET /listing Get all user's listings
GET /listing/:id Get a specific listing (owner only)
POST /listing Create listing
PUT /listing/:id Update listing (partial updates supported)
DELETE /listing/:id Delete listing

Request/Response Examples

Register:

curl -X POST http://localhost:9552/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username":"dealer1","password":"secret123","email":"dealer@example.com"}'

Login:

curl -X POST http://localhost:9552/auth/login \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"username":"dealer1","password":"secret123"}'

Get counties in Massachusetts:

curl http://localhost:9552/state/MA

Get oil prices for county 5:

curl http://localhost:9552/oil-prices/county/5

Create a listing (authenticated):

curl -X POST http://localhost:9552/listing \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "company_name": "Acme Oil",
    "is_active": true,
    "price_per_gallon": 3.29,
    "price_per_gallon_cash": 3.19,
    "bio_percent": 5,
    "service": true,
    "online_ordering": "none",
    "county_id": 5,
    "town": "Worcester"
  }'

Validation Rules

  • price_per_gallon must be > 0
  • price_per_gallon_cash must be >= 0
  • bio_percent must be 0-100
  • minimum_order must be >= 0

Setup

Environment

Create .env:

DATABASE_URL=postgres://postgres:password@192.168.1.204:5432/fuelprices
JWT_SECRET=YourSecretKeyHereAtLeast32Characters
FRONTEND_ORIGIN=http://localhost:9551
RUST_LOG=api_rust=info
Variable Required Description
DATABASE_URL Yes PostgreSQL connection string
JWT_SECRET Yes JWT signing key
FRONTEND_ORIGIN No CORS allowed origin (default http://localhost:9551)
RUST_LOG No Log level filter (default api_rust=info)

Database

Initialize the schema and seed data:

psql $DATABASE_URL -f schema.sql
psql $DATABASE_URL -f seed_categories.sql

Run Locally

cargo run

Run with Hot Reload

cargo install cargo-watch
cargo watch -x run

Docker

Production:

docker build -t api-rust .
docker run -p 9552:9552 --env-file .env api-rust

Development (with cargo-watch):

docker build -f Dockerfile.dev -t api-rust-dev .
docker run -p 9552:9552 -v $(pwd):/usr/src/app --env-file .env api-rust-dev

Authentication Flow

  1. User registers or logs in
  2. Server returns JWT as httpOnly cookie (auth_token, 24h expiry, SameSite=Lax)
  3. Subsequent requests include cookie automatically
  4. Auth middleware validates JWT, loads user from DB, attaches to request
  5. Also accepts Authorization: Bearer <token> header as fallback
  6. Logout clears the cookie

Database Tables

Table Description
users User accounts (username, hashed password, email)
company Company profiles (soft-delete via active flag)
listings Price listings per county
county Reference table of NE counties
oil_prices Scraped price data from crawler
service_categories Service category metadata

No foreign key constraints — integrity enforced at application level.

Error Responses

All errors return JSON:

{"error": "description of what went wrong"}

Status codes: 400 (validation), 401 (unauthorized), 404 (not found), 500 (internal).

Description
No description provided
Readme 209 KiB
Languages
Rust 99.8%
Dockerfile 0.2%