|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +SFA Backoffice Administration Web Application - A full-stack TypeScript/Vue.js application for managing Student Financial Assistance (SFA) applications, funding requests, assessments, and student records. The application integrates with Auth0/YNet authentication and uses Microsoft SQL Server. |
| 8 | + |
| 9 | +## Architecture |
| 10 | + |
| 11 | +### Monorepo Structure |
| 12 | + |
| 13 | +- **`/src/api`**: Node.js/TypeScript Express API backend |
| 14 | +- **`/src/web`**: Vue.js 2 + Vuetify frontend |
| 15 | +- **`/db`**: Database schema, migrations, and SQL scripts |
| 16 | + |
| 17 | +### Backend Architecture (src/api) |
| 18 | + |
| 19 | +The API follows a layered architecture: |
| 20 | + |
| 21 | +- **Routes** (`routes/`): Express route handlers organized by domain |
| 22 | + - `routes/admin/`: Administrative endpoints (reference data, applications, assessments) |
| 23 | + - `routes/portal/`: Student portal-facing endpoints |
| 24 | + - `routes/auth.ts`: Authentication configuration and middleware |
| 25 | +- **Controllers** (`controllers/`): Request/response handling logic |
| 26 | +- **Services** (`services/`): Business logic layer |
| 27 | +- **Repositories** (`repositories/`): Data access layer using Knex query builder |
| 28 | +- **Models** (`models/`): TypeScript data models and types |
| 29 | +- **Serializers** (`serializers/`): Transform data for API responses |
| 30 | +- **Middleware** (`middleware/`): Authentication, authorization, validation |
| 31 | + |
| 32 | +#### Database Access Pattern |
| 33 | + |
| 34 | +- Uses **Knex.js** query builder with MS SQL Server |
| 35 | +- Database client (`db/db-client.ts`) configured with: |
| 36 | + - Automatic camelCase/snake_case transformation (camelCase in code, snake_case in DB) |
| 37 | + - Schema-aware proxy that automatically applies `defaultSchema` to all queries |
| 38 | + - Non-standard column name handling via `NON_STANDARD_COLUMN_NAMES_TRANSFORMS` |
| 39 | +- Path aliases: `@/` maps to the API root directory (configured via tsconfig.json and tsconfig-paths) |
| 40 | + |
| 41 | +#### Authentication |
| 42 | + |
| 43 | +Dual authentication system: |
| 44 | +- **Auth0/OpenID Connect**: Primary authentication via `express-openid-connect` |
| 45 | +- **JWT**: Token-based auth via `express-jwt` with JWKS validation |
| 46 | +- **Middleware**: `RequireActive` middleware checks user is active in database |
| 47 | +- Routes under `/api/v2/admin` and `/api/v2/reference` require authentication |
| 48 | +- Portal routes (`/api/portal`) may have different auth requirements |
| 49 | + |
| 50 | +### Frontend Architecture (src/web) |
| 51 | + |
| 52 | +Vue.js 2 application with modular structure: |
| 53 | + |
| 54 | +- **Router** (`router/`): Vue Router with nested routes and auth guards |
| 55 | +- **Store** (`store/`): Vuex state management with domain-specific modules |
| 56 | +- **Modules** (`modules/`): Feature modules containing components, routes, stores, and API clients |
| 57 | + - Each module typically has: `router.js`, `store.js`, API service files |
| 58 | + - Examples: student, institution, application-type, funding-group, etc. |
| 59 | +- **Components** (`components/`): Reusable UI components organized by feature |
| 60 | +- **Views** (`views/`): Top-level page components |
| 61 | +- **API** (`api/`): HTTP client wrappers for backend communication |
| 62 | + |
| 63 | +#### Key Frontend Patterns |
| 64 | + |
| 65 | +- Vuetify UI component library |
| 66 | +- Module-based organization: each domain area (student, application, institution) has its own subdirectory |
| 67 | +- API calls handled via dedicated service files (e.g., `assessments-api.js`) |
| 68 | +- HTTP client configured in `api/http-client.js` |
| 69 | + |
| 70 | +### Database |
| 71 | + |
| 72 | +- **MS SQL Server 2019** (via Docker in development) |
| 73 | +- Schema managed via: |
| 74 | + - SQL scripts in `/db`: `2_create_objects.sql`, `3_populate.sql`, `4_functions.sql` |
| 75 | + - Knex migrations in `/src/api/data/migrations` |
| 76 | + - Migration endpoints: `/migrate/up`, `/migrate/down`, `/migrate/latest` |
| 77 | +- Default schema: configurable via `DB_DEFAULT_SCHEMA` env var (typically `sfa`) |
| 78 | + |
| 79 | +## Development Commands |
| 80 | + |
| 81 | +### Initial Setup |
| 82 | + |
| 83 | +```bash |
| 84 | +# 1. Create database password file |
| 85 | +cp db/sapassword.env.sample db/sapassword.env |
| 86 | + |
| 87 | +# 2. Start Docker services (database, S3, email, converter) |
| 88 | +docker compose -f docker-compose.development.yaml up -d |
| 89 | +# Or with dev helper: |
| 90 | +dev up |
| 91 | + |
| 92 | +# 3. Restore database backup (first time only) |
| 93 | +# Place backup at /db/backups/sfa.bak, then: |
| 94 | +dev up db |
| 95 | +# Or: docker compose -f docker-compose.development.yaml up db |
| 96 | + |
| 97 | +# 4. Configure Minio S3 (first time only) |
| 98 | +# Visit http://localhost:9090 |
| 99 | +# Create bucket named "documents" |
| 100 | +# Create access key and update API .env file |
| 101 | + |
| 102 | +# 5. Setup API environment |
| 103 | +cd src/api |
| 104 | +cp .env.sample .env.development |
| 105 | +# Update DB_PASS to match MSSQL_SA_PASSWORD from db/sapassword.env |
| 106 | +# Configure other environment variables (Auth0, S3 credentials, etc.) |
| 107 | + |
| 108 | +# 6. Setup and start web frontend |
| 109 | +cd src/web |
| 110 | +npm install |
| 111 | +npm run start |
| 112 | +# Frontend runs at http://localhost:8080 |
| 113 | +``` |
| 114 | + |
| 115 | +### Running the Application |
| 116 | + |
| 117 | +```bash |
| 118 | +# Start all services (DB, S3, Email, API) via Docker |
| 119 | +dev up |
| 120 | +# Or: docker compose -f docker-compose.development.yaml up |
| 121 | + |
| 122 | +# Start frontend (in separate terminal) |
| 123 | +cd src/web |
| 124 | +npm run start |
| 125 | + |
| 126 | +# Access application at http://localhost:8080 |
| 127 | +``` |
| 128 | + |
| 129 | +### API Development |
| 130 | + |
| 131 | +```bash |
| 132 | +cd src/api |
| 133 | + |
| 134 | +# Install dependencies |
| 135 | +npm install |
| 136 | + |
| 137 | +# Run API in development mode (with hot reload) |
| 138 | +npm run start |
| 139 | + |
| 140 | +# Run tests in watch mode |
| 141 | +npm test |
| 142 | + |
| 143 | +# Build TypeScript |
| 144 | +npm run build |
| 145 | + |
| 146 | +# Run TypeScript REPL console |
| 147 | +npm run console |
| 148 | + |
| 149 | +# Run database migrations |
| 150 | +npm run migrate:latest |
| 151 | +``` |
| 152 | + |
| 153 | +### Web Development |
| 154 | + |
| 155 | +```bash |
| 156 | +cd src/web |
| 157 | + |
| 158 | +# Install dependencies |
| 159 | +npm install |
| 160 | + |
| 161 | +# Start dev server (http://localhost:8080) |
| 162 | +npm run start |
| 163 | + |
| 164 | +# Build for production |
| 165 | +npm run build:web |
| 166 | + |
| 167 | +# Build for Docker (outputs to ../app/dist/web) |
| 168 | +npm run build:docker |
| 169 | + |
| 170 | +# Lint code |
| 171 | +npm run lint |
| 172 | +``` |
| 173 | + |
| 174 | +### Database Operations |
| 175 | + |
| 176 | +```bash |
| 177 | +# Access SQL console |
| 178 | +dev sqlcmd |
| 179 | +# Or: docker compose -f docker-compose.development.yaml exec db /opt/mssql-tools/bin/sqlcmd -U sa -s localhost -P Testing1122 |
| 180 | + |
| 181 | +# Connect to database container |
| 182 | +docker compose -f docker-compose.development.yaml exec -it db bash |
| 183 | + |
| 184 | +# Run migrations via API endpoints (when API is running) |
| 185 | +# GET http://localhost:3000/migrate/up |
| 186 | +# GET http://localhost:3000/migrate/down |
| 187 | +# GET http://localhost:3000/migrate/latest |
| 188 | +``` |
| 189 | + |
| 190 | +### Dev Helper Commands |
| 191 | + |
| 192 | +The `bin/dev` Ruby script provides shortcuts (requires Ruby via asdf): |
| 193 | + |
| 194 | +```bash |
| 195 | +dev build # Build all Docker services |
| 196 | +dev up # Start all services and watch logs |
| 197 | +dev up db # Start only database service |
| 198 | +dev down # Stop all services |
| 199 | +dev logs # Follow logs for all services |
| 200 | +dev logs api # Follow logs for specific service |
| 201 | +dev sh # Open shell in API container |
| 202 | +dev npm <command> # Run npm command in API container |
| 203 | +dev sqlcmd # Open SQL terminal |
| 204 | +dev debug # Open debug console for API |
| 205 | +``` |
| 206 | + |
| 207 | +### Testing Production Build Locally |
| 208 | + |
| 209 | +```bash |
| 210 | +# 1. Create production config |
| 211 | +cp ./src/api/.env.development ./src/api/.env.production |
| 212 | + |
| 213 | +# 2. Update config: |
| 214 | +# - Set DB_HOST=db |
| 215 | +# - Set FRONTEND_URL=http://localhost:3000 |
| 216 | +# - Set AUTH_REDIRECT=http://localhost:3000/dashboard |
| 217 | + |
| 218 | +# 3. Start development database |
| 219 | +dev up db |
| 220 | + |
| 221 | +# 4. Build and start production container |
| 222 | +docker compose up --build |
| 223 | + |
| 224 | +# Access at http://localhost:3000/dashboard |
| 225 | +``` |
| 226 | + |
| 227 | +## Key Environment Variables |
| 228 | + |
| 229 | +### API (.env.development / .env.production) |
| 230 | + |
| 231 | +- `NODE_ENV`: development | test | production |
| 232 | +- `API_PORT`: API server port (default: 3000) |
| 233 | +- `FRONTEND_URL`: Frontend URL for CORS |
| 234 | +- `AUTH_REDIRECT`: Post-login redirect URL |
| 235 | +- `DB_NAME`, `DB_USER`, `DB_PASS`, `DB_HOST`, `DB_PORT`: Database connection |
| 236 | +- `DB_DEFAULT_SCHEMA`: Default schema (typically "sfa") |
| 237 | +- `CLIENT_ID`, `ISSUER_BASE_URL`, `CLIENT_SECRET`: Auth0 configuration |
| 238 | +- `AUTH0_DOMAIN`, `AUTH0_AUDIENCE`: JWT validation |
| 239 | +- `MAIL_FROM`, `MAIL_HOST`, `MAIL_PORT`: Email configuration |
| 240 | +- `SENTRY_DSN`: Sentry error tracking (optional) |
| 241 | + |
| 242 | +## Important Technical Details |
| 243 | + |
| 244 | +### Database Conventions |
| 245 | + |
| 246 | +- **Column naming**: Use camelCase in TypeScript code; automatic conversion to snake_case for database |
| 247 | +- **Schema**: All queries use default schema from `DB_DEFAULT_SCHEMA` unless explicitly overridden |
| 248 | +- **Special columns**: Some non-standard names handled via `NON_STANDARD_COLUMN_NAMES_TRANSFORMS` |
| 249 | + |
| 250 | +### API Route Structure |
| 251 | + |
| 252 | +- `/api/portal/*`: Portal/student-facing endpoints |
| 253 | +- `/api/v2/admin/*`: Admin endpoints (requires auth + active user) |
| 254 | +- `/api/v2/reference/*`: Reference data endpoints (requires auth + active user) |
| 255 | +- `/migrate/*`: Database migration endpoints |
| 256 | +- `/auth/login`, `/auth/logout`: Authentication endpoints |
| 257 | +- `/_status`: Health check endpoint |
| 258 | + |
| 259 | +### Frontend Module Pattern |
| 260 | + |
| 261 | +Each module in `src/web/src/modules/` typically includes: |
| 262 | +- `router.js`: Route definitions |
| 263 | +- `store.js`: Vuex store module |
| 264 | +- `*-api.js`: API client functions |
| 265 | +- Components specific to that domain |
| 266 | + |
| 267 | +### Path Aliases |
| 268 | + |
| 269 | +- **API**: `@/*` resolves to `src/api/*` (TypeScript paths) |
| 270 | +- **Web**: `@/*` resolves to `src/web/src/*` (Vue.js alias) |
| 271 | + |
| 272 | +## External Services (Development) |
| 273 | + |
| 274 | +- **Database**: http://localhost:1433 (MSSQL) |
| 275 | +- **Minio S3**: http://localhost:9090 (web UI), http://localhost:9000 (API) |
| 276 | +- **MailSlurper**: http://localhost:8081 (email preview) |
| 277 | +- **PDF Converter**: http://localhost:5000 |
| 278 | +- **Frontend Dev Server**: http://localhost:8080 |
| 279 | +- **API**: http://localhost:3000 |
| 280 | + |
| 281 | +## Building for Production |
| 282 | + |
| 283 | +The production Dockerfile builds both frontend and backend into a single container: |
| 284 | +1. Builds Vue.js frontend |
| 285 | +2. Copies built files to API's dist/web directory |
| 286 | +3. API serves static frontend files via Express |
| 287 | +4. Single container serves both frontend and API |
| 288 | + |
| 289 | +## Repository Workflow |
| 290 | + |
| 291 | +- **Main branch**: `test` (not `main` or `master`) |
| 292 | +- Fork repository to contribute |
| 293 | +- Sync with upstream before creating PR |
| 294 | +- PRs target `ytgov/sfa-client:test` |
| 295 | +- Ensure `docker-compose build` succeeds before submitting PR |
0 commit comments