-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
154 lines (125 loc) · 3.8 KB
/
main.py
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
import contextlib
import secrets
import string
import sys
import uvicorn
from argon2 import PasswordHasher
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from loguru import logger
from starlette.exceptions import HTTPException
from backend.config import Session
from backend.database import init_db, stop_db
from backend.database.models import Role, User
from backend.models import BaseResponse, UnicornException
from backend.utils import ErrorCodes, to_snake_case
ALPHABET = string.ascii_letters + string.digits
FMT = (
"<green>[{time}]</green> | <level>{level}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:"
"<cyan>{line}</cyan> - <level>{message}</level>"
)
# Logger
logger.configure(
handlers=[
{"sink": sys.stdout, "format": FMT},
{
"sink": "log.log",
"format": FMT,
"rotation": "1 day",
"retention": "7 days",
},
]
)
# Create admin user and role - if not exist
@contextlib.asynccontextmanager
async def lifespan(_: FastAPI):
# Database - TortoiseORM
await init_db()
logger.info(f"Tortoise-ORM started")
password = "".join(secrets.choice(ALPHABET) for _ in range(8))
role, _ = await Role.get_or_create(
name="admin", defaults={"can_administer": True}
)
_, created = await User.get_or_create(
username="admin",
defaults={"password": PasswordHasher().hash(password), "role": role},
)
if created:
logger.info(f"Created the admin user with password {password}")
yield
await stop_db()
logger.info("Tortoise-ORM shutdown")
# Config - Pydantic
Session.set_config()
logger.info("Initializing Config")
# FastAPI - instance
app = FastAPI(title="FestivalBackend", docs_url="/", lifespan=lifespan)
logger.info("Starting FastAPI application")
# CORS
origins = [
"*",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
logger.info("Setting CORS")
# FastAPI - APIRouter
from backend.api import api
app.include_router(api)
logger.info("Initializing API routers")
# Handling Errors
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(_: Request, exc: UnicornException):
return JSONResponse(
status_code=exc.status,
content=BaseResponse(
error=True,
message=exc.message,
code=exc.code.value
if exc.code
else ErrorCodes.GENERIC_HTTP_EXCEPTION,
).dict(),
)
@app.exception_handler(HTTPException)
async def http_exception_handler(_: Request, exc: HTTPException):
error_code = to_snake_case(exc.detail)
return JSONResponse(
status_code=exc.status_code,
content=BaseResponse(
error=True,
code=getattr(
ErrorCodes,
error_code,
ErrorCodes.GENERIC_HTTP_EXCEPTION,
).value,
).dict(),
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(_: Request, __: RequestValidationError):
return JSONResponse(
status_code=422,
content=BaseResponse(
error=True, code=ErrorCodes.REQUEST_VALIDATION_ERROR.value
).dict(),
)
@app.exception_handler(Exception)
async def internal_server_error_handler(_: Request, exc: Exception):
logger.exception(exc)
return JSONResponse(
status_code=500,
content=BaseResponse(
error=True,
code=ErrorCodes.INTERNAL_ERROR_SERVER.value,
).dict(),
)
if __name__ == "__main__":
try:
uvicorn.run(app, host=Session.config.APP_HOST)
except KeyboardInterrupt:
pass