TherapEase is a full-stack web app for locating special education facilities and services in the Philippines. It is a monorepo with a Django REST backend (api/) and a Nuxt 4 SPA frontend (client/), orchestrated via Docker Compose.
Browser ──> Traefik (reverse proxy) ──> Nuxt SPA (client/, port 9002)
──> Django REST API (api/, port 9001)
├── PostgreSQL 15 (db)
├── Redis 7 (cache/Celery broker)
└── MinIO (S3-compatible storage)
- Frontend: Nuxt 4 (SSR off), Vue 3, TypeScript, Tailwind CSS v4, Flowbite v4, Leaflet maps, @vite-pwa/nuxt (PWA)
- Backend: Django 5.2 LTS, DRF, Djoser, simplejwt, celery, django-opensearch-dsl
- Infra: Docker Compose (dev), Ansible + Traefik (prod), GitHub Actions CI
api/ Django backend
apps/core/facilities/ Facility model, views, search, permissions
apps/core/users/ User/Profile/Organization models, auth, JWT
django_app/ Settings, URL conf, celery, WSGI/ASGI
client/ Nuxt frontend
assets/css/ Tailwind entry point
components/Base/ AppHeader, AppFooter, AppDropdown, etc.
components/Details/ Facility detail sub-components
components/Search/ Search results listing components
composables/useAuth.ts Auth composable (login, register, JWT, profile)
layouts/default.vue Default layout wrapper
middleware/auth.global.ts Global auth route guard
pages/ Page components (index, search, details, login, etc.)
public/ PWA icons, offline fallback, static assets
docs/ Architecture, auth, data model, search, UI docs
infrastructure/ Ansible playbooks + Traefik configs
utils/ Python data-loading scripts
- Docker and Docker Compose
- Node.js 22+ (for local frontend dev outside Docker)
- Python 3.12 (for local backend dev outside Docker)
# Copy env file and fill in values
cp .env.sample .env
# Start all services
docker compose upExposed ports:
- Frontend: http://localhost:9002
- API: http://localhost:9001
- Swagger docs: http://localhost:9001/docs/
- MinIO console: http://localhost:9003
pip install pre-commit
pre-commit installRuns on each commit: Black (Python formatting), Ruff (lint + format), check-yaml, trailing-whitespace.
| Command | Description |
|---|---|
python manage.py runserver 0.0.0.0:8000 |
Dev server |
python manage.py test |
Run all tests |
python manage.py test apps.core.facilities.tests |
Run specific test module |
python manage.py makemigrations |
Create new migrations |
python manage.py migrate |
Apply migrations |
python manage.py shell |
Django shell |
| Command | Description |
|---|---|
npm run dev |
Nuxt dev server |
npm run build |
Production build → .output/ |
npm run generate |
Static site generation |
npm run preview |
Preview production build |
npm run test |
Run Vitest tests |
npm run test:watch |
Run tests in watch mode |
Tests use Django's TestCase and DRF's APITestCase. Test files:
api/apps/core/users/tests.py— User auth, profile CRUD, organization permissions, JWTapi/apps/core/facilities/tests.py— Facility CRUD and search
cd api && python manage.py testTests use Vitest + @nuxt/test-utils. Test files:
client/tests/components/AppFilter.test.ts— Filter component testsclient/tests/components/AppCardList.test.ts— Card list component tests
cd client && npm run testGitHub Actions run backend tests with PostgreSQL + Redis service containers on push/PR to main. Frontend CI only builds the Docker image.
- Settings:
api/django_app/settings.py— all config viaos.environ.get(). - Auth: JWT via
djangorestframework-simplejwt; user management viadjoser; social login (Google/Facebook) viapython-social-auth. - Permissions: Each app has a
permissions.pywith custom DRF permission classes. - Signals:
signals.pyin each app handles auto-creation (e.g., Profile on User create, Facilities on FacilityProperties create). - Background tasks: Celery with Redis broker. Worker schedule defined in
api/django_app/celery.py. - Logging: JSON-formatted logs via
django_app/log_formatter.py. - API tracking:
drf-api-trackingmiddleware logs all API requests. - Search: Full-text search via
django-opensearch-dsl; endpoint atPOST /facilities/search. - Superuser auto-create:
entrypoint.shcreatesadmin@sample.com / adminpassword012if it doesn't exist. - Migrations: Always run
makemigrationsafter model changes and commit the migration files.
- SSR is OFF (
ssr: falseinnuxt.config.ts). This is a client-side SPA. - Auth: JWT tokens stored in localStorage, managed by
client/composables/useAuth.ts. Tokens auto-refreshed. Protected routes guarded bymiddleware/auth.global.ts. - State: No Vuex/Pinia — auth state is a simple reactive composable.
- API calls: Uses
$fetch(Nuxt's built-in HTTP client). API base URL configured viaruntimeConfig.public.API_BASE_URL. - Styling: Tailwind CSS v4 (
@import "tailwindcss"inassets/css/input.css). Flowbite v4 components. Custom Lato font. - Components follow Vue 3 Composition API with
<script setup lang="ts">. - Pages are file-based routed via Nuxt's
pages/directory. - Deprecated code:
deprecated/AppFilter.vue— do not modify, usecomponents/Search/AppFilter.vueinstead. - PWA: The app is a Progressive Web App via
@vite-pwa/nuxt. Configuration is innuxt.config.tsunder thepwakey. The<NuxtPwaManifest />component and viewport/mobile meta tags are inapp.vue. PWA icons live inpublic/. Runtime caching strategies: map tiles (CacheFirst, 30-day), API calls (NetworkFirst, 5-min). The service worker is generated at build time and outputs to.output/public/sw.js.
- Environment variables managed via
.envfile (gitignored). Template at.env.sample. - Docker Compose defines 5 services:
frontend,api,db(Postgres 15),cache(Redis 7),storage(MinIO). - Traefik handles routing in both dev (
infrastructure/traefik/traefik.dev.toml) and prod. - Ansible playbooks in
infrastructure/ansible/playbooks/for production deployment.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/facilities/ |
GET/POST | Auth (CRUD) | List/create facilities |
/api/facilities/{id}/ |
GET/PUT/DELETE | Auth | Facility detail |
/api/facilities/search |
POST | Public | Full-text search with filters |
/api/profile/ |
GET/PATCH | Auth | User profile |
/api/organization/ |
GET/POST | Auth | Organizations |
/api/auth/jwt/create/ |
POST | Public | Get JWT tokens |
/api/auth/jwt/refresh/ |
POST | Public | Refresh JWT access token |
/api/auth/users/ |
POST | Public | Register user |
/api/auth/users/activation/ |
POST | Public | Activate user account |
/api/social/{google,facebook}/ |
POST | Public | Social login |
/api/submissions/ |
GET/POST | Public (POST) / Staff (GET) | List/create facility submissions |
/api/submissions/{id}/ |
GET/PATCH/DELETE | Staff | Submission detail/update |
/api/submissions/{id}/review/ |
POST | Staff | Mark submission as in review |
/api/submissions/{id}/approve/ |
POST | Staff | Approve and merge into facilities |
/api/submissions/{id}/reject/ |
POST | Staff | Reject submission with notes |
/api/submissions/upload-image/ |
POST | Public | Upload facility image to MinIO |
- The
api/apps/custom/andapi/apps/plugins/directories are placeholders for future apps. - Backups directory (
api/backups/) is gitignored except for.gitkeep. - The
api/app.logandapi/requests.logfiles are gitignored but may exist locally. - When adding new Django models, create the app under
api/apps/core/orapi/apps/custom/as appropriate. - The data model for facilities is detailed in
docs/data_model.md— consult it before modifying facility schemas. social-coreandsocial-auth-app-djangoare used for social auth but not listed inrequirements.txtdirectly (installed as dependencies).- This project has no linter or typecheck script for the frontend — TypeScript checking happens via
nuxt dev/nuxt buildautomatically. - Frontend
node_modules/is managed inside Docker and may be root-owned on the host. Usedocker compose up --buildto install/update dependencies. - The PWA service worker is only generated during production builds (
npm run build). In dev mode (npm run dev), it registers with minimal precaching and warns about unmatched glob patterns — this is expected and harmless. - PR descriptions live in
PR.md. Update it when creating a new PR with a summary of changes and affected issues.