A public procurement oversight platform built for transparency and accountability in Kenyan county-level government contracting.
TransparentProcure enables citizens, auditors, and oversight officers to monitor tenders, track contractor compliance, flag fraud alerts, and crowdsource ward-level project updates — all backed by real-time data from a MySQL database.
- Project Overview
- Tech Stack
- Project Structure
- Prerequisites
- Database Setup (MySQL via XAMPP/LAMPP)
- Backend Setup (Spring Boot)
- Frontend Setup (React + Vite)
- Environment Variables Reference
- Running the Full System
- API Endpoints Reference
- Default Data
- Troubleshooting
| Module | Description |
|---|---|
| Dashboard | Real-time KPIs — active tenders, flagged anomalies, avg bid deviation, live ward feed |
| Community Feed | Crowdsourced ward-level project updates from citizens |
| Contractor Registry | KRA-registered contractor profiles with compliance scores |
| Fraud Monitoring | Forensic intelligence dashboard — fraud alerts with severity, status, and evidence |
| Audit Records | Procurement audit trail with type filtering and progress tracking |
| Reports | Blacklist summaries and generated procurement reports |
| Authentication | JWT-based login and citizen registration with BCrypt password hashing |
| Technology | Version | Purpose |
|---|---|---|
| Java | 21 | Runtime |
| Spring Boot | 3.2.4 | Web framework |
| Spring Security | 6.2.x | Authentication & authorisation |
| Spring Data JPA | 3.2.x | ORM layer |
| Hibernate | 6.4.x | JPA implementation |
| MySQL | 8.x | Relational database |
| JJWT | 0.12.x | JWT generation and validation |
| Lombok | 1.18.x | Boilerplate reduction |
| Maven | 3.x | Build tool |
| Technology | Version | Purpose |
|---|---|---|
| React | 18.3 | UI framework |
| Vite | 7.x | Dev server and build tool |
| Tailwind CSS | 3.4 | Utility-first styling |
| Axios | 1.x | HTTP client |
| React Router | 7.x | Client-side routing |
transparent_java/
├── springboot-backend/ # Spring Boot API
│ ├── src/main/java/com/transparentprocure/
│ │ ├── config/ # Security configuration
│ │ ├── controller/ # REST controllers (Auth, Dashboard, Feed, Registry, Fraud, Audit, Reports, Tenders)
│ │ ├── dto/ # Request/response DTOs
│ │ │ └── request/ # Incoming request bodies
│ │ ├── entity/ # JPA entities (mapped to MySQL tables)
│ │ ├── model/ # User model
│ │ ├── repository/ # Spring Data JPA repositories
│ │ ├── security/ # JWT filter, token utility, user details service
│ │ └── service/ # Business logic & database-backed data service
│ ├── src/main/resources/
│ │ ├── application.properties # App configuration (no secrets — uses .env)
│ │ └── data/ # Seed JSON files (mock_data.json, posts.json, tender.json)
│ ├── .env.example # ← Copy this to .env and fill in your credentials
│ ├── .env # ← Your local credentials (gitignored — never committed)
│ └── pom.xml # Maven dependencies
│
└── frontend/ # React + Vite SPA
├── src/
│ ├── api/apiService.js # Axios client + all API endpoint definitions
│ ├── context/AuthContext.jsx # Global authentication state (JWT + localStorage)
│ ├── components/ # Layout, Sidebar, Topbar, ProtectedRoute
│ ├── hooks/useApi.js # Generic data-fetching hook
│ └── pages/ # Login, Dashboard, Feed, Registry, Fraud, Audit, Reports
├── .env # Frontend env (VITE_API_BASE_URL — safe to track)
├── vite.config.js # Dev server + proxy to Spring Boot
└── package.json
Make sure the following are installed on your machine before starting:
| Tool | Minimum Version | Download |
|---|---|---|
| Java JDK | 21 | https://adoptium.net |
| Apache Maven | 3.8+ | https://maven.apache.org/download.cgi |
| Node.js | 18+ | https://nodejs.org |
| XAMPP or LAMPP | Any recent | https://www.apachefriends.org |
| Git | Any | https://git-scm.com |
Verify your installations:
java -version # should print: openjdk 21... mvn -version # should print: Apache Maven 3.x... node -version # should print: v18.x or v20.x or higher npm -version # should print: 9.x or higher
The backend uses MySQL 8 managed through XAMPP / LAMPP. Follow these steps exactly.
On Linux (LAMPP):
sudo /opt/lampp/lampp startOn Windows (XAMPP): Open the XAMPP Control Panel and click Start next to MySQL.
On macOS (XAMPP): Open the XAMPP application and click Start next to MySQL.
Confirm MySQL is running:
# Linux
sudo /opt/lampp/bin/mysql -u root -p
# Windows / macOS — use phpMyAdmin or the XAMPP shellXAMPP ships with MySQL root having no password by default.
You must set a password before using this application.
-- Inside the MySQL shell:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourChosenPassword';
FLUSH PRIVILEGES;
EXIT;Note down the password you set — you will need it in the next step.
You do not need to run any SQL scripts.
The Spring Boot application creates the transparent_procure database and all tables on first startup via the JPA ddl-auto=update setting.
It also seeds all initial data (contractors, tenders, fraud alerts, audits, reports, wards, counties, feed posts) automatically from the JSON files in src/main/resources/data/.
git clone https://github.com/YOUR_USERNAME/transparent_java.git
cd transparent_javaThe backend reads sensitive configuration (database password, JWT secret) from a .env file that is never committed to git.
cd springboot-backend
cp .env.example .envOpen .env in any text editor and fill in your values:
# Database
DB_URL=jdbc:mysql://localhost:3306/transparent_procure?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
DB_USERNAME=root
DB_PASSWORD=YourMySQLPasswordHere # <-- the password you set in Step 2 above
# JWT Secret — must be at least 64 characters
# Generate one with: openssl rand -hex 64
JWT_SECRET=replace_this_with_a_long_random_secret_string_of_at_least_64_characters
JWT_EXPIRATION=86400000
# Server
SERVER_PORT=3001
# App
APP_MOCK_TOKEN=mock_jwt_token_for_development_purpose_onlyJWT_SECRET rules:
- Must be at least 64 characters (required by the HS512 signing algorithm).
- All developers working on the same running instance must use the same secret (tokens are only valid on the server that signed them).
- Each developer working independently can use their own secret.
- Generate a strong one with:
openssl rand -hex 64
# Still inside springboot-backend/
mvn clean package -DskipTestsA successful build prints:
[INFO] BUILD SUCCESS
Option A — Maven (recommended for development):
# Run from inside springboot-backend/
mvn spring-boot:runOption B — JAR directly (recommended for keeping it running in background):
# Run from inside springboot-backend/
nohup java -jar target/transparent-procure-api-2.0.0.jar > /tmp/api.log 2>&1 &
echo "Started PID: $!"
# Watch the log:
tail -f /tmp/api.logThe server is ready when you see:
Started TransparentProcureApplication in X.XXX seconds
Verify it is running:
curl http://localhost:3001/api/health
# Expected: {"success":true,"data":{"status":"healthy"},...}Important: Always run the backend from inside the
springboot-backend/directory so that Spring Boot finds the.envfile in the current working directory.
# If you know the PID:
kill <PID>
# Find and kill by port:
kill $(lsof -ti :3001) # Linux / macOS# From the project root
cd frontend
npm installThe frontend/.env file is already configured and tracked in git. It contains only a non-secret URL:
VITE_API_BASE_URL=http://localhost:3001/apiNo changes are needed unless your backend runs on a different port.
# From inside frontend/
npm run devThe terminal will print something like:
VITE v7.x.x ready in 300ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
Open http://localhost:5173 in your browser.
The Vite dev server automatically proxies all
/api/*requests tohttp://localhost:3001(the Spring Boot backend). Both servers must be running at the same time.
npm run build
# Output goes to frontend/dist/| Variable | Required | Default | Description |
|---|---|---|---|
DB_URL |
✅ | jdbc:mysql://localhost:3306/transparent_procure?... |
Full JDBC connection string |
DB_USERNAME |
✅ | root |
MySQL username |
DB_PASSWORD |
✅ | (none) | MySQL password — must be set |
JWT_SECRET |
✅ | (none) | JWT signing secret — min 64 chars |
JWT_EXPIRATION |
✅ | 86400000 |
Token lifetime in milliseconds (default 24 h) |
SERVER_PORT |
✅ | 3001 |
Port for embedded Tomcat |
APP_MOCK_TOKEN |
✅ | mock_jwt_token_dev_only |
Internal dev token for seeding |
Variables without a default will cause the application to fail to start if not provided. Always copy
.env.exampleto.envand fill in all values.
| Variable | Required | Default | Description |
|---|---|---|---|
VITE_API_BASE_URL |
only in production build | http://localhost:3001/api |
Backend base URL used in production builds |
In development (
npm run dev), the Vite proxy handles all/apirequests automatically —VITE_API_BASE_URLis not used.
Open two terminal tabs and run each command in its own tab:
Terminal 1 — Backend:
cd transparent_java/springboot-backend
mvn spring-boot:runTerminal 2 — Frontend:
cd transparent_java/frontend
npm run devThen open http://localhost:5173 in your browser.
All endpoints are prefixed with /api. The backend runs on http://localhost:3001.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register |
Register a new citizen account |
POST |
/api/auth/login |
Login with email or username |
POST |
/api/auth/logout |
Logout (clears token client-side) |
POST |
/api/auth/refresh |
Refresh a JWT token |
GET |
/api/auth/me |
Get the currently authenticated user |
GET |
/api/health |
Health check |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/dashboard/stats |
KPI summary stats |
GET |
/api/dashboard/contractor-scores |
Contractor trust scores |
GET |
/api/dashboard/anomalies |
Price anomalies |
GET |
/api/dashboard/ward-feed |
Live ward feed items |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/feed/posts |
Get all posts (supports ?wardId=, ?category=, ?page=, ?limit=) |
POST |
/api/feed/posts |
Create a new post |
GET |
/api/feed/ward/{wardId} |
Get posts for a specific ward |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/registry/contractors |
List contractors (supports ?search=, ?category=, ?region=, ?status=) |
GET |
/api/registry/contractors/{id} |
Get contractor details |
POST |
/api/registry/contractors |
Create a new contractor record |
PUT |
/api/registry/contractors/{id} |
Update a contractor |
DELETE |
/api/registry/contractors/{id} |
Delete a contractor |
POST |
/api/registry/contractors/{id}/blacklist |
Blacklist a contractor |
GET |
/api/registry/blacklisted |
Get all blacklisted contractors |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/fraud/alerts |
List fraud alerts (supports ?severity=, ?status=) |
GET |
/api/fraud/alerts/{id} |
Get alert details |
POST |
/api/fraud/alerts |
Create a new fraud alert |
PUT |
/api/fraud/alerts/{id} |
Update an alert |
PATCH |
/api/fraud/alerts/{id}/resolve |
Mark an alert as resolved |
GET |
/api/fraud/patterns |
Get detected fraud patterns |
GET |
/api/fraud/risk-assessment/{tenderId} |
Risk assessment for a tender |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/audit/audits |
List audits (supports ?type=, ?status=) |
GET |
/api/audit/audits/{id} |
Get audit details |
POST |
/api/audit/audits |
Create a new audit record |
PUT |
/api/audit/audits/{id} |
Update an audit |
GET |
/api/audit/trail/{entityType}/{entityId} |
Audit trail for an entity |
GET |
/api/audit/export |
Export audit report |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/reports |
List reports (supports ?type=, ?category=) |
GET |
/api/reports/{id} |
Get report details |
POST |
/api/reports/generate |
Generate a new report |
GET |
/api/reports/{id}/export |
Export a report (?format=pdf) |
GET |
/api/reports/templates |
Get report templates |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/tenders |
List all tenders (supports ?county=, ?category=, ?status=) |
GET |
/api/tender/{id} |
Get tender details |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/utils/counties |
List all Kenyan counties |
GET |
/api/utils/wards |
List all wards |
GET |
/api/utils/search |
Global search (?q=query) |
POST |
/api/utils/upload |
File upload |
On first startup, the backend automatically seeds the database with realistic mock data:
| Table | Records | Description |
|---|---|---|
contractors |
5 | KRA-registered entities with compliance scores |
tenders |
~1,284 | Government procurement tenders |
fraud_alerts |
4 | Alerts of varying severity (high, medium, low) |
audits |
3 | Audit records with findings and recommendations |
reports |
3 | Generated procurement reports |
feed_posts |
10 | Citizen crowdsourced ward updates |
wards |
10 | County wards |
counties |
5 | Kenyan counties |
dashboard_stats |
1 | Aggregated KPI snapshot |
price_anomalies |
varies | Detected price variance records |
Data seeding only runs when the tables are empty. Restarting the server does not duplicate data.
Another instance of the backend is already running. Find and kill it:
# Linux / macOS
kill $(lsof -ti :3001)
# Windows (PowerShell)
netstat -ano | findstr :3001
taskkill /PID <PID_NUMBER> /FYour DB_PASSWORD in springboot-backend/.env does not match your MySQL root password.
- Verify your MySQL password works:
/opt/lampp/bin/mysql -u root -p # Enter your password when prompted - Update
DB_PASSWORDinspringboot-backend/.envto match. - Restart the backend.
You have not created the .env file. Run:
cd springboot-backend
cp .env.example .env
# Then edit .env and fill in DB_PASSWORD and JWT_SECRETYour JWT_SECRET in .env is shorter than 64 characters. Generate a valid one:
openssl rand -hex 64Paste the output as the value of JWT_SECRET in .env.
The Vite dev server proxy is not reaching the backend. Check:
- The backend is running on port
3001:curl http://localhost:3001/api/health - You are running the frontend with
npm run dev(not a static build). frontend/vite.config.jshas the proxy pointing tohttp://localhost:3001.
cd frontend
npm install # re-install dependencies
npm run devThis is a warning, not an error. The application still starts and runs correctly. You can silence it by removing the spring.jpa.properties.hibernate.dialect line from application.properties.
# Check if another MySQL is already running on port 3306
lsof -i :3306
# Stop the conflicting process, then restart LAMPP
sudo /opt/lampp/lampp restart- Never commit
.env— it is in.gitignore. If you accidentally do, revoke and rotate your credentials immediately. - The
.env.examplefile is safe to commit — it contains only placeholder values. - Each developer generates their own
JWT_SECRETlocally. As long as you work independently, this is fine. - The
DB_PASSWORDin.envis only your local development MySQL password — it has no access to any production system. frontend/.envcontains onlyhttp://localhost:3001/api— no secrets, safe to commit.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Commit your changes:
git commit -m "feat: describe your change" - Push to the branch:
git push origin feature/your-feature-name - Open a Pull Request
This project is developed for the Public Procurement Regulatory Authority of Kenya oversight initiative.
© 2024 TransparentProcure Kenya. All rights reserved.