update
This commit is contained in:
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
**/node_modules
|
||||
**/__pycache__
|
||||
**/*.pyc
|
||||
frontend/dist
|
||||
storage
|
||||
guides.db
|
||||
.git
|
||||
.claude-data
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ node_modules/
|
||||
frontend/dist/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.claude-data/
|
||||
|
||||
43
Dockerfile
Normal file
43
Dockerfile
Normal file
@@ -0,0 +1,43 @@
|
||||
# Stage 1: Frontend bauen
|
||||
FROM node:20-alpine AS frontend
|
||||
WORKDIR /build
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm install
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM python:3.12-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
poppler-utils \
|
||||
libpango-1.0-0 \
|
||||
libpangoft2-1.0-0 \
|
||||
libharfbuzz0b \
|
||||
libcairo2 \
|
||||
libgdk-pixbuf-2.0-0 \
|
||||
libffi-dev \
|
||||
fonts-dejavu \
|
||||
curl \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g @anthropic-ai/claude-code \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN useradd -m -u 1000 app
|
||||
|
||||
COPY backend/requirements.txt /app/backend/requirements.txt
|
||||
RUN pip install --no-cache-dir -r /app/backend/requirements.txt
|
||||
|
||||
COPY --chown=app:app backend/ /app/backend/
|
||||
COPY --chown=app:app templates/ /app/templates/
|
||||
COPY --chown=app:app --from=frontend /build/dist /app/frontend/dist
|
||||
|
||||
RUN chown app:app /app
|
||||
|
||||
USER app
|
||||
WORKDIR /app/backend
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
23
Makefile
23
Makefile
@@ -1,4 +1,12 @@
|
||||
.PHONY: install dev prod stop remove
|
||||
.PHONY: install dev prod stop logs remove auth
|
||||
|
||||
COMPOSE = docker compose
|
||||
|
||||
auth:
|
||||
@mkdir -p .claude-data storage
|
||||
@cp -aT /root/.claude .claude-data
|
||||
@chown -R 1000:1000 .claude-data storage
|
||||
@echo "Claude-Auth nach .claude-data synct, storage chowned auf uid 1000."
|
||||
|
||||
install:
|
||||
sudo apt install -y poppler-utils libpango-1.0-0 libcairo2 libgdk-pixbuf-2.0-0 libffi-dev
|
||||
@@ -11,18 +19,19 @@ dev:
|
||||
@cd backend && uvicorn main:app --reload --port 8000 &
|
||||
@cd frontend && npx vite --port 5173
|
||||
|
||||
prod:
|
||||
@echo "Backend: http://localhost:8000"
|
||||
@cd frontend && npx vite build
|
||||
@cd backend && uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
prod: auth
|
||||
$(COMPOSE) up -d --build
|
||||
|
||||
stop:
|
||||
-@pkill -f "uvicorn main:app" 2>/dev/null
|
||||
-@pkill -f "vite --port 5173" 2>/dev/null
|
||||
$(COMPOSE) down
|
||||
@echo "Server gestoppt."
|
||||
|
||||
logs:
|
||||
$(COMPOSE) logs -f
|
||||
|
||||
remove: stop
|
||||
@echo "Lösche Datenbank und generierte Dateien..."
|
||||
rm -f guides.db
|
||||
rm -rf storage/html/* storage/pdf/*
|
||||
rm -rf storage/*
|
||||
@echo "Fertig."
|
||||
|
||||
@@ -3,7 +3,8 @@ from pathlib import Path
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
TEMPLATES_DIR = PROJECT_ROOT / "templates"
|
||||
STORAGE_DIR = PROJECT_ROOT / "storage"
|
||||
DB_PATH = PROJECT_ROOT / "guides.db"
|
||||
FRONTEND_DIST = PROJECT_ROOT / "frontend" / "dist"
|
||||
DB_PATH = STORAGE_DIR / "guides.db"
|
||||
|
||||
ALLOWED_FORMATS = [
|
||||
"OnePager",
|
||||
@@ -24,15 +25,15 @@ FORMAT_META = {
|
||||
}
|
||||
|
||||
GENERATION_TIMEOUTS = {
|
||||
"OnePager": 600,
|
||||
"Cheatsheet": 600,
|
||||
"MiniGuide": 600,
|
||||
"BeginnerGuide": 900,
|
||||
"IntermediateGuide": 1200,
|
||||
"ExtendedGuide": 1500,
|
||||
"OnePager": 900,
|
||||
"Cheatsheet": 900,
|
||||
"MiniGuide": 1200,
|
||||
"BeginnerGuide": 1800,
|
||||
"IntermediateGuide": 2400,
|
||||
"ExtendedGuide": 3000,
|
||||
}
|
||||
|
||||
MAX_CONCURRENT_GENERATIONS = 10
|
||||
MAX_CONCURRENT_GENERATIONS = 6
|
||||
MAX_ITERATIONS = {
|
||||
"OnePager": 3,
|
||||
"Cheatsheet": 3,
|
||||
|
||||
@@ -45,11 +45,19 @@ async def _run_claude(guide_id: str, prompt: str, timeout: int) -> tuple[int, st
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
_active_processes[guide_id] = process
|
||||
try:
|
||||
try:
|
||||
stdout, stderr = await asyncio.wait_for(
|
||||
process.communicate(input=prompt.encode("utf-8")),
|
||||
timeout=timeout,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
process.kill()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=5)
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
raise
|
||||
return process.returncode, stdout.decode("utf-8", errors="replace"), stderr.decode("utf-8", errors="replace")
|
||||
finally:
|
||||
_active_processes.pop(guide_id, None)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from config import STORAGE_DIR
|
||||
from config import FRONTEND_DIST, STORAGE_DIR
|
||||
from database import init_db, close_db
|
||||
from routes import router
|
||||
|
||||
@@ -20,11 +20,7 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
app = FastAPI(title="Guides Generator", lifespan=lifespan)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.include_router(router)
|
||||
|
||||
if FRONTEND_DIST.exists():
|
||||
app.mount("/", StaticFiles(directory=FRONTEND_DIST, html=True), name="frontend")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
fastapi
|
||||
uvicorn[standard]
|
||||
aiosqlite
|
||||
weasyprint
|
||||
pdf2image
|
||||
|
||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
guides:
|
||||
build:
|
||||
context: .
|
||||
container_name: guides
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- web
|
||||
volumes:
|
||||
- ./storage:/app/storage
|
||||
- ./.claude-data:/home/app/.claude
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.guidesapp.rule=Host(`guides.marha.de`)"
|
||||
- "traefik.http.routers.guidesapp.entrypoints=websecure"
|
||||
- "traefik.http.routers.guidesapp.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.guidesapp.loadbalancer.server.port=8000"
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
Reference in New Issue
Block a user