Files
ad/molodetz/database/invites.py
T

94 lines
2.9 KiB
Python
Raw Normal View History

# retoor <retoor@molodetz.nl>
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