feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
from typing import Optional, Literal
|
|
|
|
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
|
|
|
from devplacepy.constants import TOPICS
|
2026-05-10 09:08:12 +02:00
|
|
|
|
|
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
class SignupForm(BaseModel):
|
|
|
|
|
username: str = Field(min_length=3, max_length=32)
|
|
|
|
|
email: str = Field(min_length=1, max_length=255)
|
2026-05-10 09:08:12 +02:00
|
|
|
password: str = Field(min_length=6, max_length=128)
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
confirm_password: str = Field(min_length=1, max_length=128)
|
2026-05-10 09:08:12 +02:00
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
@field_validator("username")
|
|
|
|
|
@classmethod
|
|
|
|
|
def username_chars(cls, value):
|
|
|
|
|
if not value.isascii() or not all(c.isalnum() or c in ("-", "_") for c in value):
|
|
|
|
|
raise ValueError("Username can only contain letters, numbers, hyphens, and underscores")
|
|
|
|
|
return value
|
2026-05-10 09:08:12 +02:00
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
@field_validator("email")
|
|
|
|
|
@classmethod
|
|
|
|
|
def email_has_at(cls, value):
|
|
|
|
|
if "@" not in value:
|
|
|
|
|
raise ValueError("Valid email is required")
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
@model_validator(mode="after")
|
|
|
|
|
def passwords_match(self):
|
|
|
|
|
if self.password != self.confirm_password:
|
|
|
|
|
raise ValueError("Passwords do not match")
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoginForm(BaseModel):
|
|
|
|
|
email: str = Field(min_length=1, max_length=255)
|
|
|
|
|
password: str = Field(min_length=1, max_length=128)
|
|
|
|
|
remember_me: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForgotPasswordForm(BaseModel):
|
|
|
|
|
email: str = Field(min_length=1, max_length=255)
|
|
|
|
|
|
|
|
|
|
@field_validator("email")
|
|
|
|
|
@classmethod
|
|
|
|
|
def email_has_at(cls, value):
|
|
|
|
|
if "@" not in value:
|
|
|
|
|
raise ValueError("Valid email is required")
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ResetPasswordForm(BaseModel):
|
2026-05-10 09:08:12 +02:00
|
|
|
password: str = Field(min_length=6, max_length=128)
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
confirm_password: str = Field(min_length=1, max_length=128)
|
2026-05-10 09:08:12 +02:00
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
@model_validator(mode="after")
|
|
|
|
|
def passwords_match(self):
|
|
|
|
|
if self.password != self.confirm_password:
|
|
|
|
|
raise ValueError("Passwords do not match")
|
|
|
|
|
return self
|
2026-05-10 09:08:12 +02:00
|
|
|
|
|
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
class PostForm(BaseModel):
|
|
|
|
|
content: str = Field(min_length=10, max_length=2000)
|
|
|
|
|
title: str = Field(default="", max_length=500)
|
|
|
|
|
topic: str = "random"
|
|
|
|
|
project_uid: str = ""
|
|
|
|
|
attachment_uids: list[str] = []
|
2026-05-10 09:08:12 +02:00
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
@field_validator("topic")
|
|
|
|
|
@classmethod
|
|
|
|
|
def valid_topic(cls, value):
|
|
|
|
|
return value if value in TOPICS else "random"
|
2026-05-10 09:08:12 +02:00
|
|
|
|
|
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
class PostEditForm(BaseModel):
|
|
|
|
|
content: str = Field(min_length=10, max_length=2000)
|
|
|
|
|
title: str = Field(default="", max_length=500)
|
|
|
|
|
topic: str = "random"
|
|
|
|
|
|
|
|
|
|
@field_validator("topic")
|
|
|
|
|
@classmethod
|
|
|
|
|
def valid_topic(cls, value):
|
|
|
|
|
return value if value in TOPICS else "random"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CommentForm(BaseModel):
|
|
|
|
|
content: str = Field(min_length=3, max_length=1000)
|
|
|
|
|
target_uid: str = ""
|
|
|
|
|
post_uid: str = ""
|
|
|
|
|
target_type: Literal["post", "project", "news", "bug", "gist"] = "post"
|
|
|
|
|
parent_uid: str = ""
|
|
|
|
|
attachment_uids: list[str] = []
|
|
|
|
|
|
|
|
|
|
@model_validator(mode="after")
|
|
|
|
|
def require_target(self):
|
|
|
|
|
if not (self.target_uid or self.post_uid):
|
|
|
|
|
raise ValueError("A target is required")
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProjectForm(BaseModel):
|
2026-05-10 09:08:12 +02:00
|
|
|
title: str = Field(min_length=1, max_length=200)
|
|
|
|
|
description: str = Field(min_length=1, max_length=5000)
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
release_date: str = ""
|
|
|
|
|
demo_date: str = ""
|
|
|
|
|
project_type: Literal["game", "game_asset", "software", "mobile_app", "website"] = "software"
|
2026-05-10 09:08:12 +02:00
|
|
|
platforms: str = Field(default="", max_length=500)
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
status: str = Field(default="In Development", max_length=100)
|
|
|
|
|
attachment_uids: list[str] = []
|
2026-05-10 09:08:12 +02:00
|
|
|
|
|
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
class MessageForm(BaseModel):
|
2026-05-10 09:08:12 +02:00
|
|
|
content: str = Field(min_length=1, max_length=2000)
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
receiver_uid: str = Field(min_length=1)
|
|
|
|
|
attachment_uids: list[str] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProfileForm(BaseModel):
|
|
|
|
|
bio: str = Field(default="", max_length=500)
|
|
|
|
|
location: str = Field(default="", max_length=200)
|
|
|
|
|
git_link: str = Field(default="", max_length=500)
|
|
|
|
|
website: str = Field(default="", max_length=500)
|
2026-05-10 09:08:12 +02:00
|
|
|
|
|
|
|
|
|
feat: replace raw form parsing with pydantic models for auth, admin, bugs, comments, and gists
Migrate all route handlers from manual `request.form()` parsing to typed `Annotated[Model, Form()]` dependencies using new pydantic models (SignupForm, LoginForm, ForgotPasswordForm, AdminRoleForm, AdminPasswordForm, BugForm, CommentForm, GistForm). Remove inline validation logic from signup, login, password reset, admin role/password change, bug creation, comment creation, and gist creation endpoints, delegating validation to model validators and field constraints. Add `RequestValidationError` exception handler with friendly error messages for auth form pages. Update model definitions to use `field_validator` and `model_validator` for cross-field checks like password matching.
2026-05-23 05:44:04 +02:00
|
|
|
class GistForm(BaseModel):
|
|
|
|
|
title: str = Field(min_length=1, max_length=200)
|
|
|
|
|
description: str = Field(default="", max_length=5000)
|
|
|
|
|
source_code: str = Field(min_length=1, max_length=50000)
|
|
|
|
|
language: str = Field(default="plaintext", max_length=50)
|
|
|
|
|
attachment_uids: list[str] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GistEditForm(BaseModel):
|
|
|
|
|
title: str = Field(min_length=1, max_length=200)
|
|
|
|
|
description: str = Field(default="", max_length=5000)
|
|
|
|
|
source_code: str = Field(min_length=1, max_length=50000)
|
|
|
|
|
language: str = Field(default="plaintext", max_length=50)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BugForm(BaseModel):
|
|
|
|
|
title: str = Field(min_length=1, max_length=200)
|
|
|
|
|
description: str = Field(min_length=1, max_length=5000)
|
|
|
|
|
attachment_uids: list[str] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VoteForm(BaseModel):
|
|
|
|
|
value: int
|
|
|
|
|
|
|
|
|
|
@field_validator("value")
|
|
|
|
|
@classmethod
|
|
|
|
|
def valid_value(cls, value):
|
|
|
|
|
if value not in (1, -1):
|
|
|
|
|
raise ValueError("value must be 1 or -1")
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRoleForm(BaseModel):
|
|
|
|
|
role: Literal["member", "admin"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminPasswordForm(BaseModel):
|
|
|
|
|
password: str = Field(min_length=6, max_length=128)
|