first commit
This commit is contained in:
commit
fd6815f11a
|
|
@ -0,0 +1,6 @@
|
|||
bin/
|
||||
include/
|
||||
lib/
|
||||
lib64/
|
||||
share/
|
||||
pyvenv.cfg
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
FROM python:3.10-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY ./app /app/app
|
||||
|
||||
EXPOSE 7860
|
||||
EXPOSE 5555
|
||||
|
||||
# 컨테이너 실행시 아무 것도 안 띄움 (docker-compose에서 실행)
|
||||
CMD ["bash"]
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# FastAPI + Celery + Redis + Flower 메인서버 예시
|
||||
|
||||
## 실행 방법
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
FastAPI: http://localhost:7860
|
||||
|
||||
Flower: http://localhost:5555
|
||||
|
||||
API 예시
|
||||
POST /process (파일 업로드)
|
||||
|
||||
GET /result/{task_id} (결과 확인)
|
||||
|
||||
|
||||
---
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
# app/beat_schedule.py
|
||||
from celery.schedules import crontab
|
||||
|
||||
beat_schedule = {
|
||||
"delete-old-files-every-10-mins": {
|
||||
"task": "app.tasks.delete_old_files",
|
||||
"schedule": crontab(minute="*/10"),
|
||||
"args": ("/app/images", 1800), # 30분 이상된 파일 삭제
|
||||
}
|
||||
}
|
||||
# celery_app.conf.beat_schedule = beat_schedule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from celery import Celery
|
||||
import os
|
||||
|
||||
# Redis URL 설정
|
||||
redis_url = os.getenv("REDIS_URL", "redis://redis:6379/0")
|
||||
|
||||
# Celery 앱 생성
|
||||
celery_app = Celery(
|
||||
"worker",
|
||||
broker=redis_url,
|
||||
backend=redis_url,
|
||||
include=['app.tasks'] # 태스크 모듈 포함
|
||||
)
|
||||
|
||||
# Celery 설정
|
||||
celery_app.conf.update(
|
||||
task_serializer='json',
|
||||
accept_content=['json'],
|
||||
result_serializer='json',
|
||||
timezone='Asia/Seoul',
|
||||
enable_utc=True,
|
||||
)
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
from fastapi import FastAPI, Request, HTTPException, Response, UploadFile, File, Form
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Dict, Optional, List
|
||||
from app.celery_worker import celery_app
|
||||
from app.supabase_auth import check_user_permission
|
||||
from celery.result import AsyncResult
|
||||
import time
|
||||
import os, shutil
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
# 로거 설정
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# ==== 입력 모델 ====
|
||||
class TranslateRequest(BaseModel):
|
||||
toggle_states: Dict
|
||||
unwanted_texts: Dict
|
||||
image_data: str
|
||||
user_id: str
|
||||
ocr_method: Optional[str] = "paddleocr"
|
||||
inpaint_method: Optional[str] = "lama"
|
||||
|
||||
class InpaintRequest(BaseModel):
|
||||
mask_image_data: str
|
||||
image_data: str
|
||||
user_id: str
|
||||
inpaint_method: Optional[str] = "lama"
|
||||
|
||||
class OCRRequest(BaseModel):
|
||||
image_data: str
|
||||
user_id: str
|
||||
ocr_method: Optional[str] = "paddleocr"
|
||||
|
||||
# ==== 리턴 모델 ====
|
||||
class OCRBox(BaseModel):
|
||||
text: str
|
||||
box: List[int] # [x1, y1, x2, y2, ...]
|
||||
|
||||
class TranslateResponse(BaseModel):
|
||||
ocr_texts: List[str]
|
||||
ocr_boxes: List[OCRBox]
|
||||
translated_texts: List[str]
|
||||
inpainted_image: str # base64
|
||||
|
||||
class InpaintResponse(BaseModel):
|
||||
inpainted_image: str # base64
|
||||
|
||||
class OCRResponse(BaseModel):
|
||||
ocr_texts: List[str]
|
||||
ocr_boxes: List[OCRBox]
|
||||
|
||||
# ==== 사용자 인증 ====
|
||||
async def validate_user(user_id):
|
||||
allowed = await check_user_permission(user_id)
|
||||
if not allowed:
|
||||
raise HTTPException(status_code=403, detail="권한이 없습니다.")
|
||||
|
||||
# ==== 셀러리 태스크 ====
|
||||
def start_celery_task(task_name, **kwargs):
|
||||
return celery_app.send_task(task_name, kwargs=kwargs)
|
||||
|
||||
# ==== 엔드포인트 ====
|
||||
@app.post("/translate_me")
|
||||
async def translate_me(req: TranslateRequest):
|
||||
await validate_user(req.user_id)
|
||||
filename = f"{uuid.uuid4().hex}_{int(time.time())}.png"
|
||||
# (여기서 파일 경로 생성 및 저장)
|
||||
task = start_celery_task("app.tasks.translate_task", **req.dict(), filename=filename)
|
||||
logger.info(f"태스크 등록: {task.id}, 파일명: {filename}")
|
||||
return {"task_id": task.id, "filename": filename}
|
||||
|
||||
@app.post("/inpaint_me")
|
||||
async def inpaint_me(req: InpaintRequest):
|
||||
await validate_user(req.user_id)
|
||||
task = start_celery_task("app.tasks.inpaint_task", **req.dict())
|
||||
return {"task_id": task.id}
|
||||
|
||||
@app.post("/ocr_me")
|
||||
async def ocr_me(req: OCRRequest):
|
||||
await validate_user(req.user_id)
|
||||
task = start_celery_task("app.tasks.ocr_task", **req.dict())
|
||||
return {"task_id": task.id}
|
||||
|
||||
@app.post("/upload_image")
|
||||
async def upload_image(user_id: str = Form(...), file: UploadFile = File(...)):
|
||||
original_dir = f"images/{user_id}/original/"
|
||||
os.makedirs(original_dir, exist_ok=True)
|
||||
filename = file.filename
|
||||
file_path = os.path.join(original_dir, filename)
|
||||
with open(file_path, "wb") as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
url = f"/images/{user_id}/original/{filename}"
|
||||
return {"url": url}
|
||||
|
||||
@app.get("/download_image/")
|
||||
def download_image(user_id: str, filename: str):
|
||||
file_path = f"images/{user_id}/translated/{filename}"
|
||||
if not os.path.exists(file_path):
|
||||
return {"error": "파일이 없습니다"}
|
||||
with open(file_path, "rb") as f:
|
||||
data = f.read()
|
||||
os.remove(file_path) # 다운로드 직후 삭제
|
||||
return Response(content=data, media_type="image/png")
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from supabase import create_client
|
||||
import os
|
||||
|
||||
SUPABASE_URL = 'http://146.56.101.199:8000'
|
||||
SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE'
|
||||
|
||||
#SUPABASE_URL = os.getenv("SUPABASE_URL")
|
||||
#SUPABASE_KEY = os.getenv("SUPABASE_KEY")
|
||||
|
||||
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
|
||||
|
||||
async def check_user_permission(user_id: str) -> bool:
|
||||
# 실제 쿼리 구조는 supabase 테이블에 맞게!
|
||||
user = supabase.table("users").select("id, membership_level").eq("id", user_id).execute()
|
||||
print(f"user info : {user}")
|
||||
if user.data and user.data[0]["membership_level"] in ["premiun", "vip"]:
|
||||
return True
|
||||
return False
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from app.celery_worker import celery_app
|
||||
|
||||
@celery_app.task(name="app.tasks.translate_task")
|
||||
def translate_task(**kwargs):
|
||||
# 실제 번역 처리 로직
|
||||
return {
|
||||
"ocr_texts": ["중국어1", "중국어2"],
|
||||
"ocr_boxes": [
|
||||
{"text": "중국어1", "box": [10, 20, 100, 120]},
|
||||
{"text": "중국어2", "box": [110, 120, 200, 220]}
|
||||
],
|
||||
"translated_texts": ["한글1", "한글2"],
|
||||
"inpainted_image": "base64string...."
|
||||
}
|
||||
|
||||
@celery_app.task(name="app.tasks.inpaint_task")
|
||||
def inpaint_task(**kwargs):
|
||||
# 실제 인페인팅 처리 로직
|
||||
return {
|
||||
"inpainted_image": "base64string...."
|
||||
}
|
||||
|
||||
@celery_app.task(name="app.tasks.ocr_task")
|
||||
def ocr_task(**kwargs):
|
||||
# 실제 OCR 처리 로직
|
||||
return {
|
||||
"ocr_texts": ["중국어1", "중국어2"],
|
||||
"ocr_boxes": [
|
||||
{"text": "중국어1", "box": [10, 20, 100, 120]},
|
||||
{"text": "중국어2", "box": [110, 120, 200, 220]}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,45 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
fastapi:
|
||||
build: .
|
||||
container_name: fastapi_app
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 7890
|
||||
volumes:
|
||||
- ./app:/app/app
|
||||
- ./images:/app/images
|
||||
ports:
|
||||
- "7890:7890"
|
||||
depends_on:
|
||||
- redis
|
||||
celery:
|
||||
build: .
|
||||
container_name: celery_worker
|
||||
command: celery -A app.celery_worker worker --loglevel=info --concurrency=2
|
||||
volumes:
|
||||
- ./app:/app/app
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
celery-beat:
|
||||
build: .
|
||||
container_name: celery_beat
|
||||
command: celery -A app.celery_worker beat --loglevel=info
|
||||
volumes:
|
||||
- ./app:/app/app
|
||||
- ./images:/app/images
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
redis:
|
||||
image: redis:6.2
|
||||
container_name: redis_server
|
||||
ports:
|
||||
- "6379:6379"
|
||||
flower:
|
||||
build: .
|
||||
container_name: flower_monitor
|
||||
command: celery -A app.celery_worker flower --port=5555
|
||||
ports:
|
||||
- "5555:5555"
|
||||
depends_on:
|
||||
- redis
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fastapi
|
||||
uvicorn[standard]
|
||||
celery[redis]
|
||||
redis
|
||||
flower
|
||||
python-dotenv
|
||||
pillow
|
||||
supabase
|
||||
python-multipart
|
||||
Loading…
Reference in New Issue