Internal IT helpdesk ticket system — employees raise tickets, IT agents resolve them, admins monitor SLA compliance via a real-time dashboard.
- Architecture
- Tech Stack
- Prerequisites
- Quick Start — Docker (Recommended)
- Quick Start — Local Dev
- Demo Accounts
- Seeding Rich Demo Data
- Environment Variables
- API
- Project Structure
- Roles & Permissions
- SLA Rules
Three decoupled tiers, each independently startable:
Browser → Frontend (React/Vite :8080) → Backend (FastAPI :3000) → database/deskflow.db (SQLite)
- Frontend — React 18 + Vite. No business logic; every data operation goes through the REST API.
- Backend — FastAPI + Uvicorn. Owns all business rules, JWT auth, role enforcement, and an SLA scheduler that runs every 5 minutes.
- Database — Single SQLite file at
database/deskflow.db. Auto-created on first boot.
| Layer | Technology |
|---|---|
| Frontend | React 18, React Router 6, Vite 5 |
| Backend | Python 3.12, FastAPI 0.115, Uvicorn 0.30 |
| Auth | PyJWT 2.9 — Bearer token, 15-min sliding window |
| Database | SQLite (file at database/deskflow.db) |
| Container | Docker + Docker Compose |
Docker path (recommended):
- Docker Engine 20.10+
- Docker Compose 2.0+
Local dev path:
- Python 3.11+
- Node.js 18+
- npm 9+
Run everything with a single command. No Python or Node installation required on the host.
# 1. Clone and enter the project
git clone <repo-url>
cd Deskflow2
# 2. Copy environment template
cp .env.example .env
# Edit .env — change DESKFLOW_JWT_SECRET before any non-demo deployment
# 3. Build and start all services
docker-compose up -dWindows (PowerShell):
.\docker-start.ps1Linux / Mac:
chmod +x docker-start.sh
./docker-start.shOnce running:
| Service | URL |
|---|---|
| Frontend | http://localhost:8080 |
| Backend API | http://localhost:3000 |
| Swagger UI | http://localhost:3000/docs |
# View logs
docker-compose logs -f
# Stop
docker-compose down
# Reset database (wipe all data)
docker-compose down -v
docker-compose up -dRun the backend and frontend in separate terminals.
cd backend
# Create and activate virtual environment
python -m venv .venv
# Windows
.venv\Scripts\Activate.ps1
# Linux / Mac
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Start the API server (auto-creates database/deskflow.db on first run)
uvicorn app.main:app --reload --port 3000The database is bootstrapped automatically on first boot — no migration command needed. Delete database/deskflow.db to reset to a fresh state.
Open a second terminal:
cd frontend
npm install
npm run dev
# Runs on http://localhost:8080 (port is locked — see vite.config.js)Both services must be running at the same time. The frontend proxies all /api/ calls to http://localhost:3000.
The database is seeded with five accounts the moment it is created. OTP login is mocked — any 4-digit number is accepted as the OTP.
| Role | Employee ID | |
|---|---|---|
| IT Admin | arun.pillai@techcorp.com | EMP-100 |
| IT Agent | amit.shah@techcorp.com | EMP-200 |
| IT Agent | priya.nair@techcorp.com | EMP-201 |
| Employee | ravi.kumar@techcorp.com | EMP-001 |
| Employee | meena.joshi@techcorp.com | EMP-002 |
Login flow: enter the email → click Generate OTP → enter any 4 digits → click Login.
The base seed only inserts users. Run the mock data seeder to populate tickets across every status, priority, and SLA state — useful for demoing the admin dashboard.
# From backend/ with the virtualenv active
cd backend
python seed_mock_data.py
# Verify counts without touching data
python seed_mock_data.py checkThe seeder wipes all existing tickets, comments, and audit logs before inserting fresh data. Users are preserved.
Copy .env.example to .env and adjust as needed.
| Variable | Default | Description |
|---|---|---|
DESKFLOW_JWT_SECRET |
deskflow-dev-secret-change-me-in-production |
JWT signing key. Change this before any non-demo deployment. |
DESKFLOW_DEV_MODE |
1 |
Enables POST /dev/run-sla-scan endpoint for on-demand SLA trigger. Set to 0 in production. |
The backend logs a warning at startup if the default JWT secret is still in use.
Base URL: http://localhost:3000/api/v1
Interactive docs available at http://localhost:3000/docs (Swagger UI) or http://localhost:3000/redoc.
Authentication: all endpoints except register and login require Authorization: Bearer <token>.
# Register
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"employeeId":"EMP-003","name":"Test User","email":"test@techcorp.com","mobile":"9000000003","role":"Employee"}'
# Request OTP
curl -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@techcorp.com"}'
# Verify OTP (any 4-digit number)
curl -X POST http://localhost:3000/api/v1/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{"email":"test@techcorp.com","otp":"1234"}'
# → returns { "data": { "token": "<jwt>", "user": {...} } }
# Create a ticket (Employee)
curl -X POST http://localhost:3000/api/v1/tickets \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{"title":"VPN not connecting","description":"Cannot connect to VPN since morning.","category":"Network","priority":"P1"}'Full endpoint reference: docs/API_SPECIFICATION.md
Deskflow2/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app + CORS + lifespan (SLA scheduler)
│ │ ├── config.py # Env vars and constants
│ │ ├── db.py # SQLite connection helper + auto-bootstrap
│ │ ├── deps.py # FastAPI dependency injection (get_db, current_user)
│ │ ├── security.py # JWT encode/decode
│ │ ├── schemas.py # Pydantic request/response models
│ │ ├── exceptions.py # DeskFlowError → HTTP error translation
│ │ ├── scheduler.py # SLA breach + auto-close loop (every 5 min)
│ │ ├── routes/ # HTTP layer — auth, tickets, comments, users, dashboard
│ │ ├── services/ # Business rules — ticket lifecycle, SLA, assignment
│ │ └── repositories/ # SQL queries — one file per aggregate
│ ├── requirements.txt
│ └── seed_mock_data.py # Wipes + reseeds demo tickets
│
├── frontend/
│ ├── src/
│ │ ├── api.js # Fetch wrapper (base URL, auth header, error handling)
│ │ ├── auth.js # sessionStorage JWT helpers
│ │ ├── App.jsx # Routes (React Router)
│ │ ├── pages/
│ │ │ ├── Login.jsx / Register.jsx / Profile.jsx
│ │ │ ├── employee/ # Tickets list, create, detail
│ │ │ ├── agent/ # Queue, unassigned pool, ticket work view
│ │ │ └── admin/ # Dashboard, all tickets, ticket admin view
│ │ └── components/ # TopNav, StatusBadge, ThemeToggle, ProtectedRoute
│ └── vite.config.js # Port locked to 8080
│
├── database/
│ ├── schema.sql # Table definitions (auto-loaded on first boot)
│ └── seed.sql # 5 demo users (auto-loaded on first boot)
│
├── docs/ # Architecture, API spec, UI flows, BRD, security
├── wireframes/ # Static HTML wireframes for all pages
│
├── docker-compose.yml # Orchestrates backend + frontend containers
├── .env.example # Environment variable template
├── docker-start.sh # Linux/Mac one-shot startup script
└── docker-start.ps1 # Windows one-shot startup script
| Action | Employee | IT Agent | IT Admin |
|---|---|---|---|
| Register / Login | ✅ | ✅ | ✅ |
| Update own profile | ✅ | ✅ | ✅ |
| Create ticket | ✅ | ❌ | ❌ |
| View own tickets | ✅ | — | — |
| Add comment (own open ticket) | ✅ | ❌ | ❌ |
| Reopen resolved ticket (within 24 h) | ✅ | ❌ | ❌ |
| Delete own ticket | ✅ | ❌ | — |
| Self-assign unassigned ticket | ❌ | ✅ | ❌ |
| Update ticket status (Open → In Progress → Resolved) | ❌ | ✅ | ❌ |
| Reassign ticket (with comment) | ❌ | ✅ | ❌ |
| Assign ticket to any agent | ❌ | ❌ | ✅ |
| View all tickets | ❌ | ❌ | ✅ |
| Admin dashboard + SLA metrics | ❌ | ❌ | ✅ |
| Delete any ticket | ❌ | ❌ | ✅ |
Post-login redirect: Employee → /tickets · IT Agent → /agent/queue · IT Admin → /admin/dashboard
The backend SLA scheduler runs every 5 minutes and enforces:
| Priority | SLA Target | Action on breach |
|---|---|---|
| P1 | 2 hours | sla_breached flag set permanently |
| P2 | 4 hours | sla_breached flag set permanently |
| P3 | 8 hours | sla_breached flag set permanently |
Additional lifecycle rules:
- A
Resolvedticket auto-closes 24 hours afterresolved_at(no manual action needed). - An
Employeecan reopen aResolvedticket within 24 hours of resolution by providing a reason. - In
DEV_MODE, trigger the scheduler on demand:POST http://localhost:3000/dev/run-sla-scan
Port already in use
# Check what holds port 3000 or 8080
# Windows
netstat -ano | findstr :3000
# Linux / Mac
lsof -i :3000Frontend shows "Network Error" / blank data
- Confirm the backend is running on port 3000.
- Check the browser console for CORS errors — the frontend must run on port 8080 (locked in
vite.config.js) to match the backend's CORS allow-list.
Database in a bad state
# Delete the DB file and restart — it will be recreated from schema.sql + seed.sql
rm database/deskflow.db
# Docker
docker-compose down -v
docker-compose up -dContainers won't build
docker-compose build --no-cache
docker-compose up -d| Document | Description |
|---|---|
| docs/ARCHITECTURE.md | 3-tier design, component diagram, SOLID decisions |
| docs/API_SPECIFICATION.md | All 19 endpoints with request/response examples |
| docs/UI_FLOW.md | Page-by-page user flow for all three roles |
| docs/BUSINESS_RULES.md | Complete business rules catalog |
| docs/SECURITY.md | Auth model, CORS, data protection |
| DOCKER_README.md | Detailed Docker deployment guide |
| wireframes/index.html | Static HTML wireframes — open in any browser |