zrb 1.0.0a20__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.
Files changed (195) hide show
  1. zrb/__init__.py +2 -1
  2. zrb/__main__.py +3 -3
  3. zrb/builtin/__init__.py +3 -0
  4. zrb/builtin/group.py +1 -0
  5. zrb/builtin/llm/llm_chat.py +5 -3
  6. zrb/builtin/llm/tool/cli.py +1 -1
  7. zrb/builtin/llm/tool/rag.py +108 -145
  8. zrb/builtin/llm/tool/web.py +1 -1
  9. zrb/builtin/project/add/fastapp/fastapp_task.py +2 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -2
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +80 -20
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +150 -42
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +16 -6
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +57 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +74 -0
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +1 -1
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +13 -0
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +23 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -0
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +7 -0
  26. 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
  27. 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
  28. 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
  29. 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
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +11 -11
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +2 -2
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +8 -8
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +47 -20
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app_factory.py +29 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +230 -102
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +236 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{db_engine.py → db_engine_factory.py} +1 -1
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +12 -0
  39. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/logger_factory.py +10 -0
  40. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/parser_factory.py +7 -0
  41. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/app.py +47 -0
  42. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +105 -0
  43. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/user_agent.py +58 -0
  44. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +37 -0
  45. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +37 -1
  46. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +1 -1
  47. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_api_client.py +16 -0
  48. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +169 -0
  49. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client_factory.py +9 -0
  50. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_direct_client.py +15 -0
  51. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +160 -0
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +18 -1
  53. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +7 -3
  54. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +117 -0
  55. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service_factory.py +11 -0
  56. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_db_repository.py +26 -0
  57. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository.py +61 -0
  58. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository_factory.py +13 -0
  59. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +89 -0
  60. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +67 -0
  61. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository_factory.py +13 -0
  62. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +137 -0
  63. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service_factory.py +7 -0
  64. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +179 -12
  65. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +67 -17
  66. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository_factory.py +2 -2
  67. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +127 -0
  68. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +7 -0
  69. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -14
  70. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +200 -30
  71. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +74 -0
  72. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +6 -0
  73. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/homepage.html +6 -0
  74. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-192x192.png +0 -0
  75. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-512x512.png +0 -0
  76. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/favicon-32x32.png +0 -0
  77. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +4 -0
  78. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +4 -0
  79. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +4 -0
  80. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +4 -0
  81. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +4 -0
  82. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +4 -0
  83. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +4 -0
  84. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +4 -0
  85. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +4 -0
  86. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +4 -0
  87. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +4 -0
  88. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +4 -0
  89. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +4 -0
  90. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +4 -0
  91. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +4 -0
  92. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +4 -0
  93. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +4 -0
  94. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +4 -0
  95. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +4 -0
  96. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +4 -0
  97. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +34 -0
  98. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -0
  99. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +17 -5
  100. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +78 -4
  101. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +48 -0
  102. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +69 -5
  103. zrb/builtin/python.py +1 -1
  104. zrb/builtin/random.py +61 -0
  105. zrb/cmd/cmd_val.py +6 -5
  106. zrb/config.py +16 -3
  107. zrb/content_transformer/any_content_transformer.py +7 -0
  108. zrb/content_transformer/content_transformer.py +6 -0
  109. zrb/runner/cli.py +14 -7
  110. zrb/runner/web_app.py +28 -238
  111. zrb/runner/web_config/config.py +91 -0
  112. zrb/runner/web_config/config_factory.py +26 -0
  113. zrb/runner/web_route/docs_route.py +17 -0
  114. zrb/runner/web_route/error_page/serve_default_404.py +28 -0
  115. zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py} +4 -3
  116. zrb/runner/{web_controller → web_route}/error_page/view.html +6 -0
  117. zrb/runner/web_route/home_page/home_page_route.py +51 -0
  118. zrb/runner/{web_controller → web_route}/home_page/view.html +1 -0
  119. zrb/runner/web_route/login_api_route.py +31 -0
  120. zrb/runner/web_route/login_page/login_page_route.py +39 -0
  121. zrb/runner/{web_controller → web_route}/login_page/view.html +1 -0
  122. zrb/runner/web_route/logout_api_route.py +18 -0
  123. zrb/runner/web_route/logout_page/logout_page_route.py +40 -0
  124. zrb/runner/{web_controller → web_route}/logout_page/view.html +1 -0
  125. zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
  126. zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +1 -0
  127. zrb/runner/web_route/node_page/node_page_route.py +50 -0
  128. zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py} +5 -5
  129. zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +1 -0
  130. zrb/runner/web_route/refresh_token_api_route.py +38 -0
  131. zrb/runner/web_route/static/refresh-token.template.js +22 -0
  132. zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +6 -3
  133. zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +10 -8
  134. zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +9 -3
  135. zrb/runner/web_route/static/static_route.py +44 -0
  136. zrb/runner/web_route/task_input_api_route.py +47 -0
  137. zrb/runner/web_route/task_session_api_route.py +147 -0
  138. zrb/runner/web_schema/session.py +5 -0
  139. zrb/runner/web_schema/token.py +11 -0
  140. zrb/runner/web_schema/user.py +32 -0
  141. zrb/runner/web_util/cookie.py +29 -0
  142. zrb/runner/{web_util.py → web_util/html.py} +1 -18
  143. zrb/runner/web_util/token.py +72 -0
  144. zrb/runner/web_util/user.py +63 -0
  145. zrb/session/session.py +6 -4
  146. zrb/session_state_logger/{default_session_state_logger.py → session_state_logger_factory.py} +1 -1
  147. zrb/task/base_task.py +56 -8
  148. zrb/task/base_trigger.py +2 -0
  149. zrb/task/cmd_task.py +9 -5
  150. zrb/task/http_check.py +2 -0
  151. zrb/task/llm_task.py +184 -71
  152. zrb/task/make_task.py +2 -0
  153. zrb/task/rsync_task.py +2 -0
  154. zrb/task/scaffolder.py +8 -5
  155. zrb/task/scheduler.py +2 -0
  156. zrb/task/tcp_check.py +2 -0
  157. zrb/task_status/task_status.py +4 -3
  158. zrb/util/cmd/command.py +1 -0
  159. zrb/util/file.py +7 -1
  160. zrb/util/llm/tool.py +3 -7
  161. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/METADATA +9 -52
  162. zrb-1.0.0b3.dist-info/RECORD +307 -0
  163. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +0 -27
  164. 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
  165. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +0 -6
  166. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +0 -6
  167. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +0 -9
  168. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +0 -20
  169. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_usecase.py +0 -245
  170. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/any_client.py +0 -33
  171. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +0 -7
  172. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +0 -6
  173. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +0 -9
  174. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  175. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase.py +0 -53
  176. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase_factory.py +0 -6
  177. zrb/runner/web_config.py +0 -288
  178. zrb/runner/web_controller/home_page/controller.py +0 -33
  179. zrb/runner/web_controller/login_page/controller.py +0 -25
  180. zrb/runner/web_controller/logout_page/controller.py +0 -26
  181. zrb-1.0.0a20.dist-info/RECORD +0 -243
  182. /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
  183. /zrb/{runner/web_controller → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission}/__init__.py +0 -0
  184. /zrb/{runner/web_controller/group_info_page → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role}/__init__.py +0 -0
  185. /zrb/runner/{web_controller/home_page → web_route}/__init__.py +0 -0
  186. /zrb/runner/{web_controller/session_page → web_route/home_page}/__init__.py +0 -0
  187. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +0 -0
  188. /zrb/runner/{web_controller/static → web_route/static/resources}/common.css +0 -0
  189. /zrb/runner/{web_controller/static → web_route/static/resources}/favicon-32x32.png +0 -0
  190. /zrb/runner/{web_controller/static → web_route/static/resources}/login/event.js +0 -0
  191. /zrb/runner/{web_controller/static → web_route/static/resources}/logout/event.js +0 -0
  192. /zrb/runner/{web_controller/static → web_route/static/resources}/pico.min.css +0 -0
  193. /zrb/runner/{web_controller/static → web_route/static/resources}/session/common-util.js +0 -0
  194. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/WHEEL +0 -0
  195. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,51 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.group.any_group import AnyGroup
