diff --git a/docker-compose.yml b/docker-compose.yml index 79f7a92b..f33cfc72 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,3 +13,17 @@ services: interval: 5s timeout: 5s retries: 10 + + postgres: + image: postgres:16 + environment: + POSTGRES_DB: database_app_test + POSTGRES_USER: app + POSTGRES_PASSWORD: secret + ports: + - "5432:5432" + healthcheck: + test: [ "CMD", "pg_isready", "-U", "app", "-d", "database_app_test" ] + interval: 5s + timeout: 5s + retries: 10 diff --git a/example/inertia-pingcrm-app/.env.example b/example/inertia-pingcrm-app/.env.example new file mode 100644 index 00000000..0c8dd991 --- /dev/null +++ b/example/inertia-pingcrm-app/.env.example @@ -0,0 +1,11 @@ +APP_NAME="Inertia Tickets" +APP_ENV=local +APP_URL=http://localhost:8000 +APP_DEBUG=true + +DB_CONNECTION=postgres +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_DATABASE=inertia +DB_USERNAME=local +DB_PASSWORD=secret diff --git a/example/inertia-pingcrm-app/app/http/controllers/auth/authenticated_session_controller.py b/example/inertia-pingcrm-app/app/http/controllers/auth/authenticated_session_controller.py index 139498de..425dd310 100644 --- a/example/inertia-pingcrm-app/app/http/controllers/auth/authenticated_session_controller.py +++ b/example/inertia-pingcrm-app/app/http/controllers/auth/authenticated_session_controller.py @@ -1,25 +1,29 @@ from fastapi import Request from fastapi.responses import RedirectResponse from fastapi_startkit.inertia import Inertia + +from app.http.requests.auth import LoginRequest from app.models.User import User + async def create(): return Inertia.render('Auth/Login', {}) -async def store(request: Request): - form = await request.json() - email = form.get("email") - password = form.get("password") + +async def store(request: LoginRequest): + email = request.email + password = request.password user = await User.where("email", email).first() - if user and user.password == password: - request.session["user_id"] = user.id - return RedirectResponse(url="/", status_code=303) + # if user and user.password == password: + # request.session["user_id"] = user.id + # return RedirectResponse(url="/", status_code=303) return Inertia.render('Auth/Login', { 'errors': {'email': 'These credentials do not match our records.'} }) + async def destroy(request: Request): request.session.clear() return RedirectResponse(url="/login", status_code=303) diff --git a/example/inertia-pingcrm-app/app/http/controllers/users_controller.py b/example/inertia-pingcrm-app/app/http/controllers/users_controller.py index 74441c6b..61749f3a 100644 --- a/example/inertia-pingcrm-app/app/http/controllers/users_controller.py +++ b/example/inertia-pingcrm-app/app/http/controllers/users_controller.py @@ -1,30 +1,30 @@ from fastapi import Request from fastapi.responses import RedirectResponse from fastapi_startkit.inertia import Inertia + from app.models.User import User async def index(): - users = await User.query().limit(10).get() + users = await User.query().limit(10).paginate() + return Inertia.render('Users/Index', { - 'users': { - 'data': [ - { - 'id': u.id, - 'name': f"{u.first_name} {u.last_name}", - 'email': u.email, - 'owner': u.owner, - 'photo': u.photo_path, - 'deleted_at': None, - } for u in users - ], - 'links': {'first': None, 'last': None, 'prev': None, 'next': None}, - 'meta': { - 'current_page': 1, 'last_page': 1, 'per_page': 10, - 'from': 1, 'to': len(users), 'total': len(users), - 'path': '/users', 'links': [], - }, - } + 'data': [ + { + 'id': u.id, + 'name': f"{u.first_name} {u.last_name}", + 'email': u.email, + 'owner': u.owner, + 'photo': u.photo_path, + 'deleted_at': None, + } for u in users.result + ], + 'meta': { + 'current_page': users.current_page, + 'last_page': users.last_page, + 'per_page': users.last_page, + 'total': users.total, + }, }) @@ -38,7 +38,7 @@ async def store(request: Request): return RedirectResponse(url="/users", status_code=303) -async def edit(user: str): +async def edit(user: int): u = await User.find(user) return Inertia.render('Users/Edit', { 'user': { @@ -48,13 +48,12 @@ async def edit(user: str): 'email': u.email, 'owner': u.owner, 'photo': u.photo_path, - 'password': '', 'deleted_at': None, } }) -async def update(request: Request, user: str): +async def update(request: Request, user: int): u = await User.find(user) form = await request.json() await u.update(form) diff --git a/example/inertia-pingcrm-app/app/http/requests/auth.py b/example/inertia-pingcrm-app/app/http/requests/auth.py new file mode 100644 index 00000000..adc5f147 --- /dev/null +++ b/example/inertia-pingcrm-app/app/http/requests/auth.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel, Field + + +class LoginRequest(BaseModel): + email: str = Field(min_length=1) + password: str = Field(min_length=1) diff --git a/example/inertia-pingcrm-app/bootstrap/application.py b/example/inertia-pingcrm-app/bootstrap/application.py index 02fd2d78..9abc2652 100644 --- a/example/inertia-pingcrm-app/bootstrap/application.py +++ b/example/inertia-pingcrm-app/bootstrap/application.py @@ -1,20 +1,20 @@ from pathlib import Path -from config.database import DatabaseConfig -from providers.fastapi_provider import FastAPIProvider -from starlette.middleware.trustedhost import TrustedHostMiddleware - -from starlette.middleware.sessions import SessionMiddleware - -from authentication.middlewares.auth import AuthMiddleware, NotAuthenticated from fastapi_startkit import Application from fastapi_startkit.exceptions import ExceptionHandler as BaseHandler from fastapi_startkit.inertia import InertiaProvider from fastapi_startkit.logging import LogProvider from fastapi_startkit.masoniteorm import DatabaseProvider from fastapi_startkit.vite import ViteProvider +from starlette.middleware.sessions import SessionMiddleware +from starlette.middleware.trustedhost import TrustedHostMiddleware from starlette.responses import RedirectResponse +from authentication.middlewares.auth import AuthMiddleware, NotAuthenticated +from config.database import DatabaseConfig +from providers.fastapi_provider import FastAPIProvider + + class ExceptionHandler(BaseHandler): def register(self): self.register_render( @@ -24,7 +24,7 @@ def register(self): app: Application = Application( - base_path=str(Path.cwd()), + base_path=Path(__file__).resolve().parent.parent, providers=[ LogProvider, (DatabaseProvider, DatabaseConfig), @@ -35,7 +35,6 @@ def register(self): exception_handler=ExceptionHandler, ) - app.add_middleware(AuthMiddleware) app.add_middleware(SessionMiddleware, secret_key="...") app.add_middleware(TrustedHostMiddleware, allowed_hosts=["*"]) diff --git a/example/inertia-pingcrm-app/resources/js/Components/Menu/MainMenu.tsx b/example/inertia-pingcrm-app/resources/js/Components/Menu/MainMenu.tsx index ec97042a..3981556f 100644 --- a/example/inertia-pingcrm-app/resources/js/Components/Menu/MainMenu.tsx +++ b/example/inertia-pingcrm-app/resources/js/Components/Menu/MainMenu.tsx @@ -1,33 +1,38 @@ -import MainMenuItem from '@/Components/Menu/MainMenuItem'; -import { Building, CircleGauge, Printer, Users } from 'lucide-react'; +import MainMenuItem from "@/Components/Menu/MainMenuItem" +import { Building, CircleGauge, Printer, Users } from "lucide-react" interface MainMenuProps { - className?: string; + className?: string; } export default function MainMenu({ className }: MainMenuProps) { - return ( -