zrb 1.0.0b9__py3-none-any.whl → 1.1.0__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.
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +99 -55
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +301 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +131 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +128 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/gateway/view/content/my-module/my-entity.html +297 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +81 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/navigation_config_file.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +8 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/navigation_config_file.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +19 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py +39 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +52 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py +95 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +277 -44
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +33 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +311 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +4 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/login.html +67 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/logout.html +49 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +160 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/style.css +14 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +94 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/pico-style.css +23 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +44 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/style.css +102 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +73 -18
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +15 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
- zrb/runner/web_route/refresh_token_api_route.py +1 -1
- zrb/runner/web_route/static/refresh-token.template.js +9 -0
- zrb/runner/web_route/static/static_route.py +1 -1
- zrb/task/base_task.py +10 -10
- zrb/util/codemod/modification_mode.py +3 -0
- zrb/util/codemod/modify_class.py +58 -0
- zrb/util/codemod/modify_class_parent.py +68 -0
- zrb/util/codemod/modify_class_property.py +128 -0
- zrb/util/codemod/modify_dict.py +75 -0
- zrb/util/codemod/modify_function.py +65 -0
- zrb/util/codemod/modify_function_call.py +68 -0
- zrb/util/codemod/modify_method.py +88 -0
- zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
- zrb/util/file.py +3 -2
- zrb/util/load.py +13 -7
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/METADATA +2 -2
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/RECORD +80 -46
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- zrb/util/codemod/append_code_to_class.py +0 -35
- zrb/util/codemod/append_code_to_function.py +0 -38
- zrb/util/codemod/append_code_to_method.py +0 -55
- zrb/util/codemod/append_key_to_dict.py +0 -51
- zrb/util/codemod/append_param_to_function_call.py +0 -39
- zrb/util/codemod/prepend_parent_to_class.py +0 -38
- zrb/util/codemod/prepend_property_to_class.py +0 -55
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
"""create_permissions
|
2
|
+
|
3
|
+
Revision ID: 8ed025bcc845
|
4
|
+
Revises: 3093c7336477
|
5
|
+
Create Date: 2025-02-08 19:09:14.536559
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
import sqlmodel # 🔥 FastApp Modification
|
13
|
+
from alembic import op
|
14
|
+
from module.auth.migration_metadata import metadata
|
15
|
+
|
16
|
+
# revision identifiers, used by Alembic.
|
17
|
+
revision: str = "8ed025bcc845"
|
18
|
+
down_revision: Union[str, None] = "3093c7336477"
|
19
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
20
|
+
depends_on: Union[str, Sequence[str], None] = None
|
21
|
+
|
22
|
+
|
23
|
+
def upgrade() -> None:
|
24
|
+
op.bulk_insert(
|
25
|
+
metadata.tables["permissions"],
|
26
|
+
[
|
27
|
+
# permission
|
28
|
+
{"name": "permission:create", "description": "create permission"},
|
29
|
+
{"name": "permission:read", "description": "read permission"},
|
30
|
+
{"name": "permission:update", "description": "update permission"},
|
31
|
+
{"name": "permission:delete", "description": "delete permission"},
|
32
|
+
# role
|
33
|
+
{"name": "role:create", "description": "create role"},
|
34
|
+
{"name": "role:read", "description": "read role"},
|
35
|
+
{"name": "role:update", "description": "update role"},
|
36
|
+
{"name": "role:delete", "description": "delete role"},
|
37
|
+
# user
|
38
|
+
{"name": "user:create", "description": "create user"},
|
39
|
+
{"name": "user:read", "description": "read user"},
|
40
|
+
{"name": "user:update", "description": "update user"},
|
41
|
+
{"name": "user:delete", "description": "delete user"},
|
42
|
+
],
|
43
|
+
)
|
44
|
+
# ### end Alembic commands ###
|
45
|
+
|
46
|
+
|
47
|
+
def downgrade() -> None:
|
48
|
+
op.execute(
|
49
|
+
sa.delete(metadata.tables["permissions"]).where(
|
50
|
+
metadata.tables["permissions"].c.name.in_(
|
51
|
+
# user
|
52
|
+
"user:create",
|
53
|
+
"user:read",
|
54
|
+
"user:update",
|
55
|
+
"user:delete",
|
56
|
+
# role
|
57
|
+
"role:create",
|
58
|
+
"role:read",
|
59
|
+
"role:update",
|
60
|
+
"role:delete",
|
61
|
+
# permission
|
62
|
+
"permission:create",
|
63
|
+
"permission:read",
|
64
|
+
"permission:update",
|
65
|
+
"permission:delete",
|
66
|
+
)
|
67
|
+
)
|
68
|
+
)
|
69
|
+
# ### end Alembic commands ###
|
@@ -49,7 +49,7 @@ class UserDBRepository(
|
|
49
49
|
|
50
50
|
def _select(self) -> Select:
|
51
51
|
return (
|
52
|
-
select(User, Role, Permission
|
52
|
+
select(User, Role, Permission)
|
53
53
|
.join(UserRole, UserRole.user_id == User.id, isouter=True)
|
54
54
|
.join(Role, Role.id == UserRole.role_id, isouter=True)
|
55
55
|
.join(RolePermission, RolePermission.role_id == Role.id, isouter=True)
|
@@ -62,7 +62,7 @@ class UserDBRepository(
|
|
62
62
|
user_map: dict[str, dict[str, Any]] = {}
|
63
63
|
user_role_map: dict[str, list[str]] = {}
|
64
64
|
user_permission_map: dict[str, list[str]] = {}
|
65
|
-
for user, role, permission
|
65
|
+
for user, role, permission in rows:
|
66
66
|
if user.id not in user_map:
|
67
67
|
user_map[user.id] = {"user": user, "roles": [], "permissions": []}
|
68
68
|
user_role_map[user.id] = []
|
@@ -241,6 +241,9 @@ class UserDBRepository(
|
|
241
241
|
return UserSessionResponse(
|
242
242
|
id=user_session.id,
|
243
243
|
user_id=user_session.user_id,
|
244
|
+
access_token=user_session.access_token,
|
244
245
|
access_token_expired_at=user_session.access_token_expired_at,
|
246
|
+
refresh_token=user_session.refresh_token,
|
245
247
|
refresh_token_expired_at=user_session.refresh_token_expired_at,
|
248
|
+
token_type="bearer",
|
246
249
|
)
|
@@ -46,8 +46,20 @@ class UserService(BaseService):
|
|
46
46
|
methods=["get"],
|
47
47
|
response_model=AuthUserResponse,
|
48
48
|
)
|
49
|
-
async def get_current_user(self, access_token: str) -> AuthUserResponse:
|
50
|
-
|
49
|
+
async def get_current_user(self, access_token: str | None) -> AuthUserResponse:
|
50
|
+
if access_token is None or access_token == "":
|
51
|
+
return self._get_guest_user()
|
52
|
+
try:
|
53
|
+
user_session = await self.user_repository.get_user_session_by_access_token(
|
54
|
+
access_token
|
55
|
+
)
|
56
|
+
user_id = user_session.user_id
|
57
|
+
if user_id == self.config.super_user:
|
58
|
+
return self._get_super_user()
|
59
|
+
user = await self.user_repository.get_by_id(user_id)
|
60
|
+
return self._to_auth_user_response(user)
|
61
|
+
except NotFoundError:
|
62
|
+
return self._get_guest_user()
|
51
63
|
|
52
64
|
@BaseService.route(
|
53
65
|
"/api/v1/user-sessions",
|
@@ -78,7 +90,7 @@ class UserService(BaseService):
|
|
78
90
|
async def update_user_session(self, refresh_token: str) -> UserSessionResponse:
|
79
91
|
current_user = await self._get_auth_user_by_refresh_token(refresh_token)
|
80
92
|
current_user_session = (
|
81
|
-
await self.
|
93
|
+
await self.user_repository.get_user_session_by_refresh_token(refresh_token)
|
82
94
|
)
|
83
95
|
token_data = self._create_user_token_data(current_user.username)
|
84
96
|
return await self.user_repository.update_user_session(
|
@@ -94,7 +106,7 @@ class UserService(BaseService):
|
|
94
106
|
)
|
95
107
|
async def delete_user_session(self, refresh_token: str) -> UserSessionResponse:
|
96
108
|
current_user_session = (
|
97
|
-
await self.
|
109
|
+
await self.user_repository.get_user_session_by_refresh_token(refresh_token)
|
98
110
|
)
|
99
111
|
await self.user_repository.delete_user_sessions([current_user_session.id])
|
100
112
|
return current_user_session
|
@@ -231,23 +243,6 @@ class UserService(BaseService):
|
|
231
243
|
user = await self.user_repository.get_by_id(user_id)
|
232
244
|
return self._to_auth_user_response(user)
|
233
245
|
|
234
|
-
async def _get_auth_user_by_access_token(
|
235
|
-
self, access_token: str
|
236
|
-
) -> AuthUserResponse:
|
237
|
-
if access_token is None or access_token == "":
|
238
|
-
return self._get_guest_user()
|
239
|
-
user_session = await self.user_repository.get_user_session_by_access_token(
|
240
|
-
access_token
|
241
|
-
)
|
242
|
-
user_id = user_session.user_id
|
243
|
-
if user_id == self.config.super_user:
|
244
|
-
return self._get_super_user()
|
245
|
-
try:
|
246
|
-
user = await self.user_repository.get_by_id(user_id)
|
247
|
-
return self._to_auth_user_response(user)
|
248
|
-
except NotFoundError:
|
249
|
-
return self._get_guest_user()
|
250
|
-
|
251
246
|
async def _get_user_by_credentials(
|
252
247
|
self, credentials: UserCredentials
|
253
248
|
) -> AuthUserResponse:
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
from my_app_name.module.gateway.schema.navigation import Navigation, Page, PageGroup
|
2
|
+
|
3
|
+
APP_NAVIGATION = Navigation()
|
4
|
+
|
5
|
+
APP_NAVIGATION.append_page(Page(name="gateway.home", caption="Home", url="/"))
|
6
|
+
|
7
|
+
auth_menu = APP_NAVIGATION.append_page_group(
|
8
|
+
PageGroup(
|
9
|
+
name="auth",
|
10
|
+
caption="Authorization",
|
11
|
+
)
|
12
|
+
)
|
13
|
+
|
14
|
+
auth_menu.append_page(
|
15
|
+
Page(
|
16
|
+
name="auth.permission",
|
17
|
+
caption="Permission",
|
18
|
+
url="/auth/permissions",
|
19
|
+
permission="permission:read",
|
20
|
+
)
|
21
|
+
)
|
22
|
+
|
23
|
+
auth_menu.append_page(
|
24
|
+
Page(
|
25
|
+
name="auth.role",
|
26
|
+
caption="Role",
|
27
|
+
url="/auth/roles",
|
28
|
+
permission="role:read",
|
29
|
+
)
|
30
|
+
)
|
31
|
+
|
32
|
+
auth_menu.append_page(
|
33
|
+
Page(
|
34
|
+
name="auth.user",
|
35
|
+
caption="User",
|
36
|
+
url="/auth/users",
|
37
|
+
permission="user:read",
|
38
|
+
)
|
39
|
+
)
|
@@ -1,18 +1,21 @@
|
|
1
|
-
import
|
1
|
+
from typing import Annotated
|
2
2
|
|
3
|
-
from fastapi import FastAPI, HTTPException, Request
|
3
|
+
from fastapi import Depends, FastAPI, HTTPException, Request
|
4
4
|
from fastapi.exception_handlers import http_exception_handler
|
5
|
-
from fastapi.responses import HTMLResponse
|
5
|
+
from fastapi.responses import HTMLResponse, RedirectResponse
|
6
6
|
from my_app_name.common.app_factory import app
|
7
7
|
from my_app_name.common.schema import BasicResponse
|
8
8
|
from my_app_name.config import (
|
9
|
-
|
9
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME,
|
10
10
|
APP_MAIN_MODULE,
|
11
11
|
APP_MODE,
|
12
12
|
APP_MODULES,
|
13
13
|
)
|
14
|
+
from my_app_name.module.auth.client.auth_client_factory import auth_client
|
14
15
|
from my_app_name.module.gateway.subroute.auth import serve_auth_route
|
15
|
-
from my_app_name.module.gateway.util.
|
16
|
+
from my_app_name.module.gateway.util.auth import get_current_user
|
17
|
+
from my_app_name.module.gateway.util.view import render_content, render_error
|
18
|
+
from my_app_name.schema.user import AuthUserResponse
|
16
19
|
|
17
20
|
|
18
21
|
def serve_route(app: FastAPI):
|
@@ -21,18 +24,48 @@ def serve_route(app: FastAPI):
|
|
21
24
|
if APP_MODE == "monolith" or APP_MAIN_MODULE == "gateway":
|
22
25
|
_serve_health_check(app)
|
23
26
|
_serve_readiness_check(app)
|
24
|
-
|
27
|
+
_serve_common_pages(app)
|
25
28
|
_handle_404(app)
|
26
29
|
|
27
30
|
# Serve auth routes
|
28
31
|
serve_auth_route(app)
|
29
32
|
|
30
33
|
|
31
|
-
def
|
34
|
+
def _serve_common_pages(app: FastAPI):
|
32
35
|
@app.get("/", include_in_schema=False)
|
33
|
-
def home_page(
|
34
|
-
|
35
|
-
|
36
|
+
def home_page(
|
37
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
38
|
+
):
|
39
|
+
return render_content(
|
40
|
+
view_path="homepage.html",
|
41
|
+
current_user=current_user,
|
42
|
+
page_name="gateway.home",
|
43
|
+
)
|
44
|
+
|
45
|
+
@app.get("/login", include_in_schema=False)
|
46
|
+
def login_page(
|
47
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
48
|
+
):
|
49
|
+
if not current_user.is_guest:
|
50
|
+
return RedirectResponse("/")
|
51
|
+
return render_content(
|
52
|
+
view_path="login.html",
|
53
|
+
current_user=current_user,
|
54
|
+
page_name="gateway.home",
|
55
|
+
partials={"show_user_info": False},
|
56
|
+
)
|
57
|
+
|
58
|
+
@app.get("/logout", include_in_schema=False)
|
59
|
+
def logout_page(
|
60
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
61
|
+
):
|
62
|
+
if current_user is None:
|
63
|
+
return RedirectResponse("/")
|
64
|
+
return render_content(
|
65
|
+
view_path="logout.html",
|
66
|
+
current_user=current_user,
|
67
|
+
page_name="gateway.home",
|
68
|
+
partials={"show_user_info": False},
|
36
69
|
)
|
37
70
|
|
38
71
|
|
@@ -60,7 +93,15 @@ def _handle_404(app: FastAPI):
|
|
60
93
|
if request.url.path.startswith("/api"):
|
61
94
|
# Re-raise the exception to let FastAPI handle it
|
62
95
|
return await http_exception_handler(request, exc)
|
63
|
-
|
96
|
+
# Get current user by cookies
|
97
|
+
current_user = None
|
98
|
+
cookie_access_token = request.cookies.get(APP_AUTH_ACCESS_TOKEN_COOKIE_NAME)
|
99
|
+
if cookie_access_token is not None and cookie_access_token != "":
|
100
|
+
current_user = await auth_client.get_current_user(cookie_access_token)
|
101
|
+
# Show error page
|
102
|
+
return render_error(
|
103
|
+
error_message="Not found", status_code=404, current_user=current_user
|
104
|
+
)
|
64
105
|
|
65
106
|
|
66
107
|
serve_route(app)
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
from my_app_name.schema.user import AuthUserResponse
|
2
|
+
from pydantic import BaseModel
|
3
|
+
|
4
|
+
|
5
|
+
class Page(BaseModel):
|
6
|
+
name: str
|
7
|
+
caption: str
|
8
|
+
url: str
|
9
|
+
permission: str | None = None
|
10
|
+
|
11
|
+
|
12
|
+
class AccessiblePage(BaseModel):
|
13
|
+
name: str
|
14
|
+
caption: str
|
15
|
+
url: str
|
16
|
+
active: bool
|
17
|
+
|
18
|
+
|
19
|
+
class PageGroup(BaseModel):
|
20
|
+
name: str
|
21
|
+
caption: str
|
22
|
+
pages: list[Page] = []
|
23
|
+
|
24
|
+
def append_page(self, submenu: Page) -> Page:
|
25
|
+
self.pages.append(submenu)
|
26
|
+
return submenu
|
27
|
+
|
28
|
+
def get_accessible_pages(
|
29
|
+
self, submenu_name: str | None = None, user: AuthUserResponse | None = None
|
30
|
+
) -> list[AccessiblePage]:
|
31
|
+
return [
|
32
|
+
AccessiblePage(
|
33
|
+
name=page.name,
|
34
|
+
caption=page.caption,
|
35
|
+
url=page.url,
|
36
|
+
active=page.name == submenu_name,
|
37
|
+
)
|
38
|
+
for page in self.pages
|
39
|
+
if _has_permission(user, page.permission)
|
40
|
+
]
|
41
|
+
|
42
|
+
|
43
|
+
class AccessiblePageGroup(BaseModel):
|
44
|
+
name: str
|
45
|
+
caption: str
|
46
|
+
pages: list[AccessiblePage]
|
47
|
+
active: bool
|
48
|
+
|
49
|
+
|
50
|
+
class Navigation(BaseModel):
|
51
|
+
items: list[PageGroup | Page] = []
|
52
|
+
|
53
|
+
def append_page_group(self, page_group: PageGroup) -> PageGroup:
|
54
|
+
self.items.append(page_group)
|
55
|
+
return page_group
|
56
|
+
|
57
|
+
def append_page(self, page: Page) -> Page:
|
58
|
+
self.items.append(page)
|
59
|
+
return page
|
60
|
+
|
61
|
+
def get_accessible_items(
|
62
|
+
self, page_name: str | None, user: AuthUserResponse | None
|
63
|
+
) -> list[AccessiblePageGroup | AccessiblePage]:
|
64
|
+
accessible_items = []
|
65
|
+
for item in self.items:
|
66
|
+
if isinstance(item, Page) and _has_permission(user, item.permission):
|
67
|
+
accessible_items.append(
|
68
|
+
AccessiblePage(
|
69
|
+
name=item.name,
|
70
|
+
caption=item.caption,
|
71
|
+
url=item.url,
|
72
|
+
active=item.name == page_name,
|
73
|
+
)
|
74
|
+
)
|
75
|
+
continue
|
76
|
+
accessible_submenus = item.get_accessible_pages(page_name, user)
|
77
|
+
if accessible_submenus:
|
78
|
+
active = any(submenu.active for submenu in accessible_submenus)
|
79
|
+
accessible_items.append(
|
80
|
+
AccessiblePageGroup(
|
81
|
+
name=item.name,
|
82
|
+
caption=item.caption,
|
83
|
+
pages=accessible_submenus,
|
84
|
+
active=active,
|
85
|
+
)
|
86
|
+
)
|
87
|
+
return accessible_items
|
88
|
+
|
89
|
+
|
90
|
+
def _has_permission(user: AuthUserResponse | None, permission: str | None):
|
91
|
+
if permission is None:
|
92
|
+
return True
|
93
|
+
if user is not None:
|
94
|
+
return user.has_permission(permission)
|
95
|
+
return False
|