5
+ from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_util.html import (
7
+ get_html_auth_link,
8
+ get_html_subgroup_info,
9
+ get_html_subtask_info,
10
+ )
11
+ from zrb.runner.web_util.user import get_user_from_request
12
+ from zrb.util.file import read_file
13
+ from zrb.util.string.format import fstring_format
14
+
15
+ if TYPE_CHECKING:
16
+ # We want fastapi to only be loaded when necessary to decrease footprint
17
+ from fastapi import FastAPI
18
+
19
+
20
+ def serve_home_page(
21
+ app: "FastAPI",
22
+ root_group: AnyGroup,
23
+ web_config: WebConfig,
24
+ ) -> None:
25
+ from fastapi import Request
26
+ from fastapi.responses import HTMLResponse
27
+
28
+ # Serve homepage
29
+ @app.get("/", response_class=HTMLResponse, include_in_schema=False)
30
+ @app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
31
+ @app.get("/ui/", response_class=HTMLResponse, include_in_schema=False)
32
+ async def home_page_ui(request: Request) -> HTMLResponse:
33
+
34
+ _DIR = os.path.dirname(__file__)
35
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
36
+ user = await get_user_from_request(web_config, request)
37
+ group_info = get_html_subgroup_info(user, "/ui/", root_group)
38
+ task_info = get_html_subtask_info(user, "/ui/", root_group)
39
+ auth_link = get_html_auth_link(user)
40
+ return HTMLResponse(
41
+ fstring_format(
42
+ _VIEW_TEMPLATE,
43
+ {
44
+ "group_info": group_info,
45
+ "task_info": task_info,
46
+ "name": root_group.name,
47
+ "description": root_group.description,
48
+ "auth_link": auth_link,
49
+ },
50
+ )
51
+ )
@@ -29,4 +29,5 @@
29
29
  {task_info}
