fastapi-rtk 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_rtk/__init__.py +533 -0
- fastapi_rtk/api/__init__.py +1536 -0
- fastapi_rtk/api/interface.py +559 -0
- fastapi_rtk/apis.py +224 -0
- fastapi_rtk/auth.py +351 -0
- fastapi_rtk/const.py +23 -0
- fastapi_rtk/db.py +763 -0
- fastapi_rtk/decorators.py +211 -0
- fastapi_rtk/dependencies.py +113 -0
- fastapi_rtk/filters.py +318 -0
- fastapi_rtk/generic/__init__.py +71 -0
- fastapi_rtk/generic/db.py +113 -0
- fastapi_rtk/generic/filters.py +219 -0
- fastapi_rtk/generic/interface.py +220 -0
- fastapi_rtk/generic/model.py +623 -0
- fastapi_rtk/globals.py +26 -0
- fastapi_rtk/hasher.py +183 -0
- fastapi_rtk/manager.py +216 -0
- fastapi_rtk/model.py +180 -0
- fastapi_rtk/models.py +218 -0
- fastapi_rtk/routers.py +293 -0
- fastapi_rtk/schemas.py +314 -0
- fastapi_rtk/types.py +23 -0
- fastapi_rtk/utils.py +156 -0
- fastapi_rtk-0.0.1.dist-info/LICENSE +21 -0
- fastapi_rtk-0.0.1.dist-info/METADATA +16 -0
- fastapi_rtk-0.0.1.dist-info/RECORD +29 -0
- fastapi_rtk-0.0.1.dist-info/WHEEL +5 -0
- fastapi_rtk-0.0.1.dist-info/top_level.txt +1 -0
fastapi_rtk/__init__.py
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import io
|
|
3
|
+
|
|
4
|
+
from fastapi import FastAPI
|
|
5
|
+
from fastapi.staticfiles import StaticFiles
|
|
6
|
+
from fastapi.templating import Jinja2Templates
|
|
7
|
+
from jinja2 import Environment, TemplateNotFound, select_autoescape
|
|
8
|
+
from sqlalchemy import and_, create_engine, insert
|
|
9
|
+
from sqlalchemy.orm import Session, sessionmaker
|
|
10
|
+
|
|
11
|
+
# Import all submodules
|
|
12
|
+
from .api import *
|
|
13
|
+
from .apis import *
|
|
14
|
+
from .auth import *
|
|
15
|
+
from .const import *
|
|
16
|
+
from .db import *
|
|
17
|
+
from .decorators import *
|
|
18
|
+
from .dependencies import *
|
|
19
|
+
from .filters import *
|
|
20
|
+
from .generic import *
|
|
21
|
+
from .globals import *
|
|
22
|
+
from .hasher import *
|
|
23
|
+
from .manager import *
|
|
24
|
+
from .model import *
|
|
25
|
+
from .models import *
|
|
26
|
+
from .routers import *
|
|
27
|
+
from .schemas import *
|
|
28
|
+
from .types import *
|
|
29
|
+
from .utils import *
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FastapiReactToolkit:
|
|
33
|
+
"""
|
|
34
|
+
The main class for the FastapiReactToolkit library.
|
|
35
|
+
|
|
36
|
+
This class provides a set of methods to initialize a FastAPI application, add APIs, manage permissions and roles,
|
|
37
|
+
and initialize the database with permissions, APIs, roles, and their relationships.
|
|
38
|
+
|
|
39
|
+
In case you need to create a synchronous session, set the `with_session` parameter to True. this will allow you to use `db` attribute to interact with the database.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
```python
|
|
43
|
+
toolkit = FastapiReactToolkit(
|
|
44
|
+
config_file="./app/config.py",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@asynccontextmanager
|
|
48
|
+
async def lifespan(app: FastAPI):
|
|
49
|
+
# Run when the app is starting up
|
|
50
|
+
toolkit.connect_to_database()
|
|
51
|
+
|
|
52
|
+
# Not needed if you setup a migration system like Alembic
|
|
53
|
+
async with sessionmanager.connect() as conn:
|
|
54
|
+
await sessionmanager.create_all(conn)
|
|
55
|
+
|
|
56
|
+
# Creating permission, apis, roles, and connecting them
|
|
57
|
+
await toolkit.init_database()
|
|
58
|
+
|
|
59
|
+
async with sessionmanager.session() as session:
|
|
60
|
+
# Add base data
|
|
61
|
+
await add_base_data(session)
|
|
62
|
+
|
|
63
|
+
yield
|
|
64
|
+
|
|
65
|
+
# Run when the app is shutting down
|
|
66
|
+
if sessionmanager._engine:
|
|
67
|
+
await sessionmanager.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
app = FastAPI(lifespan=lifespan)
|
|
71
|
+
app.add_middleware(
|
|
72
|
+
CORSMiddleware,
|
|
73
|
+
allow_origins=["*"],
|
|
74
|
+
allow_credentials=True,
|
|
75
|
+
allow_methods=["*"],
|
|
76
|
+
allow_headers=["*"],
|
|
77
|
+
)
|
|
78
|
+
toolkit.initialize(app)
|
|
79
|
+
```
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
app: FastAPI = None
|
|
83
|
+
apis: list[ModelRestApi] = None
|
|
84
|
+
|
|
85
|
+
db: sessionmaker[Session] = None
|
|
86
|
+
initialized: bool = False
|
|
87
|
+
|
|
88
|
+
_mounted = False
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
*,
|
|
93
|
+
app: FastAPI | None = None,
|
|
94
|
+
config_file: str | None = None,
|
|
95
|
+
user_manager: Type[UserManager] | None = None,
|
|
96
|
+
cookie_transport: CookieTransport | None = None,
|
|
97
|
+
bearer_transport: BearerTransport | None = None,
|
|
98
|
+
cookie_backend: AuthenticationBackend | None = None,
|
|
99
|
+
jwt_backend: AuthenticationBackend | None = None,
|
|
100
|
+
authenticator: Authenticator | None = None,
|
|
101
|
+
fast_api_users: FastAPIUsers[User, int] | None = None,
|
|
102
|
+
password_helper: PasswordHelperProtocol | None = None,
|
|
103
|
+
with_session: bool = False,
|
|
104
|
+
) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Initialize the FastAPI extension.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
app (FastAPI | None, optional): The FastAPI application instance. Defaults to None.
|
|
110
|
+
config_file (str | None, optional): The path to the configuration file. Defaults to None.
|
|
111
|
+
user_manager (Type[UserManager] | None, optional): Set this to override default user manager class. Defaults to None.
|
|
112
|
+
cookie_transport (CookieTransport | None, optional): Set this to override default cookie transport instance. Defaults to None.
|
|
113
|
+
bearer_transport (BearerTransport | None, optional): Set this to override default bearer transport instance. Defaults to None.
|
|
114
|
+
cookie_backend (AuthenticationBackend | None, optional): Set this to override default cookie backend instance. Defaults to None.
|
|
115
|
+
jwt_backend (AuthenticationBackend | None, optional): Set this to override default jwt backend instance. Defaults to None.
|
|
116
|
+
authenticator (Authenticator | None, optional): Set this to override default authenticator instance. Defaults to None.
|
|
117
|
+
fast_api_users (FastAPIUsers[User, int] | None, optional): Set this to override default FastAPIUsers instance. Defaults to None.
|
|
118
|
+
password_helper (PasswordHelperProtocol | None, optional): Set this to override default password helper instance. Defaults to None.
|
|
119
|
+
with_session (bool, optional): Flag indicating whether to enable session management. Defaults to False.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
ValueError: If SQLALCHEMY_DATABASE_URI is not set in the configuration.
|
|
123
|
+
"""
|
|
124
|
+
if config_file:
|
|
125
|
+
self.read_config_file(config_file)
|
|
126
|
+
|
|
127
|
+
# Override default classes
|
|
128
|
+
g.auth.user_manager = user_manager or g.auth.user_manager
|
|
129
|
+
g.auth.cookie_transport = cookie_transport or g.auth.cookie_transport
|
|
130
|
+
g.auth.bearer_transport = bearer_transport or g.auth.bearer_transport
|
|
131
|
+
g.auth.cookie_backend = cookie_backend or g.auth.cookie_backend
|
|
132
|
+
g.auth.jwt_backend = jwt_backend or g.auth.jwt_backend
|
|
133
|
+
g.auth.authenticator = authenticator or g.auth.authenticator
|
|
134
|
+
g.auth.fastapi_users = fast_api_users or g.auth.fastapi_users
|
|
135
|
+
g.auth.password_helper = password_helper or g.auth.password_helper
|
|
136
|
+
|
|
137
|
+
if app:
|
|
138
|
+
self.initialize(app)
|
|
139
|
+
|
|
140
|
+
if with_session:
|
|
141
|
+
if not g.config.get("SQLALCHEMY_DATABASE_URI"):
|
|
142
|
+
raise ValueError(
|
|
143
|
+
"SQLALCHEMY_DATABASE_URI is not set in the configuration"
|
|
144
|
+
)
|
|
145
|
+
engine = create_engine(g.config.get("SQLALCHEMY_DATABASE_URI"))
|
|
146
|
+
self.db = sessionmaker(bind=engine)
|
|
147
|
+
|
|
148
|
+
def initialize(self, app: FastAPI) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Initializes the FastAPI application.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
app (FastAPI): The FastAPI application instance.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
None
|
|
157
|
+
"""
|
|
158
|
+
if self.initialized:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
self.initialized = True
|
|
162
|
+
self.app = app
|
|
163
|
+
self.apis = []
|
|
164
|
+
|
|
165
|
+
# Add the APIs
|
|
166
|
+
self._init_info_api()
|
|
167
|
+
self._init_auth_api()
|
|
168
|
+
self._init_users_api()
|
|
169
|
+
self._init_roles_api()
|
|
170
|
+
self._init_permissions_api()
|
|
171
|
+
self._init_apis_api()
|
|
172
|
+
self._init_permission_apis_api()
|
|
173
|
+
|
|
174
|
+
# Add the JS manifest route
|
|
175
|
+
self._init_js_manifest()
|
|
176
|
+
|
|
177
|
+
def add_api(self, api: ModelRestApi) -> None:
|
|
178
|
+
"""
|
|
179
|
+
Adds the specified API to the FastAPI application.
|
|
180
|
+
|
|
181
|
+
Parameters:
|
|
182
|
+
- api (ModelRestApi): The API to be added.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
- None
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
- ValueError: If the API is added after the `mount()` method is called.
|
|
189
|
+
"""
|
|
190
|
+
if self._mounted:
|
|
191
|
+
raise ValueError(
|
|
192
|
+
"API Mounted after mount() was called, please add APIs before calling mount()"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
api = api if isinstance(api, ModelRestApi) else api()
|
|
196
|
+
self.apis.append(api)
|
|
197
|
+
api.integrate_router(self.app)
|
|
198
|
+
api.toolkit = self
|
|
199
|
+
|
|
200
|
+
def total_permissions(self) -> list[str]:
|
|
201
|
+
"""
|
|
202
|
+
Returns the total list of permissions required by all APIs.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
- list[str]: The total list of permissions.
|
|
206
|
+
"""
|
|
207
|
+
permissions = []
|
|
208
|
+
for api in self.apis:
|
|
209
|
+
permissions.extend(getattr(api, "permissions", []))
|
|
210
|
+
return list(set(permissions))
|
|
211
|
+
|
|
212
|
+
def read_config_file(self, config_file: str):
|
|
213
|
+
"""
|
|
214
|
+
Reads a configuration file and sets the `config` attribute with the variables defined in the file.
|
|
215
|
+
|
|
216
|
+
It will also set the `SECRET_KEY` in the global `g` object if it is defined in the configuration file.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
config_file (str): The path to the configuration file.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
None
|
|
223
|
+
"""
|
|
224
|
+
spec = importlib.util.spec_from_file_location("config", config_file)
|
|
225
|
+
config_module = importlib.util.module_from_spec(spec)
|
|
226
|
+
spec.loader.exec_module(config_module)
|
|
227
|
+
|
|
228
|
+
# Get the dictionary of variables in the module
|
|
229
|
+
g.config = {
|
|
230
|
+
key: value
|
|
231
|
+
for key, value in config_module.__dict__.items()
|
|
232
|
+
if not key.startswith("__")
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
self._post_read_config()
|
|
236
|
+
|
|
237
|
+
def mount(self):
|
|
238
|
+
"""
|
|
239
|
+
Mounts the static and template folders specified in the configuration.
|
|
240
|
+
|
|
241
|
+
PLEASE ONLY RUN THIS AFTER ALL APIS HAVE BEEN ADDED.
|
|
242
|
+
"""
|
|
243
|
+
if self._mounted:
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
self._mounted = True
|
|
247
|
+
self._mount_static_folder()
|
|
248
|
+
self._mount_template_folder()
|
|
249
|
+
|
|
250
|
+
def connect_to_database(self):
|
|
251
|
+
"""
|
|
252
|
+
Connects to the database using the configured SQLAlchemy database URI.
|
|
253
|
+
|
|
254
|
+
This method initializes the database session maker with the SQLAlchemy
|
|
255
|
+
database URI specified in the configuration. If no URI is found in the
|
|
256
|
+
configuration, the default URI is used.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
None
|
|
260
|
+
"""
|
|
261
|
+
uri = g.config.get("SQLALCHEMY_DATABASE_URI_ASYNC")
|
|
262
|
+
if not uri:
|
|
263
|
+
raise ValueError(
|
|
264
|
+
"SQLALCHEMY_DATABASE_URI_ASYNC is not set in the configuration"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
binds = g.config.get("SQLALCHEMY_BINDS")
|
|
268
|
+
session_manager.init_db(uri, binds)
|
|
269
|
+
|
|
270
|
+
async def init_database(self):
|
|
271
|
+
"""
|
|
272
|
+
Initializes the database by inserting permissions, APIs, roles, and their relationships.
|
|
273
|
+
|
|
274
|
+
The initialization process is as follows:
|
|
275
|
+
1. Inserts permissions into the database.
|
|
276
|
+
2. Inserts APIs into the database.
|
|
277
|
+
3. Inserts roles into the database.
|
|
278
|
+
4. Inserts the relationship between permissions and APIs into the database.
|
|
279
|
+
5. Inserts the relationship between permissions, APIs, and roles into the database.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
None
|
|
283
|
+
"""
|
|
284
|
+
async with session_manager.session() as db:
|
|
285
|
+
logger.info("INITIALIZING DATABASE")
|
|
286
|
+
await self._insert_permissions(db)
|
|
287
|
+
await self._insert_apis(db)
|
|
288
|
+
await self._insert_roles(db)
|
|
289
|
+
await self._associate_permission_with_api(db)
|
|
290
|
+
await self._associate_permission_api_with_role(db)
|
|
291
|
+
|
|
292
|
+
async def _insert_permissions(self, db: AsyncSession):
|
|
293
|
+
new_permissions = self.total_permissions()
|
|
294
|
+
stmt = select(Permission).where(Permission.name.in_(new_permissions))
|
|
295
|
+
result = await db.execute(stmt)
|
|
296
|
+
existing_permissions = [
|
|
297
|
+
permission.name for permission in result.scalars().all()
|
|
298
|
+
]
|
|
299
|
+
if len(new_permissions) == len(existing_permissions):
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
permission_objs = [
|
|
303
|
+
Permission(name=permission)
|
|
304
|
+
for permission in new_permissions
|
|
305
|
+
if permission not in existing_permissions
|
|
306
|
+
]
|
|
307
|
+
for permission in permission_objs:
|
|
308
|
+
logger.info(f"ADDING PERMISSION {permission}")
|
|
309
|
+
db.add(permission)
|
|
310
|
+
await db.commit()
|
|
311
|
+
|
|
312
|
+
async def _insert_apis(self, db: AsyncSession):
|
|
313
|
+
new_apis = [api.__class__.__name__ for api in self.apis]
|
|
314
|
+
stmt = select(Api).where(Api.name.in_(new_apis))
|
|
315
|
+
result = await db.execute(stmt)
|
|
316
|
+
existing_apis = [api.name for api in result.scalars().all()]
|
|
317
|
+
if len(new_apis) == len(existing_apis):
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
api_objs = [Api(name=api) for api in new_apis if api not in existing_apis]
|
|
321
|
+
for api in api_objs:
|
|
322
|
+
logger.info(f"ADDING API {api}")
|
|
323
|
+
db.add(api)
|
|
324
|
+
await db.commit()
|
|
325
|
+
|
|
326
|
+
async def _insert_roles(self, db: AsyncSession):
|
|
327
|
+
new_roles = DEFAULT_ROLES
|
|
328
|
+
stmt = select(Role).where(Role.name.in_(new_roles))
|
|
329
|
+
result = await db.execute(stmt)
|
|
330
|
+
existing_roles = [role.name for role in result.scalars().all()]
|
|
331
|
+
if len(new_roles) == len(existing_roles):
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
role_objs = [
|
|
335
|
+
Role(name=role) for role in new_roles if role not in existing_roles
|
|
336
|
+
]
|
|
337
|
+
for role in role_objs:
|
|
338
|
+
logger.info(f"ADDING ROLE {role}")
|
|
339
|
+
db.add(role)
|
|
340
|
+
await db.commit()
|
|
341
|
+
|
|
342
|
+
async def _associate_permission_with_api(self, db: AsyncSession):
|
|
343
|
+
for api in self.apis:
|
|
344
|
+
new_permissions = getattr(api, "permissions", [])
|
|
345
|
+
if not new_permissions:
|
|
346
|
+
continue
|
|
347
|
+
|
|
348
|
+
# Get the api object
|
|
349
|
+
stmt = select(Api).where(Api.name == api.__class__.__name__)
|
|
350
|
+
result = await db.execute(stmt)
|
|
351
|
+
api_obj = result.scalars().first()
|
|
352
|
+
|
|
353
|
+
if not api_obj:
|
|
354
|
+
raise ValueError(f"API {api.__class__.__name__} not found")
|
|
355
|
+
|
|
356
|
+
stmt = select(Permission).where(
|
|
357
|
+
and_(
|
|
358
|
+
Permission.name.in_(new_permissions),
|
|
359
|
+
~Permission.id.in_([p.permission_id for p in api_obj.permissions]),
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
result = await db.execute(stmt)
|
|
363
|
+
new_permissions = result.scalars().all()
|
|
364
|
+
|
|
365
|
+
if not new_permissions:
|
|
366
|
+
continue
|
|
367
|
+
|
|
368
|
+
for permission in new_permissions:
|
|
369
|
+
stmt = insert(PermissionApi).values(
|
|
370
|
+
permission_id=permission.id, api_id=api_obj.id
|
|
371
|
+
)
|
|
372
|
+
await db.execute(stmt)
|
|
373
|
+
logger.info(f"ASSOCIATING PERMISSION {permission} WITH API {api_obj}")
|
|
374
|
+
await db.commit()
|
|
375
|
+
|
|
376
|
+
async def _associate_permission_api_with_role(self, db: AsyncSession):
|
|
377
|
+
# Get admin role
|
|
378
|
+
stmt = select(Role).where(Role.name == ADMIN_ROLE)
|
|
379
|
+
result = await db.execute(stmt)
|
|
380
|
+
admin_role = result.scalars().first()
|
|
381
|
+
|
|
382
|
+
if admin_role:
|
|
383
|
+
# Get list of permission-api.assoc_permission_api_id of the admin role
|
|
384
|
+
stmt = select(PermissionApi).where(
|
|
385
|
+
~PermissionApi.roles.contains(admin_role)
|
|
386
|
+
)
|
|
387
|
+
result = await db.execute(stmt)
|
|
388
|
+
existing_assoc_permission_api_roles = result.scalars().all()
|
|
389
|
+
|
|
390
|
+
# Add admin role to all permission-api objects
|
|
391
|
+
for permission_api in existing_assoc_permission_api_roles:
|
|
392
|
+
permission_api.roles.append(admin_role)
|
|
393
|
+
logger.info(
|
|
394
|
+
f"ASSOCIATING {admin_role} WITH PERMISSION-API {permission_api}"
|
|
395
|
+
)
|
|
396
|
+
await db.commit()
|
|
397
|
+
|
|
398
|
+
# Read config based roles
|
|
399
|
+
roles_dict = g.config.get("ROLES") or g.config.get("FAB_ROLES", {})
|
|
400
|
+
|
|
401
|
+
for role_name, role_permissions in roles_dict.items():
|
|
402
|
+
stmt = select(Role).where(Role.name == role_name)
|
|
403
|
+
result = await db.execute(stmt)
|
|
404
|
+
role = result.scalars().first()
|
|
405
|
+
|
|
406
|
+
if not role:
|
|
407
|
+
role = Role(name=role_name)
|
|
408
|
+
db.add(role)
|
|
409
|
+
logger.info(f"ADDING ROLE {role}")
|
|
410
|
+
|
|
411
|
+
for apis, permissions in role_permissions:
|
|
412
|
+
api_names = apis.split("|")
|
|
413
|
+
permission_names = permissions.split("|")
|
|
414
|
+
|
|
415
|
+
stmt = (
|
|
416
|
+
select(PermissionApi)
|
|
417
|
+
.where(
|
|
418
|
+
and_(
|
|
419
|
+
Api.name.in_(api_names),
|
|
420
|
+
Permission.name.in_(permission_names),
|
|
421
|
+
)
|
|
422
|
+
)
|
|
423
|
+
.join(Permission)
|
|
424
|
+
.join(Api)
|
|
425
|
+
.options(selectinload(PermissionApi.roles))
|
|
426
|
+
)
|
|
427
|
+
result = await db.execute(stmt)
|
|
428
|
+
permission_apis = result.scalars().all()
|
|
429
|
+
|
|
430
|
+
for permission_api in permission_apis:
|
|
431
|
+
if role not in permission_api.roles:
|
|
432
|
+
permission_api.roles.append(role)
|
|
433
|
+
logger.info(
|
|
434
|
+
f"ASSOCIATING {role} WITH PERMISSION-API {permission_api}"
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
await db.commit()
|
|
438
|
+
|
|
439
|
+
def _post_read_config(self):
|
|
440
|
+
"""
|
|
441
|
+
Function to be called after setting the configuration.
|
|
442
|
+
|
|
443
|
+
- Sets the secret key in the global `g` object if it exists in the configuration.
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
None
|
|
447
|
+
"""
|
|
448
|
+
secret_key = g.config.get("SECRET_KEY")
|
|
449
|
+
if secret_key:
|
|
450
|
+
g.auth.secret_key = secret_key
|
|
451
|
+
|
|
452
|
+
def _mount_static_folder(self):
|
|
453
|
+
"""
|
|
454
|
+
Mounts the static folder specified in the configuration.
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
None
|
|
458
|
+
"""
|
|
459
|
+
# If the folder does not exist, create it
|
|
460
|
+
os.makedirs(g.config.get("STATIC_FOLDER", DEFAULT_STATIC_FOLDER), exist_ok=True)
|
|
461
|
+
|
|
462
|
+
static_folder = g.config.get("STATIC_FOLDER", DEFAULT_STATIC_FOLDER)
|
|
463
|
+
self.app.mount("/static", StaticFiles(directory=static_folder), name="static")
|
|
464
|
+
|
|
465
|
+
def _mount_template_folder(self):
|
|
466
|
+
"""
|
|
467
|
+
Mounts the template folder specified in the configuration.
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
None
|
|
471
|
+
"""
|
|
472
|
+
# If the folder does not exist, create it
|
|
473
|
+
os.makedirs(
|
|
474
|
+
g.config.get("TEMPLATE_FOLDER", DEFAULT_TEMPLATE_FOLDER), exist_ok=True
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
templates = Jinja2Templates(
|
|
478
|
+
directory=g.config.get("TEMPLATE_FOLDER", DEFAULT_TEMPLATE_FOLDER)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
@self.app.get("/{full_path:path}", response_class=HTMLResponse)
|
|
482
|
+
def index(request: Request):
|
|
483
|
+
try:
|
|
484
|
+
return templates.TemplateResponse(
|
|
485
|
+
request=request,
|
|
486
|
+
name="index.html",
|
|
487
|
+
context={"base_path": g.config.get("BASE_PATH", "/")},
|
|
488
|
+
)
|
|
489
|
+
except TemplateNotFound:
|
|
490
|
+
raise HTTPException(status_code=404, detail="Not Found")
|
|
491
|
+
|
|
492
|
+
"""
|
|
493
|
+
-----------------------------------------
|
|
494
|
+
INIT FUNCTIONS
|
|
495
|
+
-----------------------------------------
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
def _init_info_api(self):
|
|
499
|
+
self.add_api(InfoApi)
|
|
500
|
+
|
|
501
|
+
def _init_auth_api(self):
|
|
502
|
+
self.add_api(AuthApi)
|
|
503
|
+
|
|
504
|
+
def _init_users_api(self):
|
|
505
|
+
self.add_api(UsersApi)
|
|
506
|
+
|
|
507
|
+
def _init_roles_api(self):
|
|
508
|
+
self.add_api(RolesApi)
|
|
509
|
+
|
|
510
|
+
def _init_permissions_api(self):
|
|
511
|
+
self.add_api(PermissionsApi)
|
|
512
|
+
|
|
513
|
+
def _init_apis_api(self):
|
|
514
|
+
self.add_api(ViewsMenusApi)
|
|
515
|
+
|
|
516
|
+
def _init_permission_apis_api(self):
|
|
517
|
+
self.add_api(PermissionViewApi)
|
|
518
|
+
|
|
519
|
+
def _init_js_manifest(self):
|
|
520
|
+
@self.app.get("/server-config.js", response_class=StreamingResponse)
|
|
521
|
+
def js_manifest():
|
|
522
|
+
env = Environment(autoescape=select_autoescape(["html", "xml"]))
|
|
523
|
+
template_string = "window.fab_react_config = {{ react_vars |tojson }}"
|
|
524
|
+
template = env.from_string(template_string)
|
|
525
|
+
rendered_string = template.render(
|
|
526
|
+
react_vars=json.dumps(g.config.get("FAB_REACT_CONFIG", {}))
|
|
527
|
+
)
|
|
528
|
+
content = rendered_string.encode("utf-8")
|
|
529
|
+
scriptfile = io.BytesIO(content)
|
|
530
|
+
return StreamingResponse(
|
|
531
|
+
scriptfile,
|
|
532
|
+
media_type="application/javascript",
|
|
533
|
+
)
|