# retoor import hashlib import secrets from datetime import datetime, timedelta, timezone from molodetz.database.core import db, generate_uid, now_iso, parse_iso def hash_token(token): return hashlib.sha256(token.encode()).hexdigest() def suggest_username(name): cleaned = "".join(char.lower() if char.isalnum() else "_" for char in (name or "").strip()) cleaned = cleaned.strip("_") if len(cleaned) < 3: return "" return cleaned[:32] def open_invite_for_join(join_request_uid): if "member_invites" not in db.tables: return None rows = list( db["member_invites"].find( join_request_uid=join_request_uid, used_at=None, revoked_at=None, order_by=["-created_at"], ) ) stamp = datetime.now(timezone.utc) for row in rows: expires = parse_iso(row.get("expires_at")) if expires is not None and expires > stamp: return row return None def create_invite(join_request_uid, email, username, created_by, days=7): if "member_invites" in db.tables: stamp = now_iso() for row in db["member_invites"].find(join_request_uid=join_request_uid, used_at=None, revoked_at=None): db["member_invites"].update( {"id": row["id"], "revoked_at": stamp, "revoked_by": created_by}, ["id"], ) token = secrets.token_urlsafe(32) expires = datetime.now(timezone.utc) + timedelta(days=max(1, days)) row = { "uid": generate_uid(), "token_hash": hash_token(token), "join_request_uid": join_request_uid, "email": (email or "").lower(), "username": username or "", "created_by": created_by, "created_at": now_iso(), "expires_at": expires.isoformat(), "used_at": None, "used_by": None, "revoked_at": None, "revoked_by": None, } db["member_invites"].insert(row) return row, token def get_invite_by_token(token): if not token or "member_invites" not in db.tables: return None return db["member_invites"].find_one(token_hash=hash_token(token)) def invite_is_live(row): if not row or row.get("used_at") or row.get("revoked_at"): return False expires = parse_iso(row.get("expires_at")) return expires is not None and expires > datetime.now(timezone.utc) def claim_invite(uid, user_uid): db["member_invites"].update({"uid": uid, "used_at": now_iso(), "used_by": user_uid}, ["uid"]) def revoke_open_invites(join_request_uid, actor): if "member_invites" not in db.tables: return 0 stamp = now_iso() count = 0 for row in db["member_invites"].find(join_request_uid=join_request_uid, used_at=None, revoked_at=None): db["member_invites"].update({"id": row["id"], "revoked_at": stamp, "revoked_by": actor}, ["id"]) count += 1 return count