microfactory-lab / core /signups.py
kylebrodeur's picture
space: add email signup for Microfactory updates
c6cc161 verified
Raw
History Blame Contribute Delete
3.88 kB
"""Email signup for Microfactory updates.
Lightweight, privacy-first opt-in collection. Stores one JSONL row per signup:
{ts, email, consent, source, app_version}. Local runs write to data/signups.jsonl.
Space runs can additionally push to a private HF dataset via CommitScheduler if
SIGNUPS_DATASET and HF_TOKEN are set. Never writes without explicit consent.
"""
from __future__ import annotations
import json
import os
import re
import threading
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
SIGNUPS_FILE = Path(__file__).resolve().parent.parent / "data" / "signups.jsonl"
SIGNUPS_DATASET = os.environ.get("SIGNUPS_DATASET", "kylebrodeur/microfactory-signups").strip()
FLUSH_MINUTES = 5
_scheduler: Any = None
_lock = threading.Lock()
_EMAIL_RE = re.compile(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$")
def _get_scheduler():
"""Lazy-init CommitScheduler for the private signup dataset."""
global _scheduler
token = os.environ.get("HF_TOKEN", "").strip()
if not token:
return None
if not SIGNUPS_DATASET or "/" not in SIGNUPS_DATASET:
return None
if _scheduler is None:
with _lock:
if _scheduler is None:
try:
from huggingface_hub import CommitScheduler
except ImportError:
return None
SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True)
if not SIGNUPS_FILE.exists():
SIGNUPS_FILE.write_text("", encoding="utf-8")
_scheduler = CommitScheduler(
repo_id=SIGNUPS_DATASET,
repo_type="dataset",
folder_path=str(SIGNUPS_FILE.parent),
every=FLUSH_MINUTES,
token=token,
allow_patterns=["signups.jsonl"],
)
return _scheduler
def is_active() -> bool:
"""True if signup syncing to HF is configured and active."""
return _get_scheduler() is not None
def validate_email(email: str) -> str | None:
"""Return normalized email or None if invalid."""
if not email:
return None
email = email.strip().lower()
if not _EMAIL_RE.match(email):
return None
return email
def record_signup(email: str, consent: bool, source: str = "local") -> tuple[bool, str]:
"""Record one signup. Returns (ok, message).
Requires explicit consent. Email is validated. Writes locally always;
pushes to HF only when HF_TOKEN + SIGNUPS_DATASET are present.
"""
if not consent:
return False, "Please check the box to opt in before submitting."
normalized = validate_email(email)
if normalized is None:
return False, "Enter a valid email address."
row = {
"ts": datetime.now(timezone.utc).isoformat(),
"email": normalized,
"consent": True,
"source": source,
"app_version": os.environ.get("CHIEF_ENGINEER_VERSION", "0.1.0"),
}
try:
SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True)
with _lock:
with SIGNUPS_FILE.open("a", encoding="utf-8") as f:
f.write(json.dumps(row, ensure_ascii=False) + "\n")
sched = _get_scheduler()
if sched is not None:
try:
sched.trigger()
except Exception:
pass
return True, "You're on the list. Microfactory updates will hit that inbox."
except Exception as e:
return False, f"Could not save signup: {e}"
def privacy_notice() -> str:
return (
"<div class='ce-sub' style='font-size:10px;opacity:0.7;margin-top:4px;'>"
"📬 Microfactory updates: one email, no spam, unsub any time. "
"We store only your email and this timestamp. No print data, no uploaded files.</div>"
)