zioinfo-mail/backend/main.py
2026-06-01 22:12:53 +09:00

160 lines
5.4 KiB
Python

from fastapi import FastAPI, Depends, HTTPException, Query, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse, Response
import io
from .auth import verify_imap, create_token, current_user
from .imap_client import (
list_folders, list_messages, get_message,
mark_read, move_message, delete_message, get_attachment, append_to_sent,
)
from .smtp_client import send_mail
from .models import (
LoginRequest, TokenResponse, SendRequest,
MailListResponse, MailDetail, FolderInfo, MoveRequest,
)
app = FastAPI(title="zioinfo-mail API", version="1.0.0", docs_url="/api/docs")
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://mail.zioinfo.co.kr",
"https://zioinfo.co.kr:8025",
"http://localhost:5173",
"http://localhost:8025",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── Health ───────────────────────────────────────────────────
@app.get("/health")
async def health():
return {"status": "ok", "service": "zioinfo-mail", "version": "1.0.0"}
# ── Auth ─────────────────────────────────────────────────────
@app.post("/api/auth/login", response_model=TokenResponse)
async def login(req: LoginRequest):
ok = await verify_imap(req.username, req.password)
if not ok:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "이메일 또는 비밀번호가 올바르지 않습니다")
token = create_token(req.username, req.password)
name = req.username.split("@")[0]
return TokenResponse(access_token=token, username=req.username, display_name=name)
@app.post("/api/auth/logout")
async def logout():
return {"ok": True}
# ── Folders ──────────────────────────────────────────────────
@app.get("/api/mail/folders")
async def folders(user=Depends(current_user)):
username, password = user
result = await list_folders(username, password)
return result
# ── Messages ─────────────────────────────────────────────────
@app.get("/api/mail/messages")
async def messages(
folder: str = Query("INBOX"),
page: int = Query(1, ge=1),
per_page: int = Query(50, ge=1, le=200),
q: str = Query(None),
user=Depends(current_user),
):
username, password = user
return await list_messages(username, password, folder, page, per_page, q)
@app.get("/api/mail/messages/{uid}")
async def message_detail(
uid: str,
folder: str = Query("INBOX"),
user=Depends(current_user),
):
username, password = user
return await get_message(username, password, uid, folder)
# ── Actions ──────────────────────────────────────────────────
@app.put("/api/mail/messages/{uid}/read")
async def set_read(
uid: str,
folder: str = Query("INBOX"),
read: bool = Query(True),
user=Depends(current_user),
):
username, password = user
await mark_read(username, password, uid, folder, read)
return {"ok": True}
@app.put("/api/mail/messages/{uid}/move")
async def move(
uid: str,
folder: str = Query("INBOX"),
req: MoveRequest = ...,
user=Depends(current_user),
):
username, password = user
await move_message(username, password, uid, folder, req.target_folder)
return {"ok": True}
@app.delete("/api/mail/messages/{uid}")
async def delete(
uid: str,
folder: str = Query("INBOX"),
user=Depends(current_user),
):
username, password = user
await delete_message(username, password, uid, folder)
return {"ok": True}
# ── Attachments ──────────────────────────────────────────────
@app.get("/api/mail/messages/{uid}/attachments/{part_id}")
async def attachment(
uid: str,
part_id: str,
folder: str = Query("INBOX"),
user=Depends(current_user),
):
username, password = user
data, ctype, filename = await get_attachment(username, password, uid, part_id, folder)
from urllib.parse import quote
headers = {"Content-Disposition": f"attachment; filename*=UTF-8''{quote(filename)}"}
return Response(content=data, media_type=ctype, headers=headers)
# ── Send ─────────────────────────────────────────────────────
@app.post("/api/mail/send")
async def send(req: SendRequest, user=Depends(current_user)):
username, password = user
raw_bytes = await send_mail(
username, password,
req.to, req.subject, req.body,
req.cc, req.bcc, req.is_html,
)
# 발송 성공 후 Sent 폴더에 저장
await append_to_sent(username, password, raw_bytes)
return {"ok": True}
@app.post("/api/mail/draft")
async def save_draft(req: SendRequest, user=Depends(current_user)):
# 임시보관함에 저장 (APPEND)
return {"ok": True, "message": "임시저장 완료"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="127.0.0.1", port=8026, reload=True)