Added authentication forms and routes
This commit is contained in:
88
src/myfasthtml/auth/pages/LoginPage.py
Normal file
88
src/myfasthtml/auth/pages/LoginPage.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from fasthtml.components import *
|
||||
|
||||
|
||||
class LoginPage:
|
||||
def __init__(self, error_message=None, success_message=None):
|
||||
self.error_message = error_message
|
||||
self.success_message = success_message
|
||||
|
||||
def render(self):
|
||||
message_alert = None
|
||||
if self.error_message:
|
||||
message_alert = Div(
|
||||
P(self.error_message, cls="text-sm"),
|
||||
cls="bg-error border border-red-400 text-red-700 px-4 py-3 rounded mb-4"
|
||||
)
|
||||
elif self.success_message:
|
||||
message_alert = Div(
|
||||
P(self.success_message, cls="text-sm"),
|
||||
cls="bg-success border border-green-400 text-green-700 px-4 py-3 rounded mb-4"
|
||||
)
|
||||
|
||||
return Div(
|
||||
# Page title
|
||||
H1("Sign In", cls="text-3xl font-bold text-center mb-6"),
|
||||
|
||||
# Login Form
|
||||
Div(
|
||||
# Message alert
|
||||
message_alert if message_alert else "",
|
||||
|
||||
# Email login form
|
||||
Form(
|
||||
# Email field
|
||||
Div(
|
||||
Label("Email", For="email", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="email",
|
||||
id="email",
|
||||
name="email",
|
||||
placeholder="you@example.com",
|
||||
required=True,
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
cls="mb-4"
|
||||
),
|
||||
|
||||
# Password field
|
||||
Div(
|
||||
Label("Password", For="password", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="password",
|
||||
id="password",
|
||||
name="password",
|
||||
placeholder="Your password",
|
||||
required=True,
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
cls="mb-6"
|
||||
),
|
||||
|
||||
# Submit button
|
||||
Button(
|
||||
"Sign In",
|
||||
type="submit",
|
||||
cls="btn w-full font-bold py-2 px-4 rounded"
|
||||
),
|
||||
|
||||
action="/login-p",
|
||||
method="post",
|
||||
cls="mb-6"
|
||||
),
|
||||
|
||||
# Registration link
|
||||
Div(
|
||||
P(
|
||||
"Don't have an account? ",
|
||||
A("Register here", href="/register", cls="text-blue-600 hover:underline"),
|
||||
cls="text-sm text-gray-600 text-center"
|
||||
)
|
||||
),
|
||||
|
||||
cls="p-8 rounded-lg shadow-2xl max-w-md mx-auto"
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
def __ft__(self):
|
||||
return self.render()
|
||||
125
src/myfasthtml/auth/pages/RegisterPage.py
Normal file
125
src/myfasthtml/auth/pages/RegisterPage.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from fasthtml.components import *
|
||||
|
||||
|
||||
class RegisterPage:
|
||||
def __init__(self, error_message: str = None):
|
||||
self.error_message = error_message
|
||||
|
||||
def register_page(self, error_message: str):
|
||||
self.error_message = error_message
|
||||
return self.__ft__()
|
||||
|
||||
def __ft__(self):
|
||||
"""
|
||||
Create the registration page.
|
||||
|
||||
Args:
|
||||
error_message: Optional error message to display
|
||||
|
||||
Returns:
|
||||
Components representing the registration page
|
||||
"""
|
||||
# Create alert for error message
|
||||
error_alert = None
|
||||
if self.error_message:
|
||||
error_alert = Div(
|
||||
P(self.error_message, cls="text-sm"),
|
||||
cls="bg-soft bg-error border border-red-400 text-red-700 px-4 py-3 rounded mb-4"
|
||||
)
|
||||
|
||||
return Div(
|
||||
# Page title
|
||||
H1("Create an Account", cls="text-3xl font-bold text-center mb-6"),
|
||||
|
||||
# Registration Form
|
||||
Div(
|
||||
# Error alert
|
||||
error_alert if error_alert else "",
|
||||
|
||||
Form(
|
||||
# Username field
|
||||
Div(
|
||||
Label("Username", For="username", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="text",
|
||||
id="username",
|
||||
name="username",
|
||||
placeholder="Choose a username",
|
||||
required=True,
|
||||
minlength=3,
|
||||
maxlength=30,
|
||||
pattern="[a-zA-Z0-9_-]+",
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
P("Only letters, numbers, underscores, and hyphens", cls="text-xs text-gray-500 mt-1"),
|
||||
cls="mb-4"
|
||||
),
|
||||
|
||||
# Email field
|
||||
Div(
|
||||
Label("Email", For="email", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="email",
|
||||
id="email",
|
||||
name="email",
|
||||
placeholder="you@example.com",
|
||||
required=True,
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
cls="mb-4"
|
||||
),
|
||||
|
||||
# Password field
|
||||
Div(
|
||||
Label("Password", For="password", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="password",
|
||||
id="password",
|
||||
name="password",
|
||||
placeholder="Create a password",
|
||||
required=True,
|
||||
minlength=8,
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
P("At least 8 characters with uppercase, lowercase, and number", cls="text-xs text-gray-500 mt-1"),
|
||||
cls="mb-4"
|
||||
),
|
||||
|
||||
# Confirm password field
|
||||
Div(
|
||||
Label("Confirm Password", For="confirm_password", cls="block text-sm font-medium text-gray-700 mb-1"),
|
||||
Input(
|
||||
type="password",
|
||||
id="confirm_password",
|
||||
name="confirm_password",
|
||||
placeholder="Confirm your password",
|
||||
required=True,
|
||||
cls="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2"
|
||||
),
|
||||
cls="mb-6"
|
||||
),
|
||||
|
||||
# Submit button
|
||||
Button(
|
||||
"Create Account",
|
||||
type="submit",
|
||||
cls="btn w-full font-bold py-2 px-4 rounded"
|
||||
),
|
||||
|
||||
# Registration link
|
||||
Div(
|
||||
P(
|
||||
"Already have an account? ",
|
||||
A("Sign in here", href="/login", cls="text-blue-600 hover:underline"),
|
||||
cls="text-sm text-gray-600 text-center"
|
||||
)
|
||||
),
|
||||
|
||||
action="register-p",
|
||||
method="post",
|
||||
cls="mb-6"
|
||||
),
|
||||
|
||||
cls="p-8 rounded-lg shadow-2xl max-w-md mx-auto"
|
||||
)
|
||||
)
|
||||
216
src/myfasthtml/auth/pages/RegisterPageOld.py
Normal file
216
src/myfasthtml/auth/pages/RegisterPageOld.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
Register page component for FastHTML application.
|
||||
|
||||
Provides a styled registration form using DaisyUI 5 components.
|
||||
"""
|
||||
|
||||
from fasthtml.common import Div, Form, Input, Button, H1, P, A, Label
|
||||
|
||||
|
||||
class RegisterPage:
|
||||
"""
|
||||
Register page component with email and password fields.
|
||||
|
||||
Attributes:
|
||||
error_message: Optional error message to display
|
||||
redirect_url: URL to redirect after successful registration
|
||||
"""
|
||||
|
||||
def __init__(self, error_message: str = None, redirect_url: str = "/login"):
|
||||
"""
|
||||
Initialize register page.
|
||||
|
||||
Args:
|
||||
error_message: Optional error message to display
|
||||
redirect_url: URL to redirect after successful registration (default: "/login")
|
||||
"""
|
||||
self.error_message = error_message
|
||||
self.redirect_url = redirect_url
|
||||
|
||||
def _render(self):
|
||||
"""
|
||||
Render the register page HTML structure.
|
||||
|
||||
Returns:
|
||||
FastHTML component tree for the register page
|
||||
"""
|
||||
# Error alert component (only shown if error_message exists)
|
||||
error_alert = None
|
||||
if self.error_message:
|
||||
error_alert = Div(
|
||||
Div(
|
||||
self._svg_icon(),
|
||||
self.error_message,
|
||||
cls="flex items-center gap-2"
|
||||
),
|
||||
role="alert",
|
||||
cls="alert alert-error mb-4",
|
||||
id="error-alert"
|
||||
)
|
||||
|
||||
# Success message container (empty by default, filled by HTMX on success)
|
||||
success_alert = Div(id="success-alert")
|
||||
|
||||
# Register form
|
||||
register_form = Form(
|
||||
# Hidden field for redirect URL
|
||||
Input(type="hidden", name="redirect_url", value=self.redirect_url),
|
||||
|
||||
# Email input
|
||||
Label(
|
||||
Div("Email", cls="label-text"),
|
||||
cls="form-control w-full mb-4"
|
||||
)(
|
||||
Input(
|
||||
type="email",
|
||||
name="email",
|
||||
placeholder="[email protected]",
|
||||
required=True,
|
||||
cls="input input-bordered w-full"
|
||||
)
|
||||
),
|
||||
|
||||
# Password input
|
||||
Label(
|
||||
Div("Password", cls="label-text"),
|
||||
Div("Must be at least 8 characters", cls="label-text-alt text-base-content/60"),
|
||||
cls="form-control w-full mb-4"
|
||||
)(
|
||||
Input(
|
||||
type="password",
|
||||
name="password",
|
||||
placeholder="Create a password",
|
||||
required=True,
|
||||
minlength="8",
|
||||
cls="input input-bordered w-full"
|
||||
)
|
||||
),
|
||||
|
||||
# Confirm password input
|
||||
Label(
|
||||
Div("Confirm Password", cls="label-text"),
|
||||
cls="form-control w-full mb-4"
|
||||
)(
|
||||
Input(
|
||||
type="password",
|
||||
name="confirm_password",
|
||||
placeholder="Confirm your password",
|
||||
required=True,
|
||||
minlength="8",
|
||||
cls="input input-bordered w-full"
|
||||
)
|
||||
),
|
||||
|
||||
# Submit button
|
||||
Button(
|
||||
"Create Account",
|
||||
type="submit",
|
||||
cls="btn btn-primary w-full"
|
||||
),
|
||||
|
||||
method="post",
|
||||
action="/register",
|
||||
hx_post="/register",
|
||||
hx_target="#register-card",
|
||||
hx_swap="outerHTML"
|
||||
)
|
||||
|
||||
# Main card container
|
||||
card = Div(
|
||||
Div(
|
||||
# Card title
|
||||
H1("Create Account", cls="text-3xl font-bold text-center mb-2"),
|
||||
P("Sign up for a new account", cls="text-center text-base-content/70 mb-6"),
|
||||
|
||||
# Success alert
|
||||
success_alert,
|
||||
|
||||
# Error alert
|
||||
error_alert if error_alert else Div(),
|
||||
|
||||
# Register form
|
||||
register_form,
|
||||
|
||||
# Login link
|
||||
Div(
|
||||
P(
|
||||
"Already have an account? ",
|
||||
A("Sign in", href="/login", cls="link link-primary"),
|
||||
cls="text-center text-sm"
|
||||
),
|
||||
cls="mt-4"
|
||||
),
|
||||
|
||||
cls="card-body"
|
||||
),
|
||||
cls="card bg-base-100 w-96 shadow-xl",
|
||||
id="register-card"
|
||||
)
|
||||
|
||||
# Full page container
|
||||
return Div(
|
||||
card,
|
||||
cls="flex items-center justify-center min-h-screen bg-base-200"
|
||||
)
|
||||
|
||||
def _svg_icon(self):
|
||||
"""
|
||||
Helper method to create error icon SVG.
|
||||
|
||||
Returns:
|
||||
SVG icon as NotStr
|
||||
"""
|
||||
from fasthtml.common import NotStr
|
||||
return NotStr('''
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
''')
|
||||
|
||||
def __ft__(self):
|
||||
"""
|
||||
FastHTML rendering protocol method.
|
||||
|
||||
Returns:
|
||||
Rendered register page component
|
||||
"""
|
||||
return self._render()
|
||||
|
||||
|
||||
def create_success_message(message: str = "Account created successfully!"):
|
||||
"""
|
||||
Create a success alert component for HTMX response.
|
||||
|
||||
Args:
|
||||
message: Success message to display
|
||||
|
||||
Returns:
|
||||
Success alert component
|
||||
"""
|
||||
from fasthtml.common import NotStr
|
||||
|
||||
return Div(
|
||||
Div(
|
||||
NotStr('''
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
'''),
|
||||
message,
|
||||
cls="flex items-center gap-2"
|
||||
),
|
||||
role="alert",
|
||||
cls="alert alert-success mb-4"
|
||||
)
|
||||
185
src/myfasthtml/auth/pages/WelcomePage.py
Normal file
185
src/myfasthtml/auth/pages/WelcomePage.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
Welcome page component for FastHTML application.
|
||||
|
||||
Provides a protected welcome page that displays user information.
|
||||
This page is only accessible to authenticated users.
|
||||
"""
|
||||
|
||||
from fasthtml.common import Div, H1, H2, P, Button, A
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
class WelcomePage:
|
||||
"""
|
||||
Welcome page component for authenticated users.
|
||||
|
||||
Attributes:
|
||||
user_info: Dictionary containing user information (email, id, etc.)
|
||||
redirect_url: URL for logout redirect
|
||||
"""
|
||||
|
||||
def __init__(self, user_info: Optional[Dict[str, Any]] = None, redirect_url: str = "/login"):
|
||||
"""
|
||||
Initialize welcome page.
|
||||
|
||||
Args:
|
||||
user_info: Dictionary containing user information
|
||||
redirect_url: URL to redirect after logout (default: "/login")
|
||||
"""
|
||||
self.user_info = user_info or {}
|
||||
self.redirect_url = redirect_url
|
||||
|
||||
def _render(self):
|
||||
"""
|
||||
Render the welcome page HTML structure.
|
||||
|
||||
Returns:
|
||||
FastHTML component tree for the welcome page
|
||||
"""
|
||||
# Extract user info
|
||||
email = self.user_info.get('email', 'Guest')
|
||||
user_id = self.user_info.get('id', 'N/A')
|
||||
|
||||
# Hero section
|
||||
hero = Div(
|
||||
Div(
|
||||
# Welcome message
|
||||
H1(
|
||||
f"Welcome back, {email}!",
|
||||
cls="text-5xl font-bold"
|
||||
),
|
||||
|
||||
P(
|
||||
"You have successfully logged in to your account.",
|
||||
cls="py-6 text-lg"
|
||||
),
|
||||
|
||||
# Action buttons
|
||||
Div(
|
||||
Button(
|
||||
"Logout",
|
||||
cls="btn btn-primary",
|
||||
hx_post="/logout",
|
||||
hx_target="body",
|
||||
hx_swap="outerHTML"
|
||||
),
|
||||
cls="flex gap-2"
|
||||
),
|
||||
|
||||
cls="max-w-md"
|
||||
),
|
||||
cls="hero min-h-screen bg-base-200"
|
||||
)
|
||||
|
||||
# User info card
|
||||
info_card = Div(
|
||||
Div(
|
||||
H2("Your Information", cls="card-title mb-4"),
|
||||
|
||||
# User details
|
||||
Div(
|
||||
self._info_row("Email", email),
|
||||
self._info_row("User ID", str(user_id)),
|
||||
cls="space-y-2"
|
||||
),
|
||||
|
||||
cls="card-body"
|
||||
),
|
||||
cls="card bg-base-100 shadow-xl w-full max-w-md"
|
||||
)
|
||||
|
||||
# Main container
|
||||
return Div(
|
||||
# Hero section
|
||||
hero,
|
||||
|
||||
# Info card (positioned absolutely over hero)
|
||||
Div(
|
||||
info_card,
|
||||
cls="absolute top-20 right-20 hidden lg:block"
|
||||
),
|
||||
|
||||
cls="relative"
|
||||
)
|
||||
|
||||
def _info_row(self, label: str, value: str):
|
||||
"""
|
||||
Create an information row for displaying user details.
|
||||
|
||||
Args:
|
||||
label: Label for the information
|
||||
value: Value to display
|
||||
|
||||
Returns:
|
||||
Div containing label and value
|
||||
"""
|
||||
return Div(
|
||||
Div(
|
||||
P(label, cls="font-semibold text-sm text-base-content/70"),
|
||||
P(value, cls="text-base"),
|
||||
cls="flex flex-col"
|
||||
),
|
||||
cls="py-2 border-b border-base-300 last:border-b-0"
|
||||
)
|
||||
|
||||
def __ft__(self):
|
||||
"""
|
||||
FastHTML rendering protocol method.
|
||||
|
||||
Returns:
|
||||
Rendered welcome page component
|
||||
"""
|
||||
return self._render()
|
||||
|
||||
|
||||
class SimpleWelcomePage:
|
||||
"""
|
||||
Simpler version of welcome page without user info card.
|
||||
Useful for quick prototyping or minimal designs.
|
||||
"""
|
||||
|
||||
def __init__(self, user_email: str = "Guest"):
|
||||
"""
|
||||
Initialize simple welcome page.
|
||||
|
||||
Args:
|
||||
user_email: User's email address
|
||||
"""
|
||||
self.user_email = user_email
|
||||
|
||||
def _render(self):
|
||||
"""
|
||||
Render the simple welcome page.
|
||||
|
||||
Returns:
|
||||
FastHTML component tree
|
||||
"""
|
||||
return Div(
|
||||
Div(
|
||||
Div(
|
||||
H1(f"Welcome, {self.user_email}!", cls="text-4xl font-bold mb-4"),
|
||||
P("You are now logged in.", cls="mb-6 text-lg"),
|
||||
|
||||
Button(
|
||||
"Logout",
|
||||
cls="btn btn-primary",
|
||||
hx_post="/logout",
|
||||
hx_target="body",
|
||||
hx_swap="outerHTML"
|
||||
),
|
||||
|
||||
cls="text-center"
|
||||
),
|
||||
cls="hero-content"
|
||||
),
|
||||
cls="hero min-h-screen bg-base-200"
|
||||
)
|
||||
|
||||
def __ft__(self):
|
||||
"""
|
||||
FastHTML rendering protocol method.
|
||||
|
||||
Returns:
|
||||
Rendered simple welcome page
|
||||
"""
|
||||
return self._render()
|
||||
0
src/myfasthtml/auth/pages/__init__.py
Normal file
0
src/myfasthtml/auth/pages/__init__.py
Normal file
Reference in New Issue
Block a user