From d8d0ada83fc6634c9bd1bbf1703d703c2540a62a Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:12:29 -0600 Subject: [PATCH] Docs Cleanup --- DEPLOYMENT-READY.md | 437 ------------------ archive/platform-services/README.md | 88 ---- archive/platform-services/vehicles/README.md | 44 -- .../vehicles/api/__init__.py | 0 .../platform-services/vehicles/api/config.py | 50 -- .../vehicles/api/dependencies.py | 40 -- .../platform-services/vehicles/api/main.py | 202 -------- .../vehicles/api/models/__init__.py | 0 .../vehicles/api/models/responses.py | 84 ---- .../api/repositories/vehicles_repository.py | 79 ---- .../vehicles/api/routes/__init__.py | 0 .../vehicles/api/routes/vehicles.py | 116 ----- .../vehicles/api/routes/vin.py | 110 ----- .../vehicles/api/services/__init__.py | 0 .../vehicles/api/services/cache_service.py | 88 ---- .../vehicles/api/services/vehicles_service.py | 58 --- .../vehicles/docker/Dockerfile.api | 30 -- archive/platform-services/vehicles/makes.json | 69 --- .../vehicles/requirements-api.txt | 6 - .../vehicles/sql/schema/001_schema.sql | 73 --- .../sql/schema/002_constraints_indexes.sql | 23 - .../vehicles/sql/schema/003_seed_minimal.sql | 70 --- .../sql/schema/004_seed_filtered_makes.sql | 105 ----- .../sql/schema/005_seed_specific_vehicles.sql | 75 --- config/platform/production.yml.example | 23 - docs/ARCHITECTURE-OVERVIEW.md | 89 ++-- docs/PLATFORM-SERVICES.md | 33 +- docs/PROMPTS.md | 63 ++- docs/README.md | 55 ++- docs/VEHICLES-API.md | 49 +- nginx-proxy-service.yml | 21 - nginx-proxy/nginx.conf | 70 --- scripts/config-validator.sh | 10 - 33 files changed, 158 insertions(+), 2102 deletions(-) delete mode 100644 DEPLOYMENT-READY.md delete mode 100644 archive/platform-services/README.md delete mode 100644 archive/platform-services/vehicles/README.md delete mode 100644 archive/platform-services/vehicles/api/__init__.py delete mode 100644 archive/platform-services/vehicles/api/config.py delete mode 100644 archive/platform-services/vehicles/api/dependencies.py delete mode 100644 archive/platform-services/vehicles/api/main.py delete mode 100644 archive/platform-services/vehicles/api/models/__init__.py delete mode 100644 archive/platform-services/vehicles/api/models/responses.py delete mode 100644 archive/platform-services/vehicles/api/repositories/vehicles_repository.py delete mode 100644 archive/platform-services/vehicles/api/routes/__init__.py delete mode 100644 archive/platform-services/vehicles/api/routes/vehicles.py delete mode 100644 archive/platform-services/vehicles/api/routes/vin.py delete mode 100644 archive/platform-services/vehicles/api/services/__init__.py delete mode 100644 archive/platform-services/vehicles/api/services/cache_service.py delete mode 100644 archive/platform-services/vehicles/api/services/vehicles_service.py delete mode 100644 archive/platform-services/vehicles/docker/Dockerfile.api delete mode 100644 archive/platform-services/vehicles/makes.json delete mode 100644 archive/platform-services/vehicles/requirements-api.txt delete mode 100644 archive/platform-services/vehicles/sql/schema/001_schema.sql delete mode 100644 archive/platform-services/vehicles/sql/schema/002_constraints_indexes.sql delete mode 100644 archive/platform-services/vehicles/sql/schema/003_seed_minimal.sql delete mode 100644 archive/platform-services/vehicles/sql/schema/004_seed_filtered_makes.sql delete mode 100644 archive/platform-services/vehicles/sql/schema/005_seed_specific_vehicles.sql delete mode 100755 config/platform/production.yml.example delete mode 100644 nginx-proxy-service.yml delete mode 100644 nginx-proxy/nginx.conf diff --git a/DEPLOYMENT-READY.md b/DEPLOYMENT-READY.md deleted file mode 100644 index 15b8fcf..0000000 --- a/DEPLOYMENT-READY.md +++ /dev/null @@ -1,437 +0,0 @@ -# Platform Integration - Deployment Ready - -## Executive Summary - -Successfully integrated the external mvp-platform Python service into the backend as a TypeScript feature module. The application now runs with **5 containers instead of 6**, with all platform logic accessible via unified `/api/platform/*` endpoints. - -**Status**: CODE COMPLETE - Ready for container testing and deployment - -## What Changed - -### Architecture -- **Before**: 6 containers (Traefik, Frontend, Backend, PostgreSQL, Redis, **Platform**) -- **After**: 5 containers (Traefik, Frontend, Backend, PostgreSQL, Redis) -- **Reduction**: -16.7% container count, simplified deployment - -### Key Migrations -- VIN decoding: External HTTP service → Internal platform feature -- Vehicle data lookups: External HTTP service → Internal platform feature -- API endpoints: Various locations → Unified `/api/platform/*` -- Technology stack: Python FastAPI → TypeScript Fastify - -## Implementation Details - -### Wave 1: Foundation (4 agents in parallel) - -#### Agent 1: Platform Feature Creator -**Files Created**: 14 files -- Complete feature structure: `backend/src/features/platform/` -- API layer: Routes, controller with JWT authentication -- Domain layer: VIN decode service, vehicle data service, caching -- Data layer: PostgreSQL repository, vPIC client -- Tests: Unit and integration tests -- Documentation: Comprehensive README - -**Key Endpoints**: -- `GET /api/platform/years` -- `GET /api/platform/makes?year={year}` -- `GET /api/platform/models?year={year}&make_id={id}` -- `GET /api/platform/trims?year={year}&model_id={id}` -- `GET /api/platform/engines?year={year}&trim_id={id}` -- `GET /api/platform/vehicle?vin={vin}` (VIN decode) - -#### Agent 2: VIN Migration - Backend -**Files Modified**: 3 files -**Files Deleted**: 4 directories/files -- Migrated VIN decode from vehicles feature to platform feature -- Updated vehicles service: Uses `getVINDecodeService()` from platform -- Removed external platform client -- Removed vPIC client (moved to platform) -- Updated tests to mock platform service - -#### Agent 3: VIN Migration - Frontend -**Files Modified**: 2 files -- Updated API client: All calls now use `/api/platform/*` -- VIN decode: Changed to `GET /api/platform/vehicle?vin=X` -- Mobile enhancements: 44px touch targets, 16px fonts (no iOS zoom) -- Dual workflow: VIN decode OR manual dropdown selection - -#### Agent 4: Configuration Cleanup -**Files Modified**: 4 files -- Removed mvp-platform service from docker-compose.yml -- Removed PLATFORM_VEHICLES_API_URL environment variable -- Cleaned platform configuration from production.yml -- Updated Makefile: 6-container → 5-container architecture - -### Wave 2: Integration & Documentation (2 agents) - -#### Wave 2 Manual Tasks (Agent limits reached) -**Integration Verification**: -- Confirmed vehicles service integration with platform feature (vehicles.service.ts:46, 229) -- Confirmed platform routes registered in app.ts (app.ts:22, 110) -- Confirmed VIN decode workflow intact - -**Documentation Updates**: -- README.md: Updated to 5 containers -- CLAUDE.md: Updated architecture description -- docs/README.md: Added platform feature, updated container count -- AI-INDEX.md: Updated to 5-container stack - -**New Documentation**: -- `docs/PLATFORM-INTEGRATION-MIGRATION.md`: Complete migration notes -- `docs/PLATFORM-INTEGRATION-TESTING.md`: Comprehensive testing guide -- `backend/src/features/platform/README.md`: Platform feature documentation (created by Agent 1) - -### Wave 3: Deployment Preparation - -**Archive Platform Service**: -- Created: `archive/platform-services/` -- Moved: Python vehicles service to archive -- Created: Archive README with restoration instructions -- Status: Safe to delete after 30 days in production - -## Files Summary - -### Created (16 files total) -**Backend Platform Feature** (14 files): -- `backend/src/features/platform/index.ts` -- `backend/src/features/platform/README.md` -- `backend/src/features/platform/api/platform.controller.ts` -- `backend/src/features/platform/api/platform.routes.ts` -- `backend/src/features/platform/domain/vin-decode.service.ts` -- `backend/src/features/platform/domain/vehicle-data.service.ts` -- `backend/src/features/platform/domain/platform-cache.service.ts` -- `backend/src/features/platform/data/vehicle-data.repository.ts` -- `backend/src/features/platform/data/vpic-client.ts` -- `backend/src/features/platform/models/requests.ts` -- `backend/src/features/platform/models/responses.ts` -- `backend/src/features/platform/tests/unit/vin-decode.service.test.ts` -- `backend/src/features/platform/tests/unit/vehicle-data.service.test.ts` -- `backend/src/features/platform/tests/integration/platform.integration.test.ts` - -**Documentation** (2 files): -- `docs/PLATFORM-INTEGRATION-MIGRATION.md` -- `docs/PLATFORM-INTEGRATION-TESTING.md` -- `archive/platform-services/README.md` -- `DEPLOYMENT-READY.md` (this file) - -### Modified (10 files) -**Configuration**: -- `docker-compose.yml` - Removed mvp-platform service -- `.env` - Removed platform URL -- `config/app/production.yml` - Removed platform config -- `Makefile` - Updated to 5-container architecture - -**Backend**: -- `backend/src/app.ts` - Registered platform routes -- `backend/src/features/vehicles/domain/vehicles.service.ts` - Uses platform VIN decode -- `backend/src/features/vehicles/tests/unit/vehicles.service.test.ts` - Updated mocks - -**Frontend**: -- `frontend/src/features/vehicles/api/vehicles.api.ts` - Updated endpoints -- `frontend/src/features/vehicles/components/VehicleForm.tsx` - Mobile enhancements - -**Documentation**: -- `README.md` - Updated to 5 containers -- `CLAUDE.md` - Updated architecture -- `docs/README.md` - Added platform feature -- `AI-INDEX.md` - Updated architecture description - -### Deleted (4 locations) -- `backend/src/features/vehicles/external/platform-vehicles/` - Old external client -- `backend/src/features/vehicles/domain/platform-integration.service.ts` - Wrapper service -- `backend/src/features/vehicles/external/vpic/` - Moved to platform -- `backend/src/features/vehicles/tests/unit/vpic.client.test.ts` - Moved to platform tests - -### Archived (1 location) -- `mvp-platform-services/vehicles/` → `archive/platform-services/vehicles/` - -## Technical Highlights - -### VIN Decode Strategy -Multi-tier resilience: -1. **Redis Cache**: 7-day TTL for success, 1-hour for failures -2. **PostgreSQL**: `vehicles.f_decode_vin()` function for high-confidence decode -3. **vPIC API**: NHTSA fallback via circuit breaker (opossum) -4. **Graceful Degradation**: Meaningful errors when all sources fail - -### Circuit Breaker Configuration -- **Timeout**: 6 seconds -- **Error Threshold**: 50% -- **Reset Timeout**: 30 seconds -- **Monitoring**: State transition logging - -### Caching Strategy -- **Vehicle Data**: 6-hour TTL (mvp:platform:vehicle-data:*) -- **VIN Decode**: 7-day TTL success, 1-hour failure (mvp:platform:vin-decode:*) -- **Graceful Fallback**: Continues if Redis unavailable - -### Security -- **Authentication**: JWT required on all `/api/platform/*` endpoints -- **Input Validation**: Zod schemas for all requests -- **SQL Safety**: Prepared statements via node-postgres -- **Error Handling**: No internal details exposed to clients - -## Pre-Deployment Checklist - -### Code Quality -- TypeScript compilation: Needs verification in containers -- Linter: Needs verification in containers -- Tests: Need to run in Docker (see PLATFORM-INTEGRATION-TESTING.md) -- Git: Changes committed (ready for commit) - -### Integration Points -- Platform routes registered: app.ts:22, 110 -- Vehicles service integration: vehicles.service.ts:46, 229 -- Frontend API calls: Updated to /api/platform/* -- Docker compose: 5 services defined, validated with `docker compose config` - -### Documentation -- Architecture docs: Updated to 5 containers -- API documentation: Complete in platform/README.md -- Migration notes: docs/PLATFORM-INTEGRATION-MIGRATION.md -- Testing guide: docs/PLATFORM-INTEGRATION-TESTING.md -- Archive README: Restoration instructions documented - -## Deployment Steps - -### 1. Start Docker -```bash -# Ensure Docker is running -docker --version - -# Verify docker-compose.yml -docker compose config -``` - -### 2. Rebuild Containers -```bash -# Rebuild all containers with new code -make rebuild - -# Or manually: -docker compose down -docker compose build -docker compose up -d -``` - -### 3. Verify Container Count -```bash -# Should show exactly 5 services -docker compose ps - -# Expected: -# mvp-traefik - running -# mvp-frontend - running -# mvp-backend - running -# mvp-postgres - running -# mvp-redis - running -``` - -### 4. Run Tests -```bash -# TypeScript compilation -docker compose exec mvp-backend npm run type-check - -# Linter -docker compose exec mvp-backend npm run lint - -# Platform unit tests -docker compose exec mvp-backend npm test -- features/platform/tests/unit - -# Platform integration tests -docker compose exec mvp-backend npm test -- features/platform/tests/integration - -# Vehicles integration tests -docker compose exec mvp-backend npm test -- features/vehicles/tests/integration -``` - -### 5. Test Endpoints -```bash -# Health check -curl http://localhost:3001/health -# Expected: {"status":"healthy","features":["vehicles","documents","fuel-logs","stations","maintenance","platform"]} - -# With valid JWT token: -TOKEN="your-jwt-token" - -# Test years endpoint -curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:3001/api/platform/years - -# Test VIN decode -curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:3001/api/platform/vehicle?vin=1HGCM82633A123456 -``` - -### 6. Frontend Verification -```bash -# Access frontend -open https://motovaultpro.com - -# Test workflows: -# 1. Navigate to Vehicles → Add Vehicle -# 2. Test VIN decode: Enter VIN, click "Decode VIN" -# 3. Test manual selection: Select year → make → model -# 4. Test on mobile (Chrome DevTools responsive mode) -``` - -### 7. Monitor Logs -```bash -# Watch all logs -make logs - -# Or specific services: -make logs-backend | grep platform -make logs-frontend - -# Monitor for errors during testing -``` - -### 8. Performance Check -```bash -# Test cache performance -time curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:3001/api/platform/years - -# First call: < 500ms (cache miss) -# Second call: < 100ms (cache hit) -``` - -### 9. Redis Cache Verification -```bash -# Connect to Redis -docker compose exec mvp-redis redis-cli - -# Check platform cache keys -KEYS mvp:platform:* - -# Check TTL -TTL mvp:platform:years -TTL mvp:platform:vin-decode:1HGCM82633A123456 -``` - -### 10. Create Git Tag -```bash -# After all tests pass -git add . -git commit -m "Platform Integration Complete - 5 Container Architecture - -- Integrated mvp-platform Python service into backend as TypeScript platform feature -- Reduced container count from 6 to 5 -- Migrated VIN decode and vehicle data lookups to /api/platform/* -- Updated frontend to use unified platform endpoints -- Enhanced mobile responsiveness (44px touch targets, 16px fonts) -- Comprehensive testing guide and migration documentation - -Wave 1: Platform Feature Creator, VIN Migration (Backend/Frontend), Configuration Cleanup -Wave 2: Integration Verification, Documentation Updates -Wave 3: Archive Platform Service, Deployment Preparation" - -git tag v1.0-platform-integrated -``` - -## Rollback Plan - -If critical issues discovered: - -```bash -# 1. Restore docker-compose.yml -git restore docker-compose.yml - -# 2. Restore platform service -mv archive/platform-services/vehicles mvp-platform-services/ - -# 3. Rebuild containers -docker compose down -docker compose up -d - -# 4. Revert code changes -git revert HEAD -``` - -## Success Metrics - -Post-deployment monitoring (24 hours): - -- Container count: 5 (down from 6) -- All automated tests: Passing -- VIN decode response time: <500ms -- Dropdown response time: <100ms -- Redis cache hit rate: >80% -- Zero errors in logs -- Mobile + desktop: Both workflows functional -- TypeScript compilation: Zero errors -- Linter: Zero issues - -## Known Limitations - -### Testing Status -- Tests created but not yet executed (requires Docker) -- TypeScript compilation not yet verified in containers -- Integration tests not yet run in containers - -### User Testing Needed -- VIN decode workflow: Not yet tested end-to-end -- Dropdown cascade: Not yet tested in browser -- Mobile responsiveness: Not yet tested on devices - -### Performance -- Cache hit rates: Not yet measured -- Response times: Not yet benchmarked -- Circuit breaker: Not yet tested under load - -## Next Steps - -1. **Start Docker and run tests** (BLOCKING) - - See: `docs/PLATFORM-INTEGRATION-TESTING.md` - - Required before deployment - -2. **Fix any test failures** - - TypeScript errors - - Linter issues - - Test failures - -3. **End-to-end testing** - - VIN decode workflow - - Dropdown cascade - - Mobile + desktop - -4. **Performance verification** - - Response time benchmarks - - Cache hit rate measurement - - Circuit breaker testing - -5. **Production deployment** - - Create git tag - - Deploy to production - - Monitor logs for 24 hours - -6. **Archive cleanup** (after 30 days) - - Verify platform feature stable - - Delete archived Python service - - Update documentation - -## Documentation Index - -- **Migration Notes**: `docs/PLATFORM-INTEGRATION-MIGRATION.md` -- **Testing Guide**: `docs/PLATFORM-INTEGRATION-TESTING.md` -- **Platform Feature**: `backend/src/features/platform/README.md` -- **Archive Instructions**: `archive/platform-services/README.md` -- **This File**: `DEPLOYMENT-READY.md` - -## Support - -For issues or questions: -1. Review `docs/PLATFORM-INTEGRATION-TESTING.md` for troubleshooting -2. Check backend logs: `make logs-backend | grep platform` -3. Review platform feature: `backend/src/features/platform/README.md` -4. Consult migration notes: `docs/PLATFORM-INTEGRATION-MIGRATION.md` - ---- - -**Platform Integration Status**: CODE COMPLETE - -All code changes implemented and documented. Ready for Docker container testing and deployment verification. - -Next action: Start Docker and run tests per `docs/PLATFORM-INTEGRATION-TESTING.md` diff --git a/archive/platform-services/README.md b/archive/platform-services/README.md deleted file mode 100644 index 30134d0..0000000 --- a/archive/platform-services/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Archived Platform Services - -## vehicles/ -**Archived**: 2025-11-03 -**Reason**: Integrated into backend as platform feature module - -This Python FastAPI service was replaced by the TypeScript platform feature in `backend/src/features/platform/`. - -### What Was This Service? -The vehicles platform service provided: -- Vehicle hierarchical data (makes/models/trims/engines) via PostgreSQL -- VIN decoding via NHTSA vPIC API with circuit breaker -- Redis caching (6-hour TTL for vehicle data, 7-day TTL for VIN decode) -- JWT authentication -- Health checks and monitoring - -### Why Was It Archived? -1. **Architecture Simplification**: Reduced from 6 to 5 containers -2. **Technology Stack Unification**: Consolidated on Node.js/TypeScript -3. **Development Experience**: Eliminated inter-service HTTP calls -4. **Deployment Complexity**: Simplified Docker compose configuration - -### Migration Details -See: `docs/PLATFORM-INTEGRATION-MIGRATION.md` - -### New Implementation -Location: `backend/src/features/platform/` -API Endpoints: `/api/platform/*` -Language: TypeScript (Fastify) -Database: Same PostgreSQL vehicles schema -Caching: Same Redis strategy - -### Original Architecture -``` -mvp-platform (Python FastAPI) -├── Dockerfile -├── requirements.txt -├── api/ -│ ├── routes/ -│ ├── services/ -│ ├── repositories/ -│ └── models/ -└── tests/ -``` - -### New Architecture -``` -backend/src/features/platform/ (TypeScript Fastify) -├── api/ -│ ├── platform.routes.ts -│ └── platform.controller.ts -├── domain/ -│ ├── vin-decode.service.ts -│ ├── vehicle-data.service.ts -│ └── platform-cache.service.ts -├── data/ -│ ├── vehicle-data.repository.ts -│ └── vpic-client.ts -└── tests/ - ├── unit/ - └── integration/ -``` - -### Restoration (if needed) -If you need to restore this service: -```bash -# 1. Move back from archive -mv archive/platform-services/vehicles mvp-platform-services/ - -# 2. Restore docker-compose.yml configuration -git restore docker-compose.yml - -# 3. Rebuild containers -docker compose down -docker compose up -d - -# 4. Verify 6 containers running -docker compose ps -``` - -### Permanent Deletion -This directory can be permanently deleted after: -1. Platform feature proven stable in production (30+ days) -2. All stakeholders approve removal -3. Backup created of this archive directory -4. Git history confirms safe recovery if needed - -Do not delete before 2025-12-03 at earliest. diff --git a/archive/platform-services/vehicles/README.md b/archive/platform-services/vehicles/README.md deleted file mode 100644 index 33f1216..0000000 --- a/archive/platform-services/vehicles/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# MVP Platform Vehicles Service - -For full platform architecture and integration patterns, see `docs/PLATFORM-SERVICES.md`. - -## Schema Bootstrapping (Docker-First) -- Database: PostgreSQL, service `mvp-platform-vehicles-db`. -- On first start, schema files from `mvp-platform-services/vehicles/sql/schema` are executed automatically because the folder is mounted to `/docker-entrypoint-initdb.d` in `docker-compose.yml`. -- Files run in lexicographic order: - - `001_schema.sql` – creates `vehicles` schema and tables - - `002_constraints_indexes.sql` – adds uniques and indexes - - `003_seed_minimal.sql` – seeds minimal dropdown data for sanity checks - -## When Do Files Run? -- Only on the initial database initialization (i.e., when the Postgres data volume is empty). -- Subsequent `make start` runs will not reapply these files unless you reset the volume. - -## Applying Schema Changes -- Option 1 (fresh reset): - 1. `make clean` to remove volumes - 2. `make start` (the `.sql` files will be reapplied) -- Option 2 (manual apply to existing DB): - - Exec into the DB container and run the SQL files in order: - ```bash - docker compose exec mvp-platform-vehicles-db bash -lc "psql -U mvp_platform_user -d vehicles -f /docker-entrypoint-initdb.d/001_schema.sql" - docker compose exec mvp-platform-vehicles-db bash -lc "psql -U mvp_platform_user -d vehicles -f /docker-entrypoint-initdb.d/002_constraints_indexes.sql" - docker compose exec mvp-platform-vehicles-db bash -lc "psql -U mvp_platform_user -d vehicles -f /docker-entrypoint-initdb.d/003_seed_minimal.sql" - ``` - -## Quick Start -```bash -make start -make logs-platform-vehicles # View API + DB logs -``` - -## Endpoint Summary (Auth Required: Authorization: Bearer ) -- `GET /api/v1/vehicles/years` → `[number]` -- `GET /api/v1/vehicles/makes?year=YYYY` → `{ makes: [{id,name}] }` -- `GET /api/v1/vehicles/models?year=YYYY&make_id=ID` → `{ models: [...] }` -- `GET /api/v1/vehicles/trims?year=YYYY&make_id=ID&model_id=ID` → `{ trims: [...] }` -- `GET /api/v1/vehicles/engines?year=YYYY&make_id=ID&model_id=ID&trim_id=ID` → `{ engines: [...] }` - -## Notes -- Transmissions and performance tables exist for future use; no endpoints yet. -- VIN decode endpoints are pending rebuild and not documented here. diff --git a/archive/platform-services/vehicles/api/__init__.py b/archive/platform-services/vehicles/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/archive/platform-services/vehicles/api/config.py b/archive/platform-services/vehicles/api/config.py deleted file mode 100644 index 18ea664..0000000 --- a/archive/platform-services/vehicles/api/config.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -from pydantic_settings import BaseSettings -from typing import List - -# Docker-first: load secrets from mounted files when env vars are absent -_PG_SECRET_FILE = os.getenv("POSTGRES_PASSWORD_FILE", "/run/secrets/postgres-password") -if not os.getenv("POSTGRES_PASSWORD"): - try: - with open(_PG_SECRET_FILE, 'r') as f: - os.environ["POSTGRES_PASSWORD"] = f.read().strip() - except Exception: - # Leave as-is; connection will fail loudly if missing - pass - -class Settings(BaseSettings): - """Application configuration""" - - # Database settings (shared mvp-postgres) - POSTGRES_HOST: str = os.getenv("POSTGRES_HOST", "mvp-postgres") - POSTGRES_PORT: int = int(os.getenv("POSTGRES_PORT", "5432")) - POSTGRES_USER: str = os.getenv("POSTGRES_USER", "postgres") - POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD", "") - POSTGRES_DATABASE: str = os.getenv("POSTGRES_DATABASE", "motovaultpro") - - # Redis settings (shared mvp-redis) - REDIS_HOST: str = os.getenv("REDIS_HOST", "mvp-redis") - REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379")) - REDIS_DB: int = int(os.getenv("REDIS_DB", "1")) # Use DB 1 to separate from backend - - # Database connection pool settings - DATABASE_MIN_CONNECTIONS: int = int(os.getenv("DATABASE_MIN_CONNECTIONS", "5")) - DATABASE_MAX_CONNECTIONS: int = int(os.getenv("DATABASE_MAX_CONNECTIONS", "20")) - - # Cache settings - CACHE_TTL: int = int(os.getenv("CACHE_TTL", "3600")) # 1 hour default - - # Application settings - DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true" - CORS_ORIGINS: List[str] = [ - "http://localhost:3000", - "https://motovaultpro.com", - "http://localhost:3001" - ] - - class Config: - case_sensitive = True - -def get_settings() -> Settings: - """Get application settings""" - return Settings() diff --git a/archive/platform-services/vehicles/api/dependencies.py b/archive/platform-services/vehicles/api/dependencies.py deleted file mode 100644 index f35073b..0000000 --- a/archive/platform-services/vehicles/api/dependencies.py +++ /dev/null @@ -1,40 +0,0 @@ -import asyncpg -import redis.asyncio as redis -from fastapi import Request, Depends, HTTPException -import logging -from .config import get_settings - -logger = logging.getLogger(__name__) -settings = get_settings() - -async def get_db_pool(request: Request) -> asyncpg.Pool: - """Get database pool from app state""" - return request.app.state.db_pool - -async def get_db(request: Request) -> asyncpg.Connection: - """Get database connection""" - pool = await get_db_pool(request) - async with pool.acquire() as conn: - yield conn - -async def get_redis_client(request: Request) -> redis.Redis: - """Get Redis client from app state""" - return request.app.state.redis_client - -async def get_cache(request: Request): - """Get cache service from app state""" - return request.app.state.cache_service - -async def verify_bearer_token(request: Request) -> str: - """Verify Bearer token for service-to-service authentication - - Expects header: Authorization: Bearer - Compares token to settings.API_KEY - """ - auth_header = request.headers.get("Authorization", "") - if not auth_header.startswith("Bearer "): - raise HTTPException(status_code=401, detail="Missing or invalid Authorization header") - token = auth_header.split(" ", 1)[1].strip() - if token != settings.API_KEY: - raise HTTPException(status_code=401, detail="Invalid service token") - return token diff --git a/archive/platform-services/vehicles/api/main.py b/archive/platform-services/vehicles/api/main.py deleted file mode 100644 index 890f1d9..0000000 --- a/archive/platform-services/vehicles/api/main.py +++ /dev/null @@ -1,202 +0,0 @@ -import logging -from contextlib import asynccontextmanager -from fastapi import FastAPI, Request, HTTPException, Depends -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse -import asyncpg -import redis.asyncio as redis -import time - -from .config import get_settings -from .dependencies import get_db_pool, get_redis_client, get_cache, verify_bearer_token -from .routes import vehicles, vin -from .models.responses import HealthResponse -from .services.cache_service import CacheService - -# Configure logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - -settings = get_settings() - -@asynccontextmanager -async def lifespan(app: FastAPI): - """Application lifespan manager""" - # Startup - logger.info("Starting MVP Platform Vehicles API...") - - # Initialize database pool - try: - app.state.db_pool = await asyncpg.create_pool( - host=settings.POSTGRES_HOST, - port=settings.POSTGRES_PORT, - user=settings.POSTGRES_USER, - password=settings.POSTGRES_PASSWORD, - database=settings.POSTGRES_DATABASE, - min_size=settings.DATABASE_MIN_CONNECTIONS, - max_size=settings.DATABASE_MAX_CONNECTIONS, - command_timeout=30 - ) - logger.info("Database pool initialized") - except Exception as e: - logger.error(f"Failed to initialize database pool: {e}") - raise - - # Initialize Redis client - try: - app.state.redis_client = redis.Redis( - host=settings.REDIS_HOST, - port=settings.REDIS_PORT, - db=settings.REDIS_DB, - decode_responses=False, - socket_connect_timeout=5, - socket_timeout=5 - ) - # Test connection - await app.state.redis_client.ping() - logger.info("Redis client initialized") - except Exception as e: - logger.warning(f"Failed to initialize Redis client: {e}") - app.state.redis_client = None - - # Initialize cache service - app.state.cache_service = CacheService( - app.state.redis_client, - enabled=bool(app.state.redis_client), - default_ttl=settings.CACHE_TTL - ) - - yield - - # Shutdown - logger.info("Shutting down MVP Platform Vehicles API...") - - if hasattr(app.state, 'db_pool') and app.state.db_pool: - await app.state.db_pool.close() - logger.info("Database pool closed") - - if hasattr(app.state, 'redis_client') and app.state.redis_client: - await app.state.redis_client.aclose() - logger.info("Redis client closed") - -# Create FastAPI app -app = FastAPI( - title="MVP Platform Vehicles API", - description="Hierarchical Vehicle API with VIN decoding for MotoVaultPro platform services", - version="1.0.0", - lifespan=lifespan, - docs_url="/docs" if settings.DEBUG else None, - redoc_url="/redoc" if settings.DEBUG else None -) - -# Add CORS middleware -app.add_middleware( - CORSMiddleware, - allow_origins=settings.CORS_ORIGINS, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -# Request timing middleware -@app.middleware("http") -async def add_process_time_header(request: Request, call_next): - start_time = time.time() - response = await call_next(request) - process_time = time.time() - start_time - response.headers["X-Process-Time"] = str(process_time) - return response - -# Global exception handler -@app.exception_handler(Exception) -async def global_exception_handler(request: Request, exc: Exception): - logger.error(f"Unhandled exception in {request.method} {request.url.path}: {exc}") - return JSONResponse( - status_code=500, - content={"detail": "Internal server error"} - ) - -# Include routers -app.include_router(vehicles.router, prefix="/api/v1", dependencies=[Depends(verify_bearer_token)]) -app.include_router(vin.router, prefix="/api/v1", dependencies=[Depends(verify_bearer_token)]) - -# Health check endpoint -@app.api_route("/health", methods=["GET", "HEAD"], response_model=HealthResponse) -async def health_check(request: Request): - """Health check endpoint""" - db_status = "ok" - cache_status = "ok" - - # Check database - try: - db_pool = request.app.state.db_pool - async with db_pool.acquire() as conn: - await conn.fetchval("SELECT 1") - except Exception as e: - logger.error(f"Database health check failed: {e}") - db_status = "error" - - # Check cache - try: - cache = request.app.state.cache_service - if cache and cache.enabled: - await cache.redis.ping() - else: - cache_status = "disabled" - except Exception as e: - logger.error(f"Cache health check failed: {e}") - cache_status = "error" - - overall_status = "ok" if db_status == "ok" else "degraded" - - return HealthResponse( - status=overall_status, - database=db_status, - cache=cache_status, - version="1.0.0" - ) - -# Root endpoint -@app.get("/") -async def root(): - """Root endpoint with API information""" - return { - "name": "MVP Platform Vehicles API", - "version": "1.0.0", - "description": "Hierarchical Vehicle API with VIN decoding", - "docs_url": "/docs" if settings.DEBUG else "Contact administrator for documentation", - "endpoints": { - "health": "/health", - "makes": "/api/v1/vehicles/makes?year=2024", - "models": "/api/v1/vehicles/models?year=2024&make_id=1", - "trims": "/api/v1/vehicles/trims?year=2024&make_id=1&model_id=1", - "engines": "/api/v1/vehicles/engines?year=2024&make_id=1&model_id=1", - "transmissions": "/api/v1/vehicles/transmissions?year=2024&make_id=1&model_id=1", - "vin_decode": "/api/v1/vehicles/vindecode" - } - } - -# Cache stats endpoint -@app.get("/api/v1/cache/stats") -async def cache_stats(request: Request, token: str = Depends(verify_bearer_token)): - """Get cache statistics""" - try: - cache = request.app.state.cache_service - stats = await cache.get_stats() - return stats - except Exception as e: - logger.error(f"Failed to get cache stats: {e}") - raise HTTPException(status_code=500, detail="Failed to retrieve cache statistics") - -if __name__ == "__main__": - import uvicorn - uvicorn.run( - "api.main:app", - host="0.0.0.0", - port=8000, - reload=settings.DEBUG, - log_level="info" - ) diff --git a/archive/platform-services/vehicles/api/models/__init__.py b/archive/platform-services/vehicles/api/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/archive/platform-services/vehicles/api/models/responses.py b/archive/platform-services/vehicles/api/models/responses.py deleted file mode 100644 index 6cae1cf..0000000 --- a/archive/platform-services/vehicles/api/models/responses.py +++ /dev/null @@ -1,84 +0,0 @@ -from pydantic import BaseModel -from typing import List, Optional - -class MakeItem(BaseModel): - """Make item response model""" - id: int - name: str - -class ModelItem(BaseModel): - """Model item response model""" - id: int - name: str - -class TrimItem(BaseModel): - """Trim item response model""" - id: int - name: str - -class EngineItem(BaseModel): - """Engine item response model""" - id: int - name: str - -class TransmissionItem(BaseModel): - """Transmission item response model""" - name: str - -class MakesResponse(BaseModel): - """Makes response model""" - makes: List[MakeItem] - -class YearsResponse(BaseModel): - """Years response model""" - years: List[int] - -class ModelsResponse(BaseModel): - """Models response model""" - models: List[ModelItem] - -class TrimsResponse(BaseModel): - """Trims response model""" - trims: List[TrimItem] - -class EnginesResponse(BaseModel): - """Engines response model""" - engines: List[EngineItem] - -class TransmissionsResponse(BaseModel): - """Transmissions response model""" - transmissions: List[TransmissionItem] - -class VINDecodeResult(BaseModel): - """VIN decode result model""" - make: Optional[str] = None - model: Optional[str] = None - year: Optional[int] = None - trim_name: Optional[str] = None - engine_description: Optional[str] = None - transmission_description: Optional[str] = None - horsepower: Optional[float] = None - torque: Optional[float] = None - top_speed: Optional[float] = None - fuel: Optional[str] = None - confidence_score: Optional[float] = None - vehicle_type: Optional[str] = None - -class VINDecodeRequest(BaseModel): - """VIN decode request model""" - vin: str - -class VINDecodeResponse(BaseModel): - """VIN decode response model""" - vin: str - result: Optional[VINDecodeResult] - success: bool - error: Optional[str] = None - -class HealthResponse(BaseModel): - """Health check response model""" - status: str - database: str - cache: str - version: str - etl_last_run: Optional[str] = None diff --git a/archive/platform-services/vehicles/api/repositories/vehicles_repository.py b/archive/platform-services/vehicles/api/repositories/vehicles_repository.py deleted file mode 100644 index cc1b457..0000000 --- a/archive/platform-services/vehicles/api/repositories/vehicles_repository.py +++ /dev/null @@ -1,79 +0,0 @@ -import asyncpg -from typing import List, Dict - - -class VehiclesRepository: - """Repository for hierarchical vehicle queries against normalized schema""" - - async def get_years(self, db: asyncpg.Connection) -> List[int]: - rows = await db.fetch( - """ - SELECT DISTINCT year - FROM vehicles.model_year - ORDER BY year DESC - """ - ) - return [r["year"] for r in rows] - - async def get_makes(self, db: asyncpg.Connection, year: int) -> List[Dict]: - rows = await db.fetch( - """ - SELECT DISTINCT ma.id, ma.name - FROM vehicles.make ma - JOIN vehicles.model mo ON mo.make_id = ma.id - JOIN vehicles.model_year my ON my.model_id = mo.id AND my.year = $1 - ORDER BY ma.name - """, - year, - ) - return [{"id": r["id"], "name": r["name"]} for r in rows] - - async def get_models(self, db: asyncpg.Connection, year: int, make_id: int) -> List[Dict]: - rows = await db.fetch( - """ - SELECT DISTINCT mo.id, mo.name - FROM vehicles.model mo - JOIN vehicles.model_year my ON my.model_id = mo.id AND my.year = $1 - WHERE mo.make_id = $2 - ORDER BY mo.name - """, - year, - make_id, - ) - return [{"id": r["id"], "name": r["name"]} for r in rows] - - async def get_trims(self, db: asyncpg.Connection, year: int, model_id: int) -> List[Dict]: - rows = await db.fetch( - """ - SELECT t.id, t.name - FROM vehicles.trim t - JOIN vehicles.model_year my ON my.id = t.model_year_id - WHERE my.year = $1 AND my.model_id = $2 - ORDER BY t.name - """, - year, - model_id, - ) - return [{"id": r["id"], "name": r["name"]} for r in rows] - - async def get_engines( - self, db: asyncpg.Connection, year: int, model_id: int, trim_id: int - ) -> List[Dict]: - rows = await db.fetch( - """ - SELECT DISTINCT e.id, e.name - FROM vehicles.engine e - JOIN vehicles.trim_engine te ON te.engine_id = e.id - JOIN vehicles.trim t ON t.id = te.trim_id - JOIN vehicles.model_year my ON my.id = t.model_year_id - WHERE my.year = $1 - AND my.model_id = $2 - AND t.id = $3 - ORDER BY e.name - """, - year, - model_id, - trim_id, - ) - return [{"id": r["id"], "name": r["name"]} for r in rows] - diff --git a/archive/platform-services/vehicles/api/routes/__init__.py b/archive/platform-services/vehicles/api/routes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/archive/platform-services/vehicles/api/routes/vehicles.py b/archive/platform-services/vehicles/api/routes/vehicles.py deleted file mode 100644 index b26e5a0..0000000 --- a/archive/platform-services/vehicles/api/routes/vehicles.py +++ /dev/null @@ -1,116 +0,0 @@ -from fastapi import APIRouter, Depends, Query, HTTPException -import asyncpg -from ..dependencies import get_db, get_cache -# DropdownService deprecated; using normalized schema service -from ..services.vehicles_service import VehiclesService -from ..repositories.vehicles_repository import VehiclesRepository -from ..services.cache_service import CacheService -from ..models.responses import ( - MakesResponse, ModelsResponse, TrimsResponse, - EnginesResponse, - MakeItem, ModelItem, TrimItem, EngineItem -) -import logging - -logger = logging.getLogger(__name__) -router = APIRouter(prefix="/vehicles", tags=["Vehicles"]) - -@router.get("/years", response_model=list[int]) -async def get_years( - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache), -): - """Get available model years (distinct, desc)""" - service = VehiclesService(cache, VehiclesRepository()) - return await service.get_years(db) - -@router.get("/makes", response_model=MakesResponse) -async def get_makes( - year: int = Query(..., description="Model year", ge=1980, le=2050), - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache) -): - """Get makes for a specific year - - Hierarchical API: First level - requires year parameter only - """ - try: - service = VehiclesService(cache, VehiclesRepository()) - makes = await service.get_makes(db, year) - return MakesResponse(makes=[MakeItem(**m) for m in makes]) - except Exception as e: - logger.error(f"Failed to get makes for year {year}: {e}") - raise HTTPException( - status_code=500, - detail=f"Failed to retrieve makes for year {year}" - ) - -@router.get("/models", response_model=ModelsResponse) -async def get_models( - year: int = Query(..., description="Model year", ge=1980, le=2050), - make_id: int = Query(..., description="Make ID", ge=1), - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache) -): - """Get models for year and make - - Hierarchical API: Second level - requires year and make_id parameters - """ - try: - service = VehiclesService(cache, VehiclesRepository()) - models = await service.get_models(db, year, make_id) - return ModelsResponse(models=[ModelItem(**m) for m in models]) - except Exception as e: - logger.error(f"Failed to get models for year {year}, make {make_id}: {e}") - raise HTTPException( - status_code=500, - detail=f"Failed to retrieve models for year {year}, make {make_id}" - ) - -@router.get("/trims", response_model=TrimsResponse) -async def get_trims( - year: int = Query(..., description="Model year", ge=1980, le=2050), - make_id: int = Query(..., description="Make ID", ge=1), - model_id: int = Query(..., description="Model ID", ge=1), - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache) -): - """Get trims for year, make, and model - - Hierarchical API: Third level - requires year, make_id, and model_id parameters - """ - try: - service = VehiclesService(cache, VehiclesRepository()) - trims = await service.get_trims(db, year, model_id) - return TrimsResponse(trims=[TrimItem(**t) for t in trims]) - except Exception as e: - logger.error(f"Failed to get trims for year {year}, make {make_id}, model {model_id}: {e}") - raise HTTPException( - status_code=500, - detail=f"Failed to retrieve trims for year {year}, make {make_id}, model {model_id}" - ) - -@router.get("/engines", response_model=EnginesResponse) -async def get_engines( - year: int = Query(..., description="Model year", ge=1980, le=2050), - make_id: int = Query(..., description="Make ID", ge=1), - model_id: int = Query(..., description="Model ID", ge=1), - trim_id: int = Query(..., description="Trim ID", ge=1), - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache) -): - """Get engines for year, make, model, and trim""" - try: - service = VehiclesService(cache, VehiclesRepository()) - engines = await service.get_engines(db, year, model_id, trim_id) - return EnginesResponse(engines=[EngineItem(**e) for e in engines]) - except Exception as e: - logger.error( - f"Failed to get engines for year {year}, make {make_id}, model {model_id}, trim {trim_id}: {e}" - ) - raise HTTPException( - status_code=500, - detail=( - f"Failed to retrieve engines for year {year}, make {make_id}, model {model_id}, trim {trim_id}" - ) - ) diff --git a/archive/platform-services/vehicles/api/routes/vin.py b/archive/platform-services/vehicles/api/routes/vin.py deleted file mode 100644 index e564bd4..0000000 --- a/archive/platform-services/vehicles/api/routes/vin.py +++ /dev/null @@ -1,110 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException -import asyncpg -from ..dependencies import get_db, get_cache -from ..services.cache_service import CacheService -from ..models.responses import VINDecodeRequest, VINDecodeResponse, VINDecodeResult -import logging -import re - -logger = logging.getLogger(__name__) -router = APIRouter(prefix="/vehicles", tags=["VIN Decoding"]) - -def validate_vin(vin: str) -> bool: - """Validate VIN format""" - if len(vin) != 17: - return False - - # VIN cannot contain I, O, Q - if any(char in vin.upper() for char in ['I', 'O', 'Q']): - return False - - # Must be alphanumeric - if not re.match(r'^[A-HJ-NPR-Z0-9]{17}$', vin.upper()): - return False - - return True - -@router.post("/vindecode", response_model=VINDecodeResponse) -async def decode_vin( - request: VINDecodeRequest, - db: asyncpg.Connection = Depends(get_db), - cache: CacheService = Depends(get_cache) -): - """Decode VIN using PostgreSQL function with MSSQL parity - - Uses the vehicles.f_decode_vin() function to decode VIN with confidence scoring - """ - vin = request.vin.upper().strip() - - # Validate VIN format - if not validate_vin(vin): - return VINDecodeResponse( - vin=vin, - result=None, - success=False, - error="Invalid VIN format" - ) - - # Check cache first - cache_key = f"vin:decode:{vin}" - cached_result = await cache.get(cache_key) - if cached_result: - logger.debug(f"VIN decode result for {vin} retrieved from cache") - return VINDecodeResponse(**cached_result) - - try: - # Call PostgreSQL VIN decode function - query = """ - SELECT * FROM vehicles.f_decode_vin($1) - """ - - row = await db.fetchrow(query, vin) - - if row: - result = VINDecodeResult( - make=row['make'], - model=row['model'], - year=row['year'], - trim_name=row['trim_name'], - engine_description=row['engine_description'], - transmission_description=row['transmission_description'], - horsepower=row.get('horsepower'), - torque=row.get('torque'), - top_speed=row.get('top_speed'), - fuel=row.get('fuel'), - confidence_score=float(row['confidence_score']) if row['confidence_score'] else 0.0, - vehicle_type=row.get('vehicle_type') - ) - - response = VINDecodeResponse( - vin=vin, - result=result, - success=True - ) - - # Cache successful decode for 30 days - await cache.set(cache_key, response.dict(), ttl=30*24*3600) - - logger.info(f"Successfully decoded VIN {vin}: {result.make} {result.model} {result.year}") - return response - else: - # No result found - response = VINDecodeResponse( - vin=vin, - result=None, - success=False, - error="VIN not found in database" - ) - - # Cache negative result for 1 hour - await cache.set(cache_key, response.dict(), ttl=3600) - return response - - except Exception as e: - logger.error(f"Failed to decode VIN {vin}: {e}") - return VINDecodeResponse( - vin=vin, - result=None, - success=False, - error="Internal server error during VIN decoding" - ) diff --git a/archive/platform-services/vehicles/api/services/__init__.py b/archive/platform-services/vehicles/api/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/archive/platform-services/vehicles/api/services/cache_service.py b/archive/platform-services/vehicles/api/services/cache_service.py deleted file mode 100644 index 39462de..0000000 --- a/archive/platform-services/vehicles/api/services/cache_service.py +++ /dev/null @@ -1,88 +0,0 @@ -import redis.asyncio as redis -import json -import logging -from typing import Any, Optional - -logger = logging.getLogger(__name__) - -class CacheService: - """Redis cache service with JSON serialization""" - - def __init__(self, redis_client: Optional[redis.Redis], enabled: bool = True, default_ttl: int = 3600): - self.redis = redis_client - self.enabled = enabled and redis_client is not None - self.default_ttl = default_ttl - - async def get(self, key: str) -> Optional[Any]: - """Get value from cache""" - if not self.enabled: - return None - - try: - value = await self.redis.get(key) - if value: - return json.loads(value) - return None - except Exception as e: - logger.error(f"Cache get error for key {key}: {e}") - return None - - async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool: - """Set value in cache""" - if not self.enabled: - return False - - try: - ttl = ttl or self.default_ttl - json_value = json.dumps(value, default=str) # Handle datetime objects - await self.redis.setex(key, ttl, json_value) - return True - except Exception as e: - logger.error(f"Cache set error for key {key}: {e}") - return False - - async def delete(self, key: str) -> bool: - """Delete key from cache""" - if not self.enabled: - return False - - try: - deleted = await self.redis.delete(key) - return deleted > 0 - except Exception as e: - logger.error(f"Cache delete error for key {key}: {e}") - return False - - async def invalidate_dropdown_cache(self) -> int: - """Invalidate all dropdown cache entries""" - if not self.enabled: - return 0 - - try: - pattern = "dropdown:*" - keys = await self.redis.keys(pattern) - if keys: - deleted = await self.redis.delete(*keys) - logger.info(f"Invalidated {deleted} dropdown cache entries") - return deleted - return 0 - except Exception as e: - logger.error(f"Cache invalidation error: {e}") - return 0 - - async def get_stats(self) -> dict: - """Get cache statistics""" - if not self.enabled: - return {"enabled": False} - - try: - info = await self.redis.info("memory") - return { - "enabled": True, - "used_memory": info.get("used_memory_human"), - "used_memory_peak": info.get("used_memory_peak_human"), - "connected_clients": await self.redis.client_list() - } - except Exception as e: - logger.error(f"Cache stats error: {e}") - return {"enabled": True, "error": str(e)} \ No newline at end of file diff --git a/archive/platform-services/vehicles/api/services/vehicles_service.py b/archive/platform-services/vehicles/api/services/vehicles_service.py deleted file mode 100644 index e7e5fa3..0000000 --- a/archive/platform-services/vehicles/api/services/vehicles_service.py +++ /dev/null @@ -1,58 +0,0 @@ -import asyncpg -from typing import List, Dict -from ..services.cache_service import CacheService -from ..repositories.vehicles_repository import VehiclesRepository - - -class VehiclesService: - def __init__(self, cache: CacheService, repo: VehiclesRepository | None = None): - self.cache = cache - self.repo = repo or VehiclesRepository() - - async def get_years(self, db: asyncpg.Connection) -> List[int]: - cache_key = "dropdown:years" - cached = await self.cache.get(cache_key) - if cached: - return cached - years = await self.repo.get_years(db) - await self.cache.set(cache_key, years, ttl=6 * 3600) - return years - - async def get_makes(self, db: asyncpg.Connection, year: int) -> List[Dict]: - cache_key = f"dropdown:makes:{year}" - cached = await self.cache.get(cache_key) - if cached: - return cached - makes = await self.repo.get_makes(db, year) - await self.cache.set(cache_key, makes, ttl=6 * 3600) - return makes - - async def get_models(self, db: asyncpg.Connection, year: int, make_id: int) -> List[Dict]: - cache_key = f"dropdown:models:{year}:{make_id}" - cached = await self.cache.get(cache_key) - if cached: - return cached - models = await self.repo.get_models(db, year, make_id) - await self.cache.set(cache_key, models, ttl=6 * 3600) - return models - - async def get_trims(self, db: asyncpg.Connection, year: int, model_id: int) -> List[Dict]: - cache_key = f"dropdown:trims:{year}:{model_id}" - cached = await self.cache.get(cache_key) - if cached: - return cached - trims = await self.repo.get_trims(db, year, model_id) - await self.cache.set(cache_key, trims, ttl=6 * 3600) - return trims - - async def get_engines( - self, db: asyncpg.Connection, year: int, model_id: int, trim_id: int - ) -> List[Dict]: - cache_key = f"dropdown:engines:{year}:{model_id}:{trim_id}" - cached = await self.cache.get(cache_key) - if cached: - return cached - engines = await self.repo.get_engines(db, year, model_id, trim_id) - await self.cache.set(cache_key, engines, ttl=6 * 3600) - return engines - diff --git a/archive/platform-services/vehicles/docker/Dockerfile.api b/archive/platform-services/vehicles/docker/Dockerfile.api deleted file mode 100644 index a563e9d..0000000 --- a/archive/platform-services/vehicles/docker/Dockerfile.api +++ /dev/null @@ -1,30 +0,0 @@ -FROM python:3.11-slim - -# Set working directory -WORKDIR /app - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - wget \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Copy requirements and install Python dependencies -COPY requirements-api.txt . -RUN pip install --no-cache-dir -r requirements-api.txt - -# Copy application code -COPY api/ ./api/ - -# Set Python path -ENV PYTHONPATH=/app - -# Expose port -EXPOSE 8000 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ - CMD wget --quiet --tries=1 --spider http://localhost:8000/health || exit 1 - -# Run application -CMD ["python", "-m", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/archive/platform-services/vehicles/makes.json b/archive/platform-services/vehicles/makes.json deleted file mode 100644 index 38c0d39..0000000 --- a/archive/platform-services/vehicles/makes.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "manufacturers": [ - "Acura", - "Alfa Romeo", - "Aston Martin", - "Audi", - "BMW", - "Bentley", - "Buick", - "Cadillac", - "Chevrolet", - "Chrysler", - "Daewoo", - "Dodge", - "Eagle", - "Ferrari", - "Fiat", - "Fisker", - "Ford", - "GMC", - "Genesis", - "Geo", - "Honda", - "Hummer", - "Hyundai", - "Infiniti", - "Isuzu", - "Jaguar", - "Jeep", - "Kia", - "Lamborghini", - "Land Rover", - "Lexus", - "Lincoln", - "Lotus", - "Mazda", - "Maserati", - "Maybach", - "McLaren", - "Mercedes-Benz", - "Mercury", - "Mini", - "Mitsubishi", - "Nissan", - "Oldsmobile", - "Panoz", - "Plymouth", - "Polestar", - "Pontiac", - "Porsche", - "Ram", - "Rivian", - "Rolls Royce", - "Saab", - "Saturn", - "Scion", - "Smart", - "Subaru", - "Suzuki", - "Tesla", - "Toyota", - "Volkswagen", - "Volvo", - "Karma", - "Pagani", - "Koenigsegg", - "Lucid" - ] -} diff --git a/archive/platform-services/vehicles/requirements-api.txt b/archive/platform-services/vehicles/requirements-api.txt deleted file mode 100644 index 497285f..0000000 --- a/archive/platform-services/vehicles/requirements-api.txt +++ /dev/null @@ -1,6 +0,0 @@ -fastapi==0.104.1 -uvicorn[standard]==0.24.0 -asyncpg==0.29.0 -redis==5.0.1 -pydantic==2.5.0 -pydantic-settings==2.1.0 \ No newline at end of file diff --git a/archive/platform-services/vehicles/sql/schema/001_schema.sql b/archive/platform-services/vehicles/sql/schema/001_schema.sql deleted file mode 100644 index 11a6150..0000000 --- a/archive/platform-services/vehicles/sql/schema/001_schema.sql +++ /dev/null @@ -1,73 +0,0 @@ --- Vehicles Platform Service Schema (baseline) -CREATE SCHEMA IF NOT EXISTS vehicles; - --- Makes -CREATE TABLE IF NOT EXISTS vehicles.make ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL -); - --- Models -CREATE TABLE IF NOT EXISTS vehicles.model ( - id BIGSERIAL PRIMARY KEY, - make_id BIGINT NOT NULL REFERENCES vehicles.make(id) ON DELETE RESTRICT, - name TEXT NOT NULL -); - --- Model availability by year -CREATE TABLE IF NOT EXISTS vehicles.model_year ( - id BIGSERIAL PRIMARY KEY, - model_id BIGINT NOT NULL REFERENCES vehicles.model(id) ON DELETE RESTRICT, - year INTEGER NOT NULL CHECK (year BETWEEN 1950 AND 2100) -); - --- Trims (year-specific) -CREATE TABLE IF NOT EXISTS vehicles.trim ( - id BIGSERIAL PRIMARY KEY, - model_year_id BIGINT NOT NULL REFERENCES vehicles.model_year(id) ON DELETE RESTRICT, - name TEXT NOT NULL -); - --- Engines (canonical) -CREATE TABLE IF NOT EXISTS vehicles.engine ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL, - code TEXT NULL, - displacement_l NUMERIC(3,1) NULL, - cylinders SMALLINT NULL, - fuel_type TEXT NULL, - aspiration TEXT NULL -); - --- Trim to Engine mapping (many-to-many) -CREATE TABLE IF NOT EXISTS vehicles.trim_engine ( - trim_id BIGINT NOT NULL REFERENCES vehicles.trim(id) ON DELETE RESTRICT, - engine_id BIGINT NOT NULL REFERENCES vehicles.engine(id) ON DELETE RESTRICT, - PRIMARY KEY (trim_id, engine_id) -); - --- Optional: Transmissions (reserved for future) -CREATE TABLE IF NOT EXISTS vehicles.transmission ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL, - type TEXT NULL, - gears SMALLINT NULL -); - -CREATE TABLE IF NOT EXISTS vehicles.trim_transmission ( - trim_id BIGINT NOT NULL REFERENCES vehicles.trim(id) ON DELETE RESTRICT, - transmission_id BIGINT NOT NULL REFERENCES vehicles.transmission(id) ON DELETE RESTRICT, - PRIMARY KEY (trim_id, transmission_id) -); - --- Optional: Performance (reserved for future) -CREATE TABLE IF NOT EXISTS vehicles.performance ( - id BIGSERIAL PRIMARY KEY, - engine_id BIGINT NULL REFERENCES vehicles.engine(id) ON DELETE SET NULL, - trim_id BIGINT NULL REFERENCES vehicles.trim(id) ON DELETE SET NULL, - horsepower NUMERIC(6,2) NULL, - torque NUMERIC(6,2) NULL, - top_speed NUMERIC(6,2) NULL, - zero_to_sixty NUMERIC(4,2) NULL -); - diff --git a/archive/platform-services/vehicles/sql/schema/002_constraints_indexes.sql b/archive/platform-services/vehicles/sql/schema/002_constraints_indexes.sql deleted file mode 100644 index b76f78e..0000000 --- a/archive/platform-services/vehicles/sql/schema/002_constraints_indexes.sql +++ /dev/null @@ -1,23 +0,0 @@ --- Uniques and indexes to enforce data integrity and performance - --- Unique, case-insensitive names -CREATE UNIQUE INDEX IF NOT EXISTS ux_make_name ON vehicles.make (lower(name)); -CREATE UNIQUE INDEX IF NOT EXISTS ux_model_make_name ON vehicles.model (make_id, lower(name)); - --- Model/Year availability -ALTER TABLE vehicles.model_year - ADD CONSTRAINT ux_model_year UNIQUE (model_id, year); -CREATE INDEX IF NOT EXISTS ix_model_year_year_model ON vehicles.model_year (year, model_id); - --- Trims are unique per model_year by name (case-insensitive) -CREATE UNIQUE INDEX IF NOT EXISTS ux_trim_modelyear_name ON vehicles.trim (model_year_id, lower(name)); -CREATE INDEX IF NOT EXISTS ix_trim_modelyear_name ON vehicles.trim (model_year_id, name); - --- Engine uniqueness (prefer code when present) -CREATE UNIQUE INDEX IF NOT EXISTS ux_engine_code_not_null ON vehicles.engine (code) WHERE code IS NOT NULL; -CREATE UNIQUE INDEX IF NOT EXISTS ux_engine_name ON vehicles.engine (lower(name)); - --- Bridge indexes -CREATE INDEX IF NOT EXISTS ix_trim_engine_trim ON vehicles.trim_engine (trim_id); -CREATE INDEX IF NOT EXISTS ix_trim_engine_engine ON vehicles.trim_engine (engine_id); - diff --git a/archive/platform-services/vehicles/sql/schema/003_seed_minimal.sql b/archive/platform-services/vehicles/sql/schema/003_seed_minimal.sql deleted file mode 100644 index 22ae1d5..0000000 --- a/archive/platform-services/vehicles/sql/schema/003_seed_minimal.sql +++ /dev/null @@ -1,70 +0,0 @@ --- Minimal seed data for testing dropdown hierarchy -INSERT INTO vehicles.make (name) VALUES ('Honda') ON CONFLICT DO NOTHING; -INSERT INTO vehicles.make (name) VALUES ('Toyota') ON CONFLICT DO NOTHING; - --- Resolve make ids -WITH m AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('Honda') -) -INSERT INTO vehicles.model (make_id, name) -SELECT m.id, 'Civic' FROM m -ON CONFLICT DO NOTHING; - -WITH m AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('Toyota') -) -INSERT INTO vehicles.model (make_id, name) -SELECT m.id, 'Corolla' FROM m -ON CONFLICT DO NOTHING; - --- Model years -WITH mo AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Civic') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT mo.id, 2024 FROM mo ON CONFLICT DO NOTHING; - -WITH mo AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Corolla') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT mo.id, 2024 FROM mo ON CONFLICT DO NOTHING; - --- Trims -WITH my AS ( - SELECT my.id FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Civic') AND my.year = 2024 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, 'LX' FROM my ON CONFLICT DO NOTHING; - -WITH my AS ( - SELECT my.id FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Corolla') AND my.year = 2024 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, 'LE' FROM my ON CONFLICT DO NOTHING; - --- Engines -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES ('2.0L I4', 'K20', 2.0, 4, 'Gasoline', 'NA') -ON CONFLICT DO NOTHING; - -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES ('2.0L I4', 'M20', 2.0, 4, 'Gasoline', 'NA') -ON CONFLICT DO NOTHING; - --- Map engines to trims -WITH t AS ( - SELECT t.id AS trim_id, e.id AS engine_id - FROM vehicles.trim t - JOIN vehicles.model_year my ON my.id = t.model_year_id AND my.year = 2024 - JOIN vehicles.model mo ON mo.id = my.model_id - JOIN vehicles.make ma ON ma.id = mo.make_id - JOIN vehicles.engine e ON e.code IN ('K20','M20') - WHERE lower(ma.name) = lower('Honda') AND lower(mo.name) = lower('Civic') AND lower(t.name) = lower('LX') -) -INSERT INTO vehicles.trim_engine (trim_id, engine_id) -SELECT trim_id, engine_id FROM t ON CONFLICT DO NOTHING; diff --git a/archive/platform-services/vehicles/sql/schema/004_seed_filtered_makes.sql b/archive/platform-services/vehicles/sql/schema/004_seed_filtered_makes.sql deleted file mode 100644 index 7cf5d8c..0000000 --- a/archive/platform-services/vehicles/sql/schema/004_seed_filtered_makes.sql +++ /dev/null @@ -1,105 +0,0 @@ --- Seed sample data based on ETL source filter (etl/sources/makes.json) --- Focus on Chevrolet (Corvette) and GMC (Sierra 1500) - --- Makes -INSERT INTO vehicles.make (name) VALUES ('Chevrolet') ON CONFLICT DO NOTHING; -INSERT INTO vehicles.make (name) VALUES ('GMC') ON CONFLICT DO NOTHING; - --- Chevrolet Corvette -WITH chevy AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('Chevrolet') -) -INSERT INTO vehicles.model (make_id, name) -SELECT chevy.id, 'Corvette' FROM chevy -ON CONFLICT DO NOTHING; - -WITH corvette AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Corvette') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT corvette.id, 2024 FROM corvette -ON CONFLICT DO NOTHING; - --- Corvette trims (2024) -WITH my AS ( - SELECT my.id - FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Corvette') AND my.year = 2024 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, t.name -FROM my, (VALUES ('Stingray'), ('Z06')) AS t(name) -ON CONFLICT DO NOTHING; - --- Corvette engines -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES - ('6.2L V8 LT2', 'LT2', 6.2, 8, 'Gasoline', 'NA'), - ('5.5L V8 LT6', 'LT6', 5.5, 8, 'Gasoline', 'NA') -ON CONFLICT DO NOTHING; - --- Map Corvette engines to trims -WITH t AS ( - SELECT t.id AS trim_id, t.name AS trim_name - FROM vehicles.trim t - JOIN vehicles.model_year my ON my.id = t.model_year_id AND my.year = 2024 - JOIN vehicles.model mo ON mo.id = my.model_id AND lower(mo.name) = lower('Corvette') -) -INSERT INTO vehicles.trim_engine (trim_id, engine_id) -SELECT t.trim_id, e.id -FROM t -JOIN vehicles.engine e - ON (t.trim_name = 'Stingray' AND e.code = 'LT2') - OR (t.trim_name = 'Z06' AND e.code = 'LT6') -ON CONFLICT DO NOTHING; - --- GMC Sierra 1500 -WITH gmc AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('GMC') -) -INSERT INTO vehicles.model (make_id, name) -SELECT gmc.id, 'Sierra 1500' FROM gmc -ON CONFLICT DO NOTHING; - -WITH sierra AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Sierra 1500') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT sierra.id, 2024 FROM sierra -ON CONFLICT DO NOTHING; - --- Sierra trims (2024) -WITH my AS ( - SELECT my.id - FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Sierra 1500') AND my.year = 2024 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, t.name -FROM my, (VALUES ('SLE'), ('Denali')) AS t(name) -ON CONFLICT DO NOTHING; - --- Sierra engines -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES - ('5.3L V8 L84', 'L84', 5.3, 8, 'Gasoline', 'NA'), - ('6.2L V8 L87', 'L87', 6.2, 8, 'Gasoline', 'NA') -ON CONFLICT DO NOTHING; - --- Map Sierra engines to trims -WITH t AS ( - SELECT t.id AS trim_id, t.name AS trim_name - FROM vehicles.trim t - JOIN vehicles.model_year my ON my.id = t.model_year_id AND my.year = 2024 - JOIN vehicles.model mo ON mo.id = my.model_id AND lower(mo.name) = lower('Sierra 1500') -) -INSERT INTO vehicles.trim_engine (trim_id, engine_id) -SELECT t.trim_id, e.id -FROM t -JOIN vehicles.engine e - ON (t.trim_name = 'SLE' AND e.code = 'L84') - OR (t.trim_name = 'Denali' AND e.code = 'L87') -ON CONFLICT DO NOTHING; - diff --git a/archive/platform-services/vehicles/sql/schema/005_seed_specific_vehicles.sql b/archive/platform-services/vehicles/sql/schema/005_seed_specific_vehicles.sql deleted file mode 100644 index 64e16e4..0000000 --- a/archive/platform-services/vehicles/sql/schema/005_seed_specific_vehicles.sql +++ /dev/null @@ -1,75 +0,0 @@ --- Seed specific vehicle combinations requested - --- Ensure makes exist -INSERT INTO vehicles.make (name) VALUES ('GMC') ON CONFLICT DO NOTHING; -INSERT INTO vehicles.make (name) VALUES ('Chevrolet') ON CONFLICT DO NOTHING; - --- Ensure models exist under their makes -WITH m AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('GMC') -) -INSERT INTO vehicles.model (make_id, name) -SELECT m.id, 'Sierra 1500' FROM m -ON CONFLICT DO NOTHING; - -WITH m AS ( - SELECT id FROM vehicles.make WHERE lower(name) = lower('Chevrolet') -) -INSERT INTO vehicles.model (make_id, name) -SELECT m.id, 'Corvette' FROM m -ON CONFLICT DO NOTHING; - --- Model years -WITH mo AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Sierra 1500') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT mo.id, 2023 FROM mo ON CONFLICT DO NOTHING; - -WITH mo AS ( - SELECT id FROM vehicles.model WHERE lower(name) = lower('Corvette') -) -INSERT INTO vehicles.model_year (model_id, year) -SELECT mo.id, 2017 FROM mo ON CONFLICT DO NOTHING; - --- Trims -WITH my AS ( - SELECT my.id FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Sierra 1500') AND my.year = 2023 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, 'AT4x' FROM my ON CONFLICT DO NOTHING; - -WITH my AS ( - SELECT my.id FROM vehicles.model_year my - JOIN vehicles.model mo ON mo.id = my.model_id - WHERE lower(mo.name) = lower('Corvette') AND my.year = 2017 -) -INSERT INTO vehicles.trim (model_year_id, name) -SELECT my.id, 'Z06 Convertible' FROM my ON CONFLICT DO NOTHING; - --- Engines (ensure canonical engines exist) -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES ('6.2L V8 L87', 'L87', 6.2, 8, 'Gasoline', 'NA') -ON CONFLICT DO NOTHING; - -INSERT INTO vehicles.engine (name, code, displacement_l, cylinders, fuel_type, aspiration) -VALUES ('6.2L V8 LT4', 'LT4', 6.2, 8, 'Gasoline', 'SC') -ON CONFLICT DO NOTHING; - --- Map engines to trims -WITH t AS ( - SELECT t.id AS trim_id, t.name AS trim_name, my.year, mo.name AS model_name - FROM vehicles.trim t - JOIN vehicles.model_year my ON my.id = t.model_year_id - JOIN vehicles.model mo ON mo.id = my.model_id -) -INSERT INTO vehicles.trim_engine (trim_id, engine_id) -SELECT t.trim_id, e.id -FROM t -JOIN vehicles.engine e - ON (t.model_name = 'Sierra 1500' AND t.year = 2023 AND t.trim_name = 'AT4x' AND e.code = 'L87') - OR (t.model_name = 'Corvette' AND t.year = 2017 AND t.trim_name = 'Z06 Convertible' AND e.code = 'LT4') -ON CONFLICT DO NOTHING; - diff --git a/config/platform/production.yml.example b/config/platform/production.yml.example deleted file mode 100755 index 8e81959..0000000 --- a/config/platform/production.yml.example +++ /dev/null @@ -1,23 +0,0 @@ -# MotoVaultPro platform services ConfigMap template -tenants_service: - database: - host: platform-postgres - port: 5432 - name: platform - user: platform_user - auth0: - domain: motovaultpro.us.auth0.com - audience: https://api.motovaultpro.com - -vehicles_service: - database: - host: mvp-platform-vehicles-db - port: 5432 - name: vehicles - user: mvp_platform_user - redis: - host: mvp-platform-vehicles-redis - port: 6379 - cors_allow_origins: - - https://admin.motovaultpro.com - - https://motovaultpro.com diff --git a/docs/ARCHITECTURE-OVERVIEW.md b/docs/ARCHITECTURE-OVERVIEW.md index 7a3d386..7420ade 100644 --- a/docs/ARCHITECTURE-OVERVIEW.md +++ b/docs/ARCHITECTURE-OVERVIEW.md @@ -41,12 +41,6 @@ MotoVaultPro is a single-tenant vehicle management application built with a **6- +------------------+ +--------------------+ (frontend network) (backend network) | - v - +-------------------+ - | Platform | - | Python + FastAPI | - | Port: 8000 | - +-------------------+ | +---------------------------------+ | | @@ -103,14 +97,12 @@ MotoVaultPro is a single-tenant vehicle management application built with a **6- 7. Backend → Response → Frontend ``` -### Backend to Platform Flow +### Backend Platform Flow ``` -1. Backend Feature → HTTP Request → Platform Service - - URL: http://mvp-platform:8000 - - Headers: Authorization: Bearer -2. Platform → Query PostgreSQL (vehicles schema) -3. Platform → Check/Update Redis Cache -4. Platform → Response → Backend +1. Backend Feature → Calls platform domain services (TypeScript in-process) +2. Platform module → Query PostgreSQL (vehicles schema) +3. Platform module → Check/Update Redis cache +4. Platform response → Returned to caller (vehicles, frontend proxies, etc.) ``` ### Data Access Flow @@ -196,53 +188,51 @@ MotoVaultPro is a single-tenant vehicle management application built with a **6- - Session storage (future) - Rate limiting (future) -### Platform (Vehicle Data Service) -- **Technology**: Python 3.11 + FastAPI -- **Build**: Custom Dockerfile from `mvp-platform-services/vehicles/` -- **Port**: 8000 (internal) -- **Networks**: backend, database -- **Dependencies**: PostgreSQL, Redis -- **Health Check**: `wget http://localhost:8000/health` (30s interval) +### Platform Module (Vehicle Data) +- **Technology**: TypeScript feature capsule inside `backend/src/features/platform/` +- **Runtime**: Shares the `mvp-backend` Fastify container +- **Dependencies**: PostgreSQL, Redis (accessed via backend connection pool) +- **Deployment**: Bundled with backend build; no separate container or port +- **Health**: Covered by backend `/health` endpoint and feature-specific logs - **Configuration**: - - `/app/config/production.yml` - Service config - - `/app/config/shared.yml` - Shared config + - `backend/src/features/platform/domain/*.ts` - Business logic + - `backend/src/features/platform/data/*.ts` - Database + vPIC integration - **Secrets**: - - `postgres-password` - Shared database access + - Reuses backend secrets (PostgreSQL, Auth0, etc.) - **Purpose**: - Vehicle make/model/trim/engine data - VIN decoding (planned) - Standardized vehicle information -## Platform Service Integration +## Platform Module Integration ### Current Architecture -The Platform service is a **separate Python container** that provides vehicle data capabilities to the backend. This separation exists for: -- **Technology Independence**: Python ecosystem for data processing -- **Specialized Caching**: Year-based hierarchical caching strategy -- **Resource Isolation**: Independent scaling and monitoring +Platform capabilities now run **in-process** within the backend as a feature capsule. This delivers: +- **Shared Language & Tooling**: TypeScript across the stack +- **Centralized Caching**: Reuses backend Redis helpers for year-based lookups +- **Simplified Operations**: No extra container to build, scale, or monitor ### Shared Infrastructure -- **Database**: Both Backend and Platform use `mvp-postgres` -- **Cache**: Both services share `mvp-redis` -- **Network**: Connected via `backend` and `database` networks -- **Secrets**: Share database credentials +- **Database**: Uses the backend connection pool against `mvp-postgres` +- **Cache**: Shares the backend Redis client (`mvp-redis`) +- **Runtime**: Packaged with the `mvp-backend` container and Fastify plugins +- **Secrets**: Relies on the same secret files as the backend (PostgreSQL, Auth0, etc.) ### Communication Pattern -```javascript -// Backend calls Platform via internal HTTP -const response = await fetch('http://mvp-platform:8000/api/v1/vehicles/makes?year=2024', { - headers: { - 'Authorization': `Bearer ${platformApiKey}` - } +```typescript +// Vehicles feature pulling platform data via domain services +const platformService = new PlatformVehicleDataService({ + db: postgresPool, + cache: redisClient }); + +const makes = await platformService.getMakes({ year: 2024 }); ``` -### Future Evolution -**Planned**: Absorb Platform service into Backend as a feature capsule -- Timeline: Post-MVP phase -- Approach: Rewrite Python service in Node.js -- Location: `backend/src/features/vehicle-platform/` -- Benefits: Simplified deployment, shared codebase, reduced containers +### Ongoing Work +- Continue migrating remaining FastAPI utilities into TypeScript where needed +- Expand `/api/platform/*` endpoints as new lookup types are added +- Monitor backend logs for `platform` namespace entries to verify health ## Feature Capsule Architecture @@ -255,7 +245,8 @@ backend/src/features/ ├── fuel-logs/ # Fuel tracking ├── maintenance/ # Service records ├── stations/ # Gas station locations -└── documents/ # Document storage +├── documents/ # Document storage +└── platform/ # Vehicle data + VIN decoding ``` ### Feature Structure @@ -372,7 +363,6 @@ The application uses Kubernetes-inspired configuration patterns: **Secrets** (File-based): - `/run/secrets/postgres-password` - Database credentials -- `/run/secrets/platform-vehicles-api-key` - Service auth - `/run/secrets/auth0-client-secret` - OAuth credentials - `/run/secrets/google-maps-api-key` - External API keys @@ -384,14 +374,13 @@ CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets DATABASE_HOST: mvp-postgres REDIS_HOST: mvp-redis -PLATFORM_VEHICLES_API_URL: http://mvp-platform:8000 ``` ## Development Workflow ### Container Commands (via Makefile) ```bash -make start # Start all 6 containers +make start # Start all 5 containers make stop # Stop all containers make restart # Restart all containers make rebuild # Rebuild and restart containers @@ -407,12 +396,10 @@ docker ps # View specific service logs docker logs mvp-backend -f -docker logs mvp-platform -f docker logs mvp-postgres -f # Test health endpoints -curl http://localhost:3001/health # Backend -curl http://localhost:8000/health # Platform +curl http://localhost:3001/health # Backend (includes platform module) ``` ### Database Access diff --git a/docs/PLATFORM-SERVICES.md b/docs/PLATFORM-SERVICES.md index 45f6789..6efe3f8 100644 --- a/docs/PLATFORM-SERVICES.md +++ b/docs/PLATFORM-SERVICES.md @@ -1,13 +1,13 @@ -# MVP Platform Service +# MVP Platform Module ## Overview -The MVP Platform service is an **integrated service** that provides platform capabilities to the MotoVaultPro application. This service is part of the simplified 6-container architecture. +The MVP Platform module is fully integrated inside the MotoVaultPro backend container. It delivers all platform capabilities without requiring a separate service or container in the simplified five-container stack. ## Architecture -The platform service is integrated into the main application stack: -- **Service Container**: mvp-platform +The platform module runs as part of the backend service: +- **Runtime**: `mvp-backend` container (Fastify API) - **Shared Database**: Uses mvp-postgres - **Shared Cache**: Uses mvp-redis @@ -37,18 +37,17 @@ The platform provides vehicle data capabilities including: **Start All Services**: ```bash -make start # Starts all 6 containers +make start # Starts the five-container stack ``` -**Service Logs**: +**Backend Logs (includes platform module)**: ```bash -make logs # All service logs -docker logs mvp-platform +make logs-backend ``` -**Service Shell Access**: +**Backend Shell (platform code lives here)**: ```bash -docker exec -it mvp-platform sh +make shell-backend ``` ### Database Management @@ -66,10 +65,10 @@ make db-shell-app ## Deployment Strategy ### Integrated Deployment -The platform service deploys with the main application: +The platform module deploys with the main application: - Same deployment pipeline - Shares database and cache -- Deployed as part of 6-container stack +- Deployed as part of the five-container stack ## Integration Patterns @@ -85,7 +84,7 @@ Application features access platform data through shared database: **Service Discovery Problems**: - Verify Docker networking: `docker network ls` -- Check container connectivity: `docker exec -it mvp-platform sh` +- Check backend container connectivity: `docker compose exec mvp-backend sh` **Database Connection Issues**: - Verify mvp-postgres is healthy @@ -93,19 +92,19 @@ Application features access platform data through shared database: ### Health Checks -**Verify Platform Service**: +**Verify Backend Service (contains platform module)**: ```bash -docker ps | grep mvp-platform +docker compose ps mvp-backend ``` ### Logs and Debugging **Service Logs**: ```bash -docker logs mvp-platform --tail=100 -f +docker compose logs -f mvp-backend ``` **Database Logs**: ```bash -docker logs mvp-postgres --tail=100 -f +docker compose logs -f mvp-postgres ``` diff --git a/docs/PROMPTS.md b/docs/PROMPTS.md index cb40699..b6466a7 100644 --- a/docs/PROMPTS.md +++ b/docs/PROMPTS.md @@ -14,11 +14,11 @@ MotoVaultPro uses a 4-agent team for optimal development velocity and quality en - Platform service integration - Backend tests and migrations -**Platform Service Agent** - Independent microservices -- Building new platform services in `mvp-platform-services/{service}/` -- FastAPI microservice development -- ETL pipelines and service databases -- Platform service tests and deployment +**Platform Feature Agent** - Integrated platform module +- Enhancing `backend/src/features/platform/` +- Vehicle lookup + VIN decoding logic +- Redis/SQL performance tuning for platform endpoints +- Platform module tests and documentation **Mobile-First Frontend Agent** - Responsive UI/UX - React components in `frontend/src/features/{feature}/` @@ -45,10 +45,10 @@ Task: "Build responsive UI for {feature}. Read backend API docs and implement mo Test on 320px and 1920px viewports." Agent: Mobile-First Frontend Agent -# Platform microservice -Task: "Create {service} platform microservice with FastAPI. -Implement API, database, and health checks with tests." -Agent: Platform Service Agent +# Platform module work +Task: "Enhance platform feature capsule (backend/src/features/platform). +Implement API/domain/data changes with accompanying tests." +Agent: Platform Feature Agent # Quality validation Task: "Validate {feature} quality gates. Run all tests, check linting, verify mobile + desktop. @@ -109,11 +109,12 @@ Agent: Quality Enforcer Agent - Database: `backend/src/features/{feature}/data/` - After changes: `make rebuild` then check logs -### Platform Service Changes (Python) -**Agent**: Platform Service Agent -- Service: `mvp-platform-services/{service}/` -- API: `mvp-platform-services/{service}/api/` -- After changes: `make rebuild` then check service health +### Platform Module Changes (TypeScript) +**Agent**: Platform Feature Agent +- Feature capsule: `backend/src/features/platform/` +- API routes: `backend/src/features/platform/api/` +- Domain/data: `backend/src/features/platform/domain/` and `data/` +- After changes: `make rebuild` then verify platform endpoints via backend logs/tests ### Database Changes - Add migration: `backend/src/features/{feature}/migrations/00X_description.sql` @@ -159,12 +160,12 @@ Agent: Quality Enforcer Agent 4. Ensure touch targets are 44px minimum 5. Validate keyboard navigation on desktop -### Add Platform Service Integration -**Agents**: Platform Service Agent + Feature Capsule Agent -1. Platform Service Agent: Implement service endpoint -2. Feature Capsule Agent: Create client in `external/platform-{service}/` -3. Feature Capsule Agent: Add circuit breaker and caching -4. Test integration with both agents +### Add Platform Integration +**Agents**: Platform Feature Agent + Feature Capsule Agent +1. Platform Feature Agent: Implement/update platform endpoint logic +2. Feature Capsule Agent: Update consuming feature client (e.g. `external/platform-vehicles/`) +3. Feature Capsule Agent: Adjust caching/circuit breaker strategies as needed +4. Joint testing: run targeted unit/integration suites 5. Quality Enforcer Agent: Validate end-to-end ### Run Quality Checks @@ -188,9 +189,9 @@ Code is complete when: ## Architecture Quick Reference -### Hybrid Platform -- **Platform Microservices**: Independent services in `mvp-platform-services/` -- **Application Features**: Modular monolith in `backend/src/features/` +### Application Stack +- **Backend Feature Capsules**: Modular monolith in `backend/src/features/` +- **Platform Module**: Vehicle data + VIN decoding in `backend/src/features/platform/` - **Frontend**: React SPA in `frontend/src/` ### Feature Capsule Pattern @@ -206,29 +207,21 @@ backend/src/features/{feature}/ └── tests/ # Unit + integration tests ``` -### Platform Service Pattern -Each service is independent: -``` -mvp-platform-services/{service}/ -├── api/ # FastAPI application -├── database/ # SQLAlchemy models + migrations -└── tests/ # Service tests -``` ## Important Context - **Auth**: Frontend uses Auth0, backend validates JWTs - **Database**: PostgreSQL with user-isolated data (user_id scoping) -- **Platform APIs**: Authenticated via API keys (service-to-service) +- **Platform APIs**: Exposed via `/api/platform/*`, secured with Auth0 JWTs - **Caching**: Redis with feature-specific TTL strategies -- **Testing**: Jest (backend/frontend), pytest (platform services) +- **Testing**: Jest (backend + frontend) - **Docker-First**: All development in containers (production-only) ## Agent Coordination Rules ### Clear Ownership - Feature Capsule Agent: Backend application features -- Platform Service Agent: Independent microservices +- Platform Feature Agent: Platform capsule inside backend - Mobile-First Frontend Agent: All UI/UX code - Quality Enforcer Agent: Testing and validation only @@ -264,4 +257,4 @@ mvp-platform-services/{service}/ - Testing: `docs/TESTING.md` - Context Loading: `.ai/context.json` - Development Guidelines: `CLAUDE.md` -- Feature Documentation: `backend/src/features/{feature}/README.md` \ No newline at end of file +- Feature Documentation: `backend/src/features/{feature}/README.md` diff --git a/docs/README.md b/docs/README.md index fb1e344..6987854 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,5 +22,58 @@ Project documentation hub for the 5-container single-tenant architecture with in ## Notes - Canonical URLs: Frontend `https://motovaultpro.com`, Backend health `http://localhost:3001/health`. -- Hosts entry required: `127.0.0.1 motovaultpro.com`. - Feature test coverage: Basic test structure exists for vehicles and documents features; other features have placeholder tests. + + +## Cleanup Notes +> Documentation Audit + + - Documented commands make test/make test-frontend appear across README.md:12-17, backend/README.md:20-38, docs/TESTING.md:24-49, AI-INDEX.md:8, and frontend/ + README.md:8-28; the Makefile only advertises them in help (Makefile:11-12) with no corresponding targets, so the instructions currently break. + - README.md:27 and AI-INDEX.md:19 point folks to http://localhost:3001/health, but docker-compose.yml:77-135 never exposes that port, meaning the reachable + probe is https://motovaultpro.com/api/health via Traefik. + - docs/TESTING.md:11-99,169-175 commit to full per-feature suites and fixtures such as vehicles.fixtures.json, yet backend/src/features/fuel-logs/tests and + backend/src/features/maintenance/tests contain no files (see find output), and backend/src/features/vehicles/tests/fixtures is empty. + - Backend fuel-log docs still describe the legacy contract (gallons, pricePerGallon, mpg field) in backend/src/features/fuel-logs/README.md:20-78, but the + code now accepts/returns dateTime, fuelUnits, costPerUnit, efficiency, etc. (backend/src/features/fuel-logs/domain/fuel-logs.service.ts:17-320). + + Security & Platform + + - docs/VEHICLES-API.md:35-36 and 149-151 still require an API key, while the platform routes enforce Auth0 JWTs via fastify.authenticate (backend/src/ + features/platform/api/platform.routes.ts:20-42); there’s no API key configuration in the repo. + - docs/VEHICLES-API.md:38-41 promises 1-hour Redis TTLs, but PlatformCacheService stores dropdown data for six hours and successful VIN decodes for seven days + (backend/src/features/platform/domain/platform-cache.service.ts:27-110). + - docs/SECURITY.md:15-16 claims “Unauthenticated Endpoints – None,” yet /health and /api/health are open (backend/src/app.ts:69-86); docs/SECURITY.md:25- + 29 also states Postgres connections are encrypted even though the pool uses a plain postgresql:// URL without SSL options (backend/src/core/config/config- + loader.ts:213-218; backend/src/core/config/database.ts:1-16). + - docs/SECURITY.md:21-23 references the old FastAPI VIN service, but VIN decoding now lives entirely in TypeScript (backend/src/features/platform/domain/vin- + decode.service.ts:1-114). + + Feature Guides + + - backend/src/features/vehicles/README.md:15-108 still references an implemented dropdown proxy, a platform-vehicles client folder, and a platform- + vehicles.client.test.ts, yet the service methods remain TODO stubs returning empty arrays (backend/src/features/vehicles/domain/vehicles.service.ts:165-193) + and there is no such client or test file in the tree. + - docs/VEHICLES-API.md:58-97 says the frontend consumes /api/vehicles/dropdown/*, but the current client hits /platform/* and expects raw arrays (frontend/ + src/features/vehicles/api/vehicles.api.ts:35-69) while the backend responds with wrapped objects like { makes: [...] } (backend/src/features/platform/api/ + platform.controller.ts:48-94), so either the docs or the code path needs realignment. + - backend/src/features/fuel-logs/README.md:150-153 advertises a fuel-stats:vehicle:{vehicleId} Redis cache and response fields like totalLogs/averageMpg, but + getVehicleStats performs fresh queries and returns { logCount, totalFuelUnits, totalCost, averageCostPerUnit, totalDistance, averageEfficiency, unitLabels } + with no caching (backend/src/features/fuel-logs/domain/fuel-logs.service.ts:226-320). + - backend/src/features/documents/README.md:4,23-25 describes S3-compatible storage and a core/middleware/user-context dependency; in reality uploads go to + the filesystem adapter (backend/src/core/storage/storage.service.ts:1-48; backend/src/core/storage/adapters/filesystem.adapter.ts:1-86) and there is no user- + context module in backend/src/core. + - docs/DATABASE-SCHEMA.md:109-111 asserts station caching happens in Redis, but Station data is persisted in Postgres tables such as station_cache + (backend/src/features/stations/data/stations.repository.ts:11-115), and docs/DATABASE-SCHEMA.md:155-157 mentions “RESTRICT on delete” even though + migrations use ON DELETE CASCADE (backend/src/features/fuel-logs/migrations/001_create_fuel_logs_table.sql:18-21; backend/src/features/maintenance/ + migrations/002_recreate_maintenance_tables.sql:21-43). + + Questions + + - Do we want to add the missing make test / make test-frontend automation (so the documented workflow survives), or should the documentation be rewritten to + direct people to the existing docker compose exec ... npm test commands? + - For the vehicles dropdown flow, should the docs be updated to call out the current TODOs, or is finishing the proxy implementation (and aligning the + frontend/client responses) a higher priority? + + Suggested next steps: decide on the build/test command strategy, refresh the security/platform documentation to match the Auth0 setup and real cache + behaviour, and schedule a pass over the feature READMEs (vehicles, fuel logs, documents) so they match the implemented API contracts. \ No newline at end of file diff --git a/docs/VEHICLES-API.md b/docs/VEHICLES-API.md index 433a8ed..a91ccec 100644 --- a/docs/VEHICLES-API.md +++ b/docs/VEHICLES-API.md @@ -3,10 +3,10 @@ This document explains the end‑to‑end Vehicles API architecture after the platform service rebuild, how the MotoVaultPro app consumes it, how migrations/seeding work, and how to operate the stack in production‑only development. ## Overview -- Architecture: MotoVaultPro Application Service (Fastify + TS) consumes the MVP Platform service (FastAPI) with shared Postgres and Redis. +- Architecture: MotoVaultPro backend (Fastify + TypeScript) includes an integrated platform module that shares Postgres and Redis with the rest of the stack. - Goal: Predictable year→make→model→trim→engine cascades, production‑only workflow, AI‑friendly code layout and docs. -## Platform Vehicles Service +## Platform Vehicles Module ### Database Schema (Postgres schema: `vehicles`) - `make(id, name)` @@ -20,12 +20,12 @@ This document explains the end‑to‑end Vehicles API architecture after the pl Idempotent constraints/indexes added where applicable (e.g., unique lower(name), unique(model_id, year), guarded `CREATE INDEX IF NOT EXISTS`, guarded trigger). ### API Endpoints (Bearer auth required) -Prefix: `/api/v1/vehicles` -- `GET /years` → `[number]` distinct years (desc) -- `GET /makes?year={year}` → `{ makes: { id, name }[] }` -- `GET /models?year={year}&make_id={make_id}` → `{ models: { id, name }[] }` -- `GET /trims?year={year}&make_id={make_id}&model_id={model_id}` → `{ trims: { id, name }[] }` -- `GET /engines?year={year}&make_id={make_id}&model_id={model_id}&trim_id={trim_id}` → `{ engines: { id, name }[] }` +Prefix: `/api/platform` +- `GET /api/platform/years` → `[number]` distinct years (desc) +- `GET /api/platform/makes?year={year}` → `{ makes: { id, name }[] }` +- `GET /api/platform/models?year={year}&make_id={make_id}` → `{ models: { id, name }[] }` +- `GET /api/platform/trims?year={year}&make_id={make_id}&model_id={model_id}` → `{ trims: { id, name }[] }` +- `GET /api/platform/engines?year={year}&make_id={make_id}&model_id={model_id}&trim_id={trim_id}` → `{ engines: { id, name }[] }` Notes: - `make_id` is maintained for a consistent query chain, but engines are enforced by `(year, model_id, trim_id)`. @@ -41,14 +41,13 @@ Notes: - **Configurable**: Set via `CACHE_TTL` environment variable in seconds ### Seeds & Specific Examples -Seed files under `mvp-platform-services/vehicles/sql/schema/`: -- `001_schema.sql` – base tables -- `002_constraints_indexes.sql` – constraints/indexes -- `003_seed_minimal.sql` – minimal Honda/Toyota scaffolding -- `004_seed_filtered_makes.sql` – Chevrolet/GMC examples -- `005_seed_specific_vehicles.sql` – requested examples: - - 2023 GMC Sierra 1500 AT4x → Engine L87 (6.2L V8) - - 2017 Chevrolet Corvette Z06 Convertible → Engine LT4 (6.2L V8 SC) +Legacy FastAPI SQL seed scripts covered: +- Base schema (`001_schema.sql`) +- Constraints/indexes (`002_constraints_indexes.sql`) +- Minimal Honda/Toyota scaffolding (`003_seed_minimal.sql`) +- Chevrolet/GMC examples (`004_seed_filtered_makes.sql`) +- Targeted sample vehicles (`005_seed_specific_vehicles.sql`) +Contact the data team for access to these archival scripts if reseeding is required. Reapply seeds on an existing volume: - `docker compose exec -T mvp-postgres psql -U mvp_user -d mvp_db -f /docker-entrypoint-initdb.d/005_seed_specific_vehicles.sql` @@ -133,22 +132,18 @@ VIN/License rule ### Rebuild a single service - Frontend: `docker compose up -d --build frontend` -- Backend: `docker compose up -d --build backend` -- Platform API: `docker compose up -d --build mvp-platform` +- Backend (includes platform module): `docker compose up -d --build backend` ### Logs & Health -- Backend: `/health` – shows status/feature list -- Platform: `/health` – shows database/cache status -- Logs: - - `make logs-backend`, `make logs-frontend` - - `docker compose logs -f mvp-platform` +- Backend: `/health` – shows status/feature list, including platform readiness +- Logs: `make logs-backend`, `make logs-frontend` ### Common Reset Sequences - Platform seed reapply (non‑destructive): apply `005_seed_specific_vehicles.sql` and flush Redis cache. - Platform reset (WARNING - DESTRUCTIVE to shared resources): - `docker compose rm -sf mvp-postgres mvp-redis` - `docker volume rm motovaultpro_postgres_data motovaultpro_redis_data` - - `docker compose up -d mvp-postgres mvp-redis mvp-platform` + - `docker compose up -d mvp-postgres mvp-redis mvp-backend` - Note: This will destroy ALL application data, not just platform data, as database and cache are shared ## Security Summary @@ -169,9 +164,9 @@ VIN/License rule - Ensure Postgres is up; the runner now waits/retries, but confirm logs. ## Notable Files -- Platform schema & seeds: `mvp-platform-services/vehicles/sql/schema/001..005` -- Platform API code: `mvp-platform-services/vehicles/api/*` +- Platform schema & seeds: maintained by database admins (legacy FastAPI scripts available on request) +- Platform API integration: `backend/src/features/platform/api/*` - Backend dropdown proxy: `backend/src/features/vehicles/api/*` -- Backend platform client: `backend/src/features/vehicles/external/platform-vehicles/*` +- Backend platform module: `backend/src/features/platform/*` - Backend migrations runner: `backend/src/_system/migrations/run-all.ts` - Frontend vehicles UI: `frontend/src/features/vehicles/*` diff --git a/nginx-proxy-service.yml b/nginx-proxy-service.yml deleted file mode 100644 index 4f635af..0000000 --- a/nginx-proxy-service.yml +++ /dev/null @@ -1,21 +0,0 @@ - - # Main nginx proxy for subdomain routing - nginx-proxy: - image: nginx:alpine - container_name: nginx-proxy - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx-proxy/nginx.conf:/etc/nginx/nginx.conf:ro - - ./certs:/etc/nginx/certs:ro - depends_on: - - mvp-platform-landing - - admin-frontend - restart: unless-stopped - healthcheck: - test: ["CMD", "nginx", "-t"] - interval: 30s - timeout: 10s - retries: 3 - diff --git a/nginx-proxy/nginx.conf b/nginx-proxy/nginx.conf deleted file mode 100644 index 139af59..0000000 --- a/nginx-proxy/nginx.conf +++ /dev/null @@ -1,70 +0,0 @@ -events { - worker_connections 1024; -} - -http { - # Catch-all HTTP -> HTTPS redirect (handles localhost and unknown hosts) - server { - listen 80 default_server; - server_name _; - return 301 https://$host$request_uri; - } - - # Main domain - Landing page - server { - listen 80; - server_name motovaultpro.com; - return 301 https://$server_name$request_uri; - } - - server { - listen 443 ssl; - server_name motovaultpro.com; - - ssl_certificate /etc/nginx/certs/motovaultpro.com.crt; - ssl_certificate_key /etc/nginx/certs/motovaultpro.com.key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - location / { - proxy_pass http://mvp-platform-landing:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } - - # Admin subdomain - Admin tenant - server { - listen 80; - server_name admin.motovaultpro.com; - return 301 https://$server_name$request_uri; - } - - server { - listen 443 ssl; - server_name admin.motovaultpro.com; - - ssl_certificate /etc/nginx/certs/motovaultpro.com.crt; - ssl_certificate_key /etc/nginx/certs/motovaultpro.com.key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - location / { - proxy_pass http://admin-frontend:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /api/ { - proxy_pass http://admin-backend:3001; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } -} diff --git a/scripts/config-validator.sh b/scripts/config-validator.sh index f264ffd..ac17cb9 100755 --- a/scripts/config-validator.sh +++ b/scripts/config-validator.sh @@ -192,19 +192,9 @@ EOF echo "localdev123" > "$SECRETS_DIR/postgres-password.txt" echo "minioadmin" > "$SECRETS_DIR/minio-access-key.txt" echo "minioadmin123" > "$SECRETS_DIR/minio-secret-key.txt" -echo "mvp-platform-tenants-secret-key" > "$SECRETS_DIR/platform-tenants-api-key.txt" echo "admin-backend-service-token" > "$SECRETS_DIR/service-auth-token.txt" echo "your-auth0-client-secret" > "$SECRETS_DIR/auth0-client-secret.txt" echo "your-google-maps-api-key" > "$SECRETS_DIR/google-maps-api-key.txt" -EOF - ;; - "platform") - cat >> "$template_file" << 'EOF' -# Platform secrets -echo "platform123" > "$SECRETS_DIR/platform-db-password.txt" -echo "platform123" > "$SECRETS_DIR/vehicles-db-password.txt" -echo "mvp-platform-tenants-secret-key" > "$SECRETS_DIR/tenants-api-key.txt" -echo "admin-backend-service-token,mvp-platform-vehicles-service-token" > "$SECRETS_DIR/allowed-service-tokens.txt" EOF ;; esac