diff --git a/EasyDOC-frontend/easydoc-parser/main.py b/EasyDOC-frontend/easydoc-parser/main.py index 9b8b102..8641587 100644 --- a/EasyDOC-frontend/easydoc-parser/main.py +++ b/EasyDOC-frontend/easydoc-parser/main.py @@ -1,4 +1,6 @@ -from fastapi import FastAPI, UploadFile, File +from fastapi import FastAPI, UploadFile, File, Depends +from sqlalchemy.orm import Session +import sys from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List, Optional @@ -44,6 +46,16 @@ else: print(f"⚠ GOOGLE_APPLICATION_CREDENTIALS 파일 없음: {_cred_path}") +# --- DB 공유를 위한 경로 설정 --- +current_file_path = Path(__file__).resolve() +root_dir = current_file_path.parent.parent.parent + +if str(root_dir) not in sys.path: + sys.path.append(str(root_dir)) + +from database_document.database import get_db, Document +# ----------------------------- + app = FastAPI() app.add_middleware( @@ -210,8 +222,8 @@ async def parse_hwp(file: UploadFile = File(...)): @app.get("/parse/s3/{file_key:path}") -async def parse_from_s3(file_key: str): - """S3에서 파일 가져와서 파싱""" +async def parse_from_s3(file_key: str, user_email: str = "", db: Session = Depends(get_db)): + """S3에서 파일 가져와서 파싱 및 DB 저장""" print(f"[DEBUG] 파싱 요청 받음 - 파일 키: {file_key}") print(f"[DEBUG] 버킷: {BUCKET_NAME}") try: @@ -231,7 +243,6 @@ async def parse_from_s3(file_key: str): page_text = page.extract_text() if page_text: text += page_text + "\n" - return {"filename": filename, "text": text} elif ext == "hwp": ole = olefile.OleFileIO(io.BytesIO(contents)) @@ -241,10 +252,49 @@ async def parse_from_s3(file_key: str): else: text = "텍스트를 추출할 수 없습니다." ole.close() - return {"filename": filename, "text": text.strip()} + text = text.strip() else: return {"error": "지원하지 않는 파일 형식입니다."} + + # ================= DB 저장 로직 ================= + # 파일 크기 계산 + size_kb = len(contents) / 1024 + file_size_str = f"{size_kb:.1f} KB" + + # 페이지 수 계산 + total_pages = 1 + if ext == "pdf": + try: + with pdfplumber.open(io.BytesIO(contents)) as pdf: + total_pages = len(pdf.pages) + except: + total_pages = 1 + + # S3 URL 조립 + s3_url = f"https://{BUCKET_NAME}.s3.{os.getenv('AWS_DEFAULT_REGION')}.amazonaws.com/{file_key}" + + # DB 모델 생성 + new_doc = Document( + file_name=filename, + file_type=ext, + s3_url=s3_url, + extracted_text=text, + file_size=file_size_str, + page_count=total_pages, + user_email=user_email + ) + + # DB에 추가 및 커밋 + db.add(new_doc) + db.commit() + db.refresh(new_doc) + + print(f"[DEBUG] DB 저장 성공! (문서 번호: {new_doc.id}, 크기: {file_size_str}, 페이지: {total_pages})") + # ======================================================== + + # 저장된 ID와 함께 프론트엔드로 응답 + return {"id": new_doc.id, "filename": filename, "text": text} except Exception as e: return {"error": str(e)} @@ -335,7 +385,8 @@ async def analyze_with_gemini(data: dict): try: response = gemini_model.generate_content(prompt) response_text = response.text.strip() - + + # 응답 파싱 for line in response_text.split("\n"): line = line.strip() diff --git a/EasyDOC-frontend/easydoc-parser/requirements.txt b/EasyDOC-frontend/easydoc-parser/requirements.txt index 3435f10..7186df7 100644 --- a/EasyDOC-frontend/easydoc-parser/requirements.txt +++ b/EasyDOC-frontend/easydoc-parser/requirements.txt @@ -6,3 +6,8 @@ python-dotenv==1.0.0 boto3==1.34.34 google-generativeai==0.8.3 pandas==2.2.0 +sqlalchemy +pymysql +cryptography +google-cloud-aiplatform +python-multipart \ No newline at end of file diff --git a/EasyDOC-frontend/package-lock.json b/EasyDOC-frontend/package-lock.json index 08d9e63..abc4204 100644 --- a/EasyDOC-frontend/package-lock.json +++ b/EasyDOC-frontend/package-lock.json @@ -58,6 +58,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1704,6 +1705,7 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1752,6 +1754,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1884,6 +1887,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2253,6 +2257,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3198,6 +3203,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3275,6 +3281,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3565,6 +3572,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -3686,6 +3694,7 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/EasyDOC-frontend/src/pages/Login.jsx b/EasyDOC-frontend/src/pages/Login.jsx index da61338..18bacdb 100644 --- a/EasyDOC-frontend/src/pages/Login.jsx +++ b/EasyDOC-frontend/src/pages/Login.jsx @@ -22,6 +22,7 @@ export default function Login() { return ( setCurrentView("mypage")} + userEmail={userEmail} /> ); case "mypage": diff --git a/EasyDOC-frontend/src/pages/MyPage.jsx b/EasyDOC-frontend/src/pages/MyPage.jsx index 5b049e0..a95d2e8 100644 --- a/EasyDOC-frontend/src/pages/MyPage.jsx +++ b/EasyDOC-frontend/src/pages/MyPage.jsx @@ -15,7 +15,10 @@ * */ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; +import axios from "axios"; +import Viewer from "./Viewer"; +import Loading from "./Loading"; import "./mypage.css"; /** @@ -403,16 +406,86 @@ function LanguageSettingsContent() { * 그 정도 생각할 수 있을 듯. */ -function HistoryContent() { +function HistoryContent({ onDocumentClick, userEmail }) { // 문서 검색어 상태 const [searchQuery, setSearchQuery] = useState(""); + const [documents, setDocuments] = useState([]); + + useEffect(() => { + const fetchHistory = async () => { + try { + const response = await axios.get(`http://localhost:8002/api/documents?user_email=${userEmail}`); + + const formattedDocs = response.data.map(doc => { + const d = new Date(doc.created_at); + const dateStr = `${d.getFullYear()}년 ${d.getMonth() + 1}월 ${d.getDate()}일 ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; + + return { + id: doc.id, + name: doc.file_name, + level: 3, // DB에 난이도 컬럼이 없으므로 디폴트 3 지정 + category: "문서", // 기본 카테고리 + date: dateStr, + pages: doc.page_count || "-", // (DB에 값이 없으면 "-" 출력) + size: doc.file_size || "-", + }; + }); + setDocuments(formattedDocs); + } catch (error) { + console.error("이력 로딩 실패: ", error); + } + }; + fetchHistory(); + }, []); + + // 파일 다운로드 처리 함수 + const handleDownload = async (e, docId, fileName) => { + e.stopPropagation(); + + try{ + const response = await axios.get(`http://localhost:8002/api/documents/${docId}`); + const s3Url = response.data.s3_url; + + if (!s3Url) { + alert("다운로드할 원본 파일이 존재하지 않습니다."); + return; + } + + let finalFileName = fileName; + + // S3 실제 경로는 .pdf가 포함되어 있는데, 원본 파일명이 .pdf로 끝나지 않는 경우 (이미지 파일인 경우) + if (s3Url.toLowerCase().includes('.pdf') && !fileName.toLowerCase().endsWith('.pdf')) { + // 기존 확장자를 떼어내고 .pdf를 붙여줍니다. + const nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.')) || fileName; + finalFileName = `${nameWithoutExt}.pdf`; + } + + // S3에서 파일을 받아와 내 컴퓨터에 저장 + const fileResponse = await fetch(s3Url); + const blob = await fileResponse.blob(); + const downloadUrl = window.URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = downloadUrl; + link.download = finalFileName; + document.body.appendChild(link); + link.click(); + + link.remove(); + window.URL.revokeObjectURL(downloadUrl); + + } catch (error) { + console.error("다운로드 실패:", error); + alert("파일 다운로드 중 오류가 발생했습니다."); + } + }; // ===== 더미 문서 데이터 ===== // 실제 운영에서는 백엔드 API에서 fetch // 형식: { id, name, level, category, date, pages, size } // ISO 날짜 형식으로 바꿔야 하는데, 어떻게 하는지 모름 - const documents = [ + /*const documents = [ { id: 1, name: "행정기본법.pdf", @@ -590,13 +663,21 @@ function HistoryContent() {
- -