18 lines
696 B
Python
18 lines
696 B
Python
|
|
"""Small cross-cutting helpers shared across modules (kept dependency-light on purpose)."""
|
||
|
|
import re
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
|
||
|
|
# Single source of truth for "looks like an email" — used by every endpoint that accepts an
|
||
|
|
# address (auth register/reset/demo, admin invites + demo whitelist, the setup wizard). Keep
|
||
|
|
# the strictness in one place so routes don't drift between this and a looser "@"-in check.
|
||
|
|
_EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
|
||
|
|
|
||
|
|
|
||
|
|
def valid_email(value: str | None) -> bool:
|
||
|
|
return bool(value and _EMAIL_RE.match(value))
|
||
|
|
|
||
|
|
|
||
|
|
def now_utc() -> datetime:
|
||
|
|
"""Current time as a timezone-aware UTC datetime."""
|
||
|
|
return datetime.now(timezone.utc)
|