30
30
  </main>
31
31
  </body>
32
+ <script src="/refresh-token.js"></script>
32
33
  </html>
@@ -0,0 +1,31 @@
1
+ from typing import TYPE_CHECKING, Annotated
2
+
3
+ from zrb.runner.web_config.config import WebConfig
4
+ from zrb.runner.web_util.cookie import set_auth_cookie
5
+ from zrb.runner.web_util.token import generate_tokens_by_credentials
6
+
7
+ if TYPE_CHECKING:
8
+ # We want fastapi to only be loaded when necessary to decrease footprint
9
+ from fastapi import FastAPI
10
+
11
+
12
+ def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
13
+ from fastapi import Depends, Response
14
+ from fastapi.responses import JSONResponse
15
+ from fastapi.security import OAuth2PasswordRequestForm
16
+
17
+ @app.post("/api/v1/login")
18
+ async def login_api(
19
+ response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
20
+ ):
21
+ token = generate_tokens_by_credentials(
22
+ web_config=web_config,
23
+ username=form_data.username,
24
+ password=form_data.password,
25
+ )
26
+ if token is None:
27
+ return JSONResponse(
28
+ content={"detail": "Incorrect username or password"}, status_code=400
29
+ )
30
+ set_auth_cookie(web_config, response, token)
31
+ return token
@@ -0,0 +1,39 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.group.any_group import AnyGroup
5
+ from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_util.html import get_html_auth_link
7
+ from zrb.runner.web_util.user import get_user_from_request
8
+ from zrb.util.file import read_file
9
+ from zrb.util.string.format import fstring_format
10
+
11
+ if TYPE_CHECKING:
12
+ # We want fastapi to only be loaded when necessary to decrease footprint
13
+ from fastapi import FastAPI
14
+
15
+
16
+ def serve_login_page(
17
+ app: "FastAPI",
18
+ root_group: AnyGroup,
19
+ web_config: WebConfig,
20
+ ) -> None:
21
+ from fastapi import Request
22
+ from fastapi.responses import HTMLResponse
23
+
24
+ @app.get("/login", response_class=HTMLResponse, include_in_schema=False)
25
+ async def login(request: Request) -> HTMLResponse:
26
+ _DIR = os.path.dirname(__file__)
27
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
28
+ user = await get_user_from_request(web_config, request)
29
+ auth_link = get_html_auth_link(user)
30
+ return HTMLResponse(
31
+ fstring_format(
32
+ _VIEW_TEMPLATE,
33
+ {
34
+ "name": root_group.name,
35
+ "description": root_group.description,
36
+ "auth_link": auth_link,
37
+ },
38
+ )
39
+ )
@@ -45,6 +45,7 @@
45
45
  </article>
