Fix Loomio sync: use portal DB (OC returns email:null); add manual sync endpoint
This commit is contained in:
1 parent
5490eeb2fc
commit
5d3ace8fe8
1 file changed
+30
-19
+30
-19
@@ -182,25 +182,20 @@ async def is_active_member(slug: str) -> bool:
|
||||
|
||||
|
||||
async def get_active_member_emails() -> list[str]:
|
||||
"""Return the list of active financial contributor emails from Open Collective."""
|
||||
query = (
|
||||
'{ collective(slug: "%s") { members(limit: 100) { nodes { role account { email slug } } } } }'
|
||||
% OC_COLLECTIVE_SLUG
|
||||
)
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.post(OC_GRAPHQL_URL, json={"query": query})
|
||||
if r.status_code != 200:
|
||||
logger.warning("OC member list fetch failed: %s", r.status_code)
|
||||
return []
|
||||
nodes = r.json().get("data", {}).get("collective", {}).get("members", {}).get("nodes", [])
|
||||
emails = []
|
||||
for n in nodes:
|
||||
role = n.get("role", "")
|
||||
acct = n.get("account", {}) or {}
|
||||
email = acct.get("email")
|
||||
if role in ("BACKER", "ADMIN") and email:
|
||||
emails.append(email)
|
||||
return emails
|
||||
"""Return the list of active member emails from the portal's own database.
|
||||
|
||||
We use the local `members` table (populated during OAuth, which captures the
|
||||
member's email) rather than Open Collective's GraphQL, because OC returns
|
||||
`email: null` for privacy — the email field is only exposed via OAuth with
|
||||
the user's consent. The local table is the authoritative source of member
|
||||
emails.
|
||||
"""
|
||||
conn = get_db()
|
||||
rows = conn.execute(
|
||||
"SELECT email FROM members WHERE active = 1"
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [r[0] for r in rows]
|
||||
|
||||
|
||||
async def sync_loomio_memberships() -> None:
|
||||
@@ -739,6 +734,22 @@ async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@app.post("/admin/sync-loomio/{token}")
|
||||
async def admin_sync_loomio(token: str):
|
||||
"""Manually trigger a Loomio membership sync.
|
||||
|
||||
Protected by the same secret token as the Open Collective webhook. Useful
|
||||
for reconciling pre-existing members (accounts created before the sync
|
||||
existed) or recovering from a missed webhook. Idempotent — safe to call
|
||||
repeatedly.
|
||||
"""
|
||||
if not WEBHOOK_TOKEN or not secrets.compare_digest(token, WEBHOOK_TOKEN):
|
||||
raise HTTPException(401, "Invalid token")
|
||||
|
||||
await sync_loomio_memberships()
|
||||
return {"status": "synced"}
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def index():
|
||||
return {"service": "Inference Cooperative Member Portal", "version": "0.1.0"}
|
||||
Reference in new issue
Block a user