-
-
Notifications
You must be signed in to change notification settings - Fork 108
Expand file tree
/
Copy pathmain.py
More file actions
168 lines (137 loc) · 4.99 KB
/
main.py
File metadata and controls
168 lines (137 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
from datetime import datetime, UTC
import os
from typing import Annotated
from fastapi import Cookie, FastAPI, Request
from fastapi.responses import JSONResponse, RedirectResponse
from apis import api as api_v1
from apscheduler.schedulers.asyncio import AsyncIOScheduler # type: ignore
from apis.security import NotAuthenticated, get_current_user
from config.beats import clear_expired_sessions, clear_unclaimed_connections
from config import settings
from config.database import connect_db, disconnect_db
from models.user import User
from utils.exception import PermissionDenied, ServiceError
from fastapi.templating import Jinja2Templates
from utils.vite import generate_vite_tags
import urllib.parse
from fastapi import status
from contextlib import asynccontextmanager
from fastapi.staticfiles import StaticFiles
templates = Jinja2Templates(directory="templates")
@asynccontextmanager
async def lifespan(app: FastAPI):
# connect to database
await connect_db()
app.state.server_start_time = datetime.now(tz=UTC)
yield
# disconnect all db connections
await disconnect_db()
app = FastAPI(lifespan=lifespan)
app.include_router(api_v1)
scheduler = AsyncIOScheduler()
scheduler.add_job(clear_expired_sessions, "interval", hours=1)
scheduler.add_job(clear_unclaimed_connections, "interval", seconds=10)
scheduler.start()
@app.get("/")
async def render_index_template(
request: Request,
portr_session: Annotated[str | None, Cookie()] = None,
):
try:
user: User = await get_current_user(portr_session)
except NotAuthenticated:
return templates.TemplateResponse(
request=request,
name="index.html",
context={
"request": request,
"use_vite": settings.use_vite,
"vite_tags": "" if settings.use_vite else generate_vite_tags(),
},
)
first_team = await user.teams.filter().first()
if first_team is None:
return RedirectResponse(url="/instance-settings/team")
return RedirectResponse(url=f"/{first_team.slug}/overview")
@app.get("/instance-settings/{rest:path}")
async def render_index_template_for_instance_settings_routes(
request: Request,
portr_session: Annotated[str | None, Cookie()] = None,
):
try:
user: User = await get_current_user(portr_session)
except NotAuthenticated:
next_url = request.url.path + "?" + request.url.query
next_url_encoded = urllib.parse.urlencode({"next": next_url})
return RedirectResponse(url=f"/?{next_url_encoded}")
if not user.is_superuser:
return RedirectResponse(url="/")
return templates.TemplateResponse(
request=request,
name="index.html",
context={
"request": request,
"use_vite": settings.use_vite,
"vite_tags": "" if settings.use_vite else generate_vite_tags(),
},
)
@app.get("/{team}/overview")
@app.get("/{team}/connections")
@app.get("/{team}/users")
@app.get("/{team}/my-account")
@app.get("/{team}/email-settings")
async def render_index_template_for_team_routes(
request: Request,
team: str,
portr_session: Annotated[str | None, Cookie()] = None,
):
try:
user: User = await get_current_user(portr_session)
except NotAuthenticated:
next_url = request.url.path + "?" + request.url.query
next_url_encoded = urllib.parse.urlencode({"next": next_url})
return RedirectResponse(url=f"/?{next_url_encoded}")
team = await user.teams.filter(slug=team).first() # type: ignore
if team is None:
return RedirectResponse(url="/")
return templates.TemplateResponse(
request=request,
name="index.html",
context={
"request": request,
"use_vite": settings.use_vite,
"vite_tags": "" if settings.use_vite else generate_vite_tags(),
},
)
@app.exception_handler(NotAuthenticated)
async def not_authenticated_exception_handler(
request: Request, exception: NotAuthenticated
):
return JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
content={"message": "Not authenticated"},
)
@app.exception_handler(ServiceError)
async def service_error_exception_handler(request: Request, exception: ServiceError):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST, content={"message": exception.message}
)
@app.exception_handler(PermissionDenied)
async def permission_denied_exception_handler(
request: Request, exception: PermissionDenied
):
return JSONResponse(
status_code=status.HTTP_403_FORBIDDEN, content={"message": exception.message}
)
app.mount("/static", StaticFiles(directory="static"), name="static")
if not settings.use_vite:
app.mount("/", StaticFiles(directory="web/dist/static"), name="web-static")
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
workers=int(os.environ.get("UVICORN_WORKERS", 2)),
log_level="info",
)