backend/: FastAPI app (main.py), APIrouters/,auth/,database/,config/,utils/; tooling (Makefile,pyproject.toml). Data persists underbackend/db/and uploads inbackend/uploads/.frontend/: Vite + React + TypeScript (src/,vite.config.ts,eslint.config.js).docker/: Multi‑stage image (docker/app/Dockerfile) and compose files.scripts/: Utilities (start-frontend-backend.sh,fio-test.sh,.env.example).docs/,README.md,.pre-commit-config.yamlfor shared tooling.
- Backend (Python 3.11+):
- Setup:
cd backend && uv sync(ormake install). - Quick checks:
make check(syntax/import),make lint(full lint),make start(pre-flight + run). - Run server:
uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000. - Linting:
uv run flake8 .(Python code quality check).
- Setup:
- Frontend:
cd frontend && npm install.- Dev server:
npm run dev. Build:npm run build. - Linting:
npm run lint(ESLint). Type check:npm run type-check(TypeScript).
- Full stack:
- Local:
./start-frontend-backend.sh(starts backend withuvand Vite dev). - Docker:
docker compose up --build -d(seedocker/compose.yml).
- Local:
- Python: formatted with Black (88 cols), isort (Black profile), flake8; modules/functions
snake_case. Routers live inbackend/routers/*.py.- Code Quality: Use
uv run flake8 .for linting - Auto-formatting:
uv run black .anduv run isort .for consistent style
- Code Quality: Use
- TypeScript/React: ESLint (see
eslint.config.js) and Prettier via pre-commit; componentsPascalCase(e.g.,HostSelector.tsx), hookscamelCase(e.g.,useChartColors.ts).- Code Quality: Use
npm run lintfor ESLint checking - Type Safety: Use
npm run type-checkfor TypeScript validation
- Code Quality: Use
- Run
pre-commit installonce; commits should pass hooks.
CHANGELOG.md when making commits!
- Add new changes under
[Unreleased]section before committing - Move to new version section when releasing
- Use semantic versioning format
- Backend:
pytestavailable viauv run pytest. Quick smoke:cd backend && make checkorpython3 test_api.py. - Test names:
test_*.pyinbackend/. Add focused unit tests for routers and utils. - Browser smoke:
tests-with-browser/(Playwright). After app is running:cd tests-with-browser && npm i && node test-app.js.
-- If you write code, dont git commit anything without permission from the user!
- Follow Conventional Commits used here (e.g.,
feat: …,fix: …,chore: …,refactor: …). Keep subject imperative and ≤72 chars; add scope when useful. - PRs: clear description, linked issues, screenshots for UI changes, test plan/steps, and any config notes. Ensure
make check,npm run lint, and pre-commit hooks pass. - If you change/add/delete API endpoints, run:
python3 scripts/generate_endpoints.pyand commit the updateddocs/api/endpoints.json.
- Do not commit secrets; copy
.env.exampleto.env. Backend auth files:backend/.htpasswd,.htuploaders. - SQLite path:
backend/db/storage_performance.db. Persist volumes in Docker (docker/compose.yml). Update ports consistently in env, backend settings, and compose.
- After any frontend change: run
npm run lintandnpm run type-check(npx tsc --noEmit); fix all errors before PR. - After any backend change: run
uv run flake8 .for Python linting. - Prefer
uv run <cmd>for backend tasks (e.g.,uv run uvicorn ...,uv run pytest). Always runmake checkbefore starting the backend. - Manage users with:
cd backend && uv run python scripts/manage_users.py add --username <u> --password <p> [--uploader].
- Admins: full access to UI and management actions. Stored in
backend/.htpasswd. - Uploaders: can upload FIO results only. Stored in
backend/.htuploaders. - Manage users:
cd backend && uv run python scripts/manage_users.py add --username <u> --password <p> [--uploader](uselist/removeaccordingly).
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- OpenAPI JSON: http://localhost:8000/openapi.json
- Health check: http://localhost:8000/health
- Frontend: http://localhost/ (served by nginx).
- API base: http://localhost/api/ → proxies to backend
:8000.
VITE_API_URL is a build-time environment variable that configures the API base URL for the frontend application:
- Vite Build System: Variables prefixed with
VITE_are embedded into the built application at compile time - Client-Side Access: Available in browser code via
import.meta.env.VITE_API_URL - Build vs Runtime: Cannot be changed without rebuilding the application
Development (Default):
VITE_API_URL=""(empty string)- Uses relative URLs like
/api/endpoints - Vite dev server proxies
/apitohttp://localhost:8000
Docker Production:
VITE_API_URL="/api"- Full paths like
/api/endpoints - Nginx proxies
/apito backend container
Local Development with .env:
VITE_API_URL="http://localhost:8000"- Absolute URLs like
http://localhost:8000/api/endpoints - Useful when running frontend separately from backend
-
GitHub Actions: Passes
VITE_API_URL=/apias build argument -
Dockerfile:
ARG VITE_API_URL→ENV VITE_API_URL→ embedded in build -
Frontend Code:
const API_BASE_URL = import.meta.env.VITE_API_URL || ""; -
Docs: backend serves
/docsand/redoc. If/api-docsreturns 404, use/docsand/redocor adjust nginx:location /api-docs { proxy_pass http://localhost:8000/docs; }location /api-redoc { proxy_pass http://localhost:8000/redoc; }
-
Health: backend path is
/health. To expose via nginx under/api/health, add:location = /api/health { proxy_pass http://localhost:8000/health; }
-
Compose external URL: port
80:80(seedocker/compose.yml). Persist db/uploads/auth via mounted volumes.
Example build with API base injected:
docker build \
-f docker/app/Dockerfile \
--build-arg VITE_API_URL=/api \
-t fio-analyzer_app .- Health:
GET /health - Info & Filters:
GET /api/info,GET /api/filters - Test Runs:
GET /api/test-runs/(list with filters)GET /api/test-runs/performance-data?test_run_ids=1,2GET /api/test-runs/{test_run_id}PUT /api/test-runs/{test_run_id}PUT /api/test-runs/bulkDELETE /api/test-runs/{test_run_id}
- Imports:
POST /api/import/(single JSON upload)POST /api/import/bulk(scanbackend/uploads/)
- Time Series:
GET /api/time-series/servers,GET /api/time-series/allGET /api/time-series/latest,GET /api/time-series/history,GET /api/time-series/trendsPUT /api/time-series/bulk,DELETE /api/time-series/delete
- Users:
GET /api/users/,GET /api/users/me,POST /api/users/GET /api/users/{username},PUT /api/users/{username},DELETE /api/users/{username}
For the full, parameterized reference (examples, schemas), use Swagger at /docs or ReDoc at /redoc. A machine-readable index is at docs/api/endpoints.json.
The system uses a 4-level hierarchical structure for organizing and filtering test data:
-
Level 1: Host (
hostname)- The server/host identifier
- Example:
"server01","web01-vm"
-
Level 2: Host-Protocol (
hostname-protocol)- Combination of hostname and storage protocol
- Format:
"${hostname}-${protocol}" - Example:
"server01-NFS","web01-iSCSI"
-
Level 3: Host-Protocol-Type (
hostname-protocol-drive_type)- Combination of hostname, protocol, and drive type
- Format:
"${hostname}-${protocol}-${drive_type}" - Example:
"server01-NFS-ssd","web01-iSCSI-vm-raidz1"
-
Level 4: Host-Protocol-Type-Model (
hostname-protocol-drive_type-drive_model)- Full combination of all four attributes
- Format:
"${hostname}-${protocol}-${drive_type}-${drive_model}" - Example:
"server01-NFS-ssd-Samsung980PRO","web01-iSCSI-vm-raidz1-poolName-syncoff"
- Filtering: All filters must respect the hierarchical structure (see
frontend/src/components/host/HostFilters.tsxandfrontend/src/hooks/useHostFilters.ts) - Data Organization: Data is organized and grouped by this hierarchy throughout the application
- UI Components: The Host selector and filters use this hierarchical structure (see
frontend/src/components/HostSelector.tsx) - API Responses: Backend should structure data to support this hierarchy
- Test Data: When uploading test data via
fio-test.sh, ensure HOSTNAME, PROTOCOL, DRIVE_TYPE, and DRIVE_MODEL are set correctly to maintain the hierarchy
// Hierarchical filtering in useHostFilters.ts
const hostProtocolKey = `${drive.hostname}-${drive.protocol}`;
const hostProtocolTypeKey = `${drive.hostname}-${drive.protocol}-${drive.drive_type}`;
const hostProtocolTypeModelKey = `${drive.hostname}-${drive.protocol}-${drive.drive_type}-${drive.drive_model}`;When configuring test runs, the .env file must specify values that create meaningful hierarchical groupings:
HOSTNAME: Server identifier (use-vmsuffix for virtual machines)PROTOCOL: Storage protocol (NFS, iSCSI, Local, etc.)DRIVE_TYPE: Drive type (hdd, ssd, nvme, mirror, raidz1, raidz2, raidz3, etc.; usevm-prefix for VMs)DRIVE_MODEL: Drive model identifier (can include special parameters likepoolName-syncoff,poolName-syncall)
This hierarchical paradigm must be maintained consistently across all components, filters, and data structures!