Last updated: September 2026
Shree Ganesh Autodeal is a full-stack two-wheeler dealership platform with a Flutter admin app, a React customer catalog, and a Spring Boot backend. The owner can manage inventory, images, vehicle documents, categories, and sales from the mobile app, while customers can browse available vehicles through the public web catalog, receive real-time inventory updates, and subscribe to email alerts for newly listed inventory.
The backend includes Redis-backed caching for read-heavy catalog and reporting endpoints, RabbitMQ-backed asynchronous task processing for subscriber email notifications, Groq LLM vehicle description generation, and real-time inventory events over STOMP WebSockets. It also includes transaction-safe event dispatching, targeted cache eviction on inventory mutations, and API-key based protection for admin APIs.
Autodeal/
|
|-- ShreeGaneshAutodeal-backend/
| `-- ShreeGaneshAutodeal/ # Spring Boot API
|
|-- Autodeal-web-app/ # React + Vite customer catalog
|
|-- mobile-app/ # Flutter admin application
|
|-- supabase/
| `-- schema.sql # PostgreSQL schema
|
|-- screenshots/ # Mobile app screenshots
|
|-- .github/
| `-- workflows/
| |-- backend-unit-tests.yml
| `-- web-app-unit-tests.yml
|
`-- README.md
- Add, edit, and delete vehicles
- Upload multiple vehicle images
- Upload RC, insurance, invoice, and other documents
- Manage categories
- Mark vehicles as sold
- View sales reports
- Share vehicle details
- Manage inventory from mobile
- Send an API key with protected admin requests
- Centralized HTTP client with API error handling
- Multipart file uploads for images and documents
- Browse vehicle inventory
- Search vehicles
- Filter by category and status
- View vehicle images and detailed specifications
- Receive real-time inventory updates when vehicles are created or marked sold
- Store catalog inventory in Redux so list/grid UI updates automatically from shared state
- Subscribe to vehicle availability email notifications with OTP verification
- Responsive customer-facing layout
- Access public catalog APIs without authentication
- Unit testing with Vitest and React Testing Library
- REST APIs for admin, customer catalog, and newsletter subscriber flows
- API-key based authentication for admin APIs
- Public access for customer catalog and subscriber APIs
- DTO-first API responses
- Pagination, search, and dynamic filtering
- PostgreSQL persistence through Spring Data JPA
- Supabase Storage integration for vehicle media and documents
- RabbitMQ Asynchronous Messaging:
vehicle_createqueue: Dispatches new vehicle alerts to all active subscribers via email in the backgroundvehicle_llm_descriptionqueue: Asynchronously generates rich sales copy via Groq LLM without blocking vehicle creationvehicle_soldqueue: Publishes sold-vehicle inventory updates to connected web clients- Vehicle creation runs fully asynchronously: measured 89.6% faster (≈9.6x) than the synchronous equivalent (see Asynchronous Vehicle Creation Performance)
- Real-Time Inventory Updates:
- Spring STOMP endpoint exposed at
/ws - Inventory topic broadcast on
/topic/inventory VEHICLE_CREATEDevents are emitted after vehicle creation and consumed by the React catalogVEHICLE_SOLDevents are emitted after a vehicle is marked sold and remove the vehicle from the live catalog state- React stores vehicle inventory in Redux, hydrates it from REST, then patches it from WebSocket events
- Spring STOMP endpoint exposed at
- Transaction-Safe Post-Commit Dispatch:
- Leverages Spring
TransactionSynchronizationManager.afterCommit()to ensure RabbitMQ messages are only dispatched after the database transaction has committed, eliminating race conditions
- Leverages Spring
- Subscriber Onboarding with OTP Verification:
- Secure 6-digit OTP generation stored in Redis with a 5-minute TTL
- Instant OTP email delivery via Brevo / Gmail SMTP
- Verification activates the subscriber for inventory alerts
- Groq LLM Integration:
- Automated automotive sales copywriting powered by Groq API
- Formats vehicle specifications (brand, model, variant, year, km, price) into compelling dealership sales pitches
- Centralized exception handling with structured JSON error responses
- Redis-backed cache for high-read catalog endpoints
- Targeted cache eviction on inventory mutations
- Comprehensive unit and slice tests (126 backend unit tests, 57 web app tests)
- GitHub Actions CI pipeline for automated backend and frontend testing
| Layer | Current Stack |
|---|---|
| Backend | Java 21, Spring Boot 4.1.0, Spring MVC, Spring WebSocket/STOMP, Spring Data JPA, Hibernate, Maven |
| Messaging | RabbitMQ via Spring AMQP (spring-boot-starter-amqp) |
Spring Mail (spring-boot-starter-mail) via Brevo / Gmail SMTP |
|
| LLM | Groq API (llama-3.3-70b-versatile / configured model) |
| Security | Spring Security, API key authentication |
| Testing | JUnit 5 (Jupiter), Mockito, MockMvc, AssertJ, H2 in-memory DB |
| Cache | Redis through Spring Cache and Spring Data Redis |
| Database | PostgreSQL-compatible schema, H2 for tests |
| Storage | Supabase Storage |
| Web | React 19, Vite 8, TypeScript 6, Tailwind CSS 4, Redux Toolkit, React Redux, STOMP client |
| Web Testing | Vitest, React Testing Library, jsdom |
| Mobile | Flutter, Dart, Provider, http, image_picker, file_picker |
| CI/CD | GitHub Actions |
React Customer Web App Flutter Admin App
| |
| Public GET / Subscriber POST | X-ADMIN-KEY
| STOMP subscribe /topic/inventory |
v v
Public / Subscriber APIs Admin APIs
\ /
\ /
v v
Spring Boot Backend
|
+-------------------+-------------------+
| |
Database Cache & Store
| |
PostgreSQL (Source of truth) Redis Cache & OTP Store
Supabase Storage (Media/Docs)
|
+-------------------+-------------------+
|
Transaction-Safe Post-Commit Hooks
|
v
RabbitMQ Message Broker
|
+---> Queue: "vehicle_create"
| |
| +---> RabbitmqReceiver.sendNotifications()
| | |
| | v
| | EmailService (SMTP / Brevo) -> Active Subscribers
| |
| `---> VehicleWebSocketService.publishVehicleAdded()
| |
| v
| STOMP /topic/inventory -> VEHICLE_CREATED
|
+---> Queue: "vehicle_sold"
| |
| v
| VehicleWebSocketService.publishVehicleSold()
| |
| v
| STOMP /topic/inventory -> VEHICLE_SOLD
|
+---> Queue: "vehicle_llm_description"
|
v
RabbitmqReceiver.generateVehicleDescription()
|
v
LLMService (Groq API) -> Updates Vehicle Description in DB
PostgreSQL remains the source of truth. Redis is used for catalog response caching and short-lived OTP tokens. RabbitMQ reliably offloads external network calls (email sending and Groq LLM completion) and fan-out inventory events to asynchronous background threads.
Initial catalog load:
React App
-> GET /api/catalog/vehicles
-> Redux vehicles slice stores page.content
-> Vehicle grid renders from Redux selectors
Vehicle created from Flutter admin app:
Flutter Admin App
-> POST /api/admin/vehicles
-> VehicleService saves vehicle in PostgreSQL
-> afterCommit sends vehicle id to RabbitMQ vehicle_create queue
-> RabbitmqReceiver broadcasts VEHICLE_CREATED to /topic/inventory
-> React STOMP client receives event
-> React fetches /api/catalog/vehicles/{id}
-> Redux upserts the vehicle if it matches current filters
-> Vehicle grid updates without page refresh
Vehicle marked sold from Flutter admin app:
Flutter Admin App
-> POST /api/admin/vehicles/{id}/sales
-> VehicleService marks vehicle SOLD in PostgreSQL
-> afterCommit sends vehicle id to RabbitMQ vehicle_sold queue
-> RabbitmqReceiver broadcasts VEHICLE_SOLD to /topic/inventory
-> React STOMP client receives event
-> Redux removes the vehicle from the current catalog list
-> Vehicle grid updates without page refresh
The React frontend uses @stomp/stompjs to connect to the Spring WebSocket endpoint. useVehicles() performs the initial REST fetch and stores vehicles in Redux. useInventoryWebSocket() listens for /topic/inventory events and dispatches Redux actions such as vehicleUpserted and vehicleRemoved, so components that read from the vehicle slice update automatically.
The backend uses a lightweight API key authentication model for the Flutter admin application.
The admin API key is stored as a backend environment variable and configured in the Flutter admin application's API client.
Flutter Admin App
|
| X-ADMIN-KEY: <admin-api-key>
v
Spring Security Filter Chain
|
v
Admin API Key Filter
|
+---- Invalid / Missing Key ----> 401 Unauthorized
|
+---- Valid Key ----------------> ADMIN authentication
|
v
Admin Controller
The API key is validated before protected admin requests reach the controller.
| API Area | Authentication | Client | Purpose |
|---|---|---|---|
/api/catalog/** GET |
None | React Web App / Public | Browse vehicles & categories |
/api/subscribers/** POST |
None | React Web App / Public | Request & verify OTP for newsletter |
/api/admin/** |
Admin API key | Flutter Admin App | Inventory, media, docs, sales |
Only the public catalog GET APIs are intended to be accessed without authentication.
Admin GET endpoints are also protected because they may expose private inventory, documents, or sales information.
The following API operations require the admin API key:
GET /api/admin/categories
POST /api/admin/categories
PUT /api/admin/categories/{id}
DELETE /api/admin/categories/{id}
GET /api/admin/vehicles
POST /api/admin/vehicles
GET /api/admin/vehicles/{id}
PUT /api/admin/vehicles/{id}
DELETE /api/admin/vehicles/{id}
POST /api/admin/vehicles/{id}/images
GET /api/admin/vehicles/{id}/images
POST /api/admin/vehicles/{id}/documents
GET /api/admin/vehicles/{id}/documents
DELETE /api/admin/documents/{id}
POST /api/admin/vehicles/{id}/sales
GET /api/admin/sales/report
GET /api/admin/redis-stats
Flutter sends the API key using:
X-ADMIN-KEY: <admin-api-key>For example:
POST /api/admin/vehicles
Content-Type: application/json
X-ADMIN-KEY: <admin-api-key>The backend reads the key from an environment variable:
ADMIN_API_KEY=<strong-random-secret>The Spring Boot configuration maps it to:
admin.api-key=${ADMIN_API_KEY}The API key must not be committed to source control.
Security note: an API key embedded in a mobile application can potentially be extracted through reverse engineering. This approach is intended as a lightweight protection mechanism for the current single-admin application. HTTPS is required in production.
The Flutter application uses a centralized ApiClient for communication with the backend.
The API client automatically sends the admin API key with:
- GET requests
- POST requests
- PUT requests
- DELETE requests
- Multipart document uploads
- Multipart vehicle image uploads
The common request headers are:
{
'Content-Type': 'application/json',
'X-ADMIN-KEY': adminKey,
}Multipart requests explicitly attach the same API key header before uploading files.
The Flutter API client handles:
- HTTP errors
401 Unauthorized404 Not Found409 Conflict413 Payload Too Large- Server-side error messages
- Connection failures
- Invalid JSON responses
- Multipart upload failures
API errors are represented through an ApiException containing the server message and optional HTTP status code.
Vehicle images and documents are uploaded using multipart/form-data.
Multipart requests allow the application to send both regular fields and binary files in the same HTTP request.
For example, a document upload can contain:
POST /api/admin/vehicles/{id}/documents
Headers:
X-ADMIN-KEY: <admin-api-key>
Fields:
title
type
File:
document.pdf
Vehicle image uploads similarly contain image files together with fields such as startOrder and altText.
The Spring Boot backend uses Redis through Spring Cache.
| Cache | API / Service Path | TTL | Evicted When |
|---|---|---|---|
categories |
Category list | 30 min | Category create, update, delete |
vehicle-searches |
Paginated vehicle search/listing | 2 min | Vehicle/category mutations, image upload, mark sold |
public-vehicle-details |
Public vehicle detail | 5 min | Vehicle update/delete, image upload, mark sold |
admin-vehicle-details |
Admin vehicle detail with private data | 2 min | Vehicle/document/category mutations |
vehicle-images |
Vehicle image list | 5 min | Vehicle update/delete, image upload |
vehicle-documents |
Vehicle document list | 5 min | Document upload/delete, vehicle delete |
sales-reports |
Sales report dashboard | 1 min | Vehicle create/update/delete, mark sold |
- Cache keys are deterministic for paginated vehicle filters.
- Response DTOs are serializable, so Redis stores API-safe payloads instead of JPA entities.
- Mutations evict related cache entries to avoid stale inventory data.
- Redis errors fail open: the API falls back to database reads and logs cache failures instead of failing the request.
- Tests use
spring.cache.type=none, so CI/local tests do not require Redis.
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_TIMEOUT=2sTo run Redis locally:
docker run --name autodeal-redis -p 6379:6379 -d redis:7-alpineTo run the backend without Redis during development:
CACHE_TYPE=simpleRedis-backed caching with TTL-based policies and targeted cache eviction reduced average catalog API latency from ~700 ms to ~250 ms — a ~64% reduction — while sustaining a 90%+ cache hit ratio.
Live Redis stats are exposed by the admin API at:
GET /api/admin/redis-stats
which returns keyspace_hits, keyspace_misses, total_operations, and hit_ratio_percent, enabling real-time verification of the hit ratio.
These numbers are based on the repository/service query paths in the current backend. Actual latency will depend on deployment, network distance, database size, and Redis placement.
| Endpoint | Before Redis | Warm Redis Hit | Query Reduction |
|---|---|---|---|
GET /api/catalog/categories |
1 category query | 0 DB queries | 100% per hit |
GET /api/catalog/vehicles?page=0&size=24 |
About 2 DB queries: page select + count | 0 DB queries | 100% per hit |
GET /api/catalog/vehicles/{id} |
About 3 DB queries: vehicle, category, images | 0 DB queries | 100% per hit |
GET /api/admin/vehicles/{id} |
About 5 DB queries: vehicle, category, images, documents, sales | 0 DB queries | 100% per hit |
GET /api/admin/sales/report |
About 4 DB queries: sales rows + 3 status counts | 0 DB queries | 100% per hit |
Example repeated-load impact inside a TTL window:
| Scenario | Without Redis | With Redis | Reduction |
|---|---|---|---|
| 1,000 identical catalog page requests | About 2,000 DB queries | About 2 DB queries after first cache fill | About 99.9% |
| 1,000 identical public detail requests | About 3,000 DB queries | About 3 DB queries after first cache fill | About 99.9% |
| 1,000 identical sales report requests | About 4,000 DB queries | About 4 DB queries after first cache fill | About 99.9% |
Resume-ready bullet:
Integrated Redis-backed caching in a Spring Boot 4 backend for catalog, vehicle detail, media, and sales report APIs with deterministic cache keys, TTL-based policies, targeted eviction, and fail-open error handling, reducing average catalog API latency from ~700 ms to ~250 ms (~64% reduction) at a 90%+ cache hit ratio, and cutting repeated read-query load by about 99.9% within cache TTL windows. Live metrics are exposed via a
GET /api/admin/redis-statsendpoint.
Vehicle creation was benchmarked both synchronously and asynchronously in the backend. In the synchronous path, the Groq LLM description generation and subscriber email notifications run inline before the API responds. In the asynchronous path, those expensive operations are dispatched to RabbitMQ and processed in the background, so the API returns immediately after the database save.
| Approach | Measured Time |
|---|---|
Synchronous (createSync) |
3,303 ms |
Asynchronous (createAsync via RabbitMQ) |
343 ms |
The asynchronous flow saves 2,960 ms per vehicle creation, which is an ≈89.6% reduction in request time — roughly 9.6x faster. The heavier work is offloaded to the message broker and never blocks the admin API response.
- The backend instruments the vehicle creation flow with
System.nanoTime()(VehicleService.createSync/VehicleService.createAsync) and logs the elapsed duration in milliseconds. - Both paths were run against the same environment and vehicle payload so the comparison isolates the sync vs. async offloading only.
- The Flutter client also logs the round-trip request time via
StopwatchinApiClient, so end-to-end latency can be compared from the admin app side.
Note: total duration depends on deployment, network distance, database size, and how long the Groq LLM call takes. The ratio between the two paths stays consistent because async simply removes the inline external network calls from the request thread.
PORT=8080
DB_URL=
DB_USERNAME=
DB_PASSWORD=
JPA_DDL_AUTO=update
CORS_ALLOWED_ORIGINS=
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_STORAGE_BUCKET=
GROQ_API_KEY=
GROQ_API_URL=
GROQ_MODEL=
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_TIMEOUT=2s
RABBITMQ_URL=
ADMIN_API_KEY=For local development, keep secrets in application-local.properties or environment variables and do not commit them to Git.
The backend uses:
spring.config.import=optional:file:application-local.propertiesThe API key is configured through:
admin.api-key=${ADMIN_API_KEY}For tests, a test-specific value can be supplied through src/test/resources/application.properties:
admin.api-key=test-admin-key
spring.cache.type=noneNote: the backend and web app both default to http://localhost:8080 for API/WebSocket access. If the backend runs on another port, set VITE_API_BASE_URL in the React app so both REST calls and the STOMP WebSocket URL point to the same backend.
These endpoints are public and do not require an API key:
GET /api/catalog/categories
GET /api/catalog/vehicles
GET /api/catalog/vehicles/{id}
POST /api/catalog/vehicles/sendTestNotification/{id}
POST /api/subscribers/request-otp
POST /api/subscribers/verify-otp
All admin endpoints require:
X-ADMIN-KEY: <admin-api-key>GET /api/admin/categories
POST /api/admin/categories
PUT /api/admin/categories/{id}
DELETE /api/admin/categories/{id}
GET /api/admin/vehicles
POST /api/admin/vehicles
GET /api/admin/vehicles/{id}
PUT /api/admin/vehicles/{id}
DELETE /api/admin/vehicles/{id}
POST /api/admin/vehicles/{id}/images
GET /api/admin/vehicles/{id}/images
POST /api/admin/vehicles/{id}/documents
GET /api/admin/vehicles/{id}/documents
DELETE /api/admin/documents/{id}
POST /api/admin/vehicles/{id}/sales
GET /api/admin/sales/report
GET /api/admin/redis-stats
200 OK
Valid admin key and successful request.
201 Created
Resource successfully created.
204 No Content
Resource successfully deleted.
400 Bad Request
Invalid request data.
401 Unauthorized
Missing or invalid admin API key.
404 Not Found
Requested resource does not exist.
409 Conflict
Request conflicts with existing data.
413 Payload Too Large
Uploaded file/request exceeds configured limits.
500 Internal Server Error
Unexpected server-side failure.
cd ShreeGaneshAutodeal-backend/ShreeGaneshAutodeal
./mvnw spring-boot:runOn Windows PowerShell:
cd ShreeGaneshAutodeal-backend\ShreeGaneshAutodeal
.\mvnw.cmd spring-boot:runBefore starting the backend, configure the required environment variables in your environment or application-local.properties:
DB_URL=jdbc:postgresql://<host>:<port>/<dbname>
DB_USERNAME=<username>
DB_PASSWORD=<password>
ADMIN_API_KEY=<secure-admin-key>
RABBITMQ_URL=amqp://<user>:<password>@<host>:<port>
REDIS_URL=redis://<user>:<password>@<host>:<port>
GROQ_API_KEY=<groq-api-key>
GROQ_API_URL=https://api.groq.com/openai/v1/chat/completions
GROQ_MODEL=llama-3.3-70b-versatile
SUPABASE_URL=<supabase-url>
SUPABASE_SERVICE_ROLE_KEY=<supabase-key>
SUPABASE_STORAGE_BUCKET=vehicle-documents
MAIL_USERNAME=<smtp-user>
MAIL_PASSWORD=<smtp-password>
app.mail.from=noreply@shreeganeshautodeal.com
CORS_ALLOWED_ORIGINS=http://localhost:5173cd Autodeal-web-app
npm install
npm run devThe React application uses the public catalog APIs and does not need the admin API key.
For local development, the React app defaults to:
VITE_API_BASE_URL=http://localhost:8080The frontend derives the STOMP WebSocket URL from the same value:
http://localhost:8080 -> ws://localhost:8080/ws
https://api.example.com -> wss://api.example.com/ws
When a customer has the catalog open, the browser subscribes to /topic/inventory and updates the Redux vehicle state when VEHICLE_CREATED or VEHICLE_SOLD events arrive.
cd mobile-app
flutter pub get
flutter runThe Flutter application uses the admin API key for protected /api/admin/** requests.
Run the complete 126-test backend test suite:
cd ShreeGaneshAutodeal-backend\ShreeGaneshAutodeal
.\mvnw.cmd testThe React web application uses Vitest and React Testing Library for unit testing (57 tests).
Run the frontend unit test suite:
cd Autodeal-web-app
npm run test:runTo run Vitest in watch mode during development:
npm testThe frontend tests cover React component rendering, user interactions, API client behavior, and edge cases.
| Test Suite | Focus / Layer | Framework / Tools |
|---|---|---|
VehicleCard.test.tsx |
Vehicle card rendering, interactions, pricing and edge cases | Vitest, React Testing Library |
Header.test.tsx |
Header rendering, navigation links, mobile menu, accessibility, and contact links | Vitest, React Testing Library |
CategoryRail.test.tsx |
Category filter rail rendering, selection, and filter state management | Vitest, React Testing Library |
LanguageToggle.test.tsx |
Language toggle defaulting, switching, and persistence | Vitest, React Testing Library |
useVehicle.test.ts |
Single vehicle fetch hook, loading state, refetching, and error handling | Vitest |
useVehicles.test.ts |
Vehicle list fetch hook, filter state, refetching, and error handling | Vitest |
formatter.test.ts |
INR currency formatting and kilometers driven formatting | Vitest |
api-client.test.ts |
API client requests, responses and error handling | Vitest |
CategoryServiceTest |
Category CRUD, slug generation, name/slug uniqueness, string normalization | JUnit 5, Mockito |
VehicleServiceTest |
Vehicle lifecycle, sync/async creation paths, post-commit dispatch, image/document management, mark-sold flow | JUnit 5, Mockito |
RabbitmqSenderTest |
Asynchronous message dispatching to vehicle_create, vehicle_sold, and vehicle_llm_description queues |
JUnit 5, Mockito |
RabbitmqReceiverTest |
Queue listener delegation to notification, vehicle, and WebSocket services with error isolation | JUnit 5, Mockito |
NotificationServiceTest |
Active subscriber querying, parameter mapping, and notification dispatch | JUnit 5, Mockito |
LLMServiceTest |
Automotive sales prompt generation from Vehicle entity and VehicleRequest DTO | JUnit 5 |
GeminiServiceTest |
Gemini image generation, image download, prompt construction, and error handling | JUnit 5 |
PromoImageServiceTest |
Template-based promo image generation, image download, and error handling | JUnit 5 |
EmailServiceTest |
SimpleMailMessage OTP delivery and MimeMessage vehicle alert HTML rendering | JUnit 5, Mockito |
SubscriberServiceTest |
Email normalization, OTP generation/validation, subscriber status lifecycle | JUnit 5, Mockito |
OtpServiceTest |
Redis-backed 6-digit OTP generation, 5-minute TTL expiration, and one-time verification | JUnit 5, Mockito |
SubscribeControllerTest |
REST endpoint validation for OTP request and OTP verification flows | MockMvc, Mockito |
SupabaseStorageServiceTest |
File type validation, null/empty file guards, storage configuration guards | JUnit 5, Mockito |
AdminControllerTest |
Admin REST APIs for categories, vehicles, multipart document/image uploads, sales | MockMvc, Mockito |
CatalogControllerTest |
Public REST catalog endpoints, filtered vehicle searches, details, test notification | MockMvc, Mockito |
GlobalExceptionHandlerTest |
Global error translation (404, 400, 413) and validation error maps | JUnit 5 |
VehicleSpecificationsTest |
Dynamic JPA Specifications matching (search, slug, status, price range) | Spring Boot Test, H2 |
SecurityConfigTest |
API key filter chain, admin request rejection without key, public catalog access without key | JUnit 5 |
SupabasePropertiesTest |
Supabase property configuration state verification | JUnit 5 |
CacheKeysTests |
Deterministic cache key generation for Redis | JUnit 5 |
ShreeGaneshAutodealApplicationTests |
Spring Boot context load sanity check | Spring Boot Test |
The API-key security layer should be verified for at least the following cases:
Public catalog GET without API key
-> Allowed
Admin GET without API key
-> 401 Unauthorized
Admin POST without API key
-> 401 Unauthorized
Admin request with incorrect API key
-> 401 Unauthorized
Admin request with valid API key
-> Allowed
Admin multipart upload without API key
-> 401 Unauthorized
Admin multipart upload with valid API key
-> Allowed
For Spring Boot context tests, the test environment should provide a non-production API key:
admin.api-key=test-admin-key
spring.cache.type=noneDo not use the production API key in tests.
Automated testing is configured using GitHub Actions for both the backend and the React web application.
The repository contains separate workflows:
.github/
└── workflows/
├── backend-unit-tests.yml
└── web-app-unit-tests.yml
The backend workflow automatically runs the Spring Boot unit test suite using Java 21 and Maven.
It runs for repository pushes and pull requests.
The backend test command is:
./mvnw testThe frontend workflow automatically runs the Vitest unit test suite using Node.js 20.
The workflow is configured for pushes and pull requests targeting:
main
develop
The frontend project is located one level inside the repository:
Autodeal-web-app/
The workflow uses the frontend directory as its working directory and installs dependencies using the frontend package-lock.json.
The CI test commands are:
npm ci
npm run test:runThis ensures that React unit tests are automatically executed whenever changes are pushed or pull requests are opened against the configured branches.
categories
|
v
vehicles
|-- vehicle_images
|-- vehicle_documents
`-- sale_records
Important indexes in supabase/schema.sql:
idx_vehicles_statusidx_vehicles_categoryidx_vehicles_brand_modelidx_sale_records_sale_date
- JWT authentication and role-based access control if multiple admin/user roles are introduced
- Customer accounts, favorites, and test-ride bookings
- Payment gateway integration
- Analytics dashboard
- Docker Compose for backend, PostgreSQL, and Redis
- End-to-end testing
- Production observability for cache hit ratio and Redis latency
- API key rotation and secure admin credential management