46
46
  </main>
47
47
  <script src="/static/login/event.js"></script>
48
+ <script src="/refresh-token.js"></script>
48
49
  </body>
49
50
 
50
51
  </html>
@@ -0,0 +1,18 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from zrb.runner.web_config.config import WebConfig
4
+
5
+ if TYPE_CHECKING:
6
+ # We want fastapi to only be loaded when necessary to decrease footprint
7
+ from fastapi import FastAPI
8
+
9
+
10
+ def serve_logout_api(app: "FastAPI", web_config: WebConfig) -> None:
11
+ from fastapi import Response
12
+
13
+ @app.get("/api/v1/logout")
14
+ @app.post("/api/v1/logout")
15
+ async def logout_api(response: Response):
16
+ response.delete_cookie(web_config.access_token_cookie_name)
17
+ response.delete_cookie(web_config.refresh_token_cookie_name)
18
+ return {"message": "Logout successful"}
@@ -0,0 +1,40 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.group.any_group import AnyGroup
5
+ from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_util.html import get_html_auth_link
7
+ from zrb.runner.web_util.user import get_user_from_request
8
+ from zrb.util.file import read_file
9
+ from zrb.util.string.format import fstring_format
10
+
11
+ if TYPE_CHECKING:
12
+ # We want fastapi to only be loaded when necessary to decrease footprint
13
+ from fastapi import FastAPI
14
+
15
+
16
+ def serve_logout_page(
17
+ app: "FastAPI",
18
+ root_group: AnyGroup,
19
+ web_config: WebConfig,
20
+ ) -> None:
21
+ from fastapi import Request
22
+ from fastapi.responses import HTMLResponse
23
+
24
+ @app.get("/logout", response_class=HTMLResponse, include_in_schema=False)
25
+ async def logout(request: Request) -> HTMLResponse:
26
+ _DIR = os.path.dirname(__file__)
27
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
28
+ user = await get_user_from_request(web_config, request)
29
+ auth_link = get_html_auth_link(user)
30
+ return HTMLResponse(
31
+ fstring_format(
32
+ _VIEW_TEMPLATE,
33
+ {
34
+ "name": root_group.name,
35
+ "description": root_group.description,
36
+ "auth_link": auth_link,
37
+ "user": user,
38
+ },
39
+ )
40
+ )
@@ -35,6 +35,7 @@
35
35
  </article>
36
36
  </main>
37
37
  <script src="/static/logout/event.js"></script>
38
+ <script src="/refresh-token.js"></script>
38
39
  </body>
39
40
 
40
41
  </html>
