mirror of
https://github.com/velocitatem/cvfs.git
synced 2026-05-31 08:43:37 +00:00
Merge pull request #7 from velocitatem/claude/public-ready-setup-n0Sbb
Prepare repository for public deployment
This commit is contained in:
100
.env.example
100
.env.example
@@ -1,66 +1,58 @@
|
|||||||
NAME=myproject
|
# Resume Branches — environment configuration
|
||||||
COMPOSE_PROJECT_NAME=$NAME
|
# Copy this file to .env and fill in values before running docker compose.
|
||||||
|
# For standalone (no Traefik): docker compose -f docker-compose.standalone.yml up -d
|
||||||
|
# For Traefik-based production: docker compose up -d (edit Traefik labels in docker-compose.yml)
|
||||||
|
|
||||||
# Backend
|
# ── General ───────────────────────────────────────────────────────────────────
|
||||||
BACKEND_MODE=fastapi
|
NAME=cvfs
|
||||||
BACKEND_PORT=9812
|
COMPOSE_PROJECT_NAME=cvfs
|
||||||
|
|
||||||
|
# ── Public URLs ───────────────────────────────────────────────────────────────
|
||||||
|
# The URL users visit to access the app (no trailing slash).
|
||||||
|
# Standalone local: http://localhost:3000
|
||||||
|
# Production with a domain: https://cv.example.com
|
||||||
|
PUBLIC_BASE_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# Domain used to construct published CV links (hostname only, no scheme).
|
||||||
|
CV_PUBLIC_DOMAIN=localhost
|
||||||
|
|
||||||
|
# ── Backend ───────────────────────────────────────────────────────────────────
|
||||||
|
BACKEND_PORT=8080
|
||||||
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/resume_branches
|
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/resume_branches
|
||||||
|
# Comma-separated list of allowed CORS origins
|
||||||
CORS_ORIGINS=http://localhost:3000
|
CORS_ORIGINS=http://localhost:3000
|
||||||
|
|
||||||
# Ports
|
# ── PostgreSQL ────────────────────────────────────────────────────────────────
|
||||||
REDIS_PORT=6378
|
|
||||||
GRAFANA_PORT=3125
|
|
||||||
LOKI_PORT=3142
|
|
||||||
|
|
||||||
# PostgreSQL
|
|
||||||
POSTGRES_PORT=5432
|
|
||||||
POSTGRES_DB=app
|
|
||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_PASSWORD=postgres
|
POSTGRES_PASSWORD=postgres
|
||||||
POSTGRES_HOST=localhost
|
|
||||||
|
|
||||||
# MongoDB
|
# ── Redis ─────────────────────────────────────────────────────────────────────
|
||||||
MONGO_PORT=27017
|
REDIS_URL=redis://localhost:6379/0
|
||||||
MONGO_DB=app
|
|
||||||
MONGO_USER=admin
|
|
||||||
MONGO_PASSWORD=admin123
|
|
||||||
MONGO_HOST=localhost
|
|
||||||
|
|
||||||
DATABASE_TYPE=postgres
|
# ── MinIO object storage ──────────────────────────────────────────────────────
|
||||||
|
# Internal URL used by backend/worker (keep as-is for Docker deployments).
|
||||||
# Redis
|
MINIO_ENDPOINT=http://localhost:9000
|
||||||
REDIS_URL=redis://localhost:$REDIS_PORT
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
LOGDIR="/tmp/logs-$NAME/"
|
|
||||||
|
|
||||||
# Supabase (webapp auth - set NEXT_PUBLIC_REQUIRE_AUTH=true to enable gating)
|
|
||||||
NEXT_PUBLIC_REQUIRE_AUTH=false
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
|
||||||
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_supabase_anon_key_here
|
|
||||||
# Server-side proxy target (read by next.config.ts at runtime, not baked into the bundle)
|
|
||||||
API_BASE_URL=http://localhost:9812
|
|
||||||
|
|
||||||
# MinIO Object Storage (used instead of S3)
|
|
||||||
MINIO_ROOT_USER=minioadmin
|
|
||||||
MINIO_ROOT_PASSWORD=minioadmin
|
|
||||||
MINIO_ENDPOINT=http://localhost:9900
|
|
||||||
MINIO_BUCKET=resume-branches
|
MINIO_BUCKET=resume-branches
|
||||||
MINIO_REGION=us-east-1
|
MINIO_REGION=us-east-1
|
||||||
|
MINIO_ROOT_USER=minioadmin
|
||||||
|
MINIO_ROOT_PASSWORD=minioadmin
|
||||||
|
# MinIO admin console port (standalone mode only)
|
||||||
|
MINIO_CONSOLE_PORT=9001
|
||||||
|
|
||||||
# ML
|
# ── Frontend port (standalone mode only) ─────────────────────────────────────
|
||||||
ML_LATEST_WEIGHTS_PATH=/app/models/weights
|
WEBAPP_PORT=3000
|
||||||
MLFLOW_TRACKING_URI=http://localhost:5000
|
|
||||||
|
|
||||||
# AI / Agents
|
# ── Auth — OIDC (optional) ────────────────────────────────────────────────────
|
||||||
ANTHROPIC_API_KEY=sk-ant-...
|
# Set AUTH_DISABLE_VERIFICATION=false and configure OIDC to require authentication.
|
||||||
# Auth / Publishing
|
# Any OIDC-compatible provider works (Authentik, Keycloak, Auth0, Zitadel, etc.).
|
||||||
PUBLIC_BASE_URL=https://cv.alves.world
|
|
||||||
CV_PUBLIC_DOMAIN=cv.alves.world
|
|
||||||
AUTH_DISABLE_VERIFICATION=true
|
AUTH_DISABLE_VERIFICATION=true
|
||||||
# AUTH_OIDC_ISSUER=
|
AUTH_OIDC_ISSUER=
|
||||||
# AUTH_OIDC_AUDIENCE=
|
AUTH_OIDC_AUDIENCE=
|
||||||
# Optional: use Bedrock instead of direct Anthropic API
|
|
||||||
# CLAUDE_CODE_USE_BEDROCK=1
|
# Frontend OIDC config (baked into the Next.js build — requires rebuild on change)
|
||||||
# Optional: use Vertex AI
|
NEXT_PUBLIC_AUTHENTIK_ISSUER=
|
||||||
# CLAUDE_CODE_USE_VERTEX=1
|
NEXT_PUBLIC_AUTHENTIK_CLIENT_ID=
|
||||||
|
AUTHENTIK_CLIENT_SECRET=
|
||||||
|
|
||||||
|
# ── AI tailoring (optional) ───────────────────────────────────────────────────
|
||||||
|
# Leave blank to use the built-in rule-based tailoring instead of Claude.
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from sqlalchemy.orm import selectinload
|
|||||||
from dlib.cv import parse_docx_bytes
|
from dlib.cv import parse_docx_bytes
|
||||||
|
|
||||||
from app.models import CvDocument, CvVersion, PublicAsset
|
from app.models import CvDocument, CvVersion, PublicAsset
|
||||||
from app.services.storage import persist_upload
|
from app.services.storage import persist_upload, storage_client
|
||||||
|
|
||||||
|
|
||||||
async def create_document(
|
async def create_document(
|
||||||
@@ -93,11 +93,14 @@ async def delete_document(
|
|||||||
doc = await get_document(session, owner_id, document_id)
|
doc = await get_document(session, owner_id, document_id)
|
||||||
if not doc:
|
if not doc:
|
||||||
return False
|
return False
|
||||||
version_ids = [version.id for version in doc.versions]
|
artifact_keys = {v.artifact_docx_key for v in doc.versions if v.artifact_docx_key}
|
||||||
|
version_ids = [v.id for v in doc.versions]
|
||||||
if version_ids:
|
if version_ids:
|
||||||
await session.execute(
|
await session.execute(
|
||||||
delete(PublicAsset).where(PublicAsset.version_id.in_(version_ids))
|
delete(PublicAsset).where(PublicAsset.version_id.in_(version_ids))
|
||||||
)
|
)
|
||||||
await session.delete(doc)
|
await session.delete(doc)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
for key in artifact_keys:
|
||||||
|
storage_client.delete_object(key=key)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -5,32 +5,25 @@ export async function GET(
|
|||||||
{ params }: { params: Promise<{ slug: string }> }
|
{ params }: { params: Promise<{ slug: string }> }
|
||||||
) {
|
) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
const backend = process.env.API_BASE_URL ?? 'http://localhost:9812';
|
||||||
const backend = process.env.API_BASE_URL ?? "http://localhost:9812";
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${backend}/api/v1/public/${slug}`, {
|
const res = await fetch(`${backend}/api/v1/public/${encodeURIComponent(slug)}/pdf`, {
|
||||||
cache: 'no-store'
|
cache: 'no-store',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (res.status === 404) return new NextResponse('CV not found', { status: 404 });
|
||||||
return new NextResponse('CV not found', { status: 404 });
|
if (!res.ok) return new NextResponse('Failed to fetch CV', { status: res.status });
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
const pdf = await res.arrayBuffer();
|
||||||
if (!data.asset || !data.asset.artifact_key) {
|
return new NextResponse(pdf, {
|
||||||
return new NextResponse('CV not found', { status: 404 });
|
status: 200,
|
||||||
}
|
headers: {
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
// Construct MinIO public URL
|
'Content-Disposition': `inline; filename="${slug}.pdf"`,
|
||||||
const storageHost = process.env.MINIO_ENDPOINT || (process.env.NODE_ENV === 'production'
|
'Cache-Control': 'public, max-age=300',
|
||||||
? 'https://storage.cv.alves.world'
|
},
|
||||||
: 'http://localhost:9900');
|
});
|
||||||
|
|
||||||
const bucket = process.env.MINIO_BUCKET || 'resume-branches';
|
|
||||||
const downloadUrl = `${storageHost}/${bucket}/${data.asset.artifact_key}`;
|
|
||||||
|
|
||||||
return NextResponse.redirect(downloadUrl);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching public CV:', error);
|
console.error('Error fetching public CV:', error);
|
||||||
return new NextResponse('Internal Server Error', { status: 500 });
|
return new NextResponse('Internal Server Error', { status: 500 });
|
||||||
|
|||||||
@@ -1,79 +1,21 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from io import BytesIO
|
import os
|
||||||
|
import subprocess
|
||||||
from docx import Document
|
import tempfile
|
||||||
from reportlab.lib import colors
|
|
||||||
from reportlab.lib.enums import TA_CENTER, TA_LEFT
|
|
||||||
from reportlab.lib.pagesizes import A4
|
|
||||||
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
|
|
||||||
from reportlab.lib.units import cm
|
|
||||||
from reportlab.platypus import HRFlowable, Paragraph, SimpleDocTemplate, Spacer
|
|
||||||
|
|
||||||
from .parser import _detect_block_type
|
|
||||||
|
|
||||||
_STYLES = getSampleStyleSheet()
|
|
||||||
|
|
||||||
_STYLE_MAP: dict[str, ParagraphStyle] = {
|
|
||||||
"heading": ParagraphStyle(
|
|
||||||
"CVHeading", parent=_STYLES["Normal"],
|
|
||||||
fontSize=15, leading=20, spaceBefore=10, spaceAfter=4,
|
|
||||||
textColor=colors.HexColor("#111111"), fontName="Helvetica-Bold",
|
|
||||||
),
|
|
||||||
"meta": ParagraphStyle(
|
|
||||||
"CVMeta", parent=_STYLES["Normal"],
|
|
||||||
fontSize=9, leading=13, spaceAfter=2, textColor=colors.HexColor("#555555"),
|
|
||||||
),
|
|
||||||
"summary": ParagraphStyle(
|
|
||||||
"CVSummary", parent=_STYLES["Normal"],
|
|
||||||
fontSize=10, leading=14, spaceAfter=6, textColor=colors.HexColor("#333333"),
|
|
||||||
),
|
|
||||||
"bullet": ParagraphStyle(
|
|
||||||
"CVBullet", parent=_STYLES["Normal"],
|
|
||||||
fontSize=10, leading=14, spaceAfter=3, leftIndent=14,
|
|
||||||
bulletIndent=0, textColor=colors.HexColor("#222222"),
|
|
||||||
),
|
|
||||||
"skills": ParagraphStyle(
|
|
||||||
"CVSkills", parent=_STYLES["Normal"],
|
|
||||||
fontSize=10, leading=14, spaceAfter=3, textColor=colors.HexColor("#222222"),
|
|
||||||
),
|
|
||||||
"text": ParagraphStyle(
|
|
||||||
"CVText", parent=_STYLES["Normal"],
|
|
||||||
fontSize=10, leading=14, spaceAfter=4, textColor=colors.HexColor("#222222"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def docx_bytes_to_pdf(docx_bytes: bytes) -> bytes:
|
def docx_bytes_to_pdf(docx_bytes: bytes) -> bytes:
|
||||||
doc = Document(BytesIO(docx_bytes))
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
buf = BytesIO()
|
docx_path = os.path.join(tmpdir, "cv.docx")
|
||||||
pdf = SimpleDocTemplate(
|
pdf_path = os.path.join(tmpdir, "cv.pdf")
|
||||||
buf, pagesize=A4,
|
with open(docx_path, "wb") as f:
|
||||||
leftMargin=2.2 * cm, rightMargin=2.2 * cm,
|
f.write(docx_bytes)
|
||||||
topMargin=2 * cm, bottomMargin=2 * cm,
|
subprocess.run(
|
||||||
|
["libreoffice", "--headless", "--convert-to", "pdf", "--outdir", tmpdir, docx_path],
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
timeout=60,
|
||||||
)
|
)
|
||||||
story: list = []
|
with open(pdf_path, "rb") as f:
|
||||||
prev_type: str | None = None
|
return f.read()
|
||||||
|
|
||||||
for para in doc.paragraphs:
|
|
||||||
text = para.text.strip()
|
|
||||||
if not text:
|
|
||||||
if prev_type and prev_type != "empty":
|
|
||||||
story.append(Spacer(1, 4))
|
|
||||||
prev_type = "empty"
|
|
||||||
continue
|
|
||||||
|
|
||||||
block_type = _detect_block_type(getattr(para.style, "name", None), para)
|
|
||||||
style = _STYLE_MAP.get(block_type, _STYLE_MAP["text"])
|
|
||||||
|
|
||||||
# draw separator line before new heading sections (except first)
|
|
||||||
if block_type == "heading" and prev_type not in (None, "heading"):
|
|
||||||
story.append(Spacer(1, 6))
|
|
||||||
story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc")))
|
|
||||||
|
|
||||||
prefix = "\u2022\u00a0" if block_type == "bullet" else ""
|
|
||||||
story.append(Paragraph(f"{prefix}{text}", style))
|
|
||||||
prev_type = block_type
|
|
||||||
|
|
||||||
pdf.build(story)
|
|
||||||
return buf.getvalue()
|
|
||||||
|
|||||||
148
docker-compose.standalone.yml
Normal file
148
docker-compose.standalone.yml
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
# Standalone deployment — no Traefik/reverse-proxy required.
|
||||||
|
# Usage: docker compose -f docker-compose.standalone.yml up -d
|
||||||
|
# Configure via a .env file (copy .env.example and fill in values).
|
||||||
|
|
||||||
|
networks:
|
||||||
|
cvfs-network:
|
||||||
|
|
||||||
|
services:
|
||||||
|
webapp:
|
||||||
|
container_name: "cvfs-webapp"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./docker/webapp.Dockerfile
|
||||||
|
args:
|
||||||
|
NEXT_PUBLIC_AUTHENTIK_ISSUER: ${NEXT_PUBLIC_AUTHENTIK_ISSUER:-}
|
||||||
|
NEXT_PUBLIC_AUTHENTIK_CLIENT_ID: ${NEXT_PUBLIC_AUTHENTIK_CLIENT_ID:-}
|
||||||
|
NEXT_PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost:3000}
|
||||||
|
API_BASE_URL: http://cvfs-backend:8080
|
||||||
|
environment:
|
||||||
|
- API_BASE_URL=http://cvfs-backend:8080
|
||||||
|
- AUTHENTIK_ISSUER=${AUTHENTIK_ISSUER:-}
|
||||||
|
- AUTHENTIK_CLIENT_ID=${AUTHENTIK_CLIENT_ID:-}
|
||||||
|
- AUTHENTIK_CLIENT_SECRET=${AUTHENTIK_CLIENT_SECRET:-}
|
||||||
|
- NEXT_PUBLIC_AUTHENTIK_ISSUER=${NEXT_PUBLIC_AUTHENTIK_ISSUER:-}
|
||||||
|
- NEXT_PUBLIC_AUTHENTIK_CLIENT_ID=${NEXT_PUBLIC_AUTHENTIK_CLIENT_ID:-}
|
||||||
|
- NEXT_PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-http://localhost:3000}
|
||||||
|
ports:
|
||||||
|
- "${WEBAPP_PORT:-3000}:3000"
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
backend:
|
||||||
|
container_name: "cvfs-backend"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./docker/backend-fastapi.Dockerfile
|
||||||
|
environment:
|
||||||
|
- BACKEND_PORT=8080
|
||||||
|
- DATABASE_URL=postgresql+asyncpg://postgres:${POSTGRES_PASSWORD:-postgres}@cvfs-postgres:5432/resume_branches
|
||||||
|
- MINIO_ENDPOINT=http://cvfs-minio:9000
|
||||||
|
- MINIO_BUCKET=${MINIO_BUCKET:-resume-branches}
|
||||||
|
- MINIO_REGION=${MINIO_REGION:-us-east-1}
|
||||||
|
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
||||||
|
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
|
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-http://localhost:3000}
|
||||||
|
- CV_PUBLIC_DOMAIN=${CV_PUBLIC_DOMAIN:-localhost}
|
||||||
|
- CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:3000}
|
||||||
|
- REDIS_URL=redis://cvfs-redis:6379/0
|
||||||
|
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
||||||
|
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- AUTH_OIDC_ISSUER=${AUTH_OIDC_ISSUER:-}
|
||||||
|
- AUTH_OIDC_AUDIENCE=${AUTH_OIDC_AUDIENCE:-}
|
||||||
|
- AUTH_DISABLE_VERIFICATION=${AUTH_DISABLE_VERIFICATION:-true}
|
||||||
|
ports:
|
||||||
|
- "${BACKEND_PORT:-8080}:8080"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- minio
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
worker:
|
||||||
|
container_name: "cvfs-worker"
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./docker/worker.Dockerfile
|
||||||
|
environment:
|
||||||
|
- REDIS_URL=redis://cvfs-redis:6379/0
|
||||||
|
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
||||||
|
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
||||||
|
- MINIO_ENDPOINT=http://cvfs-minio:9000
|
||||||
|
- MINIO_BUCKET=${MINIO_BUCKET:-resume-branches}
|
||||||
|
- MINIO_REGION=${MINIO_REGION:-us-east-1}
|
||||||
|
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
||||||
|
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
|
- PYTHONPATH=/app
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- minio
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
redis:
|
||||||
|
container_name: "cvfs-redis"
|
||||||
|
image: redis:7-alpine
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: "cvfs-postgres"
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: resume_branches
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
minio:
|
||||||
|
image: minio/minio:latest
|
||||||
|
container_name: "cvfs-minio"
|
||||||
|
environment:
|
||||||
|
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
||||||
|
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
ports:
|
||||||
|
- "${MINIO_CONSOLE_PORT:-9001}:9001"
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
create-bucket:
|
||||||
|
image: minio/mc
|
||||||
|
container_name: "cvfs-create-bucket"
|
||||||
|
depends_on:
|
||||||
|
- minio
|
||||||
|
networks:
|
||||||
|
- cvfs-network
|
||||||
|
entrypoint: >
|
||||||
|
/bin/sh -c "
|
||||||
|
sleep 5;
|
||||||
|
mc alias set myminio http://cvfs-minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
|
||||||
|
mc mb myminio/$${MINIO_BUCKET:-resume-branches} --ignore-existing;
|
||||||
|
exit 0;
|
||||||
|
"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
postgres_data:
|
||||||
|
minio_data:
|
||||||
@@ -43,19 +43,20 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- BACKEND_PORT=8080
|
- BACKEND_PORT=8080
|
||||||
- DATABASE_URL=postgresql+asyncpg://postgres:postgres@cvfs-postgres:5432/resume_branches
|
- DATABASE_URL=postgresql+asyncpg://postgres:postgres@cvfs-postgres:5432/resume_branches
|
||||||
- MINIO_ENDPOINT=https://storage.cv.alves.world
|
- MINIO_ENDPOINT=http://cvfs-minio:9000
|
||||||
- MINIO_BUCKET=resume-branches
|
- MINIO_BUCKET=${MINIO_BUCKET:-resume-branches}
|
||||||
- MINIO_REGION=us-east-1
|
- MINIO_REGION=${MINIO_REGION:-us-east-1}
|
||||||
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
||||||
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
- PUBLIC_BASE_URL=https://cv.alves.world
|
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-https://cv.alves.world}
|
||||||
- CV_PUBLIC_DOMAIN=cv.alves.world
|
- CV_PUBLIC_DOMAIN=${CV_PUBLIC_DOMAIN:-cv.alves.world}
|
||||||
|
- CORS_ORIGINS=${CORS_ORIGINS:-https://cv.alves.world}
|
||||||
- REDIS_URL=redis://cvfs-redis:6379/0
|
- REDIS_URL=redis://cvfs-redis:6379/0
|
||||||
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
||||||
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
- AUTH_OIDC_ISSUER=${AUTH_OIDC_ISSUER}
|
- AUTH_OIDC_ISSUER=${AUTH_OIDC_ISSUER:-}
|
||||||
- AUTH_OIDC_AUDIENCE=${AUTH_OIDC_AUDIENCE}
|
- AUTH_OIDC_AUDIENCE=${AUTH_OIDC_AUDIENCE:-}
|
||||||
- AUTH_DISABLE_VERIFICATION=${AUTH_DISABLE_VERIFICATION:-false}
|
- AUTH_DISABLE_VERIFICATION=${AUTH_DISABLE_VERIFICATION:-false}
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
@@ -81,9 +82,9 @@ services:
|
|||||||
- REDIS_URL=redis://cvfs-redis:6379/0
|
- REDIS_URL=redis://cvfs-redis:6379/0
|
||||||
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
- CELERY_BROKER_URL=redis://cvfs-redis:6379/0
|
||||||
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
- CELERY_RESULT_BACKEND=redis://cvfs-redis:6379/0
|
||||||
- MINIO_ENDPOINT=https://storage.cv.alves.world
|
- MINIO_ENDPOINT=http://cvfs-minio:9000
|
||||||
- MINIO_BUCKET=resume-branches
|
- MINIO_BUCKET=${MINIO_BUCKET:-resume-branches}
|
||||||
- MINIO_REGION=us-east-1
|
- MINIO_REGION=${MINIO_REGION:-us-east-1}
|
||||||
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
|
||||||
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
- PYTHONPATH=/app
|
- PYTHONPATH=/app
|
||||||
@@ -128,7 +129,6 @@ services:
|
|||||||
command: server /data --console-address ":9001"
|
command: server /data --console-address ":9001"
|
||||||
networks:
|
networks:
|
||||||
- cvfs-network
|
- cvfs-network
|
||||||
- dokploy-network
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
create-bucket:
|
create-bucket:
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ WORKDIR /app
|
|||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
|
libreoffice-writer \
|
||||||
|
fonts-liberation \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY pyproject.toml uv.lock requirements.txt ./
|
COPY pyproject.toml uv.lock requirements.txt ./
|
||||||
|
|||||||
Reference in New Issue
Block a user