zrb 1.0.0a21__py3-none-any.whl → 1.0.0b5__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/__init__.py +2 -1
- zrb/__main__.py +3 -3
- zrb/builtin/__init__.py +3 -0
- zrb/builtin/group.py +1 -0
- zrb/builtin/llm/llm_chat.py +87 -7
- zrb/builtin/llm/previous-session.js +21 -0
- zrb/builtin/llm/tool/api.py +29 -0
- zrb/builtin/llm/tool/cli.py +1 -1
- zrb/builtin/llm/tool/rag.py +108 -145
- zrb/builtin/llm/tool/web.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_task.py +2 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +80 -20
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +150 -42
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service.py +113 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service_factory.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_db_repository.py +0 -10
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_repository.py +37 -16
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/{factory.py → my_entity_repository_factory.py} +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +16 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +57 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +74 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +13 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +23 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +7 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_api_client.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/{any_client.py → my_module_client.py} +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_client_factory.py +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_direct_client.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +11 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +8 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +47 -20
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app_factory.py +29 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +230 -102
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +236 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{db_engine.py → db_engine_factory.py} +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +12 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/logger_factory.py +10 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/parser_factory.py +7 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/app.py +47 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +105 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/user_agent.py +58 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +37 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +37 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_api_client.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +169 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client_factory.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_direct_client.py +15 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +160 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +18 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +7 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +117 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service_factory.py +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_db_repository.py +26 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository.py +61 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository_factory.py +13 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +89 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +67 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository_factory.py +13 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +137 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service_factory.py +7 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +179 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +67 -17
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository_factory.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +127 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +7 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +200 -30
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +74 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/homepage.html +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-192x192.png +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-512x512.png +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/favicon-32x32.png +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +34 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +17 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +78 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +48 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +69 -5
- zrb/builtin/python.py +1 -1
- zrb/builtin/random.py +61 -0
- zrb/builtin/todo.py +1 -0
- zrb/cmd/cmd_val.py +6 -5
- zrb/config.py +15 -4
- zrb/content_transformer/any_content_transformer.py +7 -0
- zrb/content_transformer/content_transformer.py +6 -0
- zrb/input/any_input.py +5 -0
- zrb/input/base_input.py +6 -0
- zrb/input/bool_input.py +2 -0
- zrb/input/float_input.py +2 -0
- zrb/input/int_input.py +2 -0
- zrb/input/option_input.py +2 -0
- zrb/input/password_input.py +2 -0
- zrb/input/text_input.py +2 -0
- zrb/runner/cli.py +14 -7
- zrb/runner/common_util.py +1 -1
- zrb/runner/web_app.py +28 -280
- zrb/runner/web_config/config.py +91 -0
- zrb/runner/web_config/config_factory.py +26 -0
- zrb/runner/web_route/docs_route.py +17 -0
- zrb/runner/web_route/error_page/serve_default_404.py +28 -0
- zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py} +4 -3
- zrb/runner/{web_controller → web_route}/error_page/view.html +5 -0
- zrb/runner/web_route/home_page/home_page_route.py +51 -0
- zrb/runner/web_route/login_api_route.py +31 -0
- zrb/runner/web_route/login_page/login_page_route.py +39 -0
- zrb/runner/web_route/logout_api_route.py +18 -0
- zrb/runner/web_route/logout_page/logout_page_route.py +40 -0
- zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
- zrb/runner/web_route/node_page/node_page_route.py +50 -0
- zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py} +3 -3
- zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +1 -1
- zrb/runner/web_route/refresh_token_api_route.py +38 -0
- zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +12 -6
- zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +17 -2
- zrb/runner/web_route/static/static_route.py +44 -0
- zrb/runner/web_route/task_input_api_route.py +47 -0
- zrb/runner/web_route/task_session_api_route.py +147 -0
- zrb/runner/web_schema/session.py +5 -0
- zrb/runner/web_schema/token.py +11 -0
- zrb/runner/web_schema/user.py +32 -0
- zrb/runner/web_util/cookie.py +29 -0
- zrb/runner/{web_util.py → web_util/html.py} +1 -23
- zrb/runner/web_util/token.py +72 -0
- zrb/runner/web_util/user.py +63 -0
- zrb/session/session.py +6 -4
- zrb/session_state_logger/{default_session_state_logger.py → session_state_logger_factory.py} +1 -1
- zrb/task/base_task.py +56 -8
- zrb/task/base_trigger.py +2 -0
- zrb/task/cmd_task.py +9 -5
- zrb/task/http_check.py +2 -0
- zrb/task/llm_task.py +93 -110
- zrb/task/make_task.py +2 -0
- zrb/task/rsync_task.py +2 -0
- zrb/task/scaffolder.py +8 -5
- zrb/task/scheduler.py +2 -0
- zrb/task/tcp_check.py +2 -0
- zrb/task_status/task_status.py +4 -3
- zrb/util/cmd/command.py +1 -0
- zrb/util/file.py +7 -1
- zrb/util/llm/tool.py +36 -12
- {zrb-1.0.0a21.dist-info → zrb-1.0.0b5.dist-info}/METADATA +3 -2
- zrb-1.0.0b5.dist-info/RECORD +309 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +0 -27
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_usecase.py +0 -65
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +0 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +0 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +0 -9
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +0 -20
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_usecase.py +0 -245
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/any_client.py +0 -33
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +0 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +0 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +0 -9
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase.py +0 -53
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase_factory.py +0 -6
- zrb/runner/web_config.py +0 -274
- zrb/runner/web_controller/home_page/controller.py +0 -33
- zrb/runner/web_controller/login_page/controller.py +0 -25
- zrb/runner/web_controller/logout_page/controller.py +0 -26
- zrb-1.0.0a21.dist-info/RECORD +0 -244
- /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
- /zrb/{runner/web_controller → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission}/__init__.py +0 -0
- /zrb/{runner/web_controller/group_info_page → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role}/__init__.py +0 -0
- /zrb/runner/{web_controller/home_page → web_route}/__init__.py +0 -0
- /zrb/runner/{web_controller/session_page → web_route/home_page}/__init__.py +0 -0
- /zrb/runner/{web_controller → web_route}/home_page/view.html +0 -0
- /zrb/runner/{web_controller → web_route}/login_page/view.html +0 -0
- /zrb/runner/{web_controller → web_route}/logout_page/view.html +0 -0
- /zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +0 -0
- /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +0 -0
- /zrb/runner/{refresh-token.template.js → web_route/static/refresh-token.template.js} +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/common.css +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/favicon-32x32.png +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/login/event.js +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/logout/event.js +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/pico.min.css +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/session/common-util.js +0 -0
- /zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +0 -0
- {zrb-1.0.0a21.dist-info → zrb-1.0.0b5.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a21.dist-info → zrb-1.0.0b5.dist-info}/entry_points.txt +0 -0
@@ -1,17 +1,35 @@
|
|
1
|
+
import datetime
|
2
|
+
from typing import Any, Callable
|
3
|
+
|
4
|
+
import ulid
|
1
5
|
from my_app_name.common.base_db_repository import BaseDBRepository
|
2
6
|
from my_app_name.common.error import NotFoundError
|
7
|
+
from my_app_name.config import (
|
8
|
+
APP_AUTH_GUEST_USER,
|
9
|
+
APP_AUTH_GUEST_USER_PERMISSIONS,
|
10
|
+
APP_AUTH_SUPER_USER,
|
11
|
+
APP_AUTH_SUPER_USER_PASSWORD,
|
12
|
+
APP_MAX_PARALLEL_SESSION,
|
13
|
+
APP_SESSION_EXPIRE_MINUTES,
|
14
|
+
)
|
3
15
|
from my_app_name.module.auth.service.user.repository.user_repository import (
|
4
16
|
UserRepository,
|
5
17
|
)
|
18
|
+
from my_app_name.schema.permission import Permission
|
19
|
+
from my_app_name.schema.role import Role, RolePermission
|
20
|
+
from my_app_name.schema.session import Session, SessionResponse
|
6
21
|
from my_app_name.schema.user import (
|
7
22
|
User,
|
8
23
|
UserCreateWithAudit,
|
9
24
|
UserResponse,
|
25
|
+
UserRole,
|
10
26
|
UserUpdateWithAudit,
|
11
27
|
)
|
12
28
|
from passlib.context import CryptContext
|
13
|
-
from sqlalchemy.
|
14
|
-
from
|
29
|
+
from sqlalchemy.engine import Engine
|
30
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
31
|
+
from sqlalchemy.sql import ClauseElement, ColumnElement, Select
|
32
|
+
from sqlmodel import SQLModel, delete, insert, select
|
15
33
|
|
16
34
|
# Password hashing context
|
17
35
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
@@ -32,14 +50,163 @@ class UserDBRepository(
|
|
32
50
|
entity_name = "user"
|
33
51
|
column_preprocessors = {"password": hash_password}
|
34
52
|
|
53
|
+
def __init__(
|
54
|
+
self,
|
55
|
+
engine: Engine | AsyncEngine,
|
56
|
+
super_user_username: str = APP_AUTH_SUPER_USER,
|
57
|
+
super_user_password: str = APP_AUTH_SUPER_USER_PASSWORD,
|
58
|
+
guest_user_username: str = APP_AUTH_GUEST_USER,
|
59
|
+
guest_user_password: str = APP_AUTH_SUPER_USER_PASSWORD,
|
60
|
+
guest_user_permission_names: list[str] = APP_AUTH_GUEST_USER_PERMISSIONS,
|
61
|
+
max_parallel_session: int = APP_MAX_PARALLEL_SESSION,
|
62
|
+
session_expire_minutes: int = APP_SESSION_EXPIRE_MINUTES,
|
63
|
+
filter_param_parser: (
|
64
|
+
Callable[[SQLModel, str], list[ClauseElement]] | None
|
65
|
+
) = None,
|
66
|
+
sort_param_parser: Callable[[SQLModel, str], list[ColumnElement]] | None = None,
|
67
|
+
):
|
68
|
+
super().__init__(
|
69
|
+
engine=engine,
|
70
|
+
filter_param_parser=filter_param_parser,
|
71
|
+
sort_param_parser=sort_param_parser,
|
72
|
+
)
|
73
|
+
self._super_user_username = super_user_username
|
74
|
+
self._super_user_passwored = super_user_password
|
75
|
+
self._guest_user_username = guest_user_username
|
76
|
+
self._guest_user_password = guest_user_password
|
77
|
+
self._guest_user_permission_names = guest_user_permission_names
|
78
|
+
self._max_parallel_session = max_parallel_session
|
79
|
+
self._session_expire_minutes = session_expire_minutes
|
80
|
+
self._super_user: User | None = None
|
81
|
+
self._guest_user: User | None = None
|
82
|
+
|
83
|
+
def _select(self) -> Select:
|
84
|
+
return (
|
85
|
+
select(User, Role, Permission, Session)
|
86
|
+
.join(UserRole, UserRole.user_id == User.id, isouter=True)
|
87
|
+
.join(Role, Role.id == UserRole.role_id, isouter=True)
|
88
|
+
.join(RolePermission, RolePermission.role_id == Role.id, isouter=True)
|
89
|
+
.join(
|
90
|
+
Permission, Permission.id == RolePermission.permission_id, isouter=True
|
91
|
+
)
|
92
|
+
.join(Session, Session.user_id == User.id)
|
93
|
+
)
|
94
|
+
|
95
|
+
def _rows_to_responses(self, rows: list[tuple[Any, ...]]) -> list[UserResponse]:
|
96
|
+
user_map: dict[str, dict[str, Any]] = {}
|
97
|
+
user_role_map: dict[str, list[str]] = {}
|
98
|
+
user_permission_map: dict[str, list[str]] = {}
|
99
|
+
for user, role, permission, _ in rows:
|
100
|
+
if user.id not in user_map:
|
101
|
+
user_map[user.id] = {"user": user, "roles": [], "permissions": []}
|
102
|
+
user_role_map[user.id] = []
|
103
|
+
user_permission_map[user.id] = []
|
104
|
+
if role is not None and role.id not in user_role_map[user.id]:
|
105
|
+
user_role_map[user.id].append(role.id)
|
106
|
+
user_map[user.id]["roles"].append(role.model_dump())
|
107
|
+
if (
|
108
|
+
permission is not None
|
109
|
+
and permission.id not in user_permission_map[user.id]
|
110
|
+
):
|
111
|
+
user_permission_map[user.id].append(permission.id)
|
112
|
+
user_map[user.id]["permissions"].append(permission.model_dump())
|
113
|
+
return [
|
114
|
+
UserResponse(
|
115
|
+
**data["user"].model_dump(),
|
116
|
+
roles=list(data["roles"]),
|
117
|
+
permissions=list(data["permissions"]),
|
118
|
+
)
|
119
|
+
for data in user_map.values()
|
120
|
+
]
|
121
|
+
|
122
|
+
async def add_roles(self, data: dict[str, list[str]], created_by: str):
|
123
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
124
|
+
data_dict_list: list[dict[str, Any]] = []
|
125
|
+
for user_id, role_ids in data.items():
|
126
|
+
for role_id in role_ids:
|
127
|
+
data_dict_list.append(
|
128
|
+
self._model_to_data_dict(
|
129
|
+
UserRole(
|
130
|
+
id=ulid.new().str,
|
131
|
+
user_id=user_id,
|
132
|
+
role_id=role_id,
|
133
|
+
created_at=now,
|
134
|
+
created_by=created_by,
|
135
|
+
)
|
136
|
+
)
|
137
|
+
)
|
138
|
+
async with self._session_scope() as session:
|
139
|
+
await self._execute_statement(
|
140
|
+
session, insert(UserRole).values(data_dict_list)
|
141
|
+
)
|
142
|
+
|
143
|
+
async def remove_all_roles(self, user_ids: list[str] = []):
|
144
|
+
async with self._session_scope() as session:
|
145
|
+
await self._execute_statement(
|
146
|
+
session,
|
147
|
+
delete(UserRole).where(UserRole.user_id._in(user_ids)),
|
148
|
+
)
|
149
|
+
|
35
150
|
async def get_by_credentials(self, username: str, password: str) -> UserResponse:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
151
|
+
rows = await self._select_to_response(
|
152
|
+
lambda q: q.where(
|
153
|
+
User.username == username, User.password == hash_password(password)
|
154
|
+
)
|
155
|
+
)
|
156
|
+
return self._ensure_one(rows)
|
157
|
+
|
158
|
+
async def get_by_token(self, token: str) -> UserResponse:
|
159
|
+
rows = await self._select_tor_response(
|
160
|
+
lambda q: q.where(Session.token == token)
|
161
|
+
)
|
162
|
+
return self._ensure_one(rows)
|
163
|
+
|
164
|
+
async def add_token(self, user_id: str, token: str):
|
165
|
+
async with self._session_scope() as session:
|
166
|
+
await self._execute_statement(
|
167
|
+
session,
|
168
|
+
insert(Session).values(
|
169
|
+
{
|
170
|
+
"id": ulid.new().str,
|
171
|
+
"user_id": user_id,
|
172
|
+
"token": token,
|
173
|
+
"created_by": "system",
|
174
|
+
"created_at": datetime.datetime.now(datetime.timezone.utc),
|
175
|
+
}
|
176
|
+
),
|
177
|
+
)
|
178
|
+
|
179
|
+
async def remove_token(self, user_id: str, token: str):
|
180
|
+
async with self._session_scope() as session:
|
181
|
+
await self._execute_statement(
|
182
|
+
session,
|
183
|
+
delete(Session).where(
|
184
|
+
Session.token == token, Session.user_id == user_id
|
185
|
+
),
|
186
|
+
)
|
187
|
+
|
188
|
+
async def get_sessions(self, user_id: str) -> list[SessionResponse]:
|
189
|
+
async with self._session_scope() as session:
|
190
|
+
statement = select(Session).where(Session.user_id == user_id)
|
191
|
+
result = await self._execute_statement(session, statement)
|
192
|
+
return [
|
193
|
+
SessionResponse(**session.model_dump())
|
194
|
+
for session in result.scalars().all()
|
195
|
+
]
|
196
|
+
|
197
|
+
async def remove_session(self, user_id: str, session_id: str) -> SessionResponse:
|
198
|
+
async with self._session_scope() as session:
|
199
|
+
statement = select(Session).where(
|
200
|
+
Session.user_id == user_id, Session.id == session_id
|
201
|
+
)
|
202
|
+
result = await self._execute_statement(session, statement)
|
203
|
+
session = result.scalar_one_or_none()
|
204
|
+
if not session:
|
205
|
+
raise NotFoundError(f"{self.entity_name} not found")
|
206
|
+
await self._execute_statement(
|
207
|
+
session,
|
208
|
+
delete(Session).where(
|
209
|
+
Session.id == session_id, Session.user_id == user_id
|
210
|
+
),
|
211
|
+
)
|
212
|
+
return SessionResponse(**session.model_dump())
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
|
3
|
+
from my_app_name.schema.session import SessionResponse
|
3
4
|
from my_app_name.schema.user import (
|
4
5
|
User,
|
5
6
|
UserCreateWithAudit,
|
@@ -9,34 +10,83 @@ from my_app_name.schema.user import (
|
|
9
10
|
|
10
11
|
|
11
12
|
class UserRepository(ABC):
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
async def get_by_id(self, id: str) -> UserResponse:
|
16
|
+
"""Get user by id"""
|
17
|
+
|
18
|
+
@abstractmethod
|
19
|
+
async def get_by_ids(self, id_list: list[str]) -> UserResponse:
|
20
|
+
"""Get users by ids"""
|
21
|
+
|
22
|
+
@abstractmethod
|
23
|
+
async def add_roles(self, data: dict[str, list[str]], created_by: str):
|
24
|
+
"""Add roles to user"""
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
async def remove_all_roles(self, user_ids: list[str] = []):
|
28
|
+
"""Remove roles from user"""
|
29
|
+
|
12
30
|
@abstractmethod
|
13
|
-
async def
|
14
|
-
|
31
|
+
async def get(
|
32
|
+
self,
|
33
|
+
page: int = 1,
|
34
|
+
page_size: int = 10,
|
35
|
+
filter: str | None = None,
|
36
|
+
sort: str | None = None,
|
37
|
+
) -> list[User]:
|
38
|
+
"""Get users by filter and sort"""
|
15
39
|
|
16
40
|
@abstractmethod
|
17
|
-
async def
|
18
|
-
|
41
|
+
async def count(self, filter: str | None = None) -> int:
|
42
|
+
"""Count users by filter"""
|
19
43
|
|
20
44
|
@abstractmethod
|
21
|
-
async def
|
22
|
-
|
45
|
+
async def create(self, data: UserCreateWithAudit) -> User:
|
46
|
+
"""Create a new user"""
|
23
47
|
|
24
48
|
@abstractmethod
|
25
|
-
async def
|
26
|
-
|
27
|
-
) -> UserResponse:
|
28
|
-
pass
|
49
|
+
async def create_bulk(self, data: list[UserCreateWithAudit]) -> list[User]:
|
50
|
+
"""Create some users"""
|
29
51
|
|
30
52
|
@abstractmethod
|
31
|
-
async def delete(self,
|
32
|
-
|
53
|
+
async def delete(self, id: str) -> User:
|
54
|
+
"""Delete a user"""
|
33
55
|
|
34
56
|
@abstractmethod
|
35
|
-
async def
|
36
|
-
|
37
|
-
|
38
|
-
|
57
|
+
async def delete_bulk(self, id_list: list[str]) -> list[User]:
|
58
|
+
"""Delete some users"""
|
59
|
+
|
60
|
+
@abstractmethod
|
61
|
+
async def update(self, id: str, data: UserUpdateWithAudit) -> User:
|
62
|
+
"""Update a user"""
|
63
|
+
|
64
|
+
@abstractmethod
|
65
|
+
async def update_bulk(
|
66
|
+
self, id_list: list[str], data: UserUpdateWithAudit
|
67
|
+
) -> list[User]:
|
68
|
+
"""Update some users"""
|
39
69
|
|
40
70
|
@abstractmethod
|
41
71
|
async def get_by_credentials(self, username: str, password: str) -> UserResponse:
|
42
|
-
|
72
|
+
"""Get user by credential"""
|
73
|
+
|
74
|
+
@abstractmethod
|
75
|
+
async def get_by_token(self, token: str) -> UserResponse:
|
76
|
+
"""Get user by token"""
|
77
|
+
|
78
|
+
@abstractmethod
|
79
|
+
async def add_token(self, user_id: str, token: str):
|
80
|
+
"""Add token to user"""
|
81
|
+
|
82
|
+
@abstractmethod
|
83
|
+
async def remove_token(self, user_id: str, token: str):
|
84
|
+
"""Remove token from user"""
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
async def get_sessions(self, user_id: str) -> list[SessionResponse]:
|
88
|
+
"""Get sessions"""
|
89
|
+
|
90
|
+
@abstractmethod
|
91
|
+
async def remove_session(self, user_id: str, session_id: str) -> SessionResponse:
|
92
|
+
"""Remove a session"""
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from my_app_name.common.
|
1
|
+
from my_app_name.common.db_engine_factory import db_engine
|
2
2
|
from my_app_name.config import APP_REPOSITORY_TYPE
|
3
3
|
from my_app_name.module.auth.service.user.repository.user_db_repository import (
|
4
4
|
UserDBRepository,
|
@@ -8,6 +8,6 @@ from my_app_name.module.auth.service.user.repository.user_repository import (
|
|
8
8
|
)
|
9
9
|
|
10
10
|
if APP_REPOSITORY_TYPE == "db":
|
11
|
-
user_repository: UserRepository = UserDBRepository(
|
11
|
+
user_repository: UserRepository = UserDBRepository(db_engine)
|
12
12
|
else:
|
13
13
|
user_repository: UserRepository = None
|
@@ -0,0 +1,127 @@
|
|
1
|
+
from logging import Logger
|
2
|
+
|
3
|
+
from my_app_name.common.base_service import BaseService
|
4
|
+
from my_app_name.module.auth.service.user.repository.user_repository import (
|
5
|
+
UserRepository,
|
6
|
+
)
|
7
|
+
from my_app_name.schema.user import (
|
8
|
+
MultipleUserResponse,
|
9
|
+
UserCreateWithRolesAndAudit,
|
10
|
+
UserResponse,
|
11
|
+
UserUpdateWithRolesAndAudit,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class UserService(BaseService):
|
16
|
+
|
17
|
+
def __init__(self, logger: Logger, user_repository: UserRepository):
|
18
|
+
super().__init__(logger)
|
19
|
+
self.user_repository = user_repository
|
20
|
+
|
21
|
+
@BaseService.route(
|
22
|
+
"/api/v1/users/{user_id}",
|
23
|
+
methods=["get"],
|
24
|
+
response_model=UserResponse,
|
25
|
+
)
|
26
|
+
async def get_user_by_id(self, user_id: str) -> UserResponse:
|
27
|
+
return await self.user_repository.get_by_id(user_id)
|
28
|
+
|
29
|
+
@BaseService.route(
|
30
|
+
"/api/v1/users",
|
31
|
+
methods=["get"],
|
32
|
+
response_model=MultipleUserResponse,
|
33
|
+
)
|
34
|
+
async def get_users(
|
35
|
+
self,
|
36
|
+
page: int = 1,
|
37
|
+
page_size: int = 10,
|
38
|
+
sort: str | None = None,
|
39
|
+
filter: str | None = None,
|
40
|
+
) -> MultipleUserResponse:
|
41
|
+
users = await self.user_repository.get(page, page_size, filter, sort)
|
42
|
+
count = await self.user_repository.count(filter)
|
43
|
+
return MultipleUserResponse(data=users, count=count)
|
44
|
+
|
45
|
+
@BaseService.route(
|
46
|
+
"/api/v1/users/bulk",
|
47
|
+
methods=["post"],
|
48
|
+
response_model=list[UserResponse],
|
49
|
+
)
|
50
|
+
async def create_user_bulk(
|
51
|
+
self, data: list[UserCreateWithRolesAndAudit]
|
52
|
+
) -> list[UserResponse]:
|
53
|
+
role_ids = [row.get_role_ids() for row in data]
|
54
|
+
data = [row.get_user_create_with_audit() for row in data]
|
55
|
+
users = await self.user_repository.create_bulk(data)
|
56
|
+
if len(users) > 0:
|
57
|
+
created_by = users[0].created_by
|
58
|
+
await self.user_repository.add_roles(
|
59
|
+
data={user.id: role_ids[i] for i, user in enumerate(data)},
|
60
|
+
created_by=created_by,
|
61
|
+
)
|
62
|
+
return await self.user_repository.get_by_ids([user.id for user in users])
|
63
|
+
|
64
|
+
@BaseService.route(
|
65
|
+
"/api/v1/users",
|
66
|
+
methods=["post"],
|
67
|
+
response_model=UserResponse,
|
68
|
+
)
|
69
|
+
async def create_user(self, data: UserCreateWithRolesAndAudit) -> UserResponse:
|
70
|
+
role_ids = data.get_role_ids()
|
71
|
+
data = data.get_user_create_with_audit()
|
72
|
+
user = await self.user_repository.create(data)
|
73
|
+
await self.user_repository.add_roles(
|
74
|
+
data={user.id: role_ids}, created_by=user.created_by
|
75
|
+
)
|
76
|
+
return await self.user_repository.get_by_id(user.id)
|
77
|
+
|
78
|
+
@BaseService.route(
|
79
|
+
"/api/v1/users/bulk",
|
80
|
+
methods=["put"],
|
81
|
+
response_model=UserResponse,
|
82
|
+
)
|
83
|
+
async def update_user_bulk(
|
84
|
+
self, user_ids: list[str], data: UserUpdateWithRolesAndAudit
|
85
|
+
) -> UserResponse:
|
86
|
+
role_ids = [row.get_role_ids() for row in data]
|
87
|
+
data = [row.get_user_create_with_audit() for row in data]
|
88
|
+
users = await self.user_repository.update_bulk(user_ids, data)
|
89
|
+
if len(users) > 0:
|
90
|
+
updated_by = users[0].updated_by
|
91
|
+
await self.user_repository.remove_all_roles([user.id for user in users])
|
92
|
+
await self.user_repository.add_roles(
|
93
|
+
data={user.id: role_ids[i] for i, user in enumerate(data)},
|
94
|
+
updated_by=updated_by,
|
95
|
+
)
|
96
|
+
return await self.user_repository.get_by_ids([user.id for user in users])
|
97
|
+
|
98
|
+
@BaseService.route(
|
99
|
+
"/api/v1/users/{user_id}",
|
100
|
+
methods=["put"],
|
101
|
+
response_model=UserResponse,
|
102
|
+
)
|
103
|
+
async def update_user(
|
104
|
+
self, user_id: str, data: UserUpdateWithRolesAndAudit
|
105
|
+
) -> UserResponse:
|
106
|
+
user = await self.user_repository.update(user_id, data)
|
107
|
+
return await self.user_repository.get_by_id(user.id)
|
108
|
+
|
109
|
+
@BaseService.route(
|
110
|
+
"/api/v1/users/{user_id}",
|
111
|
+
methods=["delete"],
|
112
|
+
response_model=UserResponse,
|
113
|
+
)
|
114
|
+
async def delete_user_bulk(
|
115
|
+
self, user_ids: list[str], deleted_by: str
|
116
|
+
) -> UserResponse:
|
117
|
+
users = await self.user_repository.delete_bulk(user_ids)
|
118
|
+
return await self.user_repository.get_by_ids([user.id for user in users])
|
119
|
+
|
120
|
+
@BaseService.route(
|
121
|
+
"/api/v1/users/{user_id}",
|
122
|
+
methods=["delete"],
|
123
|
+
response_model=UserResponse,
|
124
|
+
)
|
125
|
+
async def delete_user(self, user_id: str, deleted_by: str) -> UserResponse:
|
126
|
+
user = await self.user_repository.delete(user_id)
|
127
|
+
return await self.user_repository.get_by_id(user.id)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from my_app_name.common.logger_factory import logger
|
2
|
+
from my_app_name.module.auth.service.user.repository.user_repository_factory import (
|
3
|
+
user_repository,
|
4
|
+
)
|
5
|
+
from my_app_name.module.auth.service.user.user_service import UserService
|
6
|
+
|
7
|
+
user_service = UserService(logger, user_repository=user_repository)
|
@@ -1,11 +1,42 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import os
|
2
|
+
|
3
|
+
from fastapi import FastAPI, HTTPException, Request
|
4
|
+
from fastapi.exception_handlers import http_exception_handler
|
5
|
+
from fastapi.responses import HTMLResponse
|
6
|
+
from my_app_name.common.app_factory import app
|
3
7
|
from my_app_name.common.schema import BasicResponse
|
4
|
-
from my_app_name.config import
|
8
|
+
from my_app_name.config import (
|
9
|
+
APP_GATEWAY_VIEW_PATH,
|
10
|
+
APP_MAIN_MODULE,
|
11
|
+
APP_MODE,
|
12
|
+
APP_MODULES,
|
13
|
+
)
|
5
14
|
from my_app_name.module.gateway.subroute.auth import serve_auth_route
|
15
|
+
from my_app_name.module.gateway.util.view import render, render_error
|
16
|
+
|
17
|
+
|
18
|
+
def serve_route(app: FastAPI):
|
19
|
+
if APP_MODE != "monolith" and "gateway" not in APP_MODULES:
|
20
|
+
return
|
21
|
+
if APP_MODE == "monolith" or APP_MAIN_MODULE == "gateway":
|
22
|
+
_serve_health_check(app)
|
23
|
+
_serve_readiness_check(app)
|
24
|
+
_serve_homepage(app)
|
25
|
+
_handle_404(app)
|
26
|
+
|
27
|
+
# Serve auth routes
|
28
|
+
serve_auth_route(app)
|
6
29
|
|
7
30
|
|
8
|
-
def
|
31
|
+
def _serve_homepage(app: FastAPI):
|
32
|
+
@app.get("/", include_in_schema=False)
|
33
|
+
def home_page():
|
34
|
+
return render(
|
35
|
+
view_path=os.path.join(APP_GATEWAY_VIEW_PATH, "content", "homepage.html")
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def _serve_health_check(app: FastAPI):
|
9
40
|
@app.api_route("/health", methods=["GET", "HEAD"], response_model=BasicResponse)
|
10
41
|
async def health():
|
11
42
|
"""
|
@@ -14,7 +45,7 @@ def serve_health_check(app: FastAPI):
|
|
14
45
|
return BasicResponse(message="ok")
|
15
46
|
|
16
47
|
|
17
|
-
def
|
48
|
+
def _serve_readiness_check(app: FastAPI):
|
18
49
|
@app.api_route("/readiness", methods=["GET", "HEAD"], response_model=BasicResponse)
|
19
50
|
async def readiness():
|
20
51
|
"""
|
@@ -23,15 +54,13 @@ def serve_readiness_check(app: FastAPI):
|
|
23
54
|
return BasicResponse(message="ok")
|
24
55
|
|
25
56
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# Serve Auth Route
|
34
|
-
serve_auth_route(app)
|
57
|
+
def _handle_404(app: FastAPI):
|
58
|
+
@app.exception_handler(404)
|
59
|
+
async def default_404(request: Request, exc: HTTPException) -> HTMLResponse:
|
60
|
+
if request.url.path.startswith("/api"):
|
61
|
+
# Re-raise the exception to let FastAPI handle it
|
62
|
+
return await http_exception_handler(request, exc)
|
63
|
+
return render_error(error_message="Not found", status_code=404)
|
35
64
|
|
36
65
|
|
37
66
|
serve_route(app)
|