@@ -1,8 +1,8 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
- from zrb.runner.web_config import User
5
- from zrb.runner.web_util import (
4
+ from zrb.runner.web_schema.user import User
5
+ from zrb.runner.web_util.html import (
6
6
  get_html_auth_link,
7
7
  get_html_subgroup_info,
8
8
  get_html_subtask_info,
@@ -11,7 +11,7 @@ from zrb.util.file import read_file
11
11
  from zrb.util.string.format import fstring_format
12
12
 
13
13
 
14
- def show_group_info_page(user: User, root_group: AnyGroup, group: AnyGroup, url: str):
14
+ def show_group_page(user: User, root_group: AnyGroup, group: AnyGroup, url: str):
15
15
  from fastapi.responses import HTMLResponse
16
16
 
17
17
  _DIR = os.path.dirname(__file__)
@@ -33,4 +33,5 @@
33
33
  {task_info}
34
34
  </main>
35
35
  </body>
36
+ <script src="/refresh-token.js"></script>
36
37
  </html>
@@ -0,0 +1,50 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.context.shared_context import SharedContext
5
+ from zrb.group.any_group import AnyGroup
6
+ from zrb.runner.web_config.config import WebConfig
7
+ from zrb.runner.web_route.error_page.show_error_page import show_error_page
8
+ from zrb.runner.web_route.node_page.group.show_group_page import show_group_page
9
+ from zrb.runner.web_route.node_page.task.show_task_page import show_task_page
10
+ from zrb.runner.web_util.user import get_user_from_request
11
+ from zrb.session.session import Session
12
+ from zrb.task.any_task import AnyTask
13
+ from zrb.util.group import NodeNotFoundError, extract_node_from_args
14
+
15
+ if TYPE_CHECKING:
16
+ # We want fastapi to only be loaded when necessary to decrease footprint
17
+ from fastapi import FastAPI
18
+
19
+
20
+ def serve_node_page(
21
+ app: "FastAPI",
22
+ root_group: AnyGroup,
23
+ web_config: WebConfig,
24
+ ) -> None:
25
+ from fastapi import Request
26
+ from fastapi.responses import HTMLResponse
27
+
28
+ @app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
29
+ async def ui_page(path: str, request: Request) -> HTMLResponse:
30
+ user = await get_user_from_request(web_config, request)
31
+ # Avoid capturing '/ui' itself
32
+ if not path:
33
+ return show_error_page(user, root_group, 422, "Undefined path")
34
+ args = path.strip("/").split("/")
35
+ try:
36
+ node, node_path, residual_args = extract_node_from_args(root_group, args)
37
+ except NodeNotFoundError as e:
38
+ return show_error_page(user, root_group, 404, str(e))
39
+ url = f"/ui/{'/'.join(node_path)}/"
40
+ if isinstance(node, AnyTask):
41
+ if not user.can_access_task(node):
42
+ return show_error_page(user, root_group, 403, "Forbidden")
43
+ shared_ctx = SharedContext(env=dict(os.environ))
44
+ session = Session(shared_ctx=shared_ctx, root_group=root_group)
45
+ return show_task_page(user, root_group, node, session, url, residual_args)
46
+ elif isinstance(node, AnyGroup):
47
+ if not user.can_access_group(node):
48
+ return show_error_page(user, root_group, 403, "Forbidden")
49
+ return show_group_page(user, root_group, node, url)
50
+ return show_error_page(user, root_group, 404, "Not found")
@@ -2,15 +2,15 @@ import json
2
2
  import os
3
3
 
4
4
  from zrb.group.any_group import AnyGroup
5
- from zrb.runner.web_config import User
6
- from zrb.runner.web_util import get_html_auth_link
5
+ from zrb.runner.web_schema.user import User
6
+ from zrb.runner.web_util.html import get_html_auth_link
7
7
  from zrb.session.any_session import AnySession
8
8
  from zrb.task.any_task import AnyTask
9
9
  from zrb.util.file import read_file
10
10
  from zrb.util.string.format import fstring_format
11
11
 
12
12
 
13
- def show_session_page(
13
+ def show_task_page(
14
14
  user: User,
15
15
  root_group: AnyGroup,
16
16
  task: AnyTask,
@@ -33,11 +33,11 @@ def show_session_page(
33
33
  parent_url = "/".join(parent_url_parts)
34
34
  # Assemble session api url
35
35
  session_url_parts = list(url_parts)
36
- session_url_parts[1] = "api/sessions"
36
+ session_url_parts[1] = "api/v1/task-sessions"
37
37
  session_api_url = "/".join(session_url_parts)
38
38
  # Assemble input api url
39
39
  input_url_parts = list(url_parts)
40
- input_url_parts[1] = "api/inputs"
40
+ input_url_parts[1] = "api/v1/task-inputs"
41
41
  input_api_url = "/".join(input_url_parts)
42
42
  # Assemble ui url
43
43
  ui_url_parts = list(url_parts)
@@ -88,4 +88,5 @@
88
88
  <script src="/static/session/past-session.js"></script>
89
89
  <script src="/static/session/current-session.js"></script>
90
90
  <script src="/static/session/event.js"></script>
91
+ <script src="/refresh-token.js"></script>
91
92
  </html>
@@ -0,0 +1,38 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from zrb.runner.web_config.config import WebConfig
4
+ from zrb.runner.web_schema.token import RefreshTokenRequest
5
+ from zrb.runner.web_util.cookie import set_auth_cookie
6
+ from zrb.runner.web_util.token import regenerate_tokens
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_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
14
+ from fastapi import Cookie, Response
15
+ from fastapi.responses import JSONResponse
16
+
17
+ @app.post("/api/v1/refresh-token")
18
+ async def refresh_token_api(
19
+ response: Response,
20
+ body: RefreshTokenRequest = None,
21
+ refresh_token_cookie: str = Cookie(
22
+ None, alias=web_config.refresh_token_cookie_name
23
+ ),
24
+ ):
25
+ # Try to get the refresh token from the request body first
26
+ refresh_token = body.refresh_token if body else None
27
+ # If not in the body, try to get it from the cookie
28
+ if not refresh_token:
29
+ refresh_token = refresh_token_cookie
30
+ # If we still don't have a refresh token, raise an exception
31
+ if not refresh_token:
32
+ return JSONResponse(
33
+ content={"detail": "Refresh token not provided"}, status_code=400
34
+ )
35
+ # Get token
36
+ new_token = regenerate_tokens(web_config, refresh_token)
37
+ set_auth_cookie(web_config, response, new_token)
38
+ return new_token
@@ -0,0 +1,22 @@
1
+ function refreshAuthToken(){
2
+ const refreshUrl = "/api/v1/refresh-token";
3
+ async function refresh() {
4
+ try {
5
+ const response = await fetch(refreshUrl, {
6
+ method: "POST",
7
+ headers: { "Content-Type": "application/json" },
8
+ credentials: "include", // Include cookies in the request
9
+ });
10
+
11
+ if (!response.ok) {
12
+ throw new Error(`HTTP error! Status: ${response.status}`);
13
+ }
14
+ console.log("Token refreshed successfully");
15
+ } catch (error) {
16
+ console.error("Cannot refresh token", error)
17
+ }
18
+ }
19
+ setInterval(refresh, refreshIntervalSeconds * 1000);
20
+ refresh();
21
+ }
22
+ refreshAuthToken();
@@ -1,5 +1,5 @@
1
1
  const CURRENT_SESSION = {
2
- async pollCurrentSession() {
2
+ async startPolling() {
3
3
  const resultTextarea = document.getElementById("result-textarea");
4
4
  const logTextarea = document.getElementById("log-textarea");
5
5
  const submitTaskForm = document.getElementById("submit-task-form");
@@ -13,14 +13,16 @@ const CURRENT_SESSION = {
13
13
  for (const inputName in dataInputs) {
14
14
  const inputValue = dataInputs[inputName];
15
15
  const input = submitTaskForm.querySelector(`[name="${inputName}"]`);
16
- input.value = inputValue;
16
+ if (input) {
17
+ input.value = inputValue;
18
+ }
17
19
  }
18
20
  resultLineCount = data.final_result.split("\n").length;
19
21
  resultTextarea.rows = resultLineCount <= 5 ? resultLineCount : 5;
20
22
  // update text areas
21
23
  resultTextarea.value = data.final_result;
22
24
  logTextarea.value = data.log.join("\n");
23
- logTextarea.scrollTop = logTextarea.scrollHeight;
25
+ // logTextarea.scrollTop = logTextarea.scrollHeight;
24
26
  // visualize history
25
27
  this.showCurrentSession(data.task_status, data.finished);
26
28
  if (data.finished) {
@@ -49,6 +51,7 @@ const CURRENT_SESSION = {
49
51
  "Content-Type": "application/json"
50
52
  },
51
53
  });
54
+ console.log("RESPONSE", response);
52
55
  return await response.json();
53
56
  } catch (error) {
54
57
  console.error("Error:", error);
@@ -1,7 +1,7 @@
1
1
  window.addEventListener("load", async function () {
2
2
  // Get current session
3
3
  if (cfg.SESSION_NAME != "") {
4
- CURRENT_SESSION.pollCurrentSession();
4
+ CURRENT_SESSION.startPolling();
5
5
  }
6
6
  // set maxStartDate to today
7
7
  const tomorrow = new Date();
@@ -16,15 +16,13 @@ window.addEventListener("load", async function () {
16
16
  const formattedToday = UTIL.toLocalDateInputValue(today);
17
17
  const minStartAtInput = document.getElementById("min-start-at-input");
18
18
  minStartAtInput.value = formattedToday;
19
- // Update session
20
- PAST_SESSION.pollPastSession();
21
19
  });
22
20
 
23
21
 
24
22
  const submitTaskForm = document.getElementById("submit-task-form");
25
- submitTaskForm.addEventListener("input", async function(event) {
23
+ submitTaskForm.addEventListener("change", async function(event) {
26
24
  const currentInput = event.target;
27
- const inputs = Array.from(submitTaskForm.querySelectorAll("input[name]"));
25
+ const inputs = Array.from(submitTaskForm.querySelectorAll("input[name], textarea[name]"));
28
26
  const inputMap = {};
29
27
  const fixedInputNames = [];
30
28
  for (const input of inputs) {
@@ -55,6 +53,9 @@ submitTaskForm.addEventListener("input", async function(event) {
55
53
  return;
56
54
  }
57
55
  const input = submitTaskForm.querySelector(`[name="${key}"]`);
56
+ if (input === currentInput) {
57
+ return;
58
+ }
58
59
  input.value = value;
59
60
  });
60
61
  } else {
@@ -63,11 +64,12 @@ submitTaskForm.addEventListener("input", async function(event) {
63
64
  } catch (error) {
64
65
  console.error("Error during fetch:", error);
65
66
  }
66
- })
67
+ });
67
68
 
68
69
 
69
70
  function openPastSessionDialog(event) {
70
71
  event.preventDefault();
72
+ PAST_SESSION.startPolling();
71
73
  const dialog = document.getElementById("past-session-dialog")
72
74
  dialog.showModal();
73
75
  }
@@ -75,6 +77,7 @@ function openPastSessionDialog(event) {
75
77
 
76
78
  function closePastSessionDialog(event) {
77
79
  event.preventDefault();
80
+ PAST_SESSION.stopPolling();
78
81
  const dialog = document.getElementById("past-session-dialog")
79
82
  dialog.close();
80
83
  }
@@ -109,8 +112,7 @@ async function submitNewSessionForm(event) {
109
112
  const data = await response.json();
110
113
  cfg.SESSION_NAME = data.session_name;
111
114
  history.pushState(null, "", `${cfg.CURRENT_URL}${cfg.SESSION_NAME}`);
112
- await PAST_SESSION.getAndRenderPastSession(0);
113
- await CURRENT_SESSION.pollCurrentSession();
115
+ await CURRENT_SESSION.startPolling();
114
116
  } else {
115
117
  console.error("Error:", response);
116
118
  }
@@ -1,12 +1,18 @@
1
1
  const PAST_SESSION = {
2
+ shouldPoll: true,
2
3
 
3
- async pollPastSession() {
4
- while (true) {
5
- await this.getAndRenderPastSession(cfg.PAGE);
4
+ async startPolling() {
5
+ await this.getAndRenderPastSession(cfg.PAGE);
6
+ while (this.shouldPoll) {
6
7
  await UTIL.delay(5000);
8
+ await this.getAndRenderPastSession(cfg.PAGE);
7
9
  }
8
10
  },
9
11
 
12
+ stopPolling() {
13
+ this.shouldPoll = false;
14
+ },
15
+
10
16
  async getAndRenderPastSession(page) {
11
17
  cfg.PAGE=page
12
18
  const minStartAtInput = document.getElementById("min-start-at-input");
@@ -0,0 +1,44 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.runner.web_config.config import WebConfig
5
+ from zrb.util.file import read_file
6
+
7
+ if TYPE_CHECKING:
8
+ # We want fastapi to only be loaded when necessary to decrease footprint
9
+ from fastapi import FastAPI
10
+
11
+
12
+ def serve_static_resources(app: "FastAPI", web_config: WebConfig) -> None:
13
+ from fastapi import HTTPException
14
+ from fastapi.responses import FileResponse, PlainTextResponse
15
+ from fastapi.staticfiles import StaticFiles
16
+
17
+ _STATIC_DIR = os.path.join(os.path.dirname(__file__), "resources")
18
+
19
+ app.mount("/static", StaticFiles(directory=_STATIC_DIR), name="static")
20
+
21
+ # Serve static files
22
+ @app.get("/static/{file_path:path}", include_in_schema=False)
23
+ async def static_files(file_path: str):
24
+ full_path = os.path.join(_STATIC_DIR, file_path)
25
+ if os.path.isfile(full_path):
26
+ return FileResponse(full_path)
27
+ raise HTTPException(status_code=404, detail="File not found")
28
+
29
+ @app.get("/refresh-token.js", include_in_schema=False)
30
+ async def refresh_token_js():
31
+ return PlainTextResponse(
32
+ content=_get_refresh_token_js(
33
+ 60 * web_config.refresh_token_expire_minutes / 3
34
+ ),
35
+ media_type="application/javascript",
36
+ )
37
+
38
+
39
+ def _get_refresh_token_js(refresh_interval_seconds: int):
40
+ _DIR = os.path.dirname(__file__)
41
+ return read_file(
42
+ os.path.join(_DIR, "refresh-token.template.js"),
43
+ {"refreshIntervalSeconds": f"{refresh_interval_seconds}"},
44
+ )
@@ -0,0 +1,47 @@
1
+ import json
2
+ from typing import TYPE_CHECKING
3
+
4
+ from zrb.group.any_group import AnyGroup
5
+ from zrb.runner.common_util import get_run_kwargs
6
+ from zrb.runner.web_config.config import WebConfig
7
+ from zrb.runner.web_util.user import get_user_from_request
8
+ from zrb.task.any_task import AnyTask
9
+ from zrb.util.group import NodeNotFoundError, extract_node_from_args
10
+
11
+ if TYPE_CHECKING:
12
+ # We want fastapi to only be loaded when necessary to decrease footprint
13
+ from fastapi import FastAPI
14
+
15
+
16
+ def serve_task_input_api(
17
+ app: "FastAPI",
18
+ root_group: AnyGroup,
19
+ web_config: WebConfig,
20
+ ) -> None:
21
+ from fastapi import Query, Request
22
+ from fastapi.responses import JSONResponse
23
+
24
+ @app.get("/api/v1/task-inputs/{path:path}", response_model=dict[str, str])
25
+ async def get_default_inputs_api(
26
+ path: str,
27
+ request: Request,
28
+ query: str = Query("{}", description="JSON encoded inputs"),
29
+ ) -> dict[str, str]:
30
+ """
31
+ Getting input completion for path
32
+ """
33
+ user = await get_user_from_request(web_config, request)
34
+ args = path.strip("/").split("/")
35
+ try:
36
+ task, _, _ = extract_node_from_args(root_group, args)
37
+ except NodeNotFoundError:
38
+ return JSONResponse(content={"detail": "Not found"}, status_code=404)
39
+ if isinstance(task, AnyTask):
40
+ if not user.can_access_task(task):
41
+ return JSONResponse(content={"detail": "Forbidden"}, status_code=403)
42
+ query_dict = json.loads(query)
43
+ run_kwargs = get_run_kwargs(
44
+ task=task, args=[], kwargs=query_dict, prompt=False
45
+ )
46
+ return run_kwargs
47
+ return JSONResponse(content={"detail": "Not found"}, status_code=404)