95 lines
3.4 KiB
Markdown
95 lines
3.4 KiB
Markdown
# MedAssist - AI Coding Instructions
|
|
|
|
## Architecture Overview
|
|
|
|
MedAssist is a **medication tracking and planning app** with a monorepo structure:
|
|
|
|
- **Backend**: Fastify 5 + TypeScript + SQLite (Drizzle ORM) at `backend/`
|
|
- **Frontend**: React 18 + Vite + TypeScript at `frontend/`
|
|
- **Database**: SQLite with migrations in `backend/src/db/migrations/`
|
|
- **Deployment**: Docker Compose with separate dev containers
|
|
|
|
### Data Flow
|
|
```
|
|
Frontend (React) → /api/* proxy → Backend (Fastify) → SQLite
|
|
↓ (Vite rewrites /api to /)
|
|
```
|
|
|
|
The Vite proxy at `frontend/vite.config.ts` rewrites `/api/*` to `/` - so frontend calls `/api/medications` but backend route is just `/medications`.
|
|
|
|
## Development Commands
|
|
|
|
```bash
|
|
# Start dev environment (preferred)
|
|
docker compose up
|
|
|
|
# Or run services separately:
|
|
cd backend && npm run dev # tsx watch on port 3000
|
|
cd frontend && npm run dev # Vite on port 5173
|
|
|
|
# Database migrations
|
|
cd backend && npm run migrate
|
|
```
|
|
|
|
## Key Patterns
|
|
|
|
### Backend Routes (`backend/src/routes/`)
|
|
- Routes register directly on app without `/api` prefix
|
|
- Use Fastify's type-safe body/params: `app.put<{ Body: MyType }>()`
|
|
- Settings: notification config → JSON file (`data/notification-settings.json`), SMTP → `.env`
|
|
|
|
### Frontend (`frontend/src/App.tsx`)
|
|
- Single-file React app with all components and state
|
|
- Uses React Router for navigation (`/dashboard`, `/medications`, `/planner`, `/settings`)
|
|
- API calls use `/api/` prefix (proxied by Vite)
|
|
- Medication scheduling logic with "slices" (usage patterns)
|
|
|
|
### Database Schema (`backend/src/db/schema.ts`)
|
|
- `medications`: tracks count, strips, pack inventory, usage schedules as JSON
|
|
- `users`, `refreshTokens`: JWT auth with rotating refresh tokens
|
|
- `settings`: legacy table (SMTP now from `.env`, notifications from JSON file)
|
|
|
|
### Settings Architecture
|
|
```
|
|
SMTP config: .env file (read-only in UI, loaded via env_file in docker-compose)
|
|
Notifications: data/notification-settings.json (editable via UI)
|
|
```
|
|
|
|
## Conventions
|
|
|
|
- **TypeScript**: Strict mode, ESM modules (`"type": "module"`)
|
|
- **Styling**: CSS custom properties in `frontend/src/styles.css`, dark/light theme via `data-theme`
|
|
- **API responses**: Return objects directly, Fastify serializes to JSON
|
|
- **Environment**: Copy `.env.example` → `.env`, secrets must be 10+ chars
|
|
|
|
## ⚠️ Database Migrations (CRITICAL)
|
|
|
|
**When adding/modifying database columns, ALWAYS:**
|
|
|
|
1. **Update schema**: `backend/src/db/schema.ts`
|
|
2. **Create migration file**: `backend/src/db/migrations/XXXX_description.sql`
|
|
```sql
|
|
-- Example: Adding a new column
|
|
ALTER TABLE medications ADD COLUMN new_column TEXT;
|
|
```
|
|
3. **Update journal**: `backend/src/db/migrations/meta/_journal.json`
|
|
```json
|
|
{ "idx": X, "version": 1, "when": TIMESTAMP, "tag": "XXXX_description", "breakpoint": false }
|
|
```
|
|
|
|
**Why this matters**: The dev database might get updated manually, but production will break without proper migration files. This causes `SQLITE_ERROR: no such column` errors in prod.
|
|
|
|
**Migration naming**: `0001_add_strips.sql`, `0002_pack_inventory.sql`, `0003_add_image_url.sql`
|
|
|
|
## File Locations
|
|
|
|
| Purpose | Location |
|
|
|---------|----------|
|
|
| Backend entry | `backend/src/index.ts` |
|
|
| Database schema | `backend/src/db/schema.ts` |
|
|
| Migrations | `backend/src/db/migrations/*.sql` |
|
|
| Frontend app | `frontend/src/App.tsx` |
|
|
| Styles | `frontend/src/styles.css` |
|
|
| Docker dev setup | `docker-compose.yml` |
|
|
| Env template | `.env.example` |
|