131 lines
4.7 KiB
Markdown
131 lines
4.7 KiB
Markdown
# ResumeLens Development Skill
|
|
|
|
Use this skill when building or modifying features in the ResumeLens application.
|
|
|
|
## Project at a glance
|
|
|
|
- Stack: Go backend (`chi` router) + React 19 + TypeScript + Vite frontend.
|
|
- Core purpose: accept a resume PDF and job description, call OpenAI, and return structured scoring + feedback.
|
|
- Backend entrypoint: `cmd/server/main.go`.
|
|
- Frontend entrypoint: `web/src/main.tsx`.
|
|
- API endpoint: `POST /api/analyze`.
|
|
|
|
## Repository map
|
|
|
|
- `cmd/server/main.go`: starts HTTP server on `:3000`, mounts middleware and API routes.
|
|
- `internal/api/`: CORS + rate-limit middleware and route mounting.
|
|
- `internal/handlers/analyze.go`: multipart request validation + JSON response.
|
|
- `internal/services/analyzer.go`: PDF text extraction + OpenAI call + JSON parsing.
|
|
- `internal/services/prompt.go`: system prompt contract for LLM output.
|
|
- `internal/models/analysis.go`: canonical backend response schema.
|
|
- `web/src/pages/`: app routes (`/`, `/upload`, `/demo`, `/results`).
|
|
- `web/src/components/analysis/`: reusable result UI sections.
|
|
- `web/src/types/resumeAnalysis.ts`: frontend schema mirror of backend response.
|
|
- `docker-compose.yml`: local multi-container runtime (`backend` + `frontend` at `:3005`).
|
|
|
|
## Local development workflow
|
|
|
|
### Backend
|
|
|
|
- Run: `go run ./cmd/server`
|
|
- Test: `go test ./...`
|
|
- Backend listens on `http://localhost:3000`.
|
|
|
|
### Frontend
|
|
|
|
- Install deps: `cd web && npm ci`
|
|
- Dev server: `cd web && npm run dev`
|
|
- Build: `cd web && npm run build`
|
|
- Lint: `cd web && npm run lint`
|
|
|
|
### Full stack with Docker
|
|
|
|
- Run: `docker compose up --build`
|
|
- Frontend served at `http://localhost:3005`
|
|
- Nginx proxies `/api/*` to backend service (`web/nginx.conf`).
|
|
|
|
## Configuration and env vars
|
|
|
|
- Backend requires `OPENAI_API_KEY`.
|
|
- Frontend optionally uses `VITE_API_BASE_URL`.
|
|
- If unset: dev defaults to `http://localhost:3000`.
|
|
- If production build: defaults to relative path (`/api/...`) for nginx proxying.
|
|
|
|
Do not hardcode keys or expose secrets in client code.
|
|
|
|
## API contract (critical)
|
|
|
|
`POST /api/analyze` expects `multipart/form-data`:
|
|
|
|
- `resume`: uploaded file (backend expects a parseable PDF).
|
|
- `job_description`: non-empty string.
|
|
|
|
Responses:
|
|
|
|
- `200`: JSON matching `AnalysisResult` / `ResumeAnalysisResult`.
|
|
- `400`: invalid form payload (missing file/job description).
|
|
- `429`: per-IP rate limit exceeded.
|
|
- `500`: analysis failure (PDF parse issue, OpenAI issue, JSON parse issue).
|
|
|
|
Keep backend model and frontend type definitions synchronized whenever fields change.
|
|
|
|
## Existing behavior to preserve
|
|
|
|
- Rate limiting is in-memory and per source IP: max 10 requests/hour.
|
|
- CORS currently allows:
|
|
- `http://localhost:5173`
|
|
- `http://localhost`
|
|
- `http://localhost:80`
|
|
- Results page depends on router state; direct navigation to `/results` redirects to `/`.
|
|
- Download JSON action exists on results page.
|
|
- Prompt injection output fields are supported in both backend and frontend:
|
|
- `injection_detected`
|
|
- `injection_details`
|
|
|
|
## LLM integration details
|
|
|
|
- LLM call uses `openai-go` chat completions with model `gpt-4o-mini`.
|
|
- System prompt in `internal/services/prompt.go` requires strict JSON-only output.
|
|
- Parsing is strict JSON unmarshal into `models.AnalysisResult`.
|
|
|
|
When adding fields:
|
|
|
|
1. Update `internal/models/analysis.go`.
|
|
2. Update prompt JSON contract in `internal/services/prompt.go`.
|
|
3. Update `web/src/types/resumeAnalysis.ts`.
|
|
4. Update UI components in `web/src/components/analysis/` and pages consuming the data.
|
|
|
|
## Known implementation quirks
|
|
|
|
- Upload UI currently accepts files with MIME `image/*` in `handleFileSelect`, but the file input element only allows `.pdf`, and backend parser expects PDF bytes.
|
|
- PDF extraction buffers full file in memory before parsing (`io.ReadAll`), so large-file behavior should be considered when adding limits.
|
|
- Current rate limiter is process-local; scaling to multiple backend replicas will need shared storage.
|
|
|
|
## Feature development checklist
|
|
|
|
When implementing a new feature, follow this order:
|
|
|
|
1. Define data contract impact first (backend model + frontend type).
|
|
2. Update API handler/service behavior.
|
|
3. Update UI and route behavior.
|
|
4. Add or update tests (`go test ./...`; frontend lint/build).
|
|
5. Validate end-to-end flow with one manual upload + analyze run.
|
|
|
|
## Validation commands before shipping
|
|
|
|
- Backend tests: `go test ./...`
|
|
- Frontend checks: `cd web && npm run lint && npm run build`
|
|
- Optional full-stack smoke test: `docker compose up --build`
|
|
|
|
## Deployment notes
|
|
|
|
- CI workflow (`.github/workflows/deploy.yml`) builds and pushes backend/frontend images on pushes to `master`.
|
|
- Manual image commands are documented in `DEPLOY.md`.
|
|
|
|
If you add runtime dependencies or env vars, update:
|
|
|
|
- Dockerfiles
|
|
- `docker-compose.yml`
|
|
- CI workflow
|
|
- this skill file
|