|
|
@@ -18,7 +18,7 @@ from sqlalchemy import and_, asc, desc, func, or_, select
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
|
|
from app.core.db import get_db
|
|
|
|
from app.core.db import get_db
|
|
|
|
from app.deps import get_current_user
|
|
|
|
from app.deps import require_permission
|
|
|
|
from app.plugins.builtins.mail.models import (
|
|
|
|
from app.plugins.builtins.mail.models import (
|
|
|
|
ContactPgpKey,
|
|
|
|
ContactPgpKey,
|
|
|
|
Mail,
|
|
|
|
Mail,
|
|
|
@@ -185,11 +185,15 @@ async def _check_send_permission(
|
|
|
|
raise HTTPException(403, detail={"detail": "No send permission", "code": "forbidden"})
|
|
|
|
raise HTTPException(403, detail={"detail": "No send permission", "code": "forbidden"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _check_delegate_full_access(
|
|
|
|
_LEVEL_HIERARCHY = {"read": 1, "write": 2, "delete": 3, "full": 4}
|
|
|
|
db: AsyncSession, account: MailAccount, user_id: uuid.UUID
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _check_delegate_access(
|
|
|
|
|
|
|
|
db: AsyncSession, account: MailAccount, user_id: uuid.UUID, required_level: str = "read"
|
|
|
|
) -> None:
|
|
|
|
) -> None:
|
|
|
|
|
|
|
|
"""Check if user has delegate access to account with at least the required level."""
|
|
|
|
if account.user_id == user_id:
|
|
|
|
if account.user_id == user_id:
|
|
|
|
return
|
|
|
|
return # Owner has full access
|
|
|
|
delegate = (
|
|
|
|
delegate = (
|
|
|
|
await db.execute(
|
|
|
|
await db.execute(
|
|
|
|
select(MailAccountDelegate).where(
|
|
|
|
select(MailAccountDelegate).where(
|
|
|
@@ -201,10 +205,13 @@ async def _check_delegate_full_access(
|
|
|
|
)
|
|
|
|
)
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not delegate:
|
|
|
|
if not delegate:
|
|
|
|
raise HTTPException(403, detail={"detail": "No delegate access", "code": "forbidden"})
|
|
|
|
raise HTTPException(403, detail={"detail": "No delegate access to this mailbox", "code": "forbidden"})
|
|
|
|
if delegate.access_level != "full":
|
|
|
|
user_level = _LEVEL_HIERARCHY.get(delegate.access_level, 0)
|
|
|
|
|
|
|
|
required = _LEVEL_HIERARCHY.get(required_level, 1)
|
|
|
|
|
|
|
|
if user_level < required:
|
|
|
|
raise HTTPException(
|
|
|
|
raise HTTPException(
|
|
|
|
403, detail={"detail": "Read-only delegate access", "code": "forbidden"}
|
|
|
|
403,
|
|
|
|
|
|
|
|
detail={"detail": f"Insufficient delegate access (need {required_level}, have {delegate.access_level})", "code": "forbidden"}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -217,7 +224,7 @@ async def _check_delegate_full_access(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/accounts")
|
|
|
|
@router.get("/accounts")
|
|
|
|
async def list_accounts(
|
|
|
|
async def list_accounts(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -242,7 +249,7 @@ async def list_accounts(
|
|
|
|
async def create_account(
|
|
|
|
async def create_account(
|
|
|
|
data: MailAccountCreate,
|
|
|
|
data: MailAccountCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -254,7 +261,7 @@ async def create_account(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/accounts/shared")
|
|
|
|
@router.get("/accounts/shared")
|
|
|
|
async def list_shared_accounts(
|
|
|
|
async def list_shared_accounts(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
accounts = (
|
|
|
|
accounts = (
|
|
|
@@ -276,7 +283,7 @@ async def update_account(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
data: MailAccountUpdate,
|
|
|
|
data: MailAccountUpdate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -290,7 +297,7 @@ async def update_account(
|
|
|
|
async def delete_account(
|
|
|
|
async def delete_account(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -306,7 +313,7 @@ async def assign_shared_users(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
data: SharedMailboxUserAssign,
|
|
|
|
data: SharedMailboxUserAssign,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:share")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -348,7 +355,7 @@ async def create_delegate(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
data: DelegateCreate,
|
|
|
|
data: DelegateCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:share")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -389,7 +396,7 @@ async def create_send_permission(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
data: SendPermissionCreate,
|
|
|
|
data: SendPermissionCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:share")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -422,7 +429,7 @@ async def create_send_permission(
|
|
|
|
async def test_connection(
|
|
|
|
async def test_connection(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -458,7 +465,7 @@ async def test_connection(
|
|
|
|
async def trigger_imap_sync(
|
|
|
|
async def trigger_imap_sync(
|
|
|
|
account_id: str,
|
|
|
|
account_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -475,7 +482,7 @@ async def trigger_imap_sync(
|
|
|
|
async def list_folders(
|
|
|
|
async def list_folders(
|
|
|
|
account_id: str = Query(...),
|
|
|
|
account_id: str = Query(...),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -499,7 +506,7 @@ async def list_folders(
|
|
|
|
async def create_folder(
|
|
|
|
async def create_folder(
|
|
|
|
data: MailFolderCreate,
|
|
|
|
data: MailFolderCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -532,7 +539,7 @@ async def update_folder(
|
|
|
|
folder_id: str,
|
|
|
|
folder_id: str,
|
|
|
|
data: MailFolderUpdate,
|
|
|
|
data: MailFolderUpdate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -555,7 +562,7 @@ async def update_folder(
|
|
|
|
async def delete_folder(
|
|
|
|
async def delete_folder(
|
|
|
|
folder_id: str,
|
|
|
|
folder_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -568,7 +575,7 @@ async def delete_folder(
|
|
|
|
if not folder:
|
|
|
|
if not folder:
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
await _check_delegate_full_access(db, account, user_id)
|
|
|
|
await _check_delegate_access(db, account, user_id, "full")
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
await imap_delete_folder(db, f_id, tenant_id)
|
|
|
|
await imap_delete_folder(db, f_id, tenant_id)
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
@@ -587,7 +594,7 @@ async def delete_folder(
|
|
|
|
async def upload_attachment(
|
|
|
|
async def upload_attachment(
|
|
|
|
file: UploadFile = File(...),
|
|
|
|
file: UploadFile = File(...),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
"""Upload a file to be attached to an outgoing email.
|
|
|
|
"""Upload a file to be attached to an outgoing email.
|
|
|
|
|
|
|
|
|
|
|
@@ -643,7 +650,7 @@ async def upload_attachment(
|
|
|
|
async def send_mail(
|
|
|
|
async def send_mail(
|
|
|
|
data: MailSendRequest,
|
|
|
|
data: MailSendRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:send")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -708,7 +715,7 @@ async def search_mails(
|
|
|
|
page: int = Query(1, ge=1),
|
|
|
|
page: int = Query(1, ge=1),
|
|
|
|
page_size: int = Query(20, ge=1, le=100),
|
|
|
|
page_size: int = Query(20, ge=1, le=100),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
pattern = f"%{q}%"
|
|
|
|
pattern = f"%{q}%"
|
|
|
@@ -739,7 +746,7 @@ async def search_mails(
|
|
|
|
async def list_threads(
|
|
|
|
async def list_threads(
|
|
|
|
account_id: str | None = None,
|
|
|
|
account_id: str | None = None,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
stmt = select(Mail).where(Mail.tenant_id == tenant_id)
|
|
|
|
stmt = select(Mail).where(Mail.tenant_id == tenant_id)
|
|
|
@@ -764,7 +771,7 @@ async def list_threads(
|
|
|
|
async def create_template(
|
|
|
|
async def create_template(
|
|
|
|
data: MailTemplateCreate,
|
|
|
|
data: MailTemplateCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -782,7 +789,7 @@ async def create_template(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/templates")
|
|
|
|
@router.get("/templates")
|
|
|
|
async def list_templates(
|
|
|
|
async def list_templates(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
templates = (
|
|
|
|
templates = (
|
|
|
@@ -797,7 +804,7 @@ async def list_templates(
|
|
|
|
async def substitute_template(
|
|
|
|
async def substitute_template(
|
|
|
|
data: TemplateSubstituteRequest,
|
|
|
|
data: TemplateSubstituteRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tpl_id = _parse_uuid(data.template_id, "template_id")
|
|
|
|
tpl_id = _parse_uuid(data.template_id, "template_id")
|
|
|
@@ -823,7 +830,7 @@ async def substitute_template(
|
|
|
|
async def create_signature(
|
|
|
|
async def create_signature(
|
|
|
|
data: MailSignatureCreate,
|
|
|
|
data: MailSignatureCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -843,7 +850,7 @@ async def create_signature(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/signatures")
|
|
|
|
@router.get("/signatures")
|
|
|
|
async def list_signatures(
|
|
|
|
async def list_signatures(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
sigs = (
|
|
|
|
sigs = (
|
|
|
@@ -861,7 +868,7 @@ async def list_signatures(
|
|
|
|
async def create_rule(
|
|
|
|
async def create_rule(
|
|
|
|
data: MailRuleCreate,
|
|
|
|
data: MailRuleCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id") if data.account_id else None
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id") if data.account_id else None
|
|
|
@@ -881,7 +888,7 @@ async def create_rule(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/rules")
|
|
|
|
@router.get("/rules")
|
|
|
|
async def list_rules(
|
|
|
|
async def list_rules(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
rules = (
|
|
|
|
rules = (
|
|
|
@@ -898,7 +905,7 @@ async def list_rules(
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/rules/{rule_id}", status_code=204)
|
|
|
|
@router.delete("/rules/{rule_id}", status_code=204)
|
|
|
|
async def delete_rule(
|
|
|
|
async def delete_rule(
|
|
|
|
rule_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
rule_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:config"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
r_id = _parse_uuid(rule_id, "rule_id")
|
|
|
|
r_id = _parse_uuid(rule_id, "rule_id")
|
|
|
@@ -919,7 +926,7 @@ async def delete_rule(
|
|
|
|
async def configure_vacation(
|
|
|
|
async def configure_vacation(
|
|
|
|
data: VacationConfig,
|
|
|
|
data: VacationConfig,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -948,7 +955,7 @@ async def test_vacation_dedup(
|
|
|
|
account_id: str = Query(...),
|
|
|
|
account_id: str = Query(...),
|
|
|
|
sender: str = Query(...),
|
|
|
|
sender: str = Query(...),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -972,7 +979,7 @@ async def test_vacation_dedup(
|
|
|
|
async def import_pgp_key(
|
|
|
|
async def import_pgp_key(
|
|
|
|
data: PgpKeyImport,
|
|
|
|
data: PgpKeyImport,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -992,7 +999,7 @@ async def import_pgp_key(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/pgp/keys")
|
|
|
|
@router.get("/pgp/keys")
|
|
|
|
async def list_pgp_keys(
|
|
|
|
async def list_pgp_keys(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -1015,7 +1022,7 @@ async def list_pgp_keys(
|
|
|
|
async def encrypt_message(
|
|
|
|
async def encrypt_message(
|
|
|
|
data: dict = Body(...),
|
|
|
|
data: dict = Body(...),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:send")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
contact_id = (
|
|
|
|
contact_id = (
|
|
|
@@ -1043,7 +1050,7 @@ async def encrypt_message(
|
|
|
|
async def create_label(
|
|
|
|
async def create_label(
|
|
|
|
data: MailLabelCreate,
|
|
|
|
data: MailLabelCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -1055,7 +1062,7 @@ async def create_label(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/labels")
|
|
|
|
@router.get("/labels")
|
|
|
|
async def list_labels(
|
|
|
|
async def list_labels(
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
labels = (
|
|
|
|
labels = (
|
|
|
@@ -1074,7 +1081,7 @@ async def store_contact_pgp_key(
|
|
|
|
contact_id: str,
|
|
|
|
contact_id: str,
|
|
|
|
data: dict = Body(...),
|
|
|
|
data: dict = Body(...),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:config")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
c_id = _parse_uuid(contact_id, "contact_id")
|
|
|
|
c_id = _parse_uuid(contact_id, "contact_id")
|
|
|
@@ -1109,7 +1116,7 @@ async def store_contact_pgp_key(
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{mail_id}/apply-rules")
|
|
|
|
@router.post("/{mail_id}/apply-rules")
|
|
|
|
async def apply_rules(
|
|
|
|
async def apply_rules(
|
|
|
|
mail_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
mail_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:config"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
@@ -1130,7 +1137,7 @@ async def reply_mail(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailReplyRequest,
|
|
|
|
data: MailReplyRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:send")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -1141,6 +1148,7 @@ async def reply_mail(
|
|
|
|
if not original:
|
|
|
|
if not original:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
signature = None
|
|
|
|
signature = None
|
|
|
|
if data.signature_id:
|
|
|
|
if data.signature_id:
|
|
|
@@ -1175,7 +1183,7 @@ async def forward_mail_endpoint(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailForwardRequest,
|
|
|
|
data: MailForwardRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:send")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -1186,6 +1194,7 @@ async def forward_mail_endpoint(
|
|
|
|
if not original:
|
|
|
|
if not original:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
signature = None
|
|
|
|
signature = None
|
|
|
|
if data.signature_id:
|
|
|
|
if data.signature_id:
|
|
|
@@ -1221,15 +1230,18 @@ async def update_flags(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailFlagsUpdate,
|
|
|
|
data: MailFlagsUpdate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
if data.is_seen is not None:
|
|
|
|
if data.is_seen is not None:
|
|
|
|
mail.is_seen = data.is_seen
|
|
|
|
mail.is_seen = data.is_seen
|
|
|
|
if data.is_flagged is not None:
|
|
|
|
if data.is_flagged is not None:
|
|
|
@@ -1255,9 +1267,10 @@ async def download_attachment(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
att_id: str,
|
|
|
|
att_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
a_id = _parse_uuid(att_id, "att_id")
|
|
|
|
a_id = _parse_uuid(att_id, "att_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
@@ -1265,6 +1278,8 @@ async def download_attachment(
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
attachment = (
|
|
|
|
attachment = (
|
|
|
|
await db.execute(
|
|
|
|
await db.execute(
|
|
|
|
select(MailAttachment).where(
|
|
|
|
select(MailAttachment).where(
|
|
|
@@ -1300,15 +1315,18 @@ async def link_mail(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailLinkRequest,
|
|
|
|
data: MailLinkRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
if data.contact_id:
|
|
|
|
if data.contact_id:
|
|
|
|
mail.contact_id = _parse_uuid(data.contact_id, "contact_id")
|
|
|
|
mail.contact_id = _parse_uuid(data.contact_id, "contact_id")
|
|
|
|
if data.company_id:
|
|
|
|
if data.company_id:
|
|
|
@@ -1326,7 +1344,7 @@ async def create_event_from_mail(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailCreateEventRequest,
|
|
|
|
data: MailCreateEventRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
@@ -1336,6 +1354,8 @@ async def create_event_from_mail(
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
from app.plugins.builtins.calendar.models import Calendar, CalendarEntry
|
|
|
|
from app.plugins.builtins.calendar.models import Calendar, CalendarEntry
|
|
|
|
except ImportError:
|
|
|
|
except ImportError:
|
|
|
@@ -1370,9 +1390,10 @@ async def assign_label(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailLabelAssign,
|
|
|
|
data: MailLabelAssign,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
l_id = _parse_uuid(data.label_id, "label_id")
|
|
|
|
l_id = _parse_uuid(data.label_id, "label_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
@@ -1380,6 +1401,8 @@ async def assign_label(
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
label = (
|
|
|
|
label = (
|
|
|
|
await db.execute(
|
|
|
|
await db.execute(
|
|
|
|
select(MailLabel).where(and_(MailLabel.id == l_id, MailLabel.tenant_id == tenant_id))
|
|
|
|
select(MailLabel).where(and_(MailLabel.id == l_id, MailLabel.tenant_id == tenant_id))
|
|
|
@@ -1408,12 +1431,13 @@ async def assign_label(
|
|
|
|
async def create_draft(
|
|
|
|
async def create_draft(
|
|
|
|
data: MailDraftCreate,
|
|
|
|
data: MailDraftCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
mail = await save_draft(db, acc_id, tenant_id, user_id, data.model_dump())
|
|
|
|
mail = await save_draft(db, acc_id, tenant_id, user_id, data.model_dump())
|
|
|
|
return mail_to_response(mail)
|
|
|
|
return mail_to_response(mail)
|
|
|
|
|
|
|
|
|
|
|
@@ -1423,10 +1447,18 @@ async def update_draft_route(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailDraftCreate,
|
|
|
|
data: MailDraftCreate,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
|
|
|
mail = (
|
|
|
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
|
|
|
if not mail:
|
|
|
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
mail = await update_draft(db, m_id, tenant_id, data.model_dump())
|
|
|
|
mail = await update_draft(db, m_id, tenant_id, data.model_dump())
|
|
|
|
return mail_to_response(mail)
|
|
|
|
return mail_to_response(mail)
|
|
|
|
|
|
|
|
|
|
|
@@ -1438,15 +1470,18 @@ async def update_draft_route(
|
|
|
|
async def delete_mail(
|
|
|
|
async def delete_mail(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:delete")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "delete")
|
|
|
|
# Soft-delete: set deleted_at = now()
|
|
|
|
# Soft-delete: set deleted_at = now()
|
|
|
|
mail.deleted_at = datetime.now(UTC)
|
|
|
|
mail.deleted_at = datetime.now(UTC)
|
|
|
|
await db.flush()
|
|
|
|
await db.flush()
|
|
|
@@ -1463,9 +1498,10 @@ async def move_mail(
|
|
|
|
mail_id: str,
|
|
|
|
mail_id: str,
|
|
|
|
data: MailMoveRequest,
|
|
|
|
data: MailMoveRequest,
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:write")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
target_f_id = _parse_uuid(data.target_folder_id, "target_folder_id")
|
|
|
|
target_f_id = _parse_uuid(data.target_folder_id, "target_folder_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
@@ -1473,6 +1509,8 @@ async def move_mail(
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
# Verify target folder exists and belongs to same tenant
|
|
|
|
# Verify target folder exists and belongs to same tenant
|
|
|
|
target_folder = (
|
|
|
|
target_folder = (
|
|
|
|
await db.execute(
|
|
|
|
await db.execute(
|
|
|
@@ -1509,7 +1547,7 @@ async def list_mails(
|
|
|
|
sort_by: str = Query("date", pattern="^(date|from|subject)$"),
|
|
|
|
sort_by: str = Query("date", pattern="^(date|from|subject)$"),
|
|
|
|
sort_order: str = Query("desc", pattern="^(asc|desc)$"),
|
|
|
|
sort_order: str = Query("desc", pattern="^(asc|desc)$"),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
current_user: dict = Depends(require_permission("mail:read")),
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
stmt = select(Mail).where(Mail.tenant_id == tenant_id)
|
|
|
|
stmt = select(Mail).where(Mail.tenant_id == tenant_id)
|
|
|
@@ -1542,15 +1580,18 @@ async def list_mails(
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/{mail_id}")
|
|
|
|
@router.get("/{mail_id}")
|
|
|
|
async def get_mail(
|
|
|
|
async def get_mail(
|
|
|
|
mail_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user)
|
|
|
|
mail_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("mail:read"))
|
|
|
|
):
|
|
|
|
):
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
mail = (
|
|
|
|
mail = (
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
).scalar_one_or_none()
|
|
|
|
).scalar_one_or_none()
|
|
|
|
if not mail:
|
|
|
|
if not mail:
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
attachments = (
|
|
|
|
attachments = (
|
|
|
|
(await db.execute(select(MailAttachment).where(MailAttachment.mail_id == mail.id)))
|
|
|
|
(await db.execute(select(MailAttachment).where(MailAttachment.mail_id == mail.id)))
|
|
|
|
.scalars()
|
|
|
|
.scalars()
|
|
|
|