mirror of
https://github.com/velocitatem/cvfs.git
synced 2026-05-31 08:43:37 +00:00
Finish MVP and dockerize
This commit is contained in:
56
docs/resume-branches/architecture.md
Normal file
56
docs/resume-branches/architecture.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Resume Branches Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Resume Branches treats a canonical ATS-safe DOCX as a source-of-truth document graph. The stack is split into a Next.js control plane, a FastAPI backend, and a Celery worker. MinIO hosts artifacts locally and can be swapped with any S3-compatible object storage in production.
|
||||
|
||||
## Services
|
||||
|
||||
- **apps/webapp** – Next.js 15 UI with a CV tree, upload workflow, specialization browser, and publish controls. It talks to the FastAPI backend via `NEXT_PUBLIC_API_BASE_URL`.
|
||||
- **apps/backend/fastapi** – FastAPI service that handles ingest, structured patch storage, AI suggestions, and publishing. SQLAlchemy models persist into Postgres. Storage relies on MinIO.
|
||||
- **apps/worker** – Celery worker for asynchronous DOCX parsing, keyword extraction, and optional AI tailoring loops. It consumes Redis as the broker backend.
|
||||
- **dlib** – Shared domain library including: structured DOCX parsing (`dlib.cv`), patch validation, ATS guardrails, storage adapter (`dlib.storage`), MinIO client, and auth utilities.
|
||||
|
||||
## Data Model
|
||||
|
||||
- `cv_documents` – conceptual resume per owner.
|
||||
- `cv_versions` – every branch or specialization. Stores structured block snapshots and artifact pointers.
|
||||
- `cv_patches` – granular operations applied to a version.
|
||||
- `submissions` – leaf nodes representing a company/role tailoring.
|
||||
- `ai_suggestions` – AI proposals pending acceptance.
|
||||
- `public_assets` – immutable artifacts for published CV links.
|
||||
|
||||
## Flows
|
||||
|
||||
1. **Upload canonical DOCX**: The backend stores the raw file in MinIO, parses the blocks, and seeds the root branch version.
|
||||
2. **Create branches**: Provide a parent version + patch list. ATS guardrails enforce change budgets and ratio protections.
|
||||
3. **Tailoring**: Submit job description + focus keywords. Suggestions are produced via `dlib.ai.tailoring` and saved for review.
|
||||
4. **Publish**: Copy a version/submission artifact to a public slug (e.g., `https://cv.alves.world/cv/ml-engineer-stripe`).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `DATABASE_URL` – Async SQLAlchemy DSN (default local Postgres).
|
||||
- `MINIO_*` – Access, bucket, region, and endpoint for the object storage.
|
||||
- `PUBLIC_BASE_URL` / `CV_PUBLIC_DOMAIN` – Hostnames for publishable resumes.
|
||||
- `AUTH_*` – Optional OIDC issuer/audience. Disabled by default for local dev.
|
||||
- `NEXT_PUBLIC_API_BASE_URL` – Next.js uses this to call FastAPI.
|
||||
|
||||
## Local Dev
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
make init
|
||||
make dev # Next.js
|
||||
make run.backend # FastAPI API server
|
||||
make run.worker # Celery worker
|
||||
make lift.database # Postgres profile
|
||||
make lift.minio # MinIO bucket
|
||||
```
|
||||
|
||||
## Dokploy Targets
|
||||
|
||||
- Backend (FastAPI) – build from `docker/backend-fastapi.Dockerfile`, run behind `api.cv.alves.world`.
|
||||
- Webapp (Next.js) – build from `docker/webapp.Dockerfile`, exposed via `cv.alves.world`.
|
||||
- Redis, Postgres, and MinIO run either inside Dokploy or via managed offerings.
|
||||
|
||||
See `docs/resume-branches/dokploy.md` for concrete API payloads.
|
||||
146
docs/resume-branches/dokploy.md
Normal file
146
docs/resume-branches/dokploy.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Dokploy Deployment Plan (cv.alves.world)
|
||||
|
||||
Default host: `https://dev.alves.world` (`DOKPLOY_API_BASE=https://dev.alves.world/api`). Authentication uses `x-api-key: $DOKPLOY_API` loaded from `.env`.
|
||||
|
||||
```bash
|
||||
set -a
|
||||
. ./.env # must contain DOKPLOY_API
|
||||
set +a
|
||||
: "${DOKPLOY_API:?missing token}"
|
||||
DOKPLOY_BASE_URL="${DOKPLOY_BASE_URL:-https://dev.alves.world}"
|
||||
DOKPLOY_API_BASE="${DOKPLOY_BASE_URL%/}/api"
|
||||
```
|
||||
|
||||
## 1. Resolve IDs
|
||||
|
||||
Discover the Personal project and staging environment:
|
||||
|
||||
```bash
|
||||
curl -sS "$DOKPLOY_API_BASE/project.all" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $DOKPLOY_API" | jq '.[] | {id, name}'
|
||||
|
||||
PROJECT_ID="<personal-project-id>"
|
||||
|
||||
curl -sS "$DOKPLOY_API_BASE/environment.byProjectId?projectId=$PROJECT_ID" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $DOKPLOY_API" | jq '.[] | {id, name}'
|
||||
|
||||
ENVIRONMENT_ID="<personal-env-id>"
|
||||
```
|
||||
|
||||
## 2. Build + push images
|
||||
|
||||
Backend image (adjust registry/org as needed):
|
||||
|
||||
```bash
|
||||
docker build -t ghcr.io/<org>/resume-branches-backend:latest -f docker/backend-fastapi.Dockerfile .
|
||||
docker push ghcr.io/<org>/resume-branches-backend:latest
|
||||
```
|
||||
|
||||
Webapp image:
|
||||
|
||||
```bash
|
||||
docker build -t ghcr.io/<org>/resume-branches-webapp:latest -f docker/webapp.Dockerfile .
|
||||
docker push ghcr.io/<org>/resume-branches-webapp:latest
|
||||
```
|
||||
|
||||
## 3. Backend application
|
||||
|
||||
```bash
|
||||
curl -sS -X POST "$DOKPLOY_API_BASE/application.create" \
|
||||
-H "Content-Type: application/json" -H "x-api-key: $DOKPLOY_API" \
|
||||
-d @- <<'JSON'
|
||||
{
|
||||
"name": "resume-backend",
|
||||
"environmentId": "ENVIRONMENT_ID",
|
||||
"projectId": "PROJECT_ID",
|
||||
"deployment": {
|
||||
"type": "docker",
|
||||
"image": "ghcr.io/<org>/resume-branches-backend:latest",
|
||||
"env": {
|
||||
"BACKEND_PORT": "8080",
|
||||
"DATABASE_URL": "postgresql+asyncpg://postgres:postgres@postgres:5432/resume_branches",
|
||||
"MINIO_ENDPOINT": "http://minio:9000",
|
||||
"MINIO_BUCKET": "resume-branches",
|
||||
"MINIO_REGION": "us-east-1",
|
||||
"MINIO_ROOT_USER": "${MINIO_ROOT_USER}",
|
||||
"MINIO_ROOT_PASSWORD": "${MINIO_ROOT_PASSWORD}",
|
||||
"PUBLIC_BASE_URL": "https://cv.alves.world",
|
||||
"CV_PUBLIC_DOMAIN": "cv.alves.world"
|
||||
},
|
||||
"healthcheck": {
|
||||
"path": "/health",
|
||||
"interval": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
```
|
||||
|
||||
### Domain attach (api.cv.alves.world)
|
||||
|
||||
```bash
|
||||
curl -sS -X POST "$DOKPLOY_API_BASE/domain.validateDomain" \
|
||||
-H "Content-Type: application/json" -H "x-api-key: $DOKPLOY_API" \
|
||||
-d '{"domain":"api.cv.alves.world"}'
|
||||
|
||||
curl -sS -X POST "$DOKPLOY_API_BASE/domain.create" \
|
||||
-H "Content-Type: application/json" -H "x-api-key: $DOKPLOY_API" \
|
||||
-d '{
|
||||
"host":"api.cv.alves.world",
|
||||
"https": true,
|
||||
"certificateType": "letsencrypt",
|
||||
"applicationId": "<backend-app-id>",
|
||||
"domainType": "application",
|
||||
"path": "/",
|
||||
"stripPath": false
|
||||
}'
|
||||
```
|
||||
|
||||
Deploy or redeploy:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST "$DOKPLOY_API_BASE/application.deploy" \
|
||||
-H "Content-Type: application/json" -H "x-api-key: $DOKPLOY_API" \
|
||||
-d '{"applicationId":"<backend-app-id>"}'
|
||||
```
|
||||
|
||||
## 4. Webapp application (cv.alves.world)
|
||||
|
||||
```bash
|
||||
curl -sS -X POST "$DOKPLOY_API_BASE/application.create" -H "Content-Type: application/json" -H "x-api-key: $DOKPLOY_API" -d @- <<'JSON'
|
||||
{
|
||||
"name": "resume-webapp",
|
||||
"environmentId": "ENVIRONMENT_ID",
|
||||
"projectId": "PROJECT_ID",
|
||||
"deployment": {
|
||||
"type": "docker",
|
||||
"image": "ghcr.io/<org>/resume-branches-webapp:latest",
|
||||
"env": {
|
||||
"NEXT_PUBLIC_API_BASE_URL": "https://api.cv.alves.world"
|
||||
},
|
||||
"healthcheck": {
|
||||
"path": "/health",
|
||||
"interval": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
```
|
||||
|
||||
Attach `cv.alves.world` to the webapp (same `domain.validateDomain` + `domain.create`).
|
||||
|
||||
## 5. Post-deploy verification
|
||||
|
||||
```bash
|
||||
curl https://api.cv.alves.world/health
|
||||
curl https://api.cv.alves.world/api/v1/documents -H "x-api-key: <if auth enforced>"
|
||||
curl https://cv.alves.world/dashboard
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep the Dokploy panel at `dev.alves.world`. Never assign production apps to that hostname.
|
||||
- If Postgres/Redis/MinIO run inside Dokploy, create additional compose services or dedicated applications and inject their service hostnames via env vars.
|
||||
- Once DNS is live, re-run `domain.validateDomain` until Dokploy issues certificates.
|
||||
Reference in New Issue
Block a user