feat: implement initial admin setup (Story 1.1)

Add complete initial admin setup functionality including:
- SetupForm with email, password, and password confirmation fields
- Password validation (minimum 12 characters) and confirmation matching
- Email format validation
- Setup route with GET (display form) and POST (process setup) handlers
- bcrypt password hashing before storing admin credentials
- Auto-login after successful setup with session management
- First-run detection middleware that redirects to /setup if no admin exists
- Setup page returns 404 after admin account is created
- Base HTML template with Pico CSS integration
- Admin dashboard placeholder template
- 404 error template

All tests pass with 90.09% code coverage (exceeds 80% requirement).
Code passes ruff linting and mypy type checking.

Story: 1.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-22 11:40:38 -07:00
co-authored by Claude
parent b077112aba
commit 6a2ac7a8a7
15 changed files with 536 additions and 15 deletions
+5
View File
@@ -0,0 +1,5 @@
"""Forms for Sneaky Klaus application."""
from src.forms.setup import SetupForm
__all__ = ["SetupForm"]
+47
View File
@@ -0,0 +1,47 @@
"""Setup form for initial admin account creation."""
from flask_wtf import FlaskForm
from wtforms import EmailField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo, Length
class SetupForm(FlaskForm):
"""Form for initial admin setup.
Validates email format, password length, and password confirmation.
Attributes:
email: Email address for admin account.
password: Password for admin account (minimum 12 characters).
password_confirm: Password confirmation field (must match password).
"""
email = EmailField(
"Email Address",
validators=[
DataRequired(message="Email address is required."),
Email(message="Please enter a valid email address."),
],
render_kw={"placeholder": "admin@example.com"},
)
password = PasswordField(
"Password",
validators=[
DataRequired(message="Password is required."),
Length(
min=12,
message="Password must be at least 12 characters long.",
),
],
render_kw={"placeholder": "Enter a secure password"},
)
password_confirm = PasswordField(
"Confirm Password",
validators=[
DataRequired(message="Please confirm your password."),
EqualTo("password", message="Passwords must match."),
],
render_kw={"placeholder": "Re-enter your password"},
)