Skip to content

Commit 056c7fe

Browse files
committed
feat: inertia improvements
1 parent e4367dd commit 056c7fe

269 files changed

Lines changed: 3199 additions & 4454 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

example/inertia-pingcrm-app/app/http/controllers/auth/authenticated_session_controller.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from fastapi_startkit.facades import Inertia
44
from app.models.User import User
55

6-
async def create(request: Request):
7-
return Inertia.render(request, 'Auth/Login', {})
6+
async def create():
7+
return Inertia.render('Auth/Login', {})
88

99
async def store(request: Request):
1010
form = await request.json()
@@ -16,11 +16,10 @@ async def store(request: Request):
1616
request.session["user_id"] = user.id
1717
return RedirectResponse(url="/", status_code=303)
1818

19-
return Inertia.render(request, 'Auth/Login', {
19+
return Inertia.render('Auth/Login', {
2020
'errors': {'email': 'These credentials do not match our records.'}
2121
})
2222

2323
async def destroy(request: Request):
2424
request.session.clear()
2525
return RedirectResponse(url="/login", status_code=303)
26-

example/inertia-pingcrm-app/app/http/controllers/contacts_controller.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
from fastapi import Request
1+
from fastapi import Depends, Request
22
from fastapi.responses import RedirectResponse
33
from fastapi_startkit.facades import Inertia
4+
from app.http.requests.organization import OrganizationListRequest
45
from app.models.Contact import Contact
56
from app.models.Organization import Organization
67

78

8-
async def index(request: Request):
9-
search = request.query_params.get('search', '')
10-
query = Contact.query()
11-
if search:
12-
query = query.where('first_name', 'like', f'%{search}%').or_where('last_name', 'like', f'%{search}%')
13-
contacts = await query.limit(10).get()
14-
return Inertia.render(request, 'Contacts/Index', {
15-
'filters': {'search': search},
9+
async def index(filters: OrganizationListRequest = Depends()):
10+
contacts = await (
11+
Contact.query()
12+
.when(filters.search, lambda q: q.where('first_name', 'like', f'%{filters.search}%').or_where('last_name', 'like', f'%{filters.search}%'))
13+
.limit(10)
14+
.get()
15+
)
16+
return Inertia.render('Contacts/Index', {
17+
'filters': {'search': filters.search},
1618
'contacts': {
1719
'data': [
1820
{
@@ -35,9 +37,9 @@ async def index(request: Request):
3537
})
3638

3739

38-
async def create(request: Request):
40+
async def create():
3941
organizations = await Organization.query().limit(100).get()
40-
return Inertia.render(request, 'Contacts/Create', {
42+
return Inertia.render('Contacts/Create', {
4143
'organizations': [
4244
{'id': o.id, 'name': o.name} for o in organizations
4345
]
@@ -50,10 +52,10 @@ async def store(request: Request):
5052
return RedirectResponse(url="/contacts", status_code=303)
5153

5254

53-
async def edit(request: Request, contact: str):
55+
async def edit(contact: str):
5456
c = await Contact.find(contact)
5557
organizations = await Organization.query().limit(100).get()
56-
return Inertia.render(request, 'Contacts/Edit', {
58+
return Inertia.render('Contacts/Edit', {
5759
'contact': {
5860
'id': c.id,
5961
'first_name': c.first_name,
@@ -81,9 +83,9 @@ async def update(request: Request, contact: str):
8183
return RedirectResponse(url=f"/contacts/{contact}/edit", status_code=303)
8284

8385

84-
async def destroy(request: Request, contact: str):
86+
async def destroy(contact: str):
8587
return RedirectResponse(url="/contacts", status_code=303)
8688

8789

88-
async def restore(request: Request, contact: str):
89-
return RedirectResponse(url="/contacts", status_code=303)
90+
async def restore(contact: str):
91+
return RedirectResponse(url="/contacts", status_code=303)
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from fastapi import Request
21
from fastapi_startkit.facades import Inertia
32

4-
async def index(request: Request):
5-
return Inertia.render(request, 'Dashboard/Index', {})
63

4+
async def index():
5+
return Inertia.render('Dashboard/Index', {})

example/inertia-pingcrm-app/app/http/controllers/organizations_controller.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,49 @@
1+
from fastapi import Depends
12
from fastapi import Request
2-
from fastapi.responses import JSONResponse, RedirectResponse
3-
from fastapi_startkit.facades import Inertia
3+
from fastapi.responses import RedirectResponse
4+
from fastapi_startkit.inertia import Inertia
5+
6+
from app.http.requests.organization import OrganizationListRequest
47
from app.models.Organization import Organization
58

6-
async def index(request: Request):
7-
search = request.query_params.get('search', '')
8-
query = Organization.query()
9-
if search:
10-
query = query.where('name', 'like', f'%{search}%')
11-
organizations = await query.limit(10).get()
12-
return Inertia.render(request, 'Organizations/Index', {
13-
'filters': {'search': search},
14-
'organizations': {
15-
'data': [
16-
{
17-
'id': org.id,
18-
'name': org.name,
19-
'phone': org.phone,
20-
'city': org.city,
21-
'deleted_at': None,
22-
} for org in organizations
23-
],
24-
'links': {'first': None, 'last': None, 'prev': None, 'next': None},
25-
'meta': {
26-
'current_page': 1, 'last_page': 1, 'per_page': 10,
27-
'from': 1, 'to': len(organizations), 'total': len(organizations),
28-
'path': '/organizations', 'links': [],
29-
},
30-
}
9+
10+
async def index(filters: OrganizationListRequest = Depends()):
11+
paginator = await (
12+
Organization.query()
13+
.when(filters.search, lambda q: q.where('name', 'like', f'%{filters.search}%'))
14+
.paginate(page=filters.page, per_page=filters.limit)
15+
)
16+
17+
return Inertia.render('Organizations/Index', {
18+
'data': [
19+
{
20+
'id': org.id,
21+
'name': org.name,
22+
'phone': org.phone,
23+
'city': org.city,
24+
'deleted_at': None,
25+
} for org in paginator.result
26+
],
27+
'meta': {
28+
'current_page': paginator.current_page,
29+
'last_page': paginator.last_page,
30+
'per_page': paginator.per_page,
31+
'total': paginator.total,
32+
},
3133
})
3234

33-
async def create(request: Request):
34-
return Inertia.render(request, 'Organizations/Create', {})
35+
async def create():
36+
return Inertia.render('Organizations/Create')
3537

3638
async def store(request: Request):
3739
form = await request.json()
3840
form["account_id"] = 1 # hardcoded for demo
3941
await Organization.create(form)
4042
return RedirectResponse(url="/organizations", status_code=303)
4143

42-
async def edit(request: Request, organization: str):
44+
async def edit(organization: str):
4345
org = await Organization.find(organization)
44-
return Inertia.render(request, 'Organizations/Edit', {
46+
return Inertia.render('Organizations/Edit', {
4547
'organization': {
4648
'id': org.id,
4749
'name': org.name,
@@ -62,11 +64,10 @@ async def update(request: Request, organization: str):
6264
await org.update(form)
6365
return RedirectResponse(url=f"/organizations/{organization}/edit", status_code=303)
6466

65-
async def destroy(request: Request, organization: str):
67+
async def destroy(organization: str):
6668
# org = await Organization.find(organization)
6769
# await org.delete()
6870
return RedirectResponse(url="/organizations", status_code=303)
6971

70-
async def restore(request: Request, organization: str):
72+
async def restore(organization: str):
7173
return RedirectResponse(url="/organizations", status_code=303)
72-
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from fastapi import Request
21
from fastapi_startkit.facades import Inertia
32

4-
async def index(request: Request):
5-
return Inertia.render(request, 'Reports/Index', {})
63

4+
async def index():
5+
return Inertia.render('Reports/Index', {})

example/inertia-pingcrm-app/app/http/controllers/users_controller.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from app.models.User import User
55

66

7-
async def index(request: Request):
7+
async def index():
88
users = await User.query().limit(10).get()
9-
return Inertia.render(request, 'Users/Index', {
9+
return Inertia.render('Users/Index', {
1010
'users': {
1111
'data': [
1212
{
@@ -28,8 +28,8 @@ async def index(request: Request):
2828
})
2929

3030

31-
async def create(request: Request):
32-
return Inertia.render(request, 'Users/Create', {})
31+
async def create():
32+
return Inertia.render('Users/Create', {})
3333

3434

3535
async def store(request: Request):
@@ -38,9 +38,9 @@ async def store(request: Request):
3838
return RedirectResponse(url="/users", status_code=303)
3939

4040

41-
async def edit(request: Request, user: str):
41+
async def edit(user: str):
4242
u = await User.find(user)
43-
return Inertia.render(request, 'Users/Edit', {
43+
return Inertia.render('Users/Edit', {
4444
'user': {
4545
'id': u.id,
4646
'first_name': u.first_name,
@@ -61,9 +61,9 @@ async def update(request: Request, user: str):
6161
return RedirectResponse(url=f"/users/{user}/edit", status_code=303)
6262

6363

64-
async def destroy(request: Request, user: str):
64+
async def destroy(user: str):
6565
return RedirectResponse(url="/users", status_code=303)
6666

6767

68-
async def restore(request: Request, user: str):
69-
return RedirectResponse(url="/users", status_code=303)
68+
async def restore(user: str):
69+
return RedirectResponse(url="/users", status_code=303)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class OrganizationListRequest:
2+
def __init__(self, search: str = '', page: int = 1, limit: int = 10):
3+
self.search = search
4+
self.page = page
5+
self.limit = limit
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from fastapi import Request
2+
from starlette.middleware.base import BaseHTTPMiddleware
3+
4+
5+
class NotAuthenticated(Exception):
6+
pass
7+
8+
9+
class AuthMiddleware(BaseHTTPMiddleware):
10+
async def dispatch(self, request: Request, call_next):
11+
request.state.user = None
12+
if "session" in request.scope:
13+
user_id = request.session.get("user_id")
14+
if user_id:
15+
from app.models.User import User
16+
17+
user = await User.find(user_id)
18+
if user:
19+
request.state.user = {
20+
"id": user.id,
21+
"first_name": user.first_name,
22+
"last_name": user.last_name,
23+
"email": user.email,
24+
"role": "Owner" if user.owner else "User",
25+
"account": {"id": user.account_id, "name": "Acme Corporation"},
26+
}
27+
return await call_next(request)
28+
29+
30+
async def auth(request: Request):
31+
"""FastAPI dependency — raises NotAuthenticated if no user in session."""
32+
if not getattr(request.state, "user", None):
33+
raise NotAuthenticated()
Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
from pathlib import Path
22

33
from config.database import DatabaseConfig
4+
from providers.fastapi_provider import FastAPIProvider
5+
from starlette.middleware.trustedhost import TrustedHostMiddleware
6+
7+
from starlette.middleware.sessions import SessionMiddleware
8+
9+
from authentication.middlewares.auth import AuthMiddleware, NotAuthenticated
410
from fastapi_startkit import Application
5-
from fastapi_startkit.logging import LogProvider
6-
from fastapi_startkit.vite import ViteProvider
11+
from fastapi_startkit.exceptions import ExceptionHandler as BaseHandler
712
from fastapi_startkit.inertia import InertiaProvider
13+
from fastapi_startkit.logging import LogProvider
814
from fastapi_startkit.masoniteorm import DatabaseProvider
15+
from fastapi_startkit.vite import ViteProvider
16+
from starlette.responses import RedirectResponse
17+
18+
class ExceptionHandler(BaseHandler):
19+
def register(self):
20+
self.register_render(
21+
NotAuthenticated,
22+
lambda request, exc: RedirectResponse(url="/login", status_code=303)
23+
)
924

10-
from providers.fastapi_provider import FastAPIProvider
1125

1226
app: Application = Application(
1327
base_path=str(Path.cwd()),
@@ -18,4 +32,10 @@
1832
ViteProvider,
1933
InertiaProvider,
2034
],
35+
exception_handler=ExceptionHandler,
2136
)
37+
38+
39+
app.add_middleware(AuthMiddleware)
40+
app.add_middleware(SessionMiddleware, secret_key="...")
41+
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["*"])
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import asyncio
2+
3+
from dumpdie import dd
4+
5+
from bootstrap.application import app
6+
7+
from app.models.Organization import Organization
8+
9+
10+
async def index():
11+
paginator = await (
12+
Organization.query()
13+
.paginate()
14+
)
15+
16+
dd(paginator)
17+
18+
19+
if __name__ == "__main__":
20+
asyncio.run(index())

0 commit comments

Comments
 (0)