Files
gg-backend/CLAUDE.md
2026-04-23 15:00:57 +05:30

103 lines
5.3 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository layout
Monorepo with two independent npm packages plus a shared Docker dev stack:
- `backend/` — Node.js + Express 5 (ESM) API, Prisma ORM over PostgreSQL.
- `frontend/` — React 19 + Vite + TypeScript admin dashboard, styled with Tailwind 4 + shadcn/ui.
- `docker/` — Dev Dockerfiles + `entrypoint.sh` (used by `docker-compose.dev.yml` at repo root).
There is no root-level package script — run npm commands inside `backend/` or `frontend/`.
## Common commands
### Docker (full stack)
```bash
docker compose -f docker-compose.dev.yml up --build # backend :5008, frontend :3008, postgres :5432 (internal)
docker compose -f docker-compose.dev.yml down
```
The backend container's `entrypoint.sh` runs `prisma generate` then `prisma db push` (NOT `migrate deploy`) on every start — schema changes propagate without a migration step in the Docker dev flow.
### Backend (`cd backend`)
```bash
npm run dev # nodemon src/app.js
npm start # node src/app.js
npm run migrate # npx prisma migrate dev (use this locally; Docker uses db push)
npm run generate # npx prisma generate
npx prisma studio # GUI for the DB
npm run create-user <username> <password> [role] # role defaults to "admin"
```
In Docker, create an admin via:
```bash
docker exec -it gg-backend-api-backend-1 node src/utils/createUser.js <name> <password> <role>
```
### Frontend (`cd frontend`)
```bash
npm run dev # Vite dev server
npm run build # tsc -b && vite build
npm run lint # eslint .
npm run preview
```
There is no test runner configured in either package.
## Architecture
### Backend
Single Express app assembled in `backend/src/app.js`. Each domain follows the same triplet:
- `src/routes/<domain>.routes.js` — declares the Express router, applies `jwtAuthMiddleware` per-route (mixed public/protected within the same router; see `blog.routes.js` for the pattern: list/detail public, admin endpoints + writes protected).
- `src/controllers/<domain>.controller.js` — handler logic; uses Prisma directly.
- Mounted at `/api/<domain>` in `app.js`.
Domains: `departments`, `auth`, `blogs`, `upload`, `doctors`, `careers`, `candidates`, `appointments`, `inquiry`, `academics`, `email`, `newsMedia`, `import`. Static `uploads/` is served at `/uploads`.
Cross-cutting pieces:
- **Prisma client** — `src/prisma/client.js` exports a singleton. Always import from there rather than instantiating `new PrismaClient()` (the import controller currently instantiates its own — match the singleton pattern when adding new code).
- **Auth** — `src/middleware/auth.js` expects `Authorization: Bearer <jwt>`, attaches the decoded payload to `req.user`. Tokens are issued by `src/utils/jwt.js` using `JWT_SECRET`. Passwords hashed via `src/utils/password.js` (bcrypt).
- **Email** — `src/utils/sendEmail.js` wraps Postmark with `EMAIL_FROM` as sender. Recipient lists are looked up by category via `getEmailsByType(type)` against the `EmailConfig` table — controllers call this to fan out notifications (e.g. on new appointments/inquiries).
- **CORS** — `CORS_ALLOWED_ORIGINS` is a **space-separated** list (not comma-separated). Requests with no `Origin` header are allowed.
- **Body limits** — JSON/urlencoded bodies are capped at 50mb to support image-laden Editor.js payloads and bulk Excel imports.
### Data model (Prisma — `backend/prisma/schema.prisma`)
Note the natural-key relations: `Appointment.doctorId` references `Doctor.doctorId` (the string business ID), not `Doctor.id`. Same for `Department`. The `SL_NO` / `GG_ID` columns from imports become these business IDs. `Doctor``Department` is many-to-many through `DoctorDepartment`, which has a 1:1 `DoctorTiming` for weekday schedules. `NewsMedia` has cascading `NewsImage` children.
### Frontend
- Routing in `src/App.tsx`: `PublicRoute` wraps `/` (Login); everything else is wrapped by `ProtectedRoute``DashboardLayout`. Unknown paths redirect to `/department`.
- `src/context/AuthContext.tsx` is the auth source of truth; `src/services/api.ts` is the shared Axios instance that auto-injects `Authorization: Bearer <token>` from `localStorage.getItem("token")`.
- `src/api/<domain>.ts` files are thin wrappers around that axios client, one per backend domain — keep this pattern when adding endpoints.
- Path alias `@/*``src/*` (configured in both `vite.config.ts` and `tsconfig`).
- File uploads go through Bytescale (`src/components/BytescaleUploader`) — server only stores the resulting URL.
- The `/import` page parses Excel via `xlsx` and POSTs structured payloads to `/api/import`, where `importController.js` upserts across multiple tables. When changing import behavior, the frontend's column names (`SL_NO`, `GG_ID`, `Department`, `Working Status`, etc.) and the controller's destructured keys (`departments`, `doctors`, `timings`, …) must stay in sync.
## Environment variables
`backend/.env`:
```
DATABASE_URL=postgresql://user:password@db:5432/mydb
PORT=5008
JWT_SECRET=...
CORS_ALLOWED_ORIGINS=http://localhost:5173 # space-separated for multiple
BYTESCALE_SECRET_API_KEY=...
POSTMARK_API_KEY=...
EMAIL_FROM=admin@example.com
```
`frontend/.env`:
```
VITE_API_URL=http://localhost:5008/api
```