- ITSM: app_deploy.py (APK 업로드·QR 생성·랜딩 페이지) - ITSM: batch_ssh.py (다중 서버 동시 SSH 실행) - ITSM: asset_qr.py (자산 QR 태그·체크인·라벨 인쇄) - ITSM: smart_notify.py (조건 기반 알림 규칙 엔진) - ITSM: models.py (AppVersion/BatchSSHJob/AssetQRToken/SmartNotifyRule 등 7개 모델) - ITSM: main.py (4개 신규 라우터 등록) - ITSM: static/app.js (앱배포·배치SSH·자산QR·알림규칙 4개 뷰) - ITSM: static/index.html (신규 사이드바 메뉴 4개) - Manager: AppDistribution.tsx (APK 업로드 UI·QR 표시·버전 관리) - Manager: NotificationRules.tsx (알림 규칙 편집기) - Manager: App.tsx + Sidebar.tsx (신규 라우트 등록) - Mail: contacts.py (주소록 CRUD·자동완성) - Mail: signature.py (HTML 서명 관리) - Mail: Contacts.tsx + SignatureEditor.tsx (프론트엔드 컴포넌트) - Messenger: scan.tsx (자산 QR 스캔 탭) - Messenger: _layout.tsx (QR 탭 추가) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.1 KiB
Python
72 lines
2.1 KiB
Python
"""
|
|
웹메일 서명 편집기
|
|
|
|
엔드포인트:
|
|
GET /api/mail/signature — 현재 서명 조회
|
|
PUT /api/mail/signature — 서명 저장 (HTML)
|
|
"""
|
|
import re
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from auth import verify_token
|
|
from database import get_db_session as get_db
|
|
from models import MailSignature
|
|
|
|
router = APIRouter(prefix="/api/mail/signature", tags=["메일 서명"])
|
|
|
|
ALLOWED_TAGS = {'b', 'i', 'u', 'br', 'p', 'span', 'div', 'a', 'font', 'strong', 'em', 'h1', 'h2', 'h3', 'img'}
|
|
|
|
|
|
def _sanitize_html(html: str) -> str:
|
|
"""기본 HTML sanitize (script 태그 제거)."""
|
|
html = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL | re.IGNORECASE)
|
|
html = re.sub(r'on\w+="[^"]*"', '', html, flags=re.IGNORECASE)
|
|
html = re.sub(r"on\w+='[^']*'", '', html, flags=re.IGNORECASE)
|
|
return html
|
|
|
|
|
|
class SignatureUpdate(BaseModel):
|
|
html_content: str
|
|
is_active: bool = True
|
|
|
|
|
|
@router.get("/")
|
|
async def get_signature(
|
|
db: AsyncSession = Depends(get_db),
|
|
username: str = Depends(verify_token),
|
|
):
|
|
row = await db.execute(select(MailSignature).where(MailSignature.username == username))
|
|
sig = row.scalar_one_or_none()
|
|
return {
|
|
"html_content": sig.html_content if sig else "",
|
|
"is_active": sig.is_active if sig else False,
|
|
}
|
|
|
|
|
|
@router.put("/")
|
|
async def update_signature(
|
|
req: SignatureUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
username: str = Depends(verify_token),
|
|
):
|
|
clean_html = _sanitize_html(req.html_content)
|
|
row = await db.execute(select(MailSignature).where(MailSignature.username == username))
|
|
sig = row.scalar_one_or_none()
|
|
if sig:
|
|
sig.html_content = clean_html
|
|
sig.is_active = req.is_active
|
|
sig.updated_at = datetime.utcnow()
|
|
else:
|
|
sig = MailSignature(
|
|
username=username, html_content=clean_html,
|
|
is_active=req.is_active, updated_at=datetime.utcnow(),
|
|
)
|
|
db.add(sig)
|
|
await db.commit()
|
|
return {"ok": True}
|