Files
john 1aaef71f52 Initial commit: Happy Up monorepo through Sprint 5.
Document-driven MVP with FastAPI backend, Vue H5, WeChat mini shell, product demo, and Docker dev stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 11:42:40 +08:00

50 lines
1.6 KiB
Python

from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.config import settings
from app.core.security import create_access_token, hash_phone, mask_phone
from app.db.models import Tenant, User
from app.schemas.models import LoginRequest, LoginType
def authenticate(db: Session, body: LoginRequest) -> tuple[User, str]:
if body.login_type == LoginType.phone_code:
if body.code != settings.demo_sms_code:
raise ValueError("invalid_code")
phone = body.credential
phone_hash = hash_phone(phone)
user = db.scalar(select(User).where(User.phone_hash == phone_hash))
if not user:
tenant = db.scalar(select(Tenant).where(Tenant.name == "Demo Organization"))
if not tenant:
tenant = Tenant(name="Demo Organization", type="organization", status="active")
db.add(tenant)
db.flush()
user = User(
tenant_id=tenant.id,
phone=phone,
phone_hash=phone_hash,
role="parent",
status="active",
consent_signed=True,
)
db.add(user)
db.flush()
user.last_login_at = datetime.now(timezone.utc)
db.commit()
db.refresh(user)
token = create_access_token(user.id, user.role)
return user, token
raise ValueError("unsupported_login_type")
def user_to_dict(user: User) -> dict:
return {
"id": user.id,
"phoneMasked": mask_phone(user.phone or ""),
"role": user.role,
}