fix:优化数据
This commit is contained in:
183
backend/app/routers/email_configs.py
Normal file
183
backend/app/routers/email_configs.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""
|
||||
Email accounts for multi-email finance sync. Stored in data/email_configs.json.
|
||||
"""
|
||||
import json
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
router = APIRouter(prefix="/settings/email", tags=["email-configs"])
|
||||
|
||||
CONFIG_PATH = Path("data/email_configs.json")
|
||||
|
||||
|
||||
class EmailConfigCreate(BaseModel):
|
||||
host: str = Field(..., description="IMAP host")
|
||||
port: int = Field(993, description="IMAP port")
|
||||
user: str = Field(..., description="Email address")
|
||||
password: str = Field(..., description="Password or authorization code")
|
||||
mailbox: str = Field("INBOX", description="Mailbox name")
|
||||
active: bool = Field(True, description="Include in sync")
|
||||
|
||||
|
||||
class EmailConfigRead(BaseModel):
|
||||
id: str
|
||||
host: str
|
||||
port: int
|
||||
user: str
|
||||
mailbox: str
|
||||
active: bool
|
||||
|
||||
|
||||
class EmailConfigUpdate(BaseModel):
|
||||
host: str | None = None
|
||||
port: int | None = None
|
||||
user: str | None = None
|
||||
password: str | None = None
|
||||
mailbox: str | None = None
|
||||
active: bool | None = None
|
||||
|
||||
|
||||
def _load_configs() -> List[Dict[str, Any]]:
|
||||
if not CONFIG_PATH.exists():
|
||||
return []
|
||||
try:
|
||||
data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
||||
return data if isinstance(data, list) else []
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def _save_configs(configs: List[Dict[str, Any]]) -> None:
|
||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
CONFIG_PATH.write_text(
|
||||
json.dumps(configs, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _to_read(c: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return {
|
||||
"id": c["id"],
|
||||
"host": c["host"],
|
||||
"port": c["port"],
|
||||
"user": c["user"],
|
||||
"mailbox": c.get("mailbox", "INBOX"),
|
||||
"active": c.get("active", True),
|
||||
}
|
||||
|
||||
|
||||
@router.get("", response_model=List[EmailConfigRead])
|
||||
async def list_email_configs():
|
||||
"""List all email account configs (password omitted)."""
|
||||
configs = _load_configs()
|
||||
return [_to_read(c) for c in configs]
|
||||
|
||||
|
||||
@router.post("", response_model=EmailConfigRead, status_code=status.HTTP_201_CREATED)
|
||||
async def create_email_config(payload: EmailConfigCreate):
|
||||
"""Add a new email account."""
|
||||
configs = _load_configs()
|
||||
new_id = str(uuid.uuid4())
|
||||
configs.append({
|
||||
"id": new_id,
|
||||
"host": payload.host,
|
||||
"port": payload.port,
|
||||
"user": payload.user,
|
||||
"password": payload.password,
|
||||
"mailbox": payload.mailbox,
|
||||
"active": payload.active,
|
||||
})
|
||||
_save_configs(configs)
|
||||
return _to_read(configs[-1])
|
||||
|
||||
|
||||
@router.put("/{config_id}", response_model=EmailConfigRead)
|
||||
async def update_email_config(config_id: str, payload: EmailConfigUpdate):
|
||||
"""Update an email account (omit password to keep existing)."""
|
||||
configs = _load_configs()
|
||||
for c in configs:
|
||||
if c.get("id") == config_id:
|
||||
if payload.host is not None:
|
||||
c["host"] = payload.host
|
||||
if payload.port is not None:
|
||||
c["port"] = payload.port
|
||||
if payload.user is not None:
|
||||
c["user"] = payload.user
|
||||
if payload.password is not None:
|
||||
c["password"] = payload.password
|
||||
if payload.mailbox is not None:
|
||||
c["mailbox"] = payload.mailbox
|
||||
if payload.active is not None:
|
||||
c["active"] = payload.active
|
||||
_save_configs(configs)
|
||||
return _to_read(c)
|
||||
raise HTTPException(status_code=404, detail="Email config not found")
|
||||
|
||||
|
||||
@router.delete("/{config_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_email_config(config_id: str):
|
||||
"""Remove an email account."""
|
||||
configs = _load_configs()
|
||||
new_list = [c for c in configs if c.get("id") != config_id]
|
||||
if len(new_list) == len(configs):
|
||||
raise HTTPException(status_code=404, detail="Email config not found")
|
||||
_save_configs(new_list)
|
||||
|
||||
|
||||
@router.get("/{config_id}/folders")
|
||||
async def list_email_folders(config_id: str):
|
||||
"""
|
||||
List mailbox folders for this account (for choosing custom labels).
|
||||
Returns [{ "raw": "...", "decoded": "收件箱" }, ...]. Use decoded for display and for mailbox config.
|
||||
"""
|
||||
import asyncio
|
||||
from backend.app.services.email_service import list_mailboxes_for_config
|
||||
|
||||
configs = _load_configs()
|
||||
config = next((c for c in configs if c.get("id") == config_id), None)
|
||||
if not config:
|
||||
raise HTTPException(status_code=404, detail="Email config not found")
|
||||
host = config.get("host")
|
||||
user = config.get("user")
|
||||
password = config.get("password")
|
||||
port = int(config.get("port", 993))
|
||||
if not all([host, user, password]):
|
||||
raise HTTPException(status_code=400, detail="Config missing host/user/password")
|
||||
|
||||
def _fetch():
|
||||
return list_mailboxes_for_config(host, port, user, password)
|
||||
|
||||
try:
|
||||
folders = await asyncio.to_thread(_fetch)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=502, detail=f"无法连接邮箱或获取文件夹列表: {e}") from e
|
||||
|
||||
return {"folders": [{"raw": r, "decoded": d} for r, d in folders]}
|
||||
|
||||
|
||||
def get_email_configs_for_sync() -> List[Dict[str, Any]]:
|
||||
"""Return list of configs that are active (for sync). Falls back to env if file empty."""
|
||||
configs = _load_configs()
|
||||
active = [c for c in configs if c.get("active", True)]
|
||||
if active:
|
||||
return active
|
||||
# Fallback to single account from env
|
||||
import os
|
||||
host = os.getenv("IMAP_HOST")
|
||||
user = os.getenv("IMAP_USER")
|
||||
password = os.getenv("IMAP_PASSWORD")
|
||||
if host and user and password:
|
||||
return [{
|
||||
"id": "env",
|
||||
"host": host,
|
||||
"port": int(os.getenv("IMAP_PORT", "993")),
|
||||
"user": user,
|
||||
"password": password,
|
||||
"mailbox": os.getenv("IMAP_MAILBOX", "INBOX"),
|
||||
"active": True,
|
||||
}]
|
||||
return []
|
||||
Reference in New Issue
Block a user