# retoor from fastapi import APIRouter, HTTPException, Request from molodetz.database import ( create_invite, get_int_setting, get_join_request, list_join_requests, open_invite_for_join, revoke_open_invites, suggest_username, update_join_request, ) from molodetz.dependencies import json_or_form from molodetz.link_check import check_repo_link from molodetz.models import JoinStatusForm from molodetz.responses import action_result, respond, wants_json from molodetz.routers.admin._shared import admin_seo from molodetz.schemas.joins import JoinListOut from molodetz.templating import templates from molodetz.utils.audit import record from molodetz.utils.auth import not_found, require_admin router = APIRouter() def _invite_contact_email(contact): contact = (contact or "").strip() if "@" in contact and " " not in contact and "." in contact.split("@")[-1]: return contact.lower() return "" @router.get("/joins") async def joins_index(request: Request): require_admin(request) rows = [] for row in list_join_requests(): item = dict(row) invite = open_invite_for_join(row["uid"]) item["invite_open"] = invite is not None item["invite_expires_at"] = invite["expires_at"] if invite else None rows.append(item) return respond(request, "admin/joins.html", {"requests": rows, "seo": admin_seo(request, "Join requests")}, model=JoinListOut) @router.post("/joins/{uid}/status") async def joins_status(request: Request, uid: str): user = require_admin(request) row = get_join_request(uid) if row is None: not_found("Join request not found") data = await json_or_form(request, JoinStatusForm) update_join_request(uid, status=data.status) record(request, "join.request.status", payload={"status": data.status}, targets=[("join_request", uid)], user=user) return action_result(request, "/admin/joins", data={"uid": uid, "status": data.status}) @router.post("/joins/{uid}/check") async def joins_check(request: Request, uid: str): user = require_admin(request) row = get_join_request(uid) if row is None: not_found("Join request not found") result = await check_repo_link(row) record(request, "join.request.check", payload=result, targets=[("join_request", uid)], user=user) return action_result(request, "/admin/joins", data=result) @router.post("/joins/{uid}/invite") async def joins_invite(request: Request, uid: str): user = require_admin(request) row = get_join_request(uid) if row is None: not_found("Join request not found") if row.get("status") == "declined": raise HTTPException(409, "Cannot invite a declined request") days = get_int_setting("invite_expiry_days", 7) invite, token = create_invite( uid, _invite_contact_email(row.get("contact")), suggest_username(row.get("name")), user["uid"], days=days, ) claim_url = str(request.base_url).rstrip("/") + f"/invite/{token}" record( request, "join.invite.issued", payload={"expires_at": invite["expires_at"]}, targets=[("join_request", uid)], user=user, ) data = {"claim_url": claim_url, "expires_at": invite["expires_at"]} if wants_json(request): return action_result(request, "/admin/joins", data=data) return templates.TemplateResponse( request, "admin/invite_issued.html", {"join": row, "claim_url": claim_url, "expires_at": invite["expires_at"], "seo": admin_seo(request, "Invite issued")}, ) @router.post("/joins/{uid}/invite/revoke") async def joins_invite_revoke(request: Request, uid: str): user = require_admin(request) row = get_join_request(uid) if row is None: not_found("Join request not found") revoked = revoke_open_invites(uid, user["uid"]) record(request, "join.invite.revoked", payload={"revoked": revoked}, targets=[("join_request", uid)], user=user) return action_result(request, "/admin/joins", data={"revoked": revoked})