Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions App/routes/auth.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from pydantic import BaseModel
from fastapi import APIRouter, Depends, status, Request
from services import auth_svc, session_svc
from sqlalchemy import Connection
from db.database import context_get_conn
from fastapi.exceptions import HTTPException
from fastapi.responses import JSONResponse
from schemas.auth_schema import RegisterRequest, LoginRequest

router = APIRouter(prefix="/auth", tags=["auth"])

# --- 로그인 상태 확인 API ---
# 프론트엔드 새로고침 시 세션 유지 여부를 확인하고 유저 정보를 반환합니다
@router.get("/check", status_code=status.HTTP_200_OK)
@router.get("/check", status_code=status.HTTP_200_OK, response_class=JSONResponse, summary= "로그인 상태 확인 API")
async def check_session(session_user = Depends(session_svc.get_session_user_opt)):
return {"user": session_user}

# --- 회원가입 API ---
@router.post("/register", status_code=status.HTTP_201_CREATED)

@router.post("/register", status_code=status.HTTP_201_CREATED,response_class=JSONResponse, summary="회원가입 API")
async def register_user(request: Request,
user_info: RegisterRequest,
conn: Connection = Depends(context_get_conn)):
Expand All @@ -40,8 +41,7 @@ async def register_user(request: Request,

return {"message": "회원가입이 성공적으로 완료되었습니다.", "status": "success"}

# --- 로그인 API ---
@router.post("/login", status_code=status.HTTP_200_OK)
@router.post("/login", status_code=status.HTTP_200_OK,response_class=JSONResponse ,summary="로그인 API")
async def login_user(request: Request,
login_info: LoginRequest,
conn: Connection = Depends(context_get_conn)):
Expand Down Expand Up @@ -73,7 +73,7 @@ async def login_user(request: Request,
"status": "success"
}

@router.get("/logout", status_code=status.HTTP_200_OK)
@router.get("/logout", status_code=status.HTTP_200_OK, response_class=JSONResponse, summary="로그아웃")
async def logout_user(request: Request):
request.state.session.clear()
return {
Expand Down
5 changes: 3 additions & 2 deletions App/routes/home.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, status
from services import session_svc
from fastapi.responses import JSONResponse

router = APIRouter(prefix="/home", tags=["home"])

@router.get("/")
@router.get("/", status_code=status.HTTP_200_OK,response_class=JSONResponse, summary="세션 유저 정보 가져오기")
async def home_ui(session_user = Depends(session_svc.get_session_user_opt)):
return {
"session_user": session_user
Expand Down
10 changes: 6 additions & 4 deletions App/routes/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from services import session_svc, image_svc
from sqlalchemy import Connection
from db.database import context_get_conn
from fastapi.responses import JSONResponse

router = APIRouter(prefix="/image", tags=["image"])

# 사용자 전체 업로드 히스토리 조회
@router.get("/history", status_code=status.HTTP_200_OK)
@router.get("/history", status_code=status.HTTP_200_OK, response_class=JSONResponse,summary="이미지 히스토리 전체 조회")
async def get_user_histories(
conn: Connection = Depends(context_get_conn),
session_user = Depends(session_svc.get_session_user_prt) # 로그인 필수
Expand All @@ -22,7 +23,7 @@ async def get_user_histories(
}

# 특정 이미지의 상세 내역 조회 (개별 히스토리)
@router.get("/history/{image_id}", status_code=status.HTTP_200_OK)
@router.get("/history/{image_id}", status_code=status.HTTP_200_OK, response_class=JSONResponse,summary="이미지 개별 상세 조회")
async def get_user_history(
image_id: int,
conn: Connection = Depends(context_get_conn),
Expand All @@ -40,7 +41,7 @@ async def get_user_history(
}

# 이미지 히스토리 삭제
@router.delete("/history/{image_id}", status_code=status.HTTP_200_OK, summary="버튼 삭제")
@router.delete("/history/{image_id}", status_code=status.HTTP_200_OK,response_class=JSONResponse, summary="이미지 버튼 히스토리 삭제")
async def delete_image_history(
image_id: int,
conn: Connection = Depends(context_get_conn),
Expand All @@ -51,8 +52,9 @@ async def delete_image_history(
if not history:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="해당 이미지 기록을 찾을 수 없습니다.")

# DB 레코드 삭제
await image_svc.delete_image_db(conn, image_id)

# 실제 이미지 삭제
await image_svc.delete_image(history.image_loc)

return {
Expand Down
2 changes: 1 addition & 1 deletion App/routes/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,6 @@ async def get_video_detail(
)

meta = await video_svc.get_video_meta_result(conn, video_id)
frames = await video_svc.get_video_frame_results(conn, video_id)
frames = await video_svc.get_video_frame_result(conn, video_id)

return VideoDetailResponse(meta=meta, frames=frames)
17 changes: 12 additions & 5 deletions App/routes/video.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from fastapi import APIRouter, Depends, status, HTTPException
from fastapi import APIRouter, Depends, status
from fastapi.exceptions import HTTPException
from fastapi.responses import JSONResponse
from services import session_svc, video_svc
from sqlalchemy import Connection
from db.database import context_get_conn
from fastapi.responses import JSONResponse

router = APIRouter(prefix="/video", tags=["video"])

# 사용자 전체 비디오 업로드 히스토리 조회
@router.get("/history", status_code=status.HTTP_200_OK)
@router.get("/history", status_code=status.HTTP_200_OK, response_class=JSONResponse,summary="비디오 히스토리 전체 조회")
async def get_video_histories(
conn: Connection = Depends(context_get_conn),
session_user = Depends(session_svc.get_session_user_prt) # 로그인 필수
Expand All @@ -22,7 +25,7 @@ async def get_video_histories(
}

# 특정 비디오의 상세 내역 조회
@router.get("/history/{video_id}", status_code=status.HTTP_200_OK)
@router.get("/history/{video_id}", status_code=status.HTTP_200_OK,response_class=JSONResponse, summary="비디오 개별 상세 조회")
async def get_video_history(
video_id: int,
conn: Connection = Depends(context_get_conn),
Expand All @@ -43,7 +46,8 @@ async def get_video_history(
}

# 비디오 히스토리 삭제
@router.delete("/history/{video_id}", status_code=status.HTTP_200_OK, summary="버튼 삭제")
@router.delete("/history/{video_id}", status_code=status.HTTP_200_OK,
response_class=JSONResponse, summary="비디오 버튼 히스토리 삭제")
async def delete_video_history(
video_id: int,
conn: Connection = Depends(context_get_conn),
Expand All @@ -56,8 +60,11 @@ async def delete_video_history(
if not history:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="해당 비디오 기록을 찾을 수 없습니다.")

if history.status == "SUCCESS":
await video_svc.delete_video_meta_result(conn, video_id)
await video_svc.delete_video_frame_result(conn, video_id)

await video_svc.delete_video_db(conn, video_id)

await video_svc.delete_video(history.video_loc)

return {
Expand Down
2 changes: 1 addition & 1 deletion App/schemas/auth_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ class LoginRequest(BaseModel):

# [요청용] 회원가입 API (POST /auth/register) 수신 JSON 데이터 검증
class RegisterRequest(LoginRequest):
name: str = Field(min_length=2, max_length=100)
name: str = Field(min_length=2, max_length=100)
50 changes: 37 additions & 13 deletions App/services/image_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
os.makedirs(EXPLAIN_UPLOAD_DIR, exist_ok=True)
os.makedirs(UPLOAD_DIR, exist_ok=True)

# 사용자 업로드 이미지 파일 서버 내 저장 (회원/비회원 공통)
# 사용자 업로드 이미지 서버 내 저장 (회원/비회원 공통)
# 호출 : inference.py
# image_loc = await image_svc.upload_image(user_email, imagefile) -> 이미지 업로드 이후, 이미지 저장 경로 반환
async def upload_image(user_email: str | None, imagefile: UploadFile) -> str:
try:
# 사용자별 하위 디렉토리 결정
Expand Down Expand Up @@ -82,6 +84,8 @@ async def upload_image_cam(user_email: str, image_id: int, image: np.ndarray) ->
return cam_loc[1:].replace("\\", "/")

# 사용자 업로드 이미지 서버 내 삭제
# 호출 : image.py : history 삭제 할 때 db 와 실제 파일 삭제
# 호출 : inference.py : 추론 FAIL일 때 delete_video and delete_video_db 실행
async def delete_image(image_loc: str):
try:

Expand All @@ -97,7 +101,10 @@ async def delete_image(image_loc: str):
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="알수없는 이유로 문제가 발생하였습니다."
)


# 사용자 전체 히스토리 조회 (image_result 테이블 반영)
# 호출 : image.py / video.py
async def get_user_histories(conn: Connection, user_id: int):
try:
# SQL에 맞춰 테이블명과 컬럼 변경
Expand All @@ -108,8 +115,7 @@ async def get_user_histories(conn: Connection, user_id: int):
ORDER BY created_at DESC;
""")
stmt = text(query)
bind_stmt = stmt.bindparams(user_id = user_id)
result = await conn.execute(bind_stmt)
result = await conn.execute(stmt, {"user_id": user_id})

user_histories = [UserHistory(
image_id = row.id,
Expand Down Expand Up @@ -137,6 +143,7 @@ async def get_user_histories(conn: Connection, user_id: int):
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="알수없는 이유로 문제가 발생하였습니다.")

# 사용자 개별 히스토리 조회
# 호출 : image.py / video.py
async def get_user_history(conn: Connection, image_id: int):
try:
query = """
Expand All @@ -145,8 +152,7 @@ async def get_user_history(conn: Connection, image_id: int):
WHERE id = :image_id;
"""
stmt = text(query)
bind_stmt = stmt.bindparams(image_id=image_id)
result = await conn.execute(bind_stmt)
result = await conn.execute(stmt, {"image_id": image_id})

row = result.fetchone()
if row is None:
Expand Down Expand Up @@ -183,20 +189,32 @@ async def get_user_history(conn: Connection, image_id: int):
print(e)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="알수없는 이유로 문제가 발생하였습니다.")

# 비회원 데이터 5분 후 자동 삭제 태스크
# 비회원 데이터 1분 후 자동 삭제 태스크
# inference_svc.py : def process_image_task에서 추론이 성공 했을 때, 비회원일 경우 1분 후 자동 삭제
@celery_app.task(name="cleanup_anonymous_image")
def cleanup_anonymous_image(image_id: int, image_loc: str):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
async def _delete():
async with celery_db_conn() as conn:
await delete_image_db(conn, image_id)
await delete_image(image_loc)
print(f"[Cleanup] 비회원 이미지 삭제 완료 - image_id: {image_id}, image_loc: {image_loc}")
success = True
try:
await delete_image_db(conn, image_id)
except Exception as e:
print(f"[Cleanup] 이미지 DB 삭제 실패 - image_id: {image_id}, error: {e}")
success=False
try:
await delete_image(image_loc)
except Exception as e:
print(f"[Cleanup] 이미지 파일 삭제 실패 - image_loc: {image_loc}, error: {e}")
success=False

if success:
print(f"[Cleanup] 비회원 이미지 삭제 프로세스 완료 - image_id: {image_id}, image_loc: {image_loc}")

loop.run_until_complete(_delete())
except Exception as e:
print(f"[Cleanup Error] 비회원 이미지 삭제 실패 - image_id: {image_id}, error: {e}")

finally:
loop.close()

Expand All @@ -218,10 +236,12 @@ async def _delete():
loop.close()

# 이미지 DB 레코드 및 물리 파일 완전 삭제
# 호출 : image.py : history 삭제 시
# 호출 : inference.py : get_image_result 에서 FALIED일 시
async def delete_image_db(conn: Connection, image_id: int):
try:
delete_query = text("DELETE FROM image_result WHERE id = :image_id")
result = await conn.execute(delete_query.bindparams(image_id=image_id))
result = await conn.execute(delete_query, {"image_id": image_id})

if result.rowcount == 0:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"해당 이미지 id {id}는(은) 존재하지 않아 삭제할 수 없습니다.")
Expand All @@ -236,11 +256,12 @@ async def delete_image_db(conn: Connection, image_id: int):
raise

except Exception as e:
print(e)
await conn.rollback()
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="알수없는 이유로 문제가 발생하였습니다.")

# 빈 이미지 DB 생성 이후, image_id 반환(접수 완료)
# 호출 : inference.py : 빈 이미지 DB 생성 후, image_id 받기
# image_id = (conn, user_id, image_loc, version_type, model_type, domain_type)
async def register_image_result(conn: Connection, user_id: int | None, image_loc: str,
version_type: str, model_type: str, domain_type: str):
try:
Expand Down Expand Up @@ -275,6 +296,7 @@ async def register_image_result(conn: Connection, user_id: int | None, image_loc
detail="요청데이터가 제대로 전달되지 않았습니다")

# 이미지 메타데이터 + 추론 결과값 DB에 저장
# 호출 : inference_svc.py : process_image_task 추론 종료 시점
async def update_image_result(conn: Connection, image_id: int, analysis: dict,
result_msg: str, status: str):

Expand Down Expand Up @@ -320,6 +342,8 @@ async def update_image_result(conn: Connection, image_id: int, analysis: dict,
raise e

# 이미지 결과 값 가져오기
# 프론트엔드가 특정 이미지의 분석 진행 상태 및 최종 결과를 확인하고자 할 때 데이터 반환
# 호출 위치: routers/inference.py - get_image_result() API
async def get_image_result(conn: Connection,
image_id: int):
try:
Expand Down
3 changes: 2 additions & 1 deletion App/services/inference_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ async def run_inference():

finally:
if not user_id:
video_svc.cleanup_anonymous_video.apply_async(args=[video_id, video_loc], countdown=60)
is_success = result.get("status") == "success"
video_svc.cleanup_anonymous_video.apply_async(args=[video_id, video_loc, is_success], countdown=60)



Expand Down
Loading
Loading