feat(imap): add ImapIngestionProfile model, API, migration and UI
Replaces the simple binary attachment_filter string with a full ingestion profiles system: - Add FILE_TYPE_CATEGORIES dict to allowed_types.py (6 categories: pdf, office, opendocument, text, web, images) + DEFAULT_CATEGORIES / ALL_CATEGORIES + get_allowed_types_for_categories() helper - Add ImapIngestionProfile model (id, name, description, owner_id, allowed_categories JSON, is_builtin) - Update UserImapAccount: replace attachment_filter string with profile_id FK to imap_ingestion_profiles - Migration 033: creates profiles table, seeds 2 built-in profiles (Documents Only, All Files), migrates attachment_filter → profile_id - New /api/imap-profiles/ CRUD endpoints (list, create, get, update, delete) with category validation - Register imap_profiles router in app/api/__init__.py - Update imap_tasks.py: replace attachment_filter string param with profile-based allowed_categories; add _resolve_categories_for_profile() - Update imap_accounts.py API to use profile_id instead of attachment_filter - Update imap_accounts view to pass profiles + categories to template - Full UI overhaul: profiles panel + profile create/edit modal with category checkboxes; profile selector in account modal - 17 new tests (141 total), all passing Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
"""User-facing view for the per-user IMAP ingestion dashboard."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models import UserImapAccount
|
||||
from app.models import ImapIngestionProfile, UserImapAccount
|
||||
from app.utils.allowed_types import FILE_TYPE_CATEGORIES
|
||||
from app.utils.subscription import get_tier, get_user_tier_id
|
||||
from app.utils.user_scope import get_current_owner_id
|
||||
from app.views.base import APIRouter, Depends, get_db, require_login, templates
|
||||
@@ -25,6 +27,22 @@ def _get_max_mailboxes(tier: dict) -> int | None:
|
||||
return max_mb
|
||||
|
||||
|
||||
def _serialize_profile(profile: ImapIngestionProfile) -> dict:
|
||||
"""Serialize a profile for JSON embedding in the template."""
|
||||
try:
|
||||
categories = json.loads(profile.allowed_categories)
|
||||
except (ValueError, TypeError):
|
||||
categories = []
|
||||
return {
|
||||
"id": profile.id,
|
||||
"name": profile.name,
|
||||
"description": profile.description,
|
||||
"owner_id": profile.owner_id,
|
||||
"allowed_categories": categories,
|
||||
"is_builtin": profile.is_builtin,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/imap-accounts")
|
||||
@require_login
|
||||
async def imap_accounts_page(request: Request, db: Session = Depends(get_db)):
|
||||
@@ -49,11 +67,39 @@ async def imap_accounts_page(request: Request, db: Session = Depends(get_db)):
|
||||
max_mailboxes = _get_max_mailboxes(tier)
|
||||
can_add = max_mailboxes is None or (max_mailboxes > 0 and current_count < max_mailboxes)
|
||||
|
||||
# Load ingestion profiles: system-global + user's own
|
||||
profiles: list[ImapIngestionProfile] = (
|
||||
db.query(ImapIngestionProfile)
|
||||
.filter(
|
||||
(ImapIngestionProfile.owner_id == None) # noqa: E711
|
||||
| (ImapIngestionProfile.owner_id == owner_id)
|
||||
)
|
||||
.order_by(ImapIngestionProfile.is_builtin.desc(), ImapIngestionProfile.id)
|
||||
.all()
|
||||
if owner_id
|
||||
else db.query(ImapIngestionProfile)
|
||||
.filter(ImapIngestionProfile.owner_id == None) # noqa: E711
|
||||
.order_by(ImapIngestionProfile.id)
|
||||
.all()
|
||||
)
|
||||
|
||||
# Category definitions for the UI checkbox builder
|
||||
categories = [
|
||||
{
|
||||
"key": key,
|
||||
"label": info["label"],
|
||||
"description": info["description"],
|
||||
}
|
||||
for key, info in FILE_TYPE_CATEGORIES.items()
|
||||
]
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"imap_accounts.html",
|
||||
{
|
||||
"request": request,
|
||||
"accounts": accounts,
|
||||
"profiles": [_serialize_profile(p) for p in profiles],
|
||||
"categories": categories,
|
||||
"current_count": current_count,
|
||||
"max_mailboxes": max_mailboxes,
|
||||
"can_add": can_add,
|
||||
|
||||
Reference in New Issue
Block a user