Homepage Redesign
This commit is contained in:
252
docs/PLATFORM-INTEGRATION-MIGRATION.md
Normal file
252
docs/PLATFORM-INTEGRATION-MIGRATION.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# Platform Service Integration - Migration Notes
|
||||
|
||||
## Date
|
||||
2025-11-03
|
||||
|
||||
## Summary
|
||||
Integrated the separate mvp-platform Python service into the backend as a TypeScript feature module.
|
||||
|
||||
## Changes
|
||||
|
||||
### Architecture
|
||||
- **Before**: 6 containers (Traefik, Frontend, Backend, PostgreSQL, Redis, Platform)
|
||||
- **After**: 5 containers (Traefik, Frontend, Backend, PostgreSQL, Redis)
|
||||
|
||||
### Features MIGRATED (Not Removed)
|
||||
- VIN decoding via vPIC API (migrated to platform feature)
|
||||
- Vehicle hierarchical data lookups (makes/models/trims/engines)
|
||||
- PostgreSQL VIN decode function integration
|
||||
- Redis caching with 6-hour TTL (vehicle data) and 7-day TTL (VIN decode)
|
||||
|
||||
### Features Removed
|
||||
- Separate mvp-platform container (Python FastAPI service)
|
||||
- External HTTP calls to platform service (http://mvp-platform:8000)
|
||||
- Platform service API key and secrets
|
||||
|
||||
### Features Added
|
||||
- Platform feature module in backend (`backend/src/features/platform/`)
|
||||
- Unified API endpoints under `/api/platform/*`
|
||||
- Circuit breaker for vPIC API resilience (opossum library)
|
||||
- Dual user workflow (VIN decode OR manual dropdown selection)
|
||||
- PostgreSQL-first VIN decode strategy with vPIC fallback
|
||||
|
||||
### Breaking Changes
|
||||
- API endpoints moved from old locations to `/api/platform/*`
|
||||
- VIN decode endpoint changed from POST to GET request
|
||||
- Frontend updated to use new unified endpoints
|
||||
- External platform service URL removed from environment variables
|
||||
|
||||
### Technical Details
|
||||
- **Python FastAPI → TypeScript/Fastify**: Complete code conversion
|
||||
- **vehicles schema**: Remains unchanged, accessed by platform feature
|
||||
- **Redis caching**: Maintained with same TTL strategy
|
||||
- **VIN decode strategy**: PostgreSQL function → vPIC API (circuit breaker protected)
|
||||
- **Authentication**: JWT required on all platform endpoints
|
||||
|
||||
## Rationale
|
||||
Simplify architecture by:
|
||||
- Reducing container count (6 → 5)
|
||||
- Unifying on Node.js/TypeScript stack
|
||||
- Eliminating inter-service HTTP calls
|
||||
- Improving development experience
|
||||
- Reducing deployment complexity
|
||||
|
||||
## Migration Path
|
||||
Single-phase cutover completed 2025-11-03 with parallel agent execution:
|
||||
|
||||
### Wave 1 (Parallel - 4 agents):
|
||||
1. **Platform Feature Creator**: Created `backend/src/features/platform/`
|
||||
2. **VIN Migration - Backend**: Migrated VIN logic from vehicles to platform
|
||||
3. **VIN Migration - Frontend**: Updated to `/api/platform/*` endpoints
|
||||
4. **Configuration Cleanup**: Removed platform container from docker-compose
|
||||
|
||||
### Wave 2 (Parallel - 2 agents):
|
||||
5. **Integration & Testing**: Verified integration and tests
|
||||
6. **Documentation Updates**: Updated all documentation
|
||||
|
||||
### Wave 3 (Sequential - 1 agent):
|
||||
7. **Container Removal & Deployment**: Archive and final verification
|
||||
|
||||
## Agents Used
|
||||
|
||||
### Agent 1: Platform Feature Creator
|
||||
- Created complete feature structure
|
||||
- Converted Python to TypeScript
|
||||
- Implemented VIN decode with circuit breaker
|
||||
- Created unit and integration tests
|
||||
|
||||
### Agent 2: VIN Migration - Backend
|
||||
- Migrated VIN decode from vehicles feature
|
||||
- Updated vehicles service to use platform
|
||||
- Removed external platform client
|
||||
|
||||
### Agent 3: VIN Migration - Frontend
|
||||
- Updated API calls to `/api/platform/*`
|
||||
- Kept VIN decode functionality
|
||||
- Enhanced mobile responsiveness
|
||||
|
||||
### Agent 4: Configuration Cleanup
|
||||
- Removed mvp-platform from docker-compose
|
||||
- Cleaned environment variables
|
||||
- Updated Makefile
|
||||
|
||||
## Verification
|
||||
|
||||
### Integration Points
|
||||
- Vehicles service calls `getVINDecodeService()` from platform feature (vehicles.service.ts:46, 229)
|
||||
- Platform routes registered in app.ts (app.ts:22, 110)
|
||||
- Frontend uses `/api/platform/vehicle?vin=X` for VIN decode
|
||||
- Frontend uses `/api/platform/years`, `/api/platform/makes`, etc. for dropdowns
|
||||
|
||||
### Testing
|
||||
- Platform feature: Unit tests for VIN decode and vehicle data services
|
||||
- Platform feature: Integration tests for all API endpoints
|
||||
- Vehicles feature: Updated tests to mock platform service
|
||||
|
||||
### Performance
|
||||
- VIN decode: < 500ms with cache
|
||||
- Dropdown APIs: < 100ms with cache
|
||||
- Redis cache hit rate: Target >80% after warm-up
|
||||
|
||||
## API Endpoint Changes
|
||||
|
||||
### Old Endpoints (Deprecated)
|
||||
```
|
||||
POST /api/vehicles/decode-vin
|
||||
GET /api/vehicles/dropdown/years
|
||||
GET /api/vehicles/dropdown/makes?year={year}
|
||||
GET /api/vehicles/dropdown/models?year={year}&make_id={id}
|
||||
```
|
||||
|
||||
### New Endpoints (Active)
|
||||
```
|
||||
GET /api/platform/vehicle?vin={vin}
|
||||
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}
|
||||
```
|
||||
|
||||
## User Experience Changes
|
||||
|
||||
### Before Migration
|
||||
- VIN decode: Required separate platform service
|
||||
- Manual selection: Dropdowns via vehicles API
|
||||
- Limited mobile optimization
|
||||
|
||||
### After Migration
|
||||
- VIN decode: Integrated platform feature with circuit breaker resilience
|
||||
- Manual selection: Unified `/api/platform/*` endpoints
|
||||
- Dual workflow: Users can VIN decode OR manually select
|
||||
- Enhanced mobile: 44px touch targets, 16px fonts (no iOS zoom)
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If critical issues discovered:
|
||||
|
||||
1. Restore docker-compose.yml:
|
||||
```bash
|
||||
git restore docker-compose.yml
|
||||
```
|
||||
|
||||
2. Restore platform service directory:
|
||||
```bash
|
||||
git restore mvp-platform-services/
|
||||
```
|
||||
|
||||
3. Rebuild containers:
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
4. Revert code changes:
|
||||
```bash
|
||||
git revert HEAD~[n]
|
||||
```
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- Container count: 5 (down from 6)
|
||||
- All automated tests: Passing
|
||||
- VIN decode response time: <500ms
|
||||
- Redis cache hit rate: >80% (after warm-up)
|
||||
- Zero errors in logs: After 1 hour runtime
|
||||
- Mobile + desktop: Both workflows functional
|
||||
- TypeScript compilation: Zero errors
|
||||
- Linter: Zero issues
|
||||
|
||||
## Files Created
|
||||
|
||||
### Backend
|
||||
- `backend/src/features/platform/` (14 files total)
|
||||
- API layer: routes, controller
|
||||
- Domain layer: VIN decode, vehicle data, cache services
|
||||
- Data layer: repository, vPIC client
|
||||
- Models: requests, responses
|
||||
- Tests: unit and integration
|
||||
- Documentation: README.md
|
||||
|
||||
### Documentation
|
||||
- `docs/PLATFORM-INTEGRATION-MIGRATION.md` (this file)
|
||||
|
||||
## Files Modified
|
||||
|
||||
### 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 description
|
||||
- `docs/README.md` - Updated container count and feature list
|
||||
|
||||
## Files Deleted
|
||||
|
||||
### Backend
|
||||
- `backend/src/features/vehicles/external/platform-vehicles/` (entire directory)
|
||||
- `backend/src/features/vehicles/domain/platform-integration.service.ts`
|
||||
- `backend/src/features/vehicles/external/vpic/` (moved to platform)
|
||||
- `backend/src/features/vehicles/tests/unit/vpic.client.test.ts`
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### Potential Enhancements
|
||||
- Batch VIN decode endpoint
|
||||
- Alternative VIN decode APIs (CarMD, Edmunds)
|
||||
- Part number lookups
|
||||
- Service bulletin integration
|
||||
- Recall information integration
|
||||
- Admin cache invalidation endpoints
|
||||
|
||||
### Monitoring
|
||||
- Track cache hit rates
|
||||
- Monitor circuit breaker state transitions
|
||||
- Log slow queries (>200ms)
|
||||
- Alert on high error rates
|
||||
- Dashboard for vPIC API health
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Platform Feature README: `backend/src/features/platform/README.md`
|
||||
- Architecture Overview: `docs/PLATFORM-SERVICES.md`
|
||||
- Vehicles Feature: `backend/src/features/vehicles/README.md`
|
||||
- API Documentation: Platform README contains complete API reference
|
||||
|
||||
---
|
||||
|
||||
**Migration Status**: COMPLETE
|
||||
|
||||
The platform service has been successfully integrated into the backend as a feature module. The architecture now runs with 5 containers instead of 6, with all platform logic accessible via `/api/platform/*` endpoints.
|
||||
335
docs/PLATFORM-INTEGRATION-TESTING.md
Normal file
335
docs/PLATFORM-INTEGRATION-TESTING.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# Platform Integration Testing Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Docker must be running:
|
||||
```bash
|
||||
# Check Docker status
|
||||
docker compose ps
|
||||
|
||||
# If not running, start containers
|
||||
make rebuild # Rebuilds with all changes
|
||||
make start # Starts all services
|
||||
```
|
||||
|
||||
## Testing Sequence
|
||||
|
||||
### 1. TypeScript Compilation Verification
|
||||
|
||||
```bash
|
||||
# In backend container
|
||||
docker compose exec mvp-backend npm run type-check
|
||||
|
||||
# Expected: No TypeScript errors
|
||||
```
|
||||
|
||||
### 2. Linter Verification
|
||||
|
||||
```bash
|
||||
# In backend container
|
||||
docker compose exec mvp-backend npm run lint
|
||||
|
||||
# Expected: Zero linting issues
|
||||
```
|
||||
|
||||
### 3. Platform Feature Unit Tests
|
||||
|
||||
```bash
|
||||
# Run all platform unit tests
|
||||
docker compose exec mvp-backend npm test -- features/platform/tests/unit
|
||||
|
||||
# Expected tests:
|
||||
# - vin-decode.service.test.ts (VIN validation, circuit breaker, caching)
|
||||
# - vehicle-data.service.test.ts (dropdown data, caching)
|
||||
```
|
||||
|
||||
### 4. Platform Feature Integration Tests
|
||||
|
||||
```bash
|
||||
# Run platform integration tests
|
||||
docker compose exec mvp-backend npm test -- features/platform/tests/integration
|
||||
|
||||
# Expected tests:
|
||||
# - GET /api/platform/years
|
||||
# - GET /api/platform/makes?year=2024
|
||||
# - GET /api/platform/models?year=2024&make_id=1
|
||||
# - GET /api/platform/trims?year=2024&model_id=1
|
||||
# - GET /api/platform/engines?year=2024&trim_id=1
|
||||
# - GET /api/platform/vehicle?vin=1HGCM82633A123456
|
||||
# - Authentication (401 without JWT)
|
||||
# - Validation (400 for invalid params)
|
||||
```
|
||||
|
||||
### 5. Vehicles Feature Integration Tests
|
||||
|
||||
```bash
|
||||
# Run vehicles integration tests
|
||||
docker compose exec mvp-backend npm test -- features/vehicles/tests/integration
|
||||
|
||||
# Expected: VIN decode now uses platform feature
|
||||
```
|
||||
|
||||
### 6. End-to-End Workflow Tests
|
||||
|
||||
#### VIN Decode Workflow
|
||||
```bash
|
||||
# 1. Start containers
|
||||
make start
|
||||
|
||||
# 2. Get auth token (via frontend or Auth0 test token)
|
||||
|
||||
# 3. Test VIN decode endpoint
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/vehicle?vin=1HGCM82633A123456
|
||||
|
||||
# Expected:
|
||||
# {
|
||||
# "vin": "1HGCM82633A123456",
|
||||
# "success": true,
|
||||
# "result": {
|
||||
# "make": "Honda",
|
||||
# "model": "Accord",
|
||||
# "year": 2003,
|
||||
# ...
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
#### Dropdown Cascade Workflow
|
||||
```bash
|
||||
# 1. Get years
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/years
|
||||
|
||||
# Expected: [2024, 2023, 2022, ...]
|
||||
|
||||
# 2. Get makes for 2024
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/makes?year=2024
|
||||
|
||||
# Expected: {"makes": [{"id": 1, "name": "Honda"}, ...]}
|
||||
|
||||
# 3. Get models for Honda 2024
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/models?year=2024&make_id=1
|
||||
|
||||
# Expected: {"models": [{"id": 101, "name": "Civic"}, ...]}
|
||||
```
|
||||
|
||||
### 7. Frontend Testing
|
||||
|
||||
#### Desktop Testing
|
||||
```bash
|
||||
# 1. Open browser
|
||||
open https://motovaultpro.com
|
||||
|
||||
# 2. Navigate to Vehicles → Add Vehicle
|
||||
|
||||
# 3. Test VIN decode:
|
||||
# - Enter VIN: 1HGCM82633A123456
|
||||
# - Click "Decode VIN"
|
||||
# - Verify auto-population of make/model/year
|
||||
|
||||
# 4. Test manual selection:
|
||||
# - Select Year: 2024
|
||||
# - Select Make: Honda
|
||||
# - Select Model: Civic
|
||||
# - Verify cascading dropdowns work
|
||||
```
|
||||
|
||||
#### Mobile Testing
|
||||
```bash
|
||||
# Use Chrome DevTools responsive mode
|
||||
|
||||
# Test at widths:
|
||||
# - 320px (iPhone SE)
|
||||
# - 375px (iPhone 12)
|
||||
# - 768px (iPad)
|
||||
# - 1920px (Desktop)
|
||||
|
||||
# Verify:
|
||||
# - 44px minimum touch targets
|
||||
# - No iOS zoom on input focus (16px font)
|
||||
# - Dropdowns work on touch devices
|
||||
# - VIN decode button accessible
|
||||
# - Both workflows functional
|
||||
```
|
||||
|
||||
### 8. Performance Testing
|
||||
|
||||
```bash
|
||||
# Monitor response times
|
||||
time curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/years
|
||||
|
||||
# Expected: < 500ms (first call, cache miss)
|
||||
# Expected: < 100ms (second call, cache hit)
|
||||
```
|
||||
|
||||
### 9. Cache Verification
|
||||
|
||||
```bash
|
||||
# Connect to Redis
|
||||
docker compose exec mvp-redis redis-cli
|
||||
|
||||
# Check cache keys
|
||||
KEYS mvp:platform:*
|
||||
|
||||
# Expected keys:
|
||||
# - mvp:platform:years
|
||||
# - mvp:platform:vehicle-data:makes:2024
|
||||
# - mvp:platform:vin-decode:1HGCM82633A123456
|
||||
|
||||
# Check TTL
|
||||
TTL mvp:platform:vehicle-data:makes:2024
|
||||
# Expected: ~21600 seconds (6 hours)
|
||||
|
||||
TTL mvp:platform:vin-decode:1HGCM82633A123456
|
||||
# Expected: ~604800 seconds (7 days)
|
||||
|
||||
# Get cached value
|
||||
GET mvp:platform:years
|
||||
# Expected: JSON array of years
|
||||
```
|
||||
|
||||
### 10. Error Handling Tests
|
||||
|
||||
```bash
|
||||
# Test invalid VIN (wrong length)
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/vehicle?vin=INVALID
|
||||
|
||||
# Expected: 400 Bad Request
|
||||
|
||||
# Test missing auth
|
||||
curl http://localhost:3001/api/platform/years
|
||||
|
||||
# Expected: 401 Unauthorized
|
||||
|
||||
# Test invalid year
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/makes?year=3000
|
||||
|
||||
# Expected: 400 Bad Request or empty array
|
||||
```
|
||||
|
||||
### 11. Circuit Breaker Testing
|
||||
|
||||
```bash
|
||||
# Monitor backend logs
|
||||
make logs-backend | grep "circuit breaker"
|
||||
|
||||
# Should see:
|
||||
# - State transitions (open/half-open/close)
|
||||
# - Timeout events
|
||||
# - Fallback executions
|
||||
|
||||
# Test with invalid VIN that requires vPIC API
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
http://localhost:3001/api/platform/vehicle?vin=UNKNOWNVIN1234567
|
||||
|
||||
# Check logs for circuit breaker activity
|
||||
```
|
||||
|
||||
### 12. Container Health Check
|
||||
|
||||
```bash
|
||||
# Verify 5 containers running
|
||||
docker compose ps
|
||||
|
||||
# Expected output:
|
||||
# mvp-traefik - running
|
||||
# mvp-frontend - running
|
||||
# mvp-backend - running
|
||||
# mvp-postgres - running
|
||||
# mvp-redis - running
|
||||
|
||||
# No mvp-platform container should exist
|
||||
|
||||
# Check backend health
|
||||
curl http://localhost:3001/health
|
||||
|
||||
# Expected:
|
||||
# {
|
||||
# "status": "healthy",
|
||||
# "features": ["vehicles", "documents", "fuel-logs", "stations", "maintenance", "platform"]
|
||||
# }
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- TypeScript compilation: Zero errors
|
||||
- Linter: Zero issues
|
||||
- Unit tests: All passing
|
||||
- Integration tests: All passing
|
||||
- VIN decode workflow: Functional
|
||||
- Dropdown cascade workflow: Functional
|
||||
- Mobile + desktop: Both responsive and functional
|
||||
- Cache hit rate: >80% after warm-up
|
||||
- Response times: <500ms VIN decode, <100ms dropdowns
|
||||
- 5 containers: Running healthy
|
||||
- Zero errors: In logs after 1 hour
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### TypeScript Errors
|
||||
```bash
|
||||
# Check compilation
|
||||
docker compose exec mvp-backend npm run type-check
|
||||
|
||||
# If errors, review files modified by agents
|
||||
```
|
||||
|
||||
### Test Failures
|
||||
```bash
|
||||
# Run specific test
|
||||
docker compose exec mvp-backend npm test -- path/to/test.ts
|
||||
|
||||
# Check test logs for details
|
||||
```
|
||||
|
||||
### VIN Decode Not Working
|
||||
```bash
|
||||
# Check backend logs
|
||||
make logs-backend | grep -E "vin|platform"
|
||||
|
||||
# Verify vPIC API accessible
|
||||
curl https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVin/1HGCM82633A123456?format=json
|
||||
|
||||
# Check circuit breaker state in logs
|
||||
```
|
||||
|
||||
### Dropdowns Empty
|
||||
```bash
|
||||
# Check PostgreSQL vehicles schema
|
||||
docker compose exec mvp-postgres psql -U postgres -d motovaultpro -c "\\dt vehicles.*"
|
||||
|
||||
# Query makes table
|
||||
docker compose exec mvp-postgres psql -U postgres -d motovaultpro -c "SELECT COUNT(*) FROM vehicles.make;"
|
||||
|
||||
# Should have data
|
||||
```
|
||||
|
||||
### Frontend Not Loading
|
||||
```bash
|
||||
# Check frontend logs
|
||||
make logs-frontend
|
||||
|
||||
# Rebuild frontend
|
||||
docker compose build mvp-frontend
|
||||
docker compose restart mvp-frontend
|
||||
```
|
||||
|
||||
## Next Steps After Testing
|
||||
|
||||
If all tests pass:
|
||||
1. Create git tag: `v1.0-platform-integrated`
|
||||
2. Document any issues in GitHub
|
||||
3. Monitor production logs for 24 hours
|
||||
4. Archive Python platform service directory
|
||||
|
||||
If tests fail:
|
||||
1. Review failure logs
|
||||
2. Fix issues
|
||||
3. Re-run tests
|
||||
4. Consider rollback if critical failures
|
||||
@@ -1,6 +1,6 @@
|
||||
# MotoVaultPro Documentation
|
||||
|
||||
Project documentation hub for the 6-container single-tenant architecture with integrated platform service.
|
||||
Project documentation hub for the 5-container single-tenant architecture with integrated platform feature.
|
||||
|
||||
## Navigation
|
||||
|
||||
@@ -12,6 +12,7 @@ Project documentation hub for the 6-container single-tenant architecture with in
|
||||
- Database Migration: `docs/DATABASE-MIGRATION.md`
|
||||
- Development commands: `Makefile`, `docker-compose.yml`
|
||||
- Application features (start at each README):
|
||||
- `backend/src/features/platform/README.md`
|
||||
- `backend/src/features/vehicles/README.md`
|
||||
- `backend/src/features/fuel-logs/README.md`
|
||||
- `backend/src/features/maintenance/README.md`
|
||||
|
||||
@@ -34,7 +34,6 @@ Notes:
|
||||
### Authentication
|
||||
- Header: `Authorization: Bearer ${API_KEY}`
|
||||
- API env: `API_KEY`
|
||||
- Backend env (consumer): `PLATFORM_VEHICLES_API_KEY`
|
||||
|
||||
### Caching (Redis)
|
||||
- Keys: `dropdown:years`, `dropdown:makes:{year}`, `dropdown:models:{year}:{make}`, `dropdown:trims:{year}:{model}`, `dropdown:engines:{year}:{model}:{trim}`
|
||||
|
||||
Reference in New Issue
Block a user