You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
2.2 KiB
Python

# backend/app/services/export_service.py — per-user 데이터 ZIP 번들(소유권) + 민감 필드 redact
import io
import json
import zipfile
from datetime import date, datetime
from sqlmodel import Session, select
from ..models import (
ConnectorAccount,
Email,
InboxItem,
Notification,
Person,
Task,
Trip,
)
_REDACT_FIELDS = {"token_enc", "password_hash", "token_hash", "code_verifier", "scopes"}
def _ser(v):
if isinstance(v, (datetime, date)):
return v.isoformat()
return v
def _dump(rows, user_id: str) -> list[dict]:
out = []
for r in rows:
d = r.model_dump()
if d.get("user_id") not in (None, user_id) and "user_id" in d:
continue
for k in list(d.keys()):
if k in _REDACT_FIELDS and d[k]:
d[k] = "[redacted]"
else:
d[k] = _ser(d[k])
out.append(d)
return out
def _scoped(session: Session, model, user_id: str):
stmt = select(model)
if hasattr(model, "user_id"):
stmt = stmt.where(model.user_id == user_id)
return session.exec(stmt).all()
def build_export(session: Session, user: Person) -> bytes:
"""사용자 전 데이터 ZIP(profile/tasks/inbox/emails/notifications/trips/connectors)."""
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
profile = {"id": user.id, "name": user.name, "initial": user.initial, "email": user.email}
z.writestr("profile.json", json.dumps(profile, ensure_ascii=False, indent=2))
bundles = {
"tasks.json": _dump(_scoped(session, Task, user.id), user.id),
"inbox.json": _dump(_scoped(session, InboxItem, user.id), user.id),
"emails.json": _dump(_scoped(session, Email, user.id), user.id),
"notifications.json": _dump(_scoped(session, Notification, user.id), user.id),
"trips.json": _dump(_scoped(session, Trip, user.id), user.id),
"connectors.json": _dump(_scoped(session, ConnectorAccount, user.id), user.id),
}
for name, data in bundles.items():
z.writestr(name, json.dumps(data, ensure_ascii=False, indent=2))
return buf.getvalue()