zrb 1.0.0a21__py3-none-any.whl → 1.0.0b3__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 +5 -3
- 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/cmd/cmd_val.py +6 -5
- zrb/config.py +14 -1
- zrb/content_transformer/any_content_transformer.py +7 -0
- zrb/content_transformer/content_transformer.py +6 -0
- zrb/runner/cli.py +14 -7
- 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_route/refresh_token_api_route.py +38 -0
- zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +5 -2
- zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +5 -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 +184 -71
- 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 +3 -7
- {zrb-1.0.0a21.dist-info → zrb-1.0.0b3.dist-info}/METADATA +2 -1
- zrb-1.0.0b3.dist-info/RECORD +307 -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/{web_controller/session_page → web_route/node_page/task}/view.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.0b3.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a21.dist-info → zrb-1.0.0b3.dist-info}/entry_points.txt +0 -0
zrb/cmd/cmd_val.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
from collections.abc import Callable
|
3
3
|
|
4
|
-
from zrb.attr.type import fstring
|
4
|
+
from zrb.attr.type import StrAttr, fstring
|
5
5
|
from zrb.context.context import Context
|
6
|
+
from zrb.util.attr import get_str_attr
|
6
7
|
from zrb.util.file import read_file
|
7
8
|
|
8
9
|
|
@@ -13,22 +14,22 @@ class AnyCmdVal(ABC):
|
|
13
14
|
|
14
15
|
|
15
16
|
class CmdPath(AnyCmdVal):
|
16
|
-
def __init__(self, path:
|
17
|
+
def __init__(self, path: StrAttr, auto_render: bool = True):
|
17
18
|
self._path = path
|
18
19
|
self._auto_render = auto_render
|
19
20
|
|
20
21
|
def to_str(self, ctx: Context) -> str:
|
21
|
-
file_path = ctx
|
22
|
+
file_path = get_str_attr(ctx, self._path, "", self._auto_render)
|
22
23
|
return read_file(file_path)
|
23
24
|
|
24
25
|
|
25
26
|
class Cmd(AnyCmdVal):
|
26
|
-
def __init__(self, cmd:
|
27
|
+
def __init__(self, cmd: StrAttr, auto_render: bool = True):
|
27
28
|
self._cmd = cmd
|
28
29
|
self._auto_render = auto_render
|
29
30
|
|
30
31
|
def to_str(self, ctx: Context) -> str:
|
31
|
-
return ctx
|
32
|
+
return get_str_attr(ctx, self._cmd, "", self._auto_render)
|
32
33
|
|
33
34
|
|
34
35
|
SingleCmdVal = AnyCmdVal | fstring | Callable[[Context], str]
|
zrb/config.py
CHANGED
@@ -77,7 +77,20 @@ WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
|
|
77
77
|
os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
|
78
78
|
)
|
79
79
|
LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
|
80
|
-
|
80
|
+
|
81
|
+
_DEFAULT_PROMPT = """
|
82
|
+
You are a reliable assistant focused on providing accurate, helpful, and factual information.
|
83
|
+
|
84
|
+
Key guidelines:
|
85
|
+
1. Prioritize correctness and clarity.
|
86
|
+
2. Avoid guessing—clearly state if more information is needed.
|
87
|
+
3. Distinguish facts from opinions.
|
88
|
+
4. Use phrases like "to the best of my knowledge" when appropriate.
|
89
|
+
|
90
|
+
If unsure or lacking current data, inform the user and suggest verification.
|
91
|
+
Accuracy always takes precedence over completeness.
|
92
|
+
""".strip()
|
93
|
+
LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", _DEFAULT_PROMPT)
|
81
94
|
LLM_HISTORY_FILE = os.getenv(
|
82
95
|
"ZRB_LLM_HISTORY_FILE",
|
83
96
|
os.path.expanduser(os.path.join("~", ".zrb-llm-history.json")),
|
@@ -4,6 +4,13 @@ from zrb.context.any_context import AnyContext
|
|
4
4
|
|
5
5
|
|
6
6
|
class AnyContentTransformer(ABC):
|
7
|
+
|
8
|
+
@property
|
9
|
+
@abstractmethod
|
10
|
+
def name(self) -> str:
|
11
|
+
"""Transformer's name"""
|
12
|
+
pass
|
13
|
+
|
7
14
|
@abstractmethod
|
8
15
|
def match(self, ctx: AnyContext, file_path: str) -> bool:
|
9
16
|
"""
|
@@ -10,6 +10,7 @@ from zrb.util.file import read_file, write_file
|
|
10
10
|
class ContentTransformer(AnyContentTransformer):
|
11
11
|
def __init__(
|
12
12
|
self,
|
13
|
+
name: str,
|
13
14
|
match: list[str] | str | Callable[[AnyContext, str], bool],
|
14
15
|
transform: (
|
15
16
|
dict[str, str | Callable[[AnyContext], str]]
|
@@ -17,10 +18,15 @@ class ContentTransformer(AnyContentTransformer):
|
|
17
18
|
),
|
18
19
|
auto_render: bool = True,
|
19
20
|
):
|
21
|
+
self._name = name
|
20
22
|
self._match = match
|
21
23
|
self._transform_file = transform
|
22
24
|
self._auto_render = auto_render
|
23
25
|
|
26
|
+
@property
|
27
|
+
def name(self) -> str:
|
28
|
+
return self._name
|
29
|
+
|
24
30
|
def match(self, ctx: AnyContext, file_path: str) -> bool:
|
25
31
|
if callable(self._match):
|
26
32
|
return self._match(ctx, file_path)
|
zrb/runner/cli.py
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
import sys
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from zrb.config import BANNER, WEB_HTTP_PORT
|
4
|
+
from zrb.config import BANNER, VERSION, WEB_HTTP_PORT
|
5
5
|
from zrb.context.any_context import AnyContext
|
6
6
|
from zrb.context.shared_context import SharedContext
|
7
7
|
from zrb.group.group import Group
|
8
8
|
from zrb.runner.common_util import get_run_kwargs
|
9
|
-
from zrb.runner.web_app import
|
10
|
-
from zrb.runner.web_config import web_config
|
9
|
+
from zrb.runner.web_app import create_web_app
|
10
|
+
from zrb.runner.web_config.config_factory import web_config
|
11
11
|
from zrb.session.session import Session
|
12
|
-
from zrb.session_state_logger.
|
13
|
-
default_session_state_logger,
|
14
|
-
)
|
12
|
+
from zrb.session_state_logger.session_state_logger_factory import session_state_logger
|
15
13
|
from zrb.task.any_task import AnyTask
|
16
14
|
from zrb.task.make_task import make_task
|
17
15
|
from zrb.util.cli.style import (
|
@@ -151,6 +149,15 @@ class Cli(Group):
|
|
151
149
|
|
152
150
|
|
153
151
|
cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
|
152
|
+
|
153
|
+
|
154
|
+
@make_task(
|
155
|
+
name="version", description="🌟 Get current Zrb version", retries=0, group=cli
|
156
|
+
)
|
157
|
+
def get_version(_: AnyContext):
|
158
|
+
return VERSION
|
159
|
+
|
160
|
+
|
154
161
|
server_group = cli.add_group(
|
155
162
|
Group(name="server", description="🌐 Server related command")
|
156
163
|
)
|
@@ -167,6 +174,6 @@ server_group = cli.add_group(
|
|
167
174
|
async def start_server(_: AnyContext):
|
168
175
|
from uvicorn import Config, Server
|
169
176
|
|
170
|
-
app =
|
177
|
+
app = create_web_app(cli, web_config, session_state_logger)
|
171
178
|
server = Server(Config(app=app, host="0.0.0.0", port=WEB_HTTP_PORT, loop="asyncio"))
|
172
179
|
await server.serve()
|
zrb/runner/web_app.py
CHANGED
@@ -1,60 +1,38 @@
|
|
1
1
|
import asyncio
|
2
|
-
import json
|
3
|
-
import os
|
4
2
|
import sys
|
5
|
-
from
|
6
|
-
from typing import TYPE_CHECKING, Annotated
|
3
|
+
from typing import TYPE_CHECKING
|
7
4
|
|
8
5
|
from zrb.config import BANNER, VERSION
|
9
|
-
from zrb.context.shared_context import SharedContext
|
10
6
|
from zrb.group.any_group import AnyGroup
|
11
|
-
from zrb.runner.
|
12
|
-
from zrb.runner.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
from zrb.runner.
|
19
|
-
from zrb.runner.
|
20
|
-
from zrb.runner.
|
21
|
-
from zrb.runner.
|
22
|
-
from zrb.runner.
|
23
|
-
from zrb.runner.
|
24
|
-
from zrb.runner.web_util import get_refresh_token_js
|
25
|
-
from zrb.session.session import Session
|
26
|
-
from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
|
7
|
+
from zrb.runner.web_config.config import WebConfig
|
8
|
+
from zrb.runner.web_route.docs_route import serve_docs
|
9
|
+
from zrb.runner.web_route.error_page.serve_default_404 import serve_default_404
|
10
|
+
from zrb.runner.web_route.home_page.home_page_route import serve_home_page
|
11
|
+
from zrb.runner.web_route.login_api_route import serve_login_api
|
12
|
+
from zrb.runner.web_route.login_page.login_page_route import serve_login_page
|
13
|
+
from zrb.runner.web_route.logout_api_route import serve_logout_api
|
14
|
+
from zrb.runner.web_route.logout_page.logout_page_route import serve_logout_page
|
15
|
+
from zrb.runner.web_route.node_page.node_page_route import serve_node_page
|
16
|
+
from zrb.runner.web_route.refresh_token_api_route import serve_refresh_token_api
|
17
|
+
from zrb.runner.web_route.static.static_route import serve_static_resources
|
18
|
+
from zrb.runner.web_route.task_input_api_route import serve_task_input_api
|
19
|
+
from zrb.runner.web_route.task_session_api_route import serve_task_session_api
|
27
20
|
from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
|
28
|
-
from zrb.task.any_task import AnyTask
|
29
|
-
from zrb.util.group import NodeNotFoundError, extract_node_from_args, get_node_path
|
30
21
|
|
31
22
|
if TYPE_CHECKING:
|
32
23
|
# We want fastapi to only be loaded when necessary to decrease footprint
|
33
24
|
from fastapi import FastAPI
|
34
25
|
|
35
26
|
|
36
|
-
def
|
27
|
+
def create_web_app(
|
37
28
|
root_group: AnyGroup,
|
38
29
|
web_config: WebConfig,
|
39
30
|
session_state_logger: AnySessionStateLogger,
|
40
31
|
) -> "FastAPI":
|
41
32
|
from contextlib import asynccontextmanager
|
42
33
|
|
43
|
-
from fastapi import
|
44
|
-
Cookie,
|
45
|
-
Depends,
|
46
|
-
FastAPI,
|
47
|
-
HTTPException,
|
48
|
-
Query,
|
49
|
-
Request,
|
50
|
-
Response,
|
51
|
-
)
|
52
|
-
from fastapi.openapi.docs import get_swagger_ui_html
|
53
|
-
from fastapi.responses import FileResponse, HTMLResponse, PlainTextResponse
|
54
|
-
from fastapi.security import OAuth2PasswordRequestForm
|
55
|
-
from fastapi.staticfiles import StaticFiles
|
34
|
+
from fastapi import FastAPI
|
56
35
|
|
57
|
-
_STATIC_DIR = os.path.join(os.path.dirname(__file__), "web_controller", "static")
|
58
36
|
_COROS = []
|
59
37
|
|
60
38
|
@asynccontextmanager
|
@@ -75,247 +53,17 @@ def create_app(
|
|
75
53
|
lifespan=lifespan,
|
76
54
|
docs_url=None,
|
77
55
|
)
|
78
|
-
app.mount("/static", StaticFiles(directory=_STATIC_DIR), name="static")
|
79
|
-
|
80
|
-
# Serve static files
|
81
|
-
@app.get("/static/{file_path:path}", include_in_schema=False)
|
82
|
-
async def static_files(file_path: str):
|
83
|
-
full_path = os.path.join(_STATIC_DIR, file_path)
|
84
|
-
if os.path.isfile(full_path):
|
85
|
-
return FileResponse(full_path)
|
86
|
-
raise HTTPException(status_code=404, detail="File not found")
|
87
|
-
|
88
|
-
@app.get("/refresh-token.js", include_in_schema=False)
|
89
|
-
async def refresh_token_js():
|
90
|
-
return PlainTextResponse(
|
91
|
-
content=get_refresh_token_js(
|
92
|
-
60 * web_config.refresh_token_expire_minutes / 3
|
93
|
-
),
|
94
|
-
media_type="application/javascript",
|
95
|
-
)
|
96
|
-
|
97
|
-
@app.get("/docs", include_in_schema=False)
|
98
|
-
async def swagger_ui_html():
|
99
|
-
return get_swagger_ui_html(
|
100
|
-
openapi_url="/openapi.json",
|
101
|
-
title="Zrb",
|
102
|
-
swagger_favicon_url="/static/favicon-32x32.png",
|
103
|
-
)
|
104
|
-
|
105
|
-
# Serve homepage
|
106
|
-
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
|
107
|
-
@app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
|
108
|
-
@app.get("/ui/", response_class=HTMLResponse, include_in_schema=False)
|
109
|
-
async def home_page_ui(request: Request) -> HTMLResponse:
|
110
|
-
user = await web_config.get_user_from_request(request)
|
111
|
-
return show_home_page(user, root_group)
|
112
|
-
|
113
|
-
@app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
|
114
|
-
async def ui_page(path: str, request: Request) -> HTMLResponse:
|
115
|
-
user = await web_config.get_user_from_request(request)
|
116
|
-
# Avoid capturing '/ui' itself
|
117
|
-
if not path:
|
118
|
-
return show_error_page(user, root_group, 422, "Undefined path")
|
119
|
-
args = path.strip("/").split("/")
|
120
|
-
try:
|
121
|
-
node, node_path, residual_args = extract_node_from_args(root_group, args)
|
122
|
-
except NodeNotFoundError as e:
|
123
|
-
return show_error_page(user, root_group, 404, str(e))
|
124
|
-
url = f"/ui/{'/'.join(node_path)}/"
|
125
|
-
if isinstance(node, AnyTask):
|
126
|
-
if not user.can_access_task(node):
|
127
|
-
return show_error_page(user, root_group, 403, "Forbidden")
|
128
|
-
shared_ctx = SharedContext(env=dict(os.environ))
|
129
|
-
session = Session(shared_ctx=shared_ctx, root_group=root_group)
|
130
|
-
return show_session_page(
|
131
|
-
user, root_group, node, session, url, residual_args
|
132
|
-
)
|
133
|
-
elif isinstance(node, AnyGroup):
|
134
|
-
if not user.can_access_group(node):
|
135
|
-
return show_error_page(user, root_group, 403, "Forbidden")
|
136
|
-
return show_group_info_page(user, root_group, node, url)
|
137
|
-
return show_error_page(user, root_group, 404, "Not found")
|
138
|
-
|
139
|
-
@app.get("/login", response_class=HTMLResponse, include_in_schema=False)
|
140
|
-
async def login(request: Request) -> HTMLResponse:
|
141
|
-
user = await web_config.get_user_from_request(request)
|
142
|
-
return show_login_page(user, root_group)
|
143
|
-
|
144
|
-
@app.get("/logout", response_class=HTMLResponse, include_in_schema=False)
|
145
|
-
async def logout(request: Request) -> HTMLResponse:
|
146
|
-
user = await web_config.get_user_from_request(request)
|
147
|
-
return show_logout_page(user, root_group)
|
148
|
-
|
149
|
-
@app.post("/api/v1/login")
|
150
|
-
async def login_api(
|
151
|
-
response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
152
|
-
):
|
153
|
-
token = web_config.generate_tokens_by_credentials(
|
154
|
-
username=form_data.username, password=form_data.password
|
155
|
-
)
|
156
|
-
if token is None:
|
157
|
-
raise HTTPException(
|
158
|
-
status_code=400, detail="Incorrect username or password"
|
159
|
-
)
|
160
|
-
_set_auth_cookie(response, token)
|
161
|
-
return token
|
162
|
-
|
163
|
-
@app.post("/api/v1/refresh-token")
|
164
|
-
async def refresh_token_api(
|
165
|
-
response: Response,
|
166
|
-
body: RefreshTokenRequest = None,
|
167
|
-
refresh_token_cookie: str = Cookie(
|
168
|
-
None, alias=web_config.refresh_token_cookie_name
|
169
|
-
),
|
170
|
-
):
|
171
|
-
# Try to get the refresh token from the request body first
|
172
|
-
refresh_token = body.refresh_token if body else None
|
173
|
-
# If not in the body, try to get it from the cookie
|
174
|
-
if not refresh_token:
|
175
|
-
refresh_token = refresh_token_cookie
|
176
|
-
# If we still don't have a refresh token, raise an exception
|
177
|
-
if not refresh_token:
|
178
|
-
raise HTTPException(status_code=400, detail="Refresh token not provided")
|
179
|
-
# Get token
|
180
|
-
new_token = web_config.regenerate_tokens(refresh_token)
|
181
|
-
_set_auth_cookie(response, new_token)
|
182
|
-
return new_token
|
183
|
-
|
184
|
-
def _set_auth_cookie(response: Response, token: Token):
|
185
|
-
access_token_max_age = web_config.access_token_expire_minutes * 60
|
186
|
-
refresh_token_max_age = web_config.refresh_token_expire_minutes * 60
|
187
|
-
now = datetime.now(timezone.utc)
|
188
|
-
response.set_cookie(
|
189
|
-
key=web_config.access_token_cookie_name,
|
190
|
-
value=token.access_token,
|
191
|
-
httponly=True,
|
192
|
-
max_age=access_token_max_age,
|
193
|
-
expires=now + timedelta(seconds=access_token_max_age),
|
194
|
-
)
|
195
|
-
response.set_cookie(
|
196
|
-
key=web_config.refresh_token_cookie_name,
|
197
|
-
value=token.refresh_token,
|
198
|
-
httponly=True,
|
199
|
-
max_age=refresh_token_max_age,
|
200
|
-
expires=now + timedelta(seconds=refresh_token_max_age),
|
201
|
-
)
|
202
|
-
|
203
|
-
@app.get("/api/v1/logout")
|
204
|
-
@app.post("/api/v1/logout")
|
205
|
-
async def logout_api(response: Response):
|
206
|
-
response.delete_cookie(web_config.access_token_cookie_name)
|
207
|
-
response.delete_cookie(web_config.refresh_token_cookie_name)
|
208
|
-
return {"message": "Logout successful"}
|
209
|
-
|
210
|
-
@app.post("/api/v1/task-sessions/{path:path}")
|
211
|
-
async def create_new_task_session_api(
|
212
|
-
path: str,
|
213
|
-
request: Request,
|
214
|
-
) -> NewSessionResponse:
|
215
|
-
"""
|
216
|
-
Creating new session
|
217
|
-
"""
|
218
|
-
user = await web_config.get_user_from_request(request)
|
219
|
-
args = path.strip("/").split("/")
|
220
|
-
task, _, residual_args = extract_node_from_args(root_group, args)
|
221
|
-
if isinstance(task, AnyTask):
|
222
|
-
if not user.can_access_task(task):
|
223
|
-
raise HTTPException(status_code=403)
|
224
|
-
session_name = residual_args[0] if residual_args else None
|
225
|
-
if not session_name:
|
226
|
-
body = await request.json()
|
227
|
-
shared_ctx = SharedContext(env=dict(os.environ))
|
228
|
-
session = Session(shared_ctx=shared_ctx, root_group=root_group)
|
229
|
-
coro = asyncio.create_task(task.async_run(session, str_kwargs=body))
|
230
|
-
_COROS.append(coro)
|
231
|
-
coro.add_done_callback(lambda coro: _COROS.remove(coro))
|
232
|
-
return NewSessionResponse(session_name=session.name)
|
233
|
-
raise HTTPException(status_code=404)
|
234
|
-
|
235
|
-
@app.get("/api/v1/task-inputs/{path:path}", response_model=dict[str, str])
|
236
|
-
async def get_default_inputs_api(
|
237
|
-
path: str,
|
238
|
-
request: Request,
|
239
|
-
query: str = Query("{}", description="JSON encoded inputs"),
|
240
|
-
) -> dict[str, str]:
|
241
|
-
"""
|
242
|
-
Getting input completion for path
|
243
|
-
"""
|
244
|
-
user = await web_config.get_user_from_request(request)
|
245
|
-
args = path.strip("/").split("/")
|
246
|
-
task, _, _ = extract_node_from_args(root_group, args)
|
247
|
-
if isinstance(task, AnyTask):
|
248
|
-
if not user.can_access_task(task):
|
249
|
-
raise HTTPException(status_code=403)
|
250
|
-
query_dict = json.loads(query)
|
251
|
-
run_kwargs = get_run_kwargs(
|
252
|
-
task=task, args=[], kwargs=query_dict, prompt=False
|
253
|
-
)
|
254
|
-
return run_kwargs
|
255
|
-
raise HTTPException(status_code=404, detail="Not Found")
|
256
|
-
|
257
|
-
@app.get(
|
258
|
-
"/api/v1/task-sessions/{path:path}",
|
259
|
-
response_model=SessionStateLog | SessionStateLogList,
|
260
|
-
)
|
261
|
-
async def get_session_api(
|
262
|
-
path: str,
|
263
|
-
request: Request,
|
264
|
-
min_start_query: str = Query(default=None, alias="from"),
|
265
|
-
max_start_query: str = Query(default=None, alias="to"),
|
266
|
-
page: int = Query(default=0, alias="page"),
|
267
|
-
limit: int = Query(default=10, alias="limit"),
|
268
|
-
) -> SessionStateLog | SessionStateLogList:
|
269
|
-
"""
|
270
|
-
Getting existing session or sessions
|
271
|
-
"""
|
272
|
-
user = await web_config.get_user_from_request(request)
|
273
|
-
args = path.strip("/").split("/")
|
274
|
-
task, _, residual_args = extract_node_from_args(root_group, args)
|
275
|
-
if isinstance(task, AnyTask) and residual_args:
|
276
|
-
if not user.can_access_task(task):
|
277
|
-
raise HTTPException(status_code=403)
|
278
|
-
if residual_args[0] == "list":
|
279
|
-
task_path = get_node_path(root_group, task)
|
280
|
-
max_start_time = (
|
281
|
-
datetime.now()
|
282
|
-
if max_start_query is None
|
283
|
-
else datetime.strptime(max_start_query, "%Y-%m-%d %H:%M:%S")
|
284
|
-
)
|
285
|
-
min_start_time = (
|
286
|
-
max_start_time - timedelta(hours=1)
|
287
|
-
if min_start_query is None
|
288
|
-
else datetime.strptime(min_start_query, "%Y-%m-%d %H:%M:%S")
|
289
|
-
)
|
290
|
-
return _get_existing_sessions(
|
291
|
-
task_path, min_start_time, max_start_time, page, limit
|
292
|
-
)
|
293
|
-
else:
|
294
|
-
return _read_session(residual_args[0])
|
295
|
-
raise HTTPException(status_code=404, detail="Not Found")
|
296
|
-
|
297
|
-
def _get_existing_sessions(
|
298
|
-
task_path: list[str],
|
299
|
-
min_start_time: datetime,
|
300
|
-
max_start_time: datetime,
|
301
|
-
page: int,
|
302
|
-
limit: int,
|
303
|
-
) -> SessionStateLogList:
|
304
|
-
try:
|
305
|
-
return session_state_logger.list(
|
306
|
-
task_path,
|
307
|
-
min_start_time=min_start_time,
|
308
|
-
max_start_time=max_start_time,
|
309
|
-
page=page,
|
310
|
-
limit=limit,
|
311
|
-
)
|
312
|
-
except Exception as e:
|
313
|
-
raise HTTPException(status_code=500, detail=str(e))
|
314
|
-
|
315
|
-
def _read_session(session_name: str) -> SessionStateLog:
|
316
|
-
try:
|
317
|
-
return session_state_logger.read(session_name)
|
318
|
-
except Exception as e:
|
319
|
-
raise HTTPException(status_code=500, detail=str(e))
|
320
56
|
|
57
|
+
serve_default_404(app, root_group, web_config)
|
58
|
+
serve_static_resources(app, web_config)
|
59
|
+
serve_docs(app)
|
60
|
+
serve_home_page(app, root_group, web_config)
|
61
|
+
serve_login_page(app, root_group, web_config)
|
62
|
+
serve_logout_page(app, root_group, web_config)
|
63
|
+
serve_node_page(app, root_group, web_config)
|
64
|
+
serve_login_api(app, web_config)
|
65
|
+
serve_logout_api(app, web_config)
|
66
|
+
serve_refresh_token_api(app, web_config)
|
67
|
+
serve_task_input_api(app, root_group, web_config)
|
68
|
+
serve_task_session_api(app, root_group, web_config, session_state_logger, _COROS)
|
321
69
|
return app
|
@@ -0,0 +1,91 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
|
3
|
+
from zrb.runner.web_schema.user import User
|
4
|
+
from zrb.task.any_task import AnyTask
|
5
|
+
|
6
|
+
|
7
|
+
class WebConfig:
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
port: int,
|
11
|
+
secret_key: str,
|
12
|
+
access_token_expire_minutes: int,
|
13
|
+
refresh_token_expire_minutes: int,
|
14
|
+
access_token_cookie_name: str,
|
15
|
+
refresh_token_cookie_name: str,
|
16
|
+
enable_auth: bool,
|
17
|
+
super_admin_username: str,
|
18
|
+
super_admin_password: str,
|
19
|
+
guest_username: str,
|
20
|
+
guest_accessible_tasks: list[AnyTask | str] = [],
|
21
|
+
find_user_by_username: Callable[[str], User | None] | None = None,
|
22
|
+
):
|
23
|
+
self.secret_key = secret_key
|
24
|
+
self.access_token_expire_minutes = access_token_expire_minutes
|
25
|
+
self.refresh_token_expire_minutes = refresh_token_expire_minutes
|
26
|
+
self.access_token_cookie_name = access_token_cookie_name
|
27
|
+
self.refresh_token_cookie_name = refresh_token_cookie_name
|
28
|
+
self.enable_auth = enable_auth
|
29
|
+
self.port = port
|
30
|
+
self._user_list = []
|
31
|
+
self.super_admin_username = super_admin_username
|
32
|
+
self.super_admin_password = super_admin_password
|
33
|
+
self.guest_username = guest_username
|
34
|
+
self.guest_accessible_tasks = guest_accessible_tasks
|
35
|
+
self._find_user_by_username = find_user_by_username
|
36
|
+
|
37
|
+
@property
|
38
|
+
def default_user(self) -> User:
|
39
|
+
if self.enable_auth:
|
40
|
+
return User(
|
41
|
+
username=self.guest_username,
|
42
|
+
password="",
|
43
|
+
is_guest=True,
|
44
|
+
accessible_tasks=self.guest_accessible_tasks,
|
45
|
+
)
|
46
|
+
return User(
|
47
|
+
username=self.guest_username,
|
48
|
+
password="",
|
49
|
+
is_guest=True,
|
50
|
+
is_super_admin=True,
|
51
|
+
)
|
52
|
+
|
53
|
+
@property
|
54
|
+
def super_admin(self) -> User:
|
55
|
+
return User(
|
56
|
+
username=self.super_admin_username,
|
57
|
+
password=self.super_admin_password,
|
58
|
+
is_super_admin=True,
|
59
|
+
)
|
60
|
+
|
61
|
+
@property
|
62
|
+
def user_list(self) -> list[User]:
|
63
|
+
if not self.enable_auth:
|
64
|
+
return [self.default_user]
|
65
|
+
return self._user_list + [self.super_admin, self.default_user]
|
66
|
+
|
67
|
+
def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
|
68
|
+
self.guest_accessible_tasks = tasks
|
69
|
+
|
70
|
+
def set_find_user_by_username(
|
71
|
+
self, find_user_by_username: Callable[[str], User | None]
|
72
|
+
):
|
73
|
+
self._find_user_by_username = find_user_by_username
|
74
|
+
|
75
|
+
def append_user(self, user: User):
|
76
|
+
duplicates = [
|
77
|
+
existing_user
|
78
|
+
for existing_user in self.user_list
|
79
|
+
if existing_user.username == user.username
|
80
|
+
]
|
81
|
+
if len(duplicates) > 0:
|
82
|
+
raise ValueError(f"User already exists {user.username}")
|
83
|
+
self._user_list.append(user)
|
84
|
+
|
85
|
+
def find_user_by_username(self, username: str) -> User | None:
|
86
|
+
user = None
|
87
|
+
if self._find_user_by_username is not None:
|
88
|
+
user = self._find_user_by_username(username)
|
89
|
+
if user is None:
|
90
|
+
user = next((u for u in self.user_list if u.username == username), None)
|
91
|
+
return user
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from zrb.config import (
|
2
|
+
WEB_ACCESS_TOKEN_COOKIE_NAME,
|
3
|
+
WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
|
4
|
+
WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
|
5
|
+
WEB_ENABLE_AUTH,
|
6
|
+
WEB_GUEST_USERNAME,
|
7
|
+
WEB_HTTP_PORT,
|
8
|
+
WEB_REFRESH_TOKEN_COOKIE_NAME,
|
9
|
+
WEB_SECRET_KEY,
|
10
|
+
WEB_SUPER_ADMIN_PASSWORD,
|
11
|
+
WEB_SUPER_ADMIN_USERNAME,
|
12
|
+
)
|
13
|
+
from zrb.runner.web_config.config import WebConfig
|
14
|
+
|
15
|
+
web_config = WebConfig(
|
16
|
+
port=WEB_HTTP_PORT,
|
17
|
+
secret_key=WEB_SECRET_KEY,
|
18
|
+
access_token_expire_minutes=WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
|
19
|
+
refresh_token_expire_minutes=WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
|
20
|
+
access_token_cookie_name=WEB_ACCESS_TOKEN_COOKIE_NAME,
|
21
|
+
refresh_token_cookie_name=WEB_REFRESH_TOKEN_COOKIE_NAME,
|
22
|
+
enable_auth=WEB_ENABLE_AUTH,
|
23
|
+
super_admin_username=WEB_SUPER_ADMIN_USERNAME,
|
24
|
+
super_admin_password=WEB_SUPER_ADMIN_PASSWORD,
|
25
|
+
guest_username=WEB_GUEST_USERNAME,
|
26
|
+
)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
if TYPE_CHECKING:
|
4
|
+
# We want fastapi to only be loaded when necessary to decrease footprint
|
5
|
+
from fastapi import FastAPI
|
6
|
+
|
7
|
+
|
8
|
+
def serve_docs(app: "FastAPI") -> None:
|
9
|
+
from fastapi.openapi.docs import get_swagger_ui_html
|
10
|
+
|
11
|
+
@app.get("/docs", include_in_schema=False)
|
12
|
+
async def swagger_ui_html():
|
13
|
+
return get_swagger_ui_html(
|
14
|
+
openapi_url="/openapi.json",
|
15
|
+
title="Zrb",
|
16
|
+
swagger_favicon_url="/static/favicon-32x32.png",
|
17
|
+
)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from zrb.group.any_group import AnyGroup
|
4
|
+
from zrb.runner.web_config.config import WebConfig
|
5
|
+
from zrb.runner.web_route.error_page.show_error_page import show_error_page
|
6
|
+
from zrb.runner.web_util.user import get_user_from_request
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
# We want fastapi to only be loaded when necessary to decrease footprint
|
10
|
+
from fastapi import FastAPI
|
11
|
+
|
12
|
+
|
13
|
+
def serve_default_404(
|
14
|
+
app: "FastAPI",
|
15
|
+
root_group: AnyGroup,
|
16
|
+
web_config: WebConfig,
|
17
|
+
) -> None:
|
18
|
+
from fastapi import Request
|
19
|
+
from fastapi.exception_handlers import http_exception_handler
|
20
|
+
from fastapi.responses import HTMLResponse
|
21
|
+
|
22
|
+
@app.exception_handler(404)
|
23
|
+
async def default_404(request: Request, exc: Exception) -> HTMLResponse:
|
24
|
+
if request.url.path.startswith("/api"):
|
25
|
+
# Re-raise the exception to let FastAPI handle it
|
26
|
+
return await http_exception_handler(request, exc)
|
27
|
+
user = await get_user_from_request(web_config, request)
|
28
|
+
return show_error_page(user, root_group, 404, "Not found")
|
zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py}
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
from zrb.group.any_group import AnyGroup
|
4
|
-
from zrb.runner.
|
5
|
-
from zrb.runner.web_util import get_html_auth_link
|
4
|
+
from zrb.runner.web_schema.user import User
|
5
|
+
from zrb.runner.web_util.html import get_html_auth_link
|
6
6
|
from zrb.util.file import read_file
|
7
7
|
from zrb.util.string.format import fstring_format
|
8
8
|
|
@@ -23,5 +23,6 @@ def show_error_page(user: User, root_group: AnyGroup, status_code: int, message:
|
|
23
23
|
"error_status_code": status_code,
|
24
24
|
"error_message": message,
|
25
25
|
},
|
26
|
-
)
|
26
|
+
),
|
27
|
+
status_code=status_code,
|
27
28
|
)
|