# retoor <retoor@molodetz.nl>
import logging
from typing import Annotated
from fastapi import APIRouter, Request, Form
from fastapi.responses import HTMLResponse
from devplacepy.database import get_table, get_setting, get_int_setting
from devplacepy.templating import templates
from devplacepy.utils import (
create_session,
get_current_user,
register_account_async,
)
from devplacepy.seo import base_seo_context
from devplacepy.models import SignupForm
from devplacepy.config import SECONDS_PER_DAY
from devplacepy.responses import respond, action_result, wants_json, json_error
from devplacepy.schemas import AuthPageOut
from devplacepy.services.audit import record as audit
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/signup", response_class=HTMLResponse)
async def signup_page(request: Request):
user = get_current_user(request)
if user:
return action_result(request, "/feed")
seo_ctx = base_seo_context(
request,
title="Join DevPlace",
description="Create your DevPlace account and start connecting with developers.",
robots="noindex,nofollow",
)
registration_closed = get_setting("registration_open", "1") != "1"
return respond(
request,
"signup.html",
{
**seo_ctx,
"request": request,
"page": "signup",
"registration_closed": registration_closed,
},
model=AuthPageOut,
)
@router.post("/signup")
async def signup(request: Request, data: Annotated[SignupForm, Form()]):
username = data.username
email = data.email.strip().lower()
password = data.password
if get_setting("registration_open", "1") != "1":
if wants_json(request):
return json_error(403, "Registration is closed")
seo_ctx = base_seo_context(
request, title="Join DevPlace", robots="noindex,nofollow"
)
return templates.TemplateResponse(
request,
"signup.html",
{**seo_ctx, "request": request, "registration_closed": True},
)
errors = []
users = get_table("users")
if users.find_one(username=username):
errors.append("Username already taken")
if users.find_one(email=email):
errors.append("Email already registered")
if errors:
if wants_json(request):
return json_error(400, "; ".join(errors), errors=errors)
seo_ctx = base_seo_context(
request, title="Join DevPlace", robots="noindex,nofollow"
)
return templates.TemplateResponse(
request,
"signup.html",
{
**seo_ctx,
"request": request,
"errors": errors,
"username": username,
"email": email,
},
)
uid, role, is_first = await register_account_async(username, email, password)
max_age = max(1, get_int_setting("session_max_age_days", 7)) * SECONDS_PER_DAY
token = create_session(uid, max_age)
response = action_result(request, "/feed", data={"username": username})
response.set_cookie(
key="session", value=token, max_age=max_age, httponly=True, samesite="lax"
)
logger.info(f"User {username} signed up")
audit.record(
request,
"auth.signup",
user={"uid": uid, "username": username, "role": role},
target_type="user",
target_uid=uid,
target_label=username,
new_value=role,
metadata={"first_user": is_first},
summary=f"user {username} registered account",
links=[audit.target("user", uid, username)],
)
return response