103 lines
5.3 KiB
Markdown
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
|
||
|
|
```
|