Added first controls

This commit is contained in:
2025-11-26 20:53:12 +01:00
parent 459c89bae2
commit ce5328fe34
68 changed files with 37849 additions and 87048 deletions

View File

@@ -41,7 +41,7 @@ Note: `python-jose` may already be installed if you have FastAPI.
### 1. Update API configuration in `auth/utils.py`
```python
API_BASE_URL = "http://localhost:5001" # Your FastAPI backend URL
API_BASE_URL = "http://localhost:5003" # Your FastAPI backend URL
JWT_SECRET = "jwt-secret-to-change" # Must match your FastAPI secret
```

View File

@@ -16,9 +16,10 @@ from ..auth.utils import (
logout_user,
get_user_info
)
from ..core.instances import InstancesManager
def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db", base_url=None):
"""
Setup all authentication and protected routes.
@@ -27,6 +28,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
rt: Route decorator from FastHTML
mount_auth_app: Whether to mount the auth FastApi API routes
sqlite_db_path: by default, create a new SQLite database at this path
base_url: Base URL for the application (default to localhost:5001 if not provided)
"""
# ============================================================================
@@ -61,7 +63,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
RedirectResponse on success, or LoginPage with error on failure
"""
# Attempt login
auth_data = login_user(email, password)
auth_data = login_user(email, password, base_url=base_url)
if auth_data:
# Login successful - store tokens in session
@@ -69,7 +71,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
session['refresh_token'] = auth_data['refresh_token']
# Get user info and store in session
user_info = get_user_info(auth_data['access_token'])
user_info = get_user_info(auth_data['access_token'], base_url=base_url)
if user_info:
session['user_info'] = user_info
@@ -116,7 +118,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
return RegisterPage(error_message="Password must be at least 8 characters long.")
# Attempt registration
result = register_user(email, username, password)
result = register_user(email, username, password, base_url=base_url)
if result:
# Registration successful - show success message and auto-login
@@ -180,6 +182,9 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"):
if refresh_token:
logout_user(refresh_token)
# release memory
InstancesManager.clear_session(session)
# Clear session
session.clear()

View File

@@ -15,7 +15,7 @@ from fasthtml.common import RedirectResponse, Beforeware
from jose import jwt, JWTError
# Configuration
API_BASE_URL = "http://localhost:5001" # Base URL for FastAPI backend
API_BASE_URL = "http://localhost:5003" # Base URL for FastAPI backend
JWT_SECRET = "jwt-secret-to-change" # Must match FastAPI secret
JWT_ALGORITHM = "HS256"
TOKEN_REFRESH_THRESHOLD_MINUTES = 5 # Refresh token if expires in less than 5 minutes
@@ -63,14 +63,12 @@ def auth_before(request, session):
Args:
request: Starlette request object
session: FastHTML session object
Returns:
RedirectResponse to login page if authentication fails, None otherwise
"""
# Get tokens from session
access_token = session.get('access_token')
refresh_token = session.get('refresh_token')
print(f"path={request.scope['path']}, {session=}, {access_token=}, {refresh_token=}")
# If no access token, redirect to login
if not access_token:
return RedirectResponse('/login', status_code=303)
@@ -163,13 +161,14 @@ def check_token_expiry(token: str) -> Optional[float]:
return None
def login_user(email: str, password: str) -> Optional[Dict[str, Any]]:
def login_user(email: str, password: str, base_url: str = None) -> Optional[Dict[str, Any]]:
"""
Authenticate user with email and password.
Args:
email: User email address
password: User password
base_url:
Returns:
Dictionary containing access_token, refresh_token, and user_info if successful,
@@ -177,7 +176,7 @@ def login_user(email: str, password: str) -> Optional[Dict[str, Any]]:
"""
try:
response = http_client.post(
f"{API_BASE_URL}/auth/login",
f"{base_url or API_BASE_URL}/auth/login",
data={"username": email, "password": password},
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=10.0
@@ -196,7 +195,7 @@ def login_user(email: str, password: str) -> Optional[Dict[str, Any]]:
return None
def register_user(email: str, username: str, password: str) -> Optional[Dict[str, Any]]:
def register_user(email: str, username: str, password: str, base_url: str = None) -> Optional[Dict[str, Any]]:
"""
Register a new user.
@@ -204,14 +203,14 @@ def register_user(email: str, username: str, password: str) -> Optional[Dict[str
email: User email address
username: User name
password: User password
base_url:
Returns:
Dictionary containing success message if registration succeeds,
None if registration fails
"""
try:
response = http_client.post(
f"{API_BASE_URL}/auth/register",
f"{base_url or API_BASE_URL}/auth/register",
json={"email": email, "username": username, "password": password},
timeout=10.0
)
@@ -224,20 +223,20 @@ def register_user(email: str, username: str, password: str) -> Optional[Dict[str
return None
def refresh_access_token(refresh_token: str) -> Optional[Dict[str, Any]]:
def refresh_access_token(refresh_token: str, base_url: str = None) -> Optional[Dict[str, Any]]:
"""
Refresh the access token using a refresh token.
Args:
refresh_token: Valid refresh token
base_url:
Returns:
Dictionary containing new access_token and refresh_token if successful,
None if refresh fails
"""
try:
response = http_client.post(
f"{API_BASE_URL}/auth/refresh",
f"{base_url or API_BASE_URL}/auth/refresh",
json={"refresh_token": refresh_token},
timeout=10.0
)
@@ -254,20 +253,20 @@ def refresh_access_token(refresh_token: str) -> Optional[Dict[str, Any]]:
return None
def get_user_info(access_token: str) -> Optional[Dict[str, Any]]:
def get_user_info(access_token: str, base_url: str = None) -> Optional[Dict[str, Any]]:
"""
Get current user information using access token.
Args:
access_token: Valid access token
base_url:
Returns:
Dictionary containing user information if successful,
None if request fails
"""
try:
response = http_client.get(
f"{API_BASE_URL}/auth/me",
f"{base_url or API_BASE_URL}/auth/me",
headers={"Authorization": f"Bearer {access_token}"},
timeout=10.0
)
@@ -280,19 +279,36 @@ def get_user_info(access_token: str) -> Optional[Dict[str, Any]]:
return None
def logout_user(refresh_token: str) -> bool:
def save_user_info(access_token: str, user_profile: dict, base_url: str = None):
try:
response = http_client.patch(
f"{base_url or API_BASE_URL}/auth/me",
headers={"Authorization": f"Bearer {access_token}"},
timeout=10.0,
json=user_profile
)
if response.status_code == 200:
return response.json()
return None
except httpx.HTTPError:
return None
def logout_user(refresh_token: str, base_url: str = None) -> bool:
"""
Logout user by revoking the refresh token.
Args:
refresh_token: Refresh token to revoke
base_url:
Returns:
True if logout successful, False otherwise
"""
try:
response = http_client.post(
f"{API_BASE_URL}/auth/logout",
f"{base_url or API_BASE_URL}/auth/logout",
json={"refresh_token": refresh_token},
timeout=10.0
)