Prophy is a web platform for managing medical physics service operations. It combines a Django REST API with a Next.js frontend to support internal staff, commercial users, client managers, unit managers, and medical physicists in their daily workflows.
The repository is organized as a decoupled full-stack application:
backend/: Django + Django REST Framework APIfrontend/: Next.js application with TypeScript
The codebase currently implements the following areas.
- JWT login, refresh, verify, and logout flows
- User management endpoints for administrative roles
- Role-aware permissions and association management
- Client/user and unit manager association endpoints
- Support for Google OIDC authentication in the backend
- Client, unit, and equipment management APIs
- Approval workflow for changes through requisition operations:
- client operations
- unit operations
- equipment operations
- Proposal management and proposal file downloads
- Status endpoints for proposals and clients
- Appointment listing, creation, updates, filtering, and access control
- Service order creation and update flows
- Service order PDF generation
- Report creation, listing, retrieval, filtering, and download
- Report soft delete and restore flows
- Scheduled-task trigger endpoints for overdue appointments, report notifications, and contract notifications
- Institutional material CRUD
- Visibility and permission-based access rules
- Material download endpoint with role-based access control
- Public landing page and authentication pages
- Role-oriented dashboard structure for:
- Prophy Manager
- Commercial
- Internal Medical Physicist
- External Medical Physicist
- Client Manager
- Unit Manager
- Redux Toolkit + RTK Query for state and API integration
- Form validation with Zod and React Hook Form
- Cypress setup for component and E2E testing
- Client-side structured logging utilities
- Python 3.12
- Django 5.2
- Django REST Framework
- Djoser
- Simple JWT
- django-filter
- drf-yasg
- pytest, pytest-django, pytest-cov, pytest-xdist, pytest-mock
- factory-boy
- Next.js 16
- React 19
- TypeScript 5
- Redux Toolkit and RTK Query
- Tailwind CSS 4
- Zod
- Cypress
users: authentication, user profiles, role management, and association endpointsclients_management: clients, units, equipment, accessories, appointments, proposals, service orders, and reportsrequisitions: approval and workflow entities for changes to clients, units, and equipmentmaterials: institutional materials and permission handling
All API routes are served under /api/.
- Authentication and users:
/api/jwt/create//api/jwt/refresh//api/jwt/verify//api/logout//api/users//api/users/manage/
- Client management:
/api/clients//api/units//api/equipments//api/appointments//api/proposals//api/service-orders//api/reports/
- Requisitions:
/api/clients/operations//api/units/operations//api/equipments/operations/
- Materials:
/api/materials/
- API documentation:
/api/docs/
.
├── backend/
│ ├── clients_management/
│ ├── core/
│ ├── materials/
│ ├── requisitions/
│ ├── users/
│ ├── tests/
│ ├── pyproject.toml
│ └── poetry.lock
├── frontend/
│ ├── app/
│ ├── cypress/
│ ├── public/
│ └── styles/
└── README.md
- Python 3.12+
- Poetry for Python dependency management
- Node.js 20+ and npm
Copy the examples below into the appropriate files.
backend/.env:
DJANGO_SECRET_KEY=change-me
DEBUG=True
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost
FRONTEND_URL=http://localhost:3000
AUTH_COOKIE_SECURE=False
DEFAULT_FROM_EMAIL=noreply@example.comfrontend/.env.local:
NEXT_PUBLIC_HOST=http://localhost:8000
NEXT_PUBLIC_APP_VERSION=local
NEXT_PUBLIC_LOG_LEVEL=info# Backend (from backend/)
poetry install
# Frontend (from frontend/)
npm installPoetry creates an isolated virtual environment for the backend.
Prefix every backend command with poetry run so it always uses the
correct environment, regardless of what is active in your shell.
# From backend/
poetry run python manage.py migrateTo seed the database with sample data:
# From backend/
./flush_and_populate_db.shOpen two terminals.
# Terminal 1 — backend (from backend/)
poetry run python manage.py runserver
# Terminal 2 — frontend (from frontend/)
npm run devDefault local URLs:
- Frontend:
http://localhost:3000 - Backend:
http://localhost:8000 - Swagger docs:
http://localhost:8000/api/docs/
The backend reads environment variables from backend/.env.
The frontend typically uses frontend/.env.local.
Copy the committed example files to get started:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.localEach file is fully annotated — fill in the values appropriate for your environment. See the example files for the complete list of variables and their purpose.
backend/.env.example documents every variable. Key groups:
| Variable | Notes |
|---|---|
DJANGO_SECRET_KEY |
Required. Generate a fresh value for production. |
DEBUG |
True locally, False in all other envs. |
DJANGO_ALLOWED_HOSTS |
Comma-separated; required in production. |
CSRF_TRUSTED_ORIGINS |
Required in production. |
CORS_ALLOWED_ORIGINS |
Required in production. |
MAILGUN_API_KEY |
Rotate before first production deploy. |
GCS_BUCKET_NAME |
Required when using core.settings.prod. |
DATABASE_ENGINE |
sqlite (default) or postgres. |
In production, sensitive values (DJANGO_SECRET_KEY, POSTGRES_PASSWORD,
MAILGUN_API_KEY) are injected at runtime via Google Cloud Secret Manager
rather than stored in a file. The example file marks which variables follow
this pattern.
frontend/.env.example documents every variable:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_HOST |
Backend base URL used by the frontend. |
NEXT_PUBLIC_APP_VERSION |
Optional version string for log metadata. |
NEXT_PUBLIC_LOG_LEVEL |
Client log level (default: info). |
NEXT_PUBLIC_LOG_ENDPOINT |
Optional endpoint for shipping client logs. |
The default local database in the current backend settings is SQLite.
The backend is now prepared for two database modes:
- development default: SQLite
- production target: PostgreSQL / Cloud SQL
You can keep using SQLite locally. For PostgreSQL-based environments, configure one of the following:
DATABASE_ENGINE=postgreswithDATABASE_URLDATABASE_ENGINE=postgreswithPOSTGRES_DB,POSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_HOST, andPOSTGRES_PORT
Run migrations from backend/:
poetry run python manage.py migrateIf you need to create new migrations during development:
poetry run python manage.py makemigrationsThe repository includes backend/flush_and_populate_db.sh, which:
- flushes the database
- cleans local media files
- runs the
populatemanagement command
From backend/:
./flush_and_populate_db.shThe staging stack builds fully self-contained images and wires up Postgres, Nginx, and Cypress. Use it to validate E2E flows before shipping — not for active feature development.
# From repo root
docker compose -f docker-compose.yml -f docker-compose.staging.yml up --buildOr via the frontend npm script:
# From frontend/
npm run e2e:dockerThe staging stack runs:
backend—stagingimage target behind Gunicorn/Uvicorn on port 8080frontend—stagingimage target on port 8080proxy— Nginx (infra/nginx/app.conf) on port 8080postgres— PostgreSQL 16 on port 5432cypress— headless Cypress run against the proxybackend-tests— pytest run inside the staging backend image
Ports exposed to the host:
| Service | Host port |
|---|---|
| proxy | 8080 |
| backend | 8000 |
| frontend | 3000 |
| postgres | 5432 |
The production deployment target is Cloud Run with separate frontend and backend services.
Build the backend production image from the prod target:
docker build -f backend/Dockerfile --target prod -t prophy-backend:prod .This image is intended to run the Django application in production.
Build the frontend production image from the prod target:
docker build -f frontend/Dockerfile --target prod \
--build-arg NEXT_PUBLIC_HOST=https://api.prophy.com \
-t prophy-frontend:prod .Note — environment-specific image:
NEXT_PUBLIC_HOSTis baked into the client JavaScript bundle atnext buildtime and cannot be overridden at runtime. A frontend image is therefore tied to a specific backend URL; rebuild the image whenever the backend URL changes.For local staging (docker-compose with the nginx proxy), omit
--build-arg NEXT_PUBLIC_HOSTso the fallback/api/path is used instead.
This image is intended to run the Next.js application in production.
Recommended production topology:
- one Cloud Run service for the Django backend
- one Cloud Run service for the Next.js frontend
- Cloud SQL for the production database
- GCS for production object storage where applicable
Run natively against your local SQLite database — no Docker needed.
From backend/:
poetry run pytestTwo modes:
Local — run against locally running servers (fastest iteration):
# Requires both servers already running on :3000 and :8000
npm run cypress:open # interactive UI
npm run cypress:run # headlessStaging — run against fully-built Docker images (CI parity):
npm run e2e:dockerThe db:seed Cypress task resets and repopulates the backend through the
django_cypress integration before each E2E spec.
Swagger UI is available at:
/api/docs/
This route is configured with drf-yasg in backend/core/urls.py.
If you want to contribute:
- fork the repository
- create a feature branch
- open a pull request targeting
main