import asyncio from fastapi import APIRouter, Depends, Query from typing import Optional from app.lib_general_v3 import AccountContext, get_account_context, DelayParams from app.models.response_models import Resp_Body_Base, mk_resp from app.methods.e_novi_mailman_methods import ( test_novi_connection, test_mailman_connection, get_mailman_lists, get_mailman_list_members, subscribe_member_to_list, unsubscribe_member_from_list, get_novi_members, mirror_novi_group_to_mailman_list, mirror_all_configured_mappings, ) router = APIRouter() # ── Connection Tests ────────────────────────────────────────────────────── @router.get('/test_connection/novi', response_model=Resp_Body_Base) async def test_novi( account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Verify Novi AMS API credentials from IDAA site cfg_json.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) result = test_novi_connection() if result.get('ok'): return mk_resp(data=result) return mk_resp(data=result, status_code=401, status_message="Novi connection failed.") @router.get('/test_connection/mailman', response_model=Resp_Body_Base) async def test_mailman( account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Verify Mailman 3 REST API credentials from IDAA site cfg_json.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) result = test_mailman_connection() if result.get('ok'): return mk_resp(data=result) return mk_resp(data=result, status_code=401, status_message="Mailman connection failed.") # ── Inspection / Preview ────────────────────────────────────────────────── @router.get('/mailman/lists/{list_id}/members', response_model=Resp_Body_Base) async def list_mailman_list_members( list_id: str, count: int = Query(100, ge=1, le=500), page: int = Query(1, ge=1), account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Return members of a specific Mailman 3 list.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) data = get_mailman_list_members(list_id=list_id, count=count, page=page) if data is not None: return mk_resp(data={"count": len(data), "members": data}) return mk_resp(data=False, status_code=500, status_message=f"Failed to fetch members for list '{list_id}'.") @router.post('/mailman/lists/{list_id}/subscribe', response_model=Resp_Body_Base) async def mailman_subscribe( list_id: str, email: str = Query(..., description="Email address to subscribe"), display_name: str = Query('', description="Optional display name"), account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Subscribe an email address to a Mailman 3 list.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) ok = subscribe_member_to_list(list_id=list_id, email=email, display_name=display_name) if ok: return mk_resp(data={"list_id": list_id, "email": email}, status_message="Subscribed successfully.") return mk_resp(data=False, status_code=500, status_message=f"Failed to subscribe {email} to {list_id}.") @router.delete('/mailman/lists/{list_id}/subscribe', response_model=Resp_Body_Base) async def mailman_unsubscribe( list_id: str, email: str = Query(..., description="Email address to unsubscribe"), account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Unsubscribe an email address from a Mailman 3 list.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) ok = unsubscribe_member_from_list(list_id=list_id, email=email) if ok: return mk_resp(data={"list_id": list_id, "email": email}, status_message="Unsubscribed successfully.") return mk_resp(data=False, status_code=500, status_message=f"Failed to unsubscribe {email} from {list_id}.") @router.get('/mailman/lists', response_model=Resp_Body_Base) async def list_mailman_lists( account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Return all mailing lists from this Mailman 3 instance.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) data = get_mailman_lists() if data is not None: return mk_resp(data=data) return mk_resp(data=False, status_code=500, status_message="Failed to fetch Mailman lists.") @router.get('/novi/members', response_model=Resp_Body_Base) async def list_novi_members( status_filter: Optional[str] = Query(None, description="Novi membership status filter (e.g. 'Active', 'Lapsed')"), page_size: int = Query(100, ge=1, le=500), offset: int = Query(0, ge=0), account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """Fetch a page of Novi AMS members.""" if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) data = get_novi_members(status_filter=status_filter, page_size=page_size, offset=offset) if data is not None: return mk_resp(data={"count": len(data), "members": data}) return mk_resp(data=False, status_code=500, status_message="Failed to fetch members from Novi.") # ── Sync ────────────────────────────────────────────────────────────────── @router.post('/sync', response_model=Resp_Body_Base) async def sync_all_mappings( account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """ Run all Novi → Mailman mirror syncs configured in novi_mailman_sync (IDAA cfg_json). This is the cron target — call on a schedule to keep all lists in sync. """ if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) results = mirror_all_configured_mappings() if results is not None: return mk_resp(data=results, status_message=f"Mirror sync complete. {len(results)} mapping(s) processed.") return mk_resp(data=False, status_code=500, status_message="Mirror sync failed.") @router.post('/sync/group/{novi_group_guid}', response_model=Resp_Body_Base) async def sync_single_group( novi_group_guid: str, mailman_list_id: str = Query(..., description="Target Mailman list, e.g. 'mm3@idaa.org'"), account: AccountContext = Depends(get_account_context), delay: DelayParams = Depends(), ): """ Mirror a single Novi group to a specific Mailman list. Useful for testing or forcing a refresh of one mapping. """ if delay.sleep_time_s > 0: await asyncio.sleep(delay.sleep_time_s) result = mirror_novi_group_to_mailman_list(novi_group_guid, mailman_list_id) if result: return mk_resp(data=result, status_message="Mirror sync complete.") return mk_resp(data=False, status_code=500, status_message="Mirror sync failed.")