diff --git a/pyproject.toml b/pyproject.toml index a610683..c82fa30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,16 @@ classifiers = [ # Note: 'requirements.txt' is for development, this is for the package # ------------------------------------------------------------------- dependencies = [ + "argon2-cffi", + "email-validator", + "httptools", "myauth", "myutils", + "python-fasthtml", + "PyYAML", + "uvloop", + "watchfiles", + "websockets", ] [project.urls] @@ -62,5 +70,11 @@ dev = [ # This section tells the build system where to find your package code # ------------------------------------------------------------------- [tool.setuptools] -package-dir = {"" = "src"} +package-dir = { "" = "src" } packages = ["myfasthtml"] + +[tool.setuptools.package-data] +myfasthtml = [ + "assets/*.css", + "assets/*.js" +] \ No newline at end of file diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 6ed01cf..0000000 --- a/src/main.py +++ /dev/null @@ -1,31 +0,0 @@ -from fasthtml import serve -from fasthtml.components import Link, Script -from fasthtml.fastapp import fast_app -from myauth import create_auth_app_for_sqlite - -from myfasthtml.auth.routes import setup_auth_routes -from myfasthtml.auth.utils import create_auth_beforeware - -beforeware = create_auth_beforeware() -# Create FastHTML app -daisyui_offline_links = [ - Link(href="./myfasthtml/assets/daisyui-5.css", rel="stylesheet", type="text/css"), - Link(href="./myfasthtml/assets/daisyui-5-themes.css", rel="stylesheet", type="text/css"), - Script(src="./myfasthtml/assets/tailwindcss-browser@4.js"), -] - -daisyui_online_links = [ - Link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/daisyui@5/daisyui.css'), - Script(src='https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4'), -] - -app, rt = fast_app( - before=beforeware, - hdrs=tuple(daisyui_offline_links) -) - -# Setup authentication routes -setup_auth_routes(app, rt) - -if __name__ == "__main__": - serve() diff --git a/src/myfasthtml/auth/pages/LoginPage.py b/src/myfasthtml/auth/pages/LoginPage.py index 7607499..7c59143 100644 --- a/src/myfasthtml/auth/pages/LoginPage.py +++ b/src/myfasthtml/auth/pages/LoginPage.py @@ -65,7 +65,7 @@ class LoginPage: cls="btn w-full font-bold py-2 px-4 rounded" ), - action="/login-p", + action="/login", method="post", cls="mb-6" ), diff --git a/src/myfasthtml/auth/pages/RegisterPage.py b/src/myfasthtml/auth/pages/RegisterPage.py index 5f6bd74..c006db1 100644 --- a/src/myfasthtml/auth/pages/RegisterPage.py +++ b/src/myfasthtml/auth/pages/RegisterPage.py @@ -115,7 +115,7 @@ class RegisterPage: ) ), - action="register-p", + action="register", method="post", cls="mb-6" ), diff --git a/src/myfasthtml/auth/routes.py b/src/myfasthtml/auth/routes.py index 31ec983..db92c18 100644 --- a/src/myfasthtml/auth/routes.py +++ b/src/myfasthtml/auth/routes.py @@ -33,7 +33,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"): # PUBLIC ROUTES (Login & Register) # ============================================================================ - @rt("/login") + @rt("/login", methods=["GET"]) def get(error: str = None): """ Display login page. @@ -46,7 +46,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"): """ return LoginPage(error_message=error) - @rt("/login-p") + @rt("/login", methods=["POST"]) def post(email: str, password: str, session, redirect_url: str = "/"): """ Handle login form submission. @@ -79,7 +79,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"): # Login failed - return error message via HTMX return LoginPage(error_message="Invalid email or password. Please try again.") - @rt("/register") + @rt("/register", methods=["GET"]) def get(error: str = None): """ Display registration page. @@ -92,7 +92,7 @@ def setup_auth_routes(app, rt, mount_auth_app=True, sqlite_db_path="Users.db"): """ return RegisterPage(error_message=error) - @rt("/register-p") + @rt("/register", methods=["POST"]) def post(email: str, username: str, password: str, confirm_password: str, session): """ Handle registration form submission. diff --git a/src/myfasthtml/auth/utils.py b/src/myfasthtml/auth/utils.py index c594c42..dc7d369 100644 --- a/src/myfasthtml/auth/utils.py +++ b/src/myfasthtml/auth/utils.py @@ -26,10 +26,10 @@ DEFAULT_SKIP_PATTERNS = [ r'/static/.*', r'.*\.css', r'.*\.js', + r'/myfasthtml/.*\.css', + r'/myfasthtml/.*\.js', '/login', - '/login-p', '/register', - '/register-p', '/logout', ] diff --git a/src/myfasthtml/myfastapp.py b/src/myfasthtml/myfastapp.py new file mode 100644 index 0000000..cada01f --- /dev/null +++ b/src/myfasthtml/myfastapp.py @@ -0,0 +1,82 @@ +from importlib.resources import files +from pathlib import Path +from typing import Optional, Any + +import fasthtml.fastapp +from fasthtml.components import Link, Script +from starlette.responses import Response + +from myfasthtml.auth.routes import setup_auth_routes + + +def get_asset_path(filename): + """Get asset file path""" + return files("myfasthtml") / "assets" / filename + + +# Get assets directory path +assets_path = files("myfasthtml") / "assets" +assets_dir = Path(str(assets_path)) + + +def get_asset_content(filename): + """Get asset file content""" + return get_asset_path(filename).read_text() + + +def create_app(daisyui: Optional[bool] = False, mount_auth_app: Optional[bool] = False, **kwargs) -> Any: + """ + Creates and configures a FastHtml application with optional support for daisyUI themes and + authentication routes. + + :param daisyui: Flag to enable or disable inclusion of daisyUI-related assets for styling. + Defaults to False. + :type daisyui: Optional[bool] + + :param mount_auth_app: Flag to enable or disable mounting of authentication routes. + Defaults to False. + :type mount_auth_app: Optional[bool] + + :param kwargs: Arbitrary keyword arguments forwarded to the application initialization logic. + + :return: A tuple containing the FastHtml application instance and the associated router. + :rtype: Any + """ + hdrs = [] + + if daisyui: + hdrs = [ + Link(href="/myfasthtml/daisyui-5.css", rel="stylesheet", type="text/css"), + Link(href="/myfasthtml/daisyui-5-themes.css", rel="stylesheet", type="text/css"), + Script(src="/myfasthtml/tailwindcss-browser@4.js"), + ] + + app, rt = fasthtml.fastapp.fast_app(hdrs=tuple(hdrs), **kwargs) + + # remove the global static files routes + static_route_exts_get = app.routes.pop(0) + + # Serve assets + @app.get("/myfasthtml/{filename:path}.{ext:static}") + def serve_asset(filename: str, ext: str): + path = filename + "." + ext + try: + content = get_asset_content(path) + + if filename.endswith('.css'): + return Response(content, media_type="text/css") + elif filename.endswith('.js'): + return Response(content, media_type="application/javascript") + else: + return Response(content) + except Exception as e: + return Response(f"Asset not found: {path}", status_code=404) + + # and put it back after the myfasthtml static files routes + app.routes.append(static_route_exts_get) + + if mount_auth_app: + # Setup authentication routes + setup_auth_routes(app, rt) + + return app, rt