{"id":25922213,"url":"https://github.com/andreasthinks/fasthtml-admin","last_synced_at":"2025-07-17T07:35:45.660Z","repository":{"id":279795062,"uuid":"939988241","full_name":"AndreasThinks/fasthtml-admin","owner":"AndreasThinks","description":"Providing helper user management and authentication functions for FastHTML","archived":false,"fork":false,"pushed_at":"2025-02-27T18:12:23.000Z","size":94,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-12T14:26:13.759Z","etag":null,"topics":["fasthtml","python","starlette"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AndreasThinks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-27T12:41:05.000Z","updated_at":"2025-05-20T08:25:43.000Z","dependencies_parsed_at":"2025-02-27T18:31:13.271Z","dependency_job_id":"2bab5965-a6df-41a9-abfa-472a95a7960e","html_url":"https://github.com/AndreasThinks/fasthtml-admin","commit_stats":null,"previous_names":["andreasthinks/fasthtml-admin"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/AndreasThinks/fasthtml-admin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreasThinks%2Ffasthtml-admin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreasThinks%2Ffasthtml-admin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreasThinks%2Ffasthtml-admin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreasThinks%2Ffasthtml-admin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AndreasThinks","download_url":"https://codeload.github.com/AndreasThinks/fasthtml-admin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreasThinks%2Ffasthtml-admin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265290292,"owners_count":23741592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fasthtml","python","starlette"],"created_at":"2025-03-03T16:17:47.900Z","updated_at":"2025-07-17T07:35:45.560Z","avatar_url":"https://github.com/AndreasThinks.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"A Python library for user authentication, admin management, and database administration in FastHTML applications.\n\n## Features\n\n- User management (registration, authentication, and confirmation)\n- Admin user creation and management\n- Sqlite database download, upload, backup and restore\n- Built-in validation functions for passwords, emails, and more\n- Extensible validation system for custom validation rules\n- HTMX integration for real-time form validation\n- OAuth integration \n\n## Installation\n\nYou can install the package using pip or uv.\n\n```bash\npip install fasthtml-admin\n```\n\n## Usage\n\nFor detailed usage, look at our [example application](https://github.com/AndreasThinks/fasthtml-admin/blob/main/example.py).\n\n### User Management\n\n```python\nfrom fasthtml.common import database\nfrom fasthtml_admin import UserManager, UserCredential\n\n# Create a FastHTML database\ndb = database(\"data/myapp.db\")\n\n# Initialize UserManager with the database\nuser_manager = UserManager(db)\n\n# Create a new user\ntry:\n    user = user_manager.create_user(\n        email=\"user@example.com\",\n        password=\"secure_password\"\n    )\n    print(f\"User created with ID: {user.id}\")\nexcept ValueError as e:\n    print(f\"Error creating user: {e}\")\n\n# Authenticate a user\nuser = user_manager.authenticate_user(\"user@example.com\", \"secure_password\")\nif user:\n    print(f\"User authenticated: {user.email}\")\nelse:\n    print(\"Authentication failed\")\n\n# Confirm a user\nsuccess = user_manager.confirm_user(\"user@example.com\")\nif success:\n    print(\"User confirmed\")\nelse:\n    print(\"User confirmation failed\")\n```\n\n### Admin Management\n\nYou can quickly create admin users, who have elevated access.\n\n```python\nfrom fasthtml.common import database\nfrom fasthtml_admin import UserManager, AdminManager\n\n# Create a FastHTML database\ndb = database(\"data/myapp.db\")\n\n# Initialize UserManager with the database\nuser_manager = UserManager(db)\n\n# Initialize AdminManager with the UserManager\nadmin_manager = AdminManager(user_manager)\n\n# Ensure an admin user exists\nadmin = admin_manager.ensure_admin(\"admin@example.com\", \"admin_password\")\nprint(f\"Admin user: {admin.email}\")\n\n# Backup the database\nbackup_path = admin_manager.backup_database(\"data/myapp.db\", backup_dir=\"backups\")\nprint(f\"Database backed up to: {backup_path}\")\n\n# Restore the database from a backup\nadmin_manager.restore_database(\"data/myapp.db\", backup_path)\nprint(\"Database restored successfully\")\n\n# Upload a database file\nwith open(\"path/to/uploaded_file.db\", \"rb\") as f:\n    file_content = f.read()\n    admin_manager.upload_database(\"data/myapp.db\", file_content)\nprint(\"Database uploaded successfully\")\n```\n\n### User Confirmation\n\nIf you wish to confirm users (for example, using an email confirmation flow), you can create and register confirmation tokens.\n\n```python\nfrom fasthtml.common import database\nfrom fasthtml_admin import UserManager, ConfirmToken\n\n# Create a FastHTML database\ndb = database(\"data/myapp.db\")\n\n# Create a token store for confirmation tokens\nconfirm_tokens = db.create(ConfirmToken, pk=\"token\")\n\n# Initialize UserManager with the database\nuser_manager = UserManager(db)\n\n# Generate a confirmation token\ntoken = user_manager.generate_confirmation_token(\"user@example.com\", confirm_tokens)\nprint(f\"Generated token: {token}\")\n\n# Define an email sender function\ndef send_confirmation_email(email, token):\n    print(f\"Sending confirmation email to {email}\")\n    print(f\"Confirmation link: http://example.com/confirm-email/{token}\")\n    return True\n\n# Send a confirmation email\nsend_confirmation_email(\"user@example.com\", token)\n\n# In your route handler for confirmation:\n# confirm_token = confirm_tokens[token]\n# user_manager.confirm_user(confirm_token.email)\n# confirm_token.is_used = True\n# confirm_tokens.update(confirm_token)\n```\n\n### Authentication with Beforeware\n\nThe library provides functions to implement FastHTML's best practices for authentication using Beforeware:\n\n```python\nfrom fasthtml.common import *\nfrom fasthtml_admin import UserManager, auth_before, get_current_user\n\n# Initialize UserManager\ndb = database(\"data/myapp.db\")\nuser_manager = UserManager(db)\n\n# Define a custom auth_before function that uses the library's auth_before\ndef app_auth_before(req, sess):\n    return auth_before(req, sess, user_manager, \n                      login_url='/login',\n                      public_paths=['/', '/login', '/register', '/confirm-email'])\n\n# Create a FastHTML app with session support and authentication\nbeforeware = Beforeware(app_auth_before)\napp, rt = fast_app(\n    secret_key=\"your-secret-key-here\",  # In production, use a secure random key\n    before=beforeware,\n    session_cookie=\"session\",\n    max_age=3600 * 24 * 7,  # 7 days\n    sess_path=\"/\",\n    same_site=\"lax\",\n    sess_https_only=False  # Set to True in production with HTTPS\n)\n\n# In your route handlers, use get_current_user to get the current user\n@app.get(\"/dashboard\")\ndef dashboard(session):\n    user = get_current_user(session, user_manager)\n    # The auth_before Beforeware will handle redirecting if not logged in\n    \n    return Container(\n        H1(\"Dashboard\"),\n        P(f\"Welcome to your dashboard, {user.email}!\"),\n        # ...\n    )\n```\n\n## Integrating with Existing FastHTML Applications\n\n### Step 1: Install the Library\n\n```bash\npip install fasthtml-admin\n```\n\n### Step 2: Set Up Database and User Management\n\n```python\nfrom fasthtml.common import *\nfrom fasthtml_admin import UserManager, UserCredential, AdminManager, ConfirmToken\n\n# Create or connect to your existing database\ndb = database(\"data/myapp.db\")\n\n# Create a token store for confirmation tokens\nconfirm_tokens = db.create(ConfirmToken, pk=\"token\")\n\n# Initialize UserManager with your database\nuser_manager = UserManager(db)\n\n# Initialize AdminManager with your UserManager\nadmin_manager = AdminManager(user_manager)\n\n# Create an admin user if needed\nadmin_email = \"admin@example.com\"\nadmin_password = \"adminpass\"\nadmin_manager.ensure_admin(admin_email, admin_password)\n```\n\n### Extending the User Class\n\nYou can extend the basic `UserCredential` class with your own fields:\n\n```python\nfrom dataclasses import dataclass\nfrom fasthtml_admin import UserCredential, UserManager\n\n@dataclass\nclass ExtendedUser(UserCredential):\n    \"\"\"\n    Extended user class with additional fields.\n    \"\"\"\n    first_name: str = \"\"\n    last_name: str = \"\"\n    phone: str = \"\"\n    bio: str = \"\"\n    profile_image: str = \"\"\n\n# Initialize UserManager with the extended user class\nuser_manager = UserManager(db, user_class=ExtendedUser)\n\n# Create a user with additional fields\nuser = user_manager.create_user(\n    email=\"user@example.com\",\n    password=\"secure_password\",\n    first_name=\"John\",\n    last_name=\"Doe\",\n    phone=\"555-123-4567\",\n    bio=\"A short bio about the user\",\n    profile_image=\"https://example.com/profile.jpg\"\n)\n```\n\nThis allows you to store additional user information in the database without having to create and manage separate tables.\n\n### Step 3: Set Up Authentication with Beforeware\n\n```python\nfrom fasthtml.common import *\nfrom fasthtml_admin import auth_before, get_current_user\n\n# Define a custom auth_before function that uses the library's auth_before\ndef app_auth_before(req, sess):\n    return auth_before(req, sess, user_manager, \n                      login_url='/login',\n                      public_paths=['/', '/login', '/register', '/confirm-email'])\n\n# Create a FastHTML app with session support and authentication\nbeforeware = Beforeware(app_auth_before)\napp, rt = fast_app(\n    secret_key=\"your-secret-key-here\",  # In production, use a secure random key\n    before=beforeware,\n    session_cookie=\"session\",\n    max_age=3600 * 24 * 7,  # 7 days\n    sess_path=\"/\",\n    same_site=\"lax\",\n    sess_https_only=False  # Set to True in production with HTTPS\n)\n```\n\n### Step 4: Add Authentication Routes\n\n```python\n@app.get(\"/login\")\ndef get_login(session):\n    user = get_current_user(session, user_manager)\n    if user:\n        return RedirectResponse(\"/\")\n    \n    form = Form(\n        H1(\"Login\"),\n        Input(name=\"email\", type=\"email\", placeholder=\"Email\", required=True),\n        Input(name=\"password\", type=\"password\", placeholder=\"Password\", required=True),\n        Button(\"Login\", type=\"submit\"),\n        P(A(\"Don't have an account? Register\", href=\"/register\")),\n        action=\"/login\",\n        method=\"post\"\n    )\n    \n    return Container(form)\n\n@app.post(\"/login\")\ndef post_login(email: str, password: str, session):\n    user = user_manager.authenticate_user(email, password)\n    \n    if not user:\n        return Container(\n            H1(\"Login Failed\"),\n            P(\"Invalid email or password.\"),\n            A(\"Try Again\", href=\"/login\", cls=\"button\")\n        )\n    \n    # Check if user is confirmed\n    is_confirmed = user.is_confirmed if user_manager.is_db else user[\"is_confirmed\"]\n    if not is_confirmed:\n        return Container(\n            H1(\"Login Failed\"),\n            P(\"Your email has not been confirmed.\"),\n            P(\"Please check your email for a confirmation link.\"),\n            A(\"Try Again\", href=\"/login\", cls=\"button\")\n        )\n    \n    # Store user ID in session\n    user_id = user.id if user_manager.is_db else user[\"id\"]\n    session['user_id'] = user_id\n    \n    # Redirect to dashboard\n    # Use status_code 303 to change the method from POST to GET\n    return RedirectResponse(\"/dashboard\", status_code=303)\n\n@app.get(\"/logout\")\ndef logout(session):\n    # Clear session\n    if 'user_id' in session:\n        del session['user_id']\n    \n    return RedirectResponse(\"/\")\n```\n\n### Step 5: Add Registration and Confirmation Routes\n\n```python\n@app.get(\"/register\")\ndef get_register(session):\n    user = get_current_user(session, user_manager)\n    if user:\n        return RedirectResponse(\"/\")\n    \n    form = Form(\n        H1(\"Register\"),\n        Input(name=\"email\", type=\"email\", placeholder=\"Email\", required=True),\n        Input(name=\"password\", type=\"password\", placeholder=\"Password\", required=True),\n        Input(name=\"confirm_password\", type=\"password\", placeholder=\"Confirm Password\", required=True),\n        Button(\"Register\", type=\"submit\"),\n        P(A(\"Already have an account? Login\", href=\"/login\")),\n        action=\"/register\",\n        method=\"post\"\n    )\n    \n    return Container(form)\n\n@app.post(\"/register\")\ndef post_register(email: str, password: str, confirm_password: str):\n    if password != confirm_password:\n        return Container(\n            H1(\"Registration Failed\"),\n            P(\"Passwords do not match.\"),\n            A(\"Try Again\", href=\"/register\", cls=\"button\")\n        )\n    \n    try:\n        # Create user\n        user = user_manager.create_user(email, password)\n        \n        # Generate confirmation token\n        token = user_manager.generate_confirmation_token(email, confirm_tokens)\n        \n        # Send confirmation email\n        send_confirmation_email(email, token)\n        \n        return Container(\n            H1(\"Registration Successful\"),\n            P(\"A confirmation email has been sent to your email address.\"),\n            P(\"Please check your email and click the confirmation link to activate your account.\"),\n            A(\"Login\", href=\"/login\", cls=\"button\")\n        )\n    except ValueError as e:\n        return Container(\n            H1(\"Registration Failed\"),\n            P(str(e)),\n            A(\"Try Again\", href=\"/register\", cls=\"button\")\n        )\n\n@app.get(\"/confirm-email/{token}\")\ndef confirm_email(token: str):\n    try:\n        # Find token in database\n        confirm_token = confirm_tokens[token]\n        \n        # Check if token is already used\n        if confirm_token.is_used:\n            return Container(\n                H1(\"Confirmation Failed\"),\n                P(\"This confirmation link has already been used.\"),\n                A(\"Login\", href=\"/login\", cls=\"button\")\n            )\n        \n        # Check if token is expired\n        # Convert expiry from string to datetime if needed\n        expiry = confirm_token.expiry\n        if isinstance(expiry, str):\n            expiry = datetime.fromisoformat(expiry)\n        if expiry \u003c datetime.now():\n            return Container(\n                H1(\"Confirmation Failed\"),\n                P(\"This confirmation link has expired.\"),\n                A(\"Register Again\", href=\"/register\", cls=\"button\")\n            )\n        \n        # Confirm user\n        user_manager.confirm_user(confirm_token.email)\n        \n        # Mark token as used\n        confirm_token.is_used = True\n        confirm_tokens.update(confirm_token)\n        \n        return Container(\n            H1(\"Email Confirmed\"),\n            P(\"Your email has been confirmed. You can now login.\"),\n            A(\"Login\", href=\"/login\", cls=\"button\")\n        )\n    except (KeyError, IndexError, Exception) as e:\n        # NotFoundError is raised by FastHTML database when a record is not found\n        if not isinstance(e, Exception) or \"NotFoundError\" not in str(type(e)):\n            # If it's not a NotFoundError, re-raise it\n            if not isinstance(e, (KeyError, IndexError)):\n                raise\n        return Container(\n            H1(\"Confirmation Failed\"),\n            P(\"Invalid confirmation link.\"),\n            A(\"Register Again\", href=\"/register\", cls=\"button\")\n        )\n```\n\n### Step 6: Protect Your Routes\n\nWith the auth_before Beforeware in place, your routes are automatically protected. You can still check for admin privileges or other specific conditions in your route handlers:\n\n```python\n@app.get(\"/admin\")\ndef admin_panel(session):\n    user = get_current_user(session, user_manager)\n    # The auth_before Beforeware will handle redirecting if not logged in\n    \n    is_admin = user.is_admin if user_manager.is_db else user[\"is_admin\"]\n    if not is_admin:\n        return Container(\n            H1(\"Access Denied\"),\n            P(\"You do not have permission to access this page.\"),\n            A(\"Go to Dashboard\", href=\"/dashboard\", cls=\"button\")\n        )\n    \n    # Your admin panel code here\n    return Container(\n        H1(\"Admin Panel\"),\n        # ...\n    )\n```\n\n### Best Practices\n\n1. **Security**: Always store passwords securely using the provided `hash_password` function.\n2. **Sessions**: Use FastHTML's built-in session support with a secure random key.\n3. **Authentication**: Use the provided `auth_before` function with Beforeware for centralized authentication.\n4. **User Management**: Use the `get_current_user` function to retrieve the current user from the session.\n5. **Email Confirmation**: Implement a real email sending function for production.\n6. **Error Handling**: Add comprehensive error handling for database operations.\n7. **CSRF Protection**: Add CSRF protection for form submissions.\n8. **Rate Limiting**: Implement rate limiting for login attempts to prevent brute force attacks.\n9. **HTTPS**: In production, set `sess_https_only=True` and use HTTPS.\n\n## Validation System\n\nThe library includes a robust validation system for validating user input. It provides built-in validators for common validation tasks and allows you to register custom validators.\n\n### Built-in Validators\n\nThe following validators are included out of the box:\n\n1. **Password Strength Validation**\n   ```python\n   from fasthtml_admin import validation_manager\n   \n   # Returns a score (0-100) and a list of issues\n   score, issues = validation_manager.validate(\"password_strength\", \"my_password\")\n   \n   # Check if password is strong enough\n   if score \u003e= 50:\n       print(\"Password is strong enough\")\n   else:\n       print(f\"Password issues: {', '.join(issues)}\")\n   ```\n\n2. **Email Format Validation**\n   ```python\n   from fasthtml_admin import validation_manager\n   \n   # Returns a boolean and a message\n   is_valid, message = validation_manager.validate(\"email_format\", \"user@example.com\")\n   \n   if is_valid:\n       print(\"Email format is valid\")\n   else:\n       print(message)\n   ```\n\n3. **Passwords Match Validation**\n   ```python\n   from fasthtml_admin import validation_manager\n   \n   # Returns a boolean and a message\n   is_match, message = validation_manager.validate(\"passwords_match\", \"password1\", \"password1\")\n   \n   if is_match:\n       print(\"Passwords match\")\n   else:\n       print(message)\n   ```\n\n### Custom Validators\n\nYou can register your own custom validators with the validation system:\n\n```python\nfrom fasthtml_admin import validation_manager\n\n# Define a custom validator function\ndef validate_username(username: str) -\u003e tuple[bool, str]:\n    \"\"\"\n    Validate username format and return (is_valid, message).\n    \"\"\"\n    if not username:\n        return False, \"Username is required\"\n    \n    if len(username) \u003c 3:\n        return False, \"Username must be at least 3 characters\"\n    \n    if len(username) \u003e 20:\n        return False, \"Username must be at most 20 characters\"\n    \n    if not re.match(r'^[a-zA-Z0-9_]+$', username):\n        return False, \"Username must contain only letters, numbers, and underscores\"\n    \n    return True, \"Username is valid\"\n\n# Register the custom validator\nvalidation_manager.register_validator(\"username\", validate_username)\n\n# Use the custom validator\nis_valid, message = validation_manager.validate(\"username\", \"user123\")\n```\n\n### HTMX Integration for Real-time Validation\n\nThe library can be easily integrated with HTMX to provide real-time validation feedback to users. Here's an example of how to set up real-time validation for a registration form:\n\n```python\n@app.get(\"/register\")\ndef get_register(session):\n    # Create a form with HTMX validation\n    form = Form(\n        H1(\"Registration with Real-time Validation\"),\n        \n        # Username field with HTMX validation\n        Div(\n            Label(\"Username\", \n                  Input(name=\"username\", placeholder=\"Username\", required=True,\n                        hx_post=\"/validate/username\",\n                        hx_trigger=\"keyup changed delay:500ms\",\n                        hx_target=\"#username-feedback\")),\n            Div(id=\"username-feedback\", cls=\"feedback\"),\n            cls=\"form-group\"\n        ),\n        \n        # Email field with HTMX validation\n        Div(\n            Label(\"Email\", \n                  Input(name=\"email\", type=\"email\", placeholder=\"Email\", required=True,\n                        hx_post=\"/validate/email\",\n                        hx_trigger=\"keyup changed delay:500ms\",\n                        hx_target=\"#email-feedback\")),\n            Div(id=\"email-feedback\", cls=\"feedback\"),\n            cls=\"form-group\"\n        ),\n        \n        # Password field with HTMX validation\n        Div(\n            Label(\"Password\", \n                  Input(name=\"password\", type=\"password\", placeholder=\"Password\", required=True,\n                        hx_post=\"/validate/password\",\n                        hx_trigger=\"keyup changed delay:500ms\",\n                        hx_target=\"#password-feedback\")),\n            Div(id=\"password-feedback\", cls=\"feedback\"),\n            cls=\"form-group\"\n        ),\n        \n        Button(\"Register\", type=\"submit\"),\n        action=\"/register\",\n        method=\"post\"\n    )\n    \n    return Container(form)\n\n@app.post(\"/validate/username\")\ndef validate_username_endpoint(username: str):\n    is_valid, message = validation_manager.validate(\"username\", username)\n    cls = \"valid\" if is_valid else \"invalid\"\n    return Div(message, cls=f\"feedback {cls}\")\n\n@app.post(\"/validate/email\")\ndef validate_email_endpoint(email: str):\n    is_valid, message = validation_manager.validate(\"email_format\", email)\n    cls = \"valid\" if is_valid else \"invalid\"\n    return Div(message, cls=f\"feedback {cls}\")\n\n@app.post(\"/validate/password\")\ndef validate_password_endpoint(password: str):\n    score, issues = validation_manager.validate(\"password_strength\", password)\n    is_valid = score \u003e= 50\n    \n    if is_valid:\n        message = f\"Password strength: {score}/100\"\n        cls = \"valid\"\n    else:\n        message = f\"Issues: {', '.join(issues)}\"\n        cls = \"invalid\"\n    \n    return Div(message, cls=f\"feedback {cls}\")\n```\n\nThis setup provides immediate feedback to users as they type, improving the user experience and reducing form submission errors.\n\n## Example Applications\n\nThe library includes example FastHTML applications that demonstrate all the features:\n\n### Basic Example\n\n```bash\ncd fasthtml-admin\npython example.py\n```\n\nThis will start a web server at http://localhost:8000 with the following features:\n- User registration with email confirmation\n- User login with session management\n- Admin panel with database backup and restore\n- Authentication using FastHTML's Beforeware\n- Real-time form validation using HTMX\n\n### OAuth Example\n\n```bash\ncd fasthtml-admin\npython example_oauth.py\n```\n\nThis example demonstrates OAuth integration with the fasthtml_admin library:\n- Authentication with GitHub OAuth\n- Integration with the UserManager for user creation and retrieval\n- Protected routes with authentication\n- Session management\n\nTo use the OAuth example, you need to:\n1. Register an OAuth app on GitHub (or your preferred provider)\n2. Set the required environment variables (e.g., GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET)\n3. Set the Authorization callback URL to http://localhost:8000/auth_redirect\n\n## OAuth Integration\n\nThe library includes an `OAuthManager` class that integrates with FastHTML's OAuth support to provide third-party authentication. Here's how to use it:\n\n```python\nfrom fasthtml.common import *\nfrom fasthtml.oauth import GitHubAppClient\nfrom fasthtml_admin import UserManager, OAuthManager, get_current_user\n\n# Initialize UserManager\ndb = database(\"data/myapp.db\")\nuser_manager = UserManager(db)\n\n# Create an OAuth client\nclient = GitHubAppClient(\n    client_id=os.environ.get(\"GITHUB_CLIENT_ID\"),\n    client_secret=os.environ.get(\"GITHUB_CLIENT_SECRET\")\n)\n\n# Create a FastHTML app\napp, rt = fast_app(\n    secret_key=\"your-secret-key-here\",\n    session_cookie=\"session\",\n    max_age=3600 * 24 * 7  # 7 days\n)\n\n# Initialize OAuthManager with the app, client, and user manager\noauth = OAuthManager(\n    app=app,\n    client=client,\n    user_manager=user_manager,\n    redir_path='/auth_redirect',  # Path for OAuth callback\n    login_path='/login',          # Path to redirect to for login\n    dashboard_path='/dashboard'   # Path to redirect to after successful authentication\n)\n\n# Add routes\n@app.get('/')\ndef home(session, request):\n    user = get_current_user(session, user_manager)\n    login_link = oauth.login_link(request)\n    \n    if user:\n        # User is logged in\n        return Container(\n            H1(f\"Welcome, {user.email}!\"),\n            P(\"You are logged in via OAuth.\"),\n            A(\"Logout\", href=\"/logout\", cls=\"button\")\n        )\n    else:\n        # User is not logged in\n        return Container(\n            H1(\"Welcome\"),\n            P(\"Please log in to continue.\"),\n            A(\"Login with GitHub\", href=login_link, cls=\"button\")\n        )\n\n@app.get('/dashboard')\ndef dashboard(session):\n    user = get_current_user(session, user_manager)\n    # Display user information\n    return Container(\n        H1(f\"Welcome, {user.email}!\"),\n        P(\"You are logged in via OAuth.\")\n    )\n```\n\nThe `OAuthManager` class handles:\n\n1. Setting up the OAuth flow with the provider\n2. Creating or retrieving users based on OAuth information\n3. Managing user sessions\n4. Protecting routes with authentication\n\nIt works with any OAuth provider supported by FastHTML, including:\n- GitHub\n- Google\n- Discord\n- HuggingFace\n- Auth0\n\nFor a complete example, see `example_oauth.py`.\n\n## Maintenance Mode\n\nThe library includes a persistent maintenance mode feature that allows administrators to temporarily restrict access to the system for all non-admin users. When maintenance mode is enabled, non-admin users (including anonymous users) are redirected to a maintenance page.\n\n```python\nfrom fasthtml.common import *\nfrom fasthtml_admin import UserManager, AdminManager, auth_before\n\n# Initialize UserManager and AdminManager\ndb = database(\"data/myapp.db\")\nuser_manager = UserManager(db)\nadmin_manager = AdminManager(user_manager)\n\n# Set up authentication with maintenance mode support\ndef app_auth_before(req, sess):\n    return auth_before(req, sess, user_manager, \n                      login_url='/login',\n                      public_paths=['/', '/register'],\n                      admin_manager=admin_manager,\n                      maintenance_url='/maintenance')\n\n# Create a FastHTML app with authentication\nbeforeware = Beforeware(app_auth_before)\napp, rt = fast_app(\n    secret_key=\"your-secret-key-here\",\n    before=beforeware,\n    session_cookie=\"session\"\n)\n\n# Add a maintenance page\n@app.get(\"/maintenance\")\ndef maintenance_page():\n    return Container(\n        H1(\"System Maintenance\"),\n        P(\"The system is currently undergoing maintenance.\"),\n        P(\"Please check back later.\"),\n        P(\"If you are an administrator, please log in to access the system.\"),\n        A(\"Login\", href=\"/login\", cls=\"button\")\n    )\n\n# Add controls for admins to toggle maintenance mode\n@app.post(\"/admin/maintenance-mode\")\ndef toggle_maintenance_mode(enabled: str, session):\n    user = get_current_user(session, user_manager)\n    is_admin = user.is_admin if user_manager.is_db else user[\"is_admin\"]\n    \n    if not is_admin:\n        return RedirectResponse(\"/dashboard\", status_code=303)\n    \n    # Convert string to boolean\n    enable_mode = enabled.lower() == \"true\"\n    \n    # Set maintenance mode\n    admin_manager.set_maintenance_mode(enable_mode)\n    \n    return RedirectResponse(\"/admin\", status_code=303)\n```\n\nThis example demonstrates:\n1. Setting up the AdminManager with maintenance mode support\n2. Configuring the auth_before function to check for maintenance mode\n3. Adding a maintenance page that users will be redirected to\n4. Adding controls for admins to toggle maintenance mode on/off\n\nWhen maintenance mode is enabled:\n- Admin users can still access all parts of the system\n- Non-admin users (including anonymous users) are redirected to the maintenance page when they try to access any part of the system\n- The login page remains accessible so admins can log in\n- The maintenance page is always accessible\n\n### Persistent Maintenance Mode\n\nThe maintenance mode state is stored in the database, making it persistent across application restarts. This means that if you enable maintenance mode and restart your application, it will remain in maintenance mode.\n\n```python\n# Initialize AdminManager\nadmin_manager = AdminManager(user_manager)\n\n# Check current maintenance mode status\nis_maintenance = admin_manager.is_maintenance_mode()\nprint(f\"Maintenance mode is {'enabled' if is_maintenance else 'disabled'}\")\n\n# Enable maintenance mode\nadmin_manager.set_maintenance_mode(True)\n\n# Disable maintenance mode\nadmin_manager.set_maintenance_mode(False)\n```\n\nThe maintenance mode state is stored in a system_settings table in the database, which is automatically created when you initialize the AdminManager. This ensures that the maintenance mode state is preserved even if the application is restarted.\n\n## Database Upload Example\n\nHere's an example of how to implement a database upload feature in your FastHTML application:\n\n```python\nfrom fasthtml.common import limiter, RedirectResponse, Container, H1, P, A, Form, Input, Button, Titled\n\n@app.get(\"/admin/upload-db\")\ndef get_upload_db(session):\n    user = get_current_user(session, user_manager)\n    # Check if user is admin\n    is_admin = user.is_admin if user_manager.is_db else user[\"is_admin\"]\n    if not is_admin:\n        return Container(\n            H1(\"Access Denied\"),\n            P(\"You do not have permission to access this page.\"),\n            A(\"Go to Dashboard\", href=\"/dashboard\", cls=\"button\")\n        )\n    \n    # Create upload form\n    form = Form(\n        H1(\"Upload Database\"),\n        P(\"Warning: This will replace the current database with the uploaded file.\"),\n        Input(name=\"dbfile\", type=\"file\", accept=\".db\", required=True),\n        Button(\"Upload\", type=\"submit\"),\n        A(\"Cancel\", href=\"/admin\", cls=\"button secondary\"),\n        action=\"/admin/upload-db\",\n        method=\"post\",\n        enctype=\"multipart/form-data\"\n    )\n    \n    return Container(form)\n\n@limiter.limit(\"30/day\")  # Rate limit to prevent abuse\n@app.post(\"/admin/upload-db\")\nasync def post_upload_db(request, session):\n    user = get_current_user(session, user_manager)\n    # Check if user is admin\n    is_admin = user.is_admin if user_manager.is_db else user[\"is_admin\"]\n    if not is_admin:\n        return Container(\n            H1(\"Access Denied\"),\n            P(\"You do not have permission to access this page.\"),\n            A(\"Go to Dashboard\", href=\"/dashboard\", cls=\"button\")\n        )\n    \n    try:\n        # Process form data\n        form = await request.form()\n        file = form[\"dbfile\"]\n        if not file.filename.endswith('.db'):\n            return Titled(\"Error\", P(\"Invalid file type. Please upload a .db file.\"))\n        \n        # Use AdminManager to upload database\n        file_content = await file.read()\n        admin_manager.upload_database(\"data/myapp.db\", file_content)\n        \n        return RedirectResponse(\"/admin?success=true\", status_code=303)\n            \n    except ValueError as e:\n        return Titled(\"Error\", P(str(e)))\n    except Exception as e:\n        return Titled(\"Error\", P(f\"Failed to process upload request: {str(e)}\"))\n```\n\nThis example demonstrates:\n1. A GET route to display the upload form\n2. A POST route to handle the file upload\n3. Rate limiting to prevent abuse\n4. Admin permission checks\n5. File validation (must be a .db file)\n6. Using the AdminManager's upload_database method to handle the upload\n7. Error handling for different types of errors\n\n## Customization\n\nThe library is designed to be flexible and customizable. You can extend the provided classes or implement your own versions of the interfaces to fit your specific needs.\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreasthinks%2Ffasthtml-admin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreasthinks%2Ffasthtml-admin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreasthinks%2Ffasthtml-admin/lists"}