A full-stack web application for managing device distribution across different organizational levels with role-based access control.
- 1. Core Idea
- 2. Technology Stack
- 3. System Architecture
- 4. Project Structure
- 5. Key Features
- 6. Role Hierarchy & Access Control
- 7. Core Workflows
- 8. API Overview
- 9. Security Features
- 10. Setup Instructions
- 11. Configuration
- 12. Running Both Servers
- 13. Demo Accounts
- 14. Monitoring
- 14. Monitoring
- 15. Testing
- 16. Building for Production
- 17. Troubleshooting
- 18. Development Notes
- 19. Contributing
- 20. License
The Distribution Management System (DMS) provides end-to-end visibility and control over how devices move through an organisational hierarchy:
- Devices are registered and given a trackable identity.
- Distribution requests flow through a structured approval chain.
- Defects and returns are captured, categorised, and resolved.
- Role-scoped dashboards give every actor — from Admin to Operator — the right view of the system.
- FastAPI — Modern Python async web framework
- MySQL 8.4 — Relational database with aiomysql async driver
- JWT — JSON Web Tokens for stateless authentication
- Pydantic — Data validation and serialisation
- Bcrypt — Secure password hashing
- Prometheus — Metrics collection and query engine
- Grafana — Metrics visualisation and dashboards
- React — Component-based UI library
- Vite — Fast build toolchain and dev server
- Tailwind CSS — Utility-first CSS framework
- React Router — Client-side routing
- Context API — Lightweight state management
flowchart TD
subgraph Client ["Presentation Layer"]
FE[React + Vite Frontend\nRole-scoped Dashboards]
end
subgraph Gateway ["API Gateway"]
API[FastAPI Application\nRoutes · Middleware · Auth]
end
subgraph Services ["Business Logic Layer"]
US[User Service]
DS[Device Service]
DIS[Distribution Service]
DEF[Defect Service]
RET[Return Service]
APR[Approval Service]
NOT[Notification Service]
REP[Report Service]
end
subgraph Data ["Persistence Layer"]
DB[(MySQL 8.4\nRelational Database)]
end
subgraph Monitoring ["Observability Stack"]
P[Prometheus\nMetrics Scraper]
G[Grafana\nDashboards]
end
FE -- HTTP/REST --> API
API --> Services
Services --> DB
P -- /metrics --> API
G -.-> P
distribution-management-system/
├── backend/
│ ├── app/
│ │ ├── core/
│ │ │ ├── metrics.py # Prometheus metric definitions
│ │ │ └── ...
│ │ ├── models/ # Pydantic models
│ │ ├── routes/ # API endpoints
│ │ ├── services/
│ │ │ └── ...
│ │ ├── middleware/ # Auth & error handling
│ │ ├── utils/ # Helper functions
│ │ ├── schemas/ # Response schemas
│ │ ├── main.py # FastAPI app entry point
│ │ ├── config.py # Settings
│ │ └── database.py # MySQL connection pool & schema
│ ├── requirements.txt
│ ├── .env
│ └── README.md
│
├── frontend/
│ ├── src/
│ │ ├── components/ # Reusable UI components
│ │ ├── pages/ # Page-level components
│ │ ├── context/ # Context providers
│ │ ├── services/ # API service layer
│ │ ├── App.jsx
│ │ └── main.jsx
│ ├── public/
│ │ └── favicon.svg
│ ├── index.html
│ ├── package.json
│ ├── vite.config.js
│ └── tailwind.config.js
│
├── monitoring/
│ ├── prometheus/
│ │ ├── prometheus.yml # Scrape config
│ │ └── alert.rules.yml # Alert rules
│ └── grafana/
│ ├── datasources/ # Provisioned data source
│ └── dashboards/ # Pre-built dashboards
│
├── docker-compose.yml
├── docker-compose.override.yml
├── nginx/
└── README.md
- Create, read, update, delete users
- Role-based access control (Admin, Manager, Distributor, Sub-Distributor, Operator)
- User status management (Active, Inactive, Suspended)
- Profile management
- Register new devices with serial number tracking
- Track device status, location, and current holder
- Full device history and audit trail
- Create and manage distribution requests
- Structured approval workflow
- Status tracking: Pending → Approved → Delivered / Rejected
- Report and categorise device defects by type and severity
- Resolution workflow with history tracking
- Create and approve return requests
- Reason categorisation and status tracking
- Centralised approval dashboard for distributions, returns, and defects
- Approval notes and full history
- Real-time notification centre
- Unread count badge and mark-as-read functionality
- Inventory, distribution, defect, return, user activity, and device utilisation reports
- Role-specific views with live statistics, activity feeds, charts, and system alerts
- Prometheus scrapes the backend
/metricsendpoint every 10s for HTTP and database performance metrics - Grafana provisions dashboards automatically on startup:
- Backend API — HTTP request rates, latencies, error rates, in-flight requests
- Database — MySQL query throughput, durations, failure rates, active connections
- No business-domain metrics are exported; monitoring is scoped to performance signals used when investigating issues
flowchart TD
ADM[Admin\nFull System Access]
MGR[Manager\nManagement Operations]
DIST[Distributor\nDistribution Management]
SDIST[Sub-Distributor\nSub-Distribution Management]
OPR[Operator\nField Operations]
ADM --> MGR
MGR --> DIST
DIST --> SDIST
SDIST --> OPR
| Role | Access Scope | |
|---|---|---|
| Admin | admin@dms.com | Full system access |
| Manager | manager@dms.com | Management operations |
| Distributor | distributor@dms.com | Distribution management |
| Sub-Distributor | subdist@dms.com | Sub-distribution management |
| Operator | operator@dms.com | Field operations |
stateDiagram-v2
[*] --> Pending : Distribution request created
Pending --> Approved : Approver accepts
Pending --> Rejected : Approver rejects
Approved --> Delivered : Device handed over
Rejected --> [*]
Delivered --> [*]
stateDiagram-v2
[*] --> Reported : Defect / Return request filed
Reported --> UnderReview : Assigned to reviewer
UnderReview --> Resolved : Fix confirmed / Return accepted
UnderReview --> Rejected : Request declined
Resolved --> [*]
Rejected --> [*]
sequenceDiagram
participant U as User (any role)
participant API as FastAPI Backend
participant DB as MongoDB
participant APR as Approver (Manager/Admin)
participant NOT as Notification Service
U->>API: Submit distribution / defect / return request
API->>DB: Persist request with status=Pending
API->>NOT: Trigger notification to approver
NOT-->>APR: Alert: new request awaiting review
APR->>API: Approve or Reject with notes
API->>DB: Update request status
API->>NOT: Notify originating user of decision
NOT-->>U: Alert: request approved / rejected
All endpoints are mounted under /api.
POST /api/auth/login— User loginPOST /api/auth/logout— User logoutGET /api/auth/me— Get current userPUT /api/auth/password— Change password
GET /api/users— List users (paginated)GET /api/users/{id}— Get user by IDPOST /api/users— Create userPUT /api/users/{id}— Update userDELETE /api/users/{id}— Delete userPATCH /api/users/{id}/status— Update user status
GET /api/devices— List devices (paginated)GET /api/devices/{id}— Get device by IDGET /api/devices/available— Get available devicesGET /api/devices/track/{serial}— Track by serial numberGET /api/devices/{id}/history— Get device historyPOST /api/devices— Register devicePUT /api/devices/{id}— Update deviceDELETE /api/devices/{id}— Delete devicePATCH /api/devices/{id}/status— Update device status
GET /api/distributions— List distributionsGET /api/distributions/{id}— Get distribution by IDGET /api/distributions/pending— Get pending distributionsPOST /api/distributions— Create distributionPATCH /api/distributions/{id}/status— Update statusDELETE /api/distributions/{id}— Cancel distribution
GET /api/defects— List defect reportsGET /api/defects/{id}— Get defect by IDPOST /api/defects— Create defect reportPUT /api/defects/{id}— Update defectPATCH /api/defects/{id}/status— Update statusPATCH /api/defects/{id}/resolve— Resolve defectDELETE /api/defects/{id}— Delete defect
GET /api/returns— List return requestsGET /api/returns/{id}— Get return by IDPOST /api/returns— Create return requestPATCH /api/returns/{id}/status— Update statusDELETE /api/returns/{id}— Cancel return
GET /api/approvals— List pending approvalsGET /api/approvals/{id}— Get approval by IDPOST /api/approvals/{id}/approve— Approve requestPOST /api/approvals/{id}/reject— Reject request
GET /api/operators— List operatorsGET /api/operators/{id}— Get operator by IDGET /api/operators/{id}/devices— Get operator devicesPOST /api/operators— Create operatorPUT /api/operators/{id}— Update operatorDELETE /api/operators/{id}— Delete operator
GET /api/notifications— List notificationsGET /api/notifications/unread— Get unread countPATCH /api/notifications/{id}/read— Mark as readPATCH /api/notifications/read-all— Mark all as readDELETE /api/notifications/{id}— Delete notification
GET /api/reports/inventory— Inventory reportGET /api/reports/distribution-summary— Distribution summaryGET /api/reports/defect-summary— Defect summaryGET /api/reports/return-summary— Return summaryGET /api/reports/user-activity— User activity reportGET /api/reports/device-utilization— Device utilisation report
GET /api/dashboard/stats— Dashboard statisticsGET /api/dashboard/recent-activities— Recent activitiesGET /api/dashboard/advanced-metrics— Advanced graph/metrics payloadGET /api/dashboard/charts/distributions— Distribution chart dataGET /api/dashboard/charts/defects— Defect chart dataGET /api/dashboard/alerts— System alerts
POST /api/devices/bulk-upload— Bulk register devices (CSV/XLSX/XLS)POST /api/users/bulk-upload— Bulk create users (CSV/XLSX/XLS)
GET /metrics— Prometheus metrics endpoint (no auth)GET /health— Health check
flowchart LR
REQ[Incoming Request] --> CORS[CORS Check]
CORS --> JWT[JWT Token Validation]
JWT --> RBAC[Role-Based Access Check]
RBAC --> PYDANTIC[Input Validation\nPydantic]
PYDANTIC --> HANDLER[Route Handler]
HANDLER --> BCRYPT[Bcrypt Password Hashing\nfor auth mutations]
- JWT-based authentication with configurable token expiry
- Password hashing with bcrypt
- Role-based access control (RBAC) with permission-based route protection
- Token expiration and refresh support
- CORS configuration for allowed origins
- Input validation with Pydantic on all request bodies
- Python 3.10+
- Node.js 18+
- MySQL 8.4
- Docker & Docker Compose (recommended for monitoring stack)
The entire stack including MySQL, backend, frontend, reverse proxy, Prometheus, and Grafana can be started with Docker Compose:
# Ensure environment variables are set
cp .env.example .env # or configure manually
# Start all services
docker compose up -d
# Wait for services to be healthy, then access:| Service | URL |
|---|---|
| Web Application | https://localhost |
| Backend API | https://localhost/api |
| API Docs (Swagger) | https://localhost/docs |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3000 (admin / password from .env) |
-
Navigate to the backend directory:
cd backend -
Create and activate a virtual environment (recommended):
python -m venv venv source venv/bin/activate # Linux / macOS venv\Scripts\activate # Windows
-
Install dependencies:
pip install -r requirements.txt
-
Configure environment variables:
- Copy
.env.exampleto.env - Update the MySQL connection string if needed
- Copy
-
Start the backend server:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8080
-
Access API docs:
- Swagger UI: http://localhost:8080/docs
- ReDoc: http://localhost:8080/redoc
-
Navigate to the frontend directory:
cd frontend -
Install dependencies:
npm install
-
Start the development server:
npm run dev
-
Open the app at: http://localhost:5173
DB_HOST=localhost
DB_PORT=3306
DB_USER=dms_user
DB_PASSWORD=your-db-password
DB_NAME=distribution_management_system
SECRET_KEY=your-secret-key-here
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=1440
REFRESH_TOKEN_EXPIRE_DAYS=7
CORS_ORIGINS=http://localhost:5173,http://localhost:3002
ENVIRONMENT=developmentVITE_API_URL=http://localhost:8080/apiTerminal 1 (Backend):
cd backend
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8080Terminal 2 (Frontend):
cd frontend
npm run devCreate start.ps1 in the root directory:
Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd backend; python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8080"
Start-Sleep -Seconds 3
Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd frontend; npm run dev"Run with:
.\start.ps1| Password | Role | Access Level | |
|---|---|---|---|
| admin@dms.com | admin123 | Admin | Full system access |
| manager@dms.com | manager123 | Manager | Management operations |
| distributor@dms.com | dist123 | Distributor | Distribution management |
| subdist@dms.com | subdist123 | Sub Distributor | Sub-distribution management |
| operator@dms.com | operator123 | Operator | Field operations |
Grafana is available at http://localhost:3000 when running via Docker Compose.
| Default Credential | Value |
|---|---|
| Username | admin |
| Password | Set via GRAFANA_ADMIN_PASSWORD in .env (defaults to admin) |
Two dashboards are provisioned automatically on startup:
| Dashboard | UID | Key Panels |
|---|---|---|
| Backend API | backend-api |
HTTP request rate, latency, error rate, in-flight requests, uptime |
| Database | database-metrics |
MySQL query throughput, duration, failure rate, active connections |
The backend exposes a /metrics endpoint scraped by Prometheus:
# View raw metrics directly
curl http://localhost:8080/metrics┌─────────────────┐ scrape(10s) ┌────────────┐ query ┌─────────┐
│ FastAPI App │ ────────────────→ │ Prometheus │ ←────────── │ Grafana │
│ /metrics │ │ :9090 │ │ :3000 │
└────────┬────────┘ └────────────┘ └─────────┘
│
└── SQLAlchemy engine events record MySQL query metrics live
app/core/metrics.py— Declares the Prometheus metric objects (HTTP request metrics, MySQL query metrics, uptime).app/database_sqlalchemy.py— Registers SQLAlchemy engine event listeners that record MySQL query count, duration, failures, and active connections live (no background collector).- Prometheus scrapes the
/metricsHTTP endpoint every 10 seconds (configured inmonitoring/prometheus/prometheus.yml). - Grafana uses Prometheus as a data source (configured in
monitoring/grafana/datasources/datasource.yml) and loads dashboards frommonitoring/grafana/dashboards/.
| Metric | Type | Description |
|---|---|---|
http_requests_total |
Counter | Total HTTP requests by method/endpoint/status |
http_request_duration_seconds |
Histogram | HTTP latency distribution |
http_requests_in_progress |
Gauge | Concurrent in-flight requests |
http_errors_total |
Counter | HTTP 4xx/5xx responses |
mysql_queries_total |
Counter | MySQL queries by operation type |
mysql_query_duration_seconds |
Histogram | MySQL query latency |
mysql_query_failures_total |
Counter | Failed MySQL queries |
mysql_active_connections |
Gauge | Active DB connections |
app_uptime_seconds |
Gauge | Seconds since last restart |
app_info |
Info | App metadata (name, framework, Python version) |
# Login
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@dms.com","password":"admin123"}'
# Get dashboard stats (replace TOKEN with actual token)
curl http://localhost:8080/api/dashboard/stats \
-H "Authorization: Bearer TOKEN"- Open http://localhost:5173
- Login with any demo credential
- Navigate through features
- Check the browser console for errors
cd backend
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8080cd frontend
npm run build
npm run preview # Test the production build locallyThe production build outputs to frontend/dist/.
MongoDB Connection Error:
- Verify your internet connection
- Confirm the MongoDB Atlas cluster is running
- Ensure your IP address is whitelisted in MongoDB Atlas
Port 8080 Already in Use:
# Windows
netstat -ano | findstr :8080
taskkill /PID <PID> /F
# Linux / macOS
lsof -i :8080
kill -9 <PID>Port 5173 Already in Use:
- Change the port in
frontend/vite.config.js - Update
CORS_ORIGINSinbackend/.envto match
API Connection Error:
- Ensure the backend is running on port 8080
- Verify
.envhasVITE_API_URL=http://localhost:8080/api - Check CORS settings in the backend
Module Not Found:
cd frontend
rm -rf node_modules package-lock.json
npm installGrafana shows no HTTP/DB metric data:
- Ensure the backend is running and Prometheus can reach
backend:8080/metrics - Check Prometheus targets at http://localhost:9090/targets — the
backendjob should be UP - HTTP metrics appear on the first request; MySQL metrics appear as soon as queries run (recorded live via SQLAlchemy engine events — no collector to wait for)
- Verify
monitoring/prometheus/prometheus.ymlhastargets: ["backend:8080"]
Grafana dashboards not appearing:
- Check Grafana logs:
docker compose logs grafana - Verify dashboard JSON files exist in
monitoring/grafana/dashboards/ - The provisioning directory is mounted at
/etc/grafana/provisioning/dashboards - Restart Grafana:
docker compose restart grafana
Prometheus target down:
# From within the Docker network, test connectivity
docker compose exec backend wget -qO- http://localhost:8080/metrics | head -20- Seed data is automatically created on first startup via
seed_initial_data() - Includes 5 demo users, 20 sample devices, and example records
- Tables are created automatically by
init_db()on startup (seebackend/app/database.py) - To reset: drop and recreate the database, then restart the backend
- Backend CORS is configured for
localhost:5173andlocalhost:3002 - Update
backend/.envif using different ports
- Fork the repository
- Create a feature branch:
git checkout -b feature/AmazingFeature - Commit your changes:
git commit -m 'Add some AmazingFeature' - Push to the branch:
git push origin feature/AmazingFeature - Open a Pull Request
This project is licensed under the MIT License.
For issues and questions:
- Review the troubleshooting section above
- Check API documentation at http://localhost:8080/docs
- Inspect the browser console for frontend errors
- Review terminal output for backend errors
Happy Coding! 🚀