[1.0.0] #19
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -0,0 +1,102 @@
|
||||
# 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
|
||||
```
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
||||
.env*
|
||||
|
||||
/src/generated/prisma
|
||||
|
||||
+37
-37
@@ -1,44 +1,44 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/dev/Dockerfile.main
|
||||
ports:
|
||||
- "5008:5008"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/dev/Dockerfile.main
|
||||
ports:
|
||||
- "127.0.0.1:5008:5008"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/dev/Dockerfile.frontend
|
||||
ports:
|
||||
- "3008:3000"
|
||||
env_file:
|
||||
- ./frontend/.env
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/dev/Dockerfile.frontend
|
||||
ports:
|
||||
- "127.0.0.1:3008:3000"
|
||||
env_file:
|
||||
- ./frontend/.env
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: postgres_db
|
||||
environment:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_DB: mydb
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: postgres_db
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
postgres_data:
|
||||
|
||||
+1
-1
@@ -24,6 +24,6 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
#env files
|
||||
.env
|
||||
.env*
|
||||
.env.*.local
|
||||
|
||||
|
||||
Reference in New Issue
Block a user