Skip to content

Commit ef9bdac

Browse files
authored
Merge branch 'main' into fix/cuda_gpu
2 parents 57517fa + d70cd87 commit ef9bdac

28 files changed

+1600
-213
lines changed

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
push:
7+
branches: [ main ]
8+
paths:
9+
- '**/*.py'
10+
- 'requirements.txt'
11+
- '.github/workflows/ci.yml'
12+
13+
jobs:
14+
lint-test:
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 10
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version: '3.11'
23+
cache: 'pip'
24+
25+
- name: Install deps
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install -r requirements.txt
29+
30+
- name: Syntax check
31+
run: |
32+
python -m py_compile $(git ls-files '*.py' | tr '\n' ' ')
33+
34+
- name: Import smoke
35+
run: |
36+
python - << 'PY'
37+
from importlib import import_module
38+
import_module('main')
39+
print('Import OK')
40+
PY
41+

.github/workflows/docker.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Docker Build & Push (Backend)
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- '**'
8+
- '!README.md'
9+
10+
jobs:
11+
docker:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
packages: write
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Log in to GHCR
20+
uses: docker/login-action@v3
21+
with:
22+
registry: ghcr.io
23+
username: ${{ github.actor }}
24+
password: ${{ secrets.GITHUB_TOKEN }}
25+
26+
- name: Build and push
27+
uses: docker/build-push-action@v6
28+
with:
29+
context: .
30+
file: ./Dockerfile
31+
push: true
32+
tags: |
33+
ghcr.io/${{ github.repository }}:backend-latest
34+
ghcr.io/${{ github.repository }}:backend-${{ github.sha }}
35+

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ nohup.out
44
__pycache__/
55
venv
66
./venv
7-
uploads/*
7+
uploads/*

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM python:3.11-slim
2+
3+
ENV PYTHONDONTWRITEBYTECODE=1 \
4+
PYTHONUNBUFFERED=1
5+
6+
WORKDIR /app
7+
8+
# System dependencies for pdf2image
9+
RUN apt-get update && apt-get install -y --no-install-recommends \
10+
poppler-utils \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
COPY Backend/requirements.txt ./requirements.txt
14+
RUN pip install --no-cache-dir -r requirements.txt
15+
16+
COPY Backend/ ./
17+
18+
EXPOSE 8080
19+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
20+

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Makefile
22
run:
3-
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
3+
uvicorn main:app --host 0.0.0.0 --port 8080 --reload --http h11

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Noteflow Backend (FastAPI)
2+
3+
## Overview
4+
- FastAPI backend for Noteflow
5+
- OCR pipeline supports images, PDF, DOC/DOCX, HWP (via utilities and system tools)
6+
7+
## Run (local)
8+
```
9+
python -m venv .venv
10+
source .venv/bin/activate
11+
pip install -r requirements.txt
12+
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
13+
```
14+
15+
Env (optional):
16+
- `SECRET_KEY`, `ACCESS_TOKEN_EXPIRE_MINUTES`
17+
- Database URLs if you connect a DB (current code uses provided models)
18+
19+
## OCR system tools (optional but recommended)
20+
- PyMuPDF (Python) used by default for PDF text extraction
21+
- Optional fallbacks/tools:
22+
- Poppler (`pdftoppm`) for `pdf2image`
23+
- LibreOffice (`soffice`) for .doc → .pdf
24+
- `hwp5txt` for .hwp text extraction
25+
- If missing, the API still returns 200 with `warnings` explaining limitations.
26+
27+
## API Highlights
28+
- `POST /api/v1/files/ocr` — OCR and create note (accepts file + optional `folder_id`, `langs`, `max_pages`)
29+
- `POST /api/v1/files/upload` — Upload files to folder
30+
- `POST /api/v1/files/audio` — STT from audio, create/append to note
31+
32+
## CI (GitHub Actions)
33+
- This folder includes `.github/workflows/ci.yml` to lint/smoke-test on push/PR.
34+
- Python 3.11, `pip install -r requirements.txt`, syntax check and import smoke.
35+
36+
## Docker (optional; for later)
37+
- Dockerfile included. Build & run locally:
38+
```
39+
docker build -t noteflow-backend .
40+
docker run --rm -p 8080:8080 noteflow-backend
41+
```
42+
- GitHub Actions container build:
43+
- `.github/workflows/docker.yml` pushes to GHCR:
44+
- `ghcr.io/<owner>/<repo>:backend-latest`
45+
- `ghcr.io/<owner>/<repo>:backend-<sha>`
46+
- Deployment example (SSH) once you’re ready:
47+
```
48+
docker login ghcr.io -u <USER> -p <TOKEN>
49+
docker pull ghcr.io/<owner>/<repo>:backend-latest
50+
docker run -d --name backend --restart=always -p 8080:8080 ghcr.io/<owner>/<repo>:backend-latest
51+
```
52+
53+
## Notes
54+
- If you split this folder into its own repository root, the included `.github/workflows/*.yml` files will work as-is.
55+
- OCR uses model-first path (EasyOCR + TrOCR) and falls back to tesseract when available.

db.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
from sqlalchemy import create_engine
22
from sqlalchemy.orm import sessionmaker
3+
from models import Base # ensure models are imported so metadata knows all tables
4+
import os
35

4-
DATABASE_URL = "mysql+mysqlconnector://noteflow:NoteFlow123!@localhost/noteflow"
5-
engine = create_engine(DATABASE_URL)
6+
DATABASE_URL = os.getenv("DATABASE_URL", "mysql+mysqlconnector://noteflow:NoteFlow123!@localhost/noteflow")
7+
8+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
69
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
710

8-
# 데이터베이스 의존성
11+
def init_db():
12+
Base.metadata.create_all(bind=engine)
13+
914
def get_db():
1015
db = SessionLocal()
1116
try:
1217
yield db
1318
finally:
14-
db.close()
19+
db.close()

main.py

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,54 @@
1-
# src/main.py
1+
# Backend/main.py
22
import os
33
from dotenv import load_dotenv
44
# 환경 변수를 최대한 빨리 로드하여 GPU 설정(CUDA_VISIBLE_DEVICES)이 라우터 임포트 전에 적용되도록 함
55
load_dotenv()
66
from fastapi import FastAPI
77
from fastapi.middleware.cors import CORSMiddleware
8+
from fastapi.staticfiles import StaticFiles
9+
10+
from db import init_db
811
from routers.auth import router as auth_router
912
from routers.note import router as note_router
10-
from routers.folder import router as folder_router
11-
from fastapi.staticfiles import StaticFiles
12-
from routers.file import router as file_router
13-
import logging
14-
import uvicorn
13+
from routers.folder import router as folder_router
14+
from routers.checklist import router as checklist_router
15+
from routers.file import router as file_router
16+
1517

1618
# 1) 환경변수 로드 (상단에서 선 로드됨)
1719

18-
# 2) 로깅 설정
19-
logging.basicConfig(level=logging.INFO)
20-
logger = logging.getLogger(__name__)
20+
import uvicorn
2121

22-
# 3) FastAPI 앱 생성
23-
app = FastAPI()
2422

25-
# 4) CORS 설정
26-
origins = [
27-
"http://localhost:5174",
28-
]
23+
load_dotenv()
24+
25+
app = FastAPI()
2926

3027
app.add_middleware(
3128
CORSMiddleware,
32-
allow_origins=["*"],
29+
allow_origins=["*"], # 개발 중 전체 허용
3330
allow_credentials=True,
3431
allow_methods=["*"],
3532
allow_headers=["*"],
3633
)
3734

38-
# 5) 라우터 등록
35+
# 정적 파일(업로드) 서빙
36+
os.makedirs(os.path.join(os.path.dirname(__file__), "uploads"), exist_ok=True)
37+
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "uploads")), name="static")
38+
39+
# 라우터 등록
3940
app.include_router(auth_router)
4041
app.include_router(note_router)
41-
app.include_router(folder_router)
42+
app.include_router(folder_router)
4243
app.include_router(file_router)
44+
app.include_router(checklist_router)
4345

44-
# 6) 루트 엔드포인트
4546
@app.get("/")
46-
def read_root():
47+
def root():
4748
return {"message": "mini"}
4849

49-
# 7) 실행 설정
50+
# 앱 시작 시(uvicorn main:app) 한 번만 테이블 생성 (개발용)
51+
init_db()
52+
5053
if __name__ == "__main__":
51-
uvicorn.run(
52-
"main:app",
53-
host="0.0.0.0",
54-
port=8080,
55-
reload=True,
56-
env_file=".env"
57-
)
54+
uvicorn.run("main:app", host="0.0.0.0", port=8080, reload=True)

models/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
from .user import User
1+
from .base import Base
2+
from .user import User
3+
from .folder import Folder
4+
from .note import Note
5+
from .file import File
6+
from .checklist import Checklist
7+
8+
__all__ = ["Base", "User", "Folder", "Note", "File", "Checklist"]

models/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
from sqlalchemy.ext.declarative import declarative_base
2+
from sqlalchemy.orm import declarative_base
3+
24

35
Base = declarative_base()

0 commit comments

Comments
 (0)