zrb 1.0.0a20__py3-none-any.whl → 1.0.0b1__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/builtin/llm/llm_chat.py +2 -2
- 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 +4 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +16 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +91 -9
- 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 → my_entity_service.py} +7 -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_factory.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +37 -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 +22 -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/api_client.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +1 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +10 -10
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +4 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +42 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{base_usecase.py → base_service.py} +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/view.py +37 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +24 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/{user_usecase.py → user_service.py} +7 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +42 -13
- 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/python.py +1 -1
- zrb/config.py +2 -2
- zrb/content_transformer/any_content_transformer.py +7 -0
- zrb/content_transformer/content_transformer.py +6 -0
- zrb/runner/cli.py +4 -6
- zrb/runner/web_app.py +28 -238
- 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} +2 -2
- zrb/runner/{web_controller → web_route}/error_page/view.html +6 -0
- zrb/runner/web_route/home_page/home_page_route.py +51 -0
- zrb/runner/{web_controller → web_route}/home_page/view.html +1 -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_controller → web_route}/login_page/view.html +1 -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 → web_route}/logout_page/view.html +1 -0
- zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
- zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +1 -0
- 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} +5 -5
- zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +1 -0
- zrb/runner/web_route/refresh_token_api_route.py +38 -0
- zrb/runner/web_route/static/refresh-token.template.js +22 -0
- zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +1 -1
- zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +5 -6
- zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +9 -3
- 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 +102 -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 -18
- zrb/runner/web_util/token.py +68 -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 +29 -4
- zrb/task/base_trigger.py +2 -0
- zrb/task/cmd_task.py +2 -0
- zrb/task/http_check.py +2 -0
- zrb/task/llm_task.py +2 -0
- 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-1.0.0a20.dist-info → zrb-1.0.0b1.dist-info}/METADATA +8 -52
- {zrb-1.0.0a20.dist-info → zrb-1.0.0b1.dist-info}/RECORD +126 -81
- 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 -288
- zrb/runner/web_controller/home_page/__init__.py +0 -0
- 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/runner/web_controller/session_page/__init__.py +0 -0
- /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
- /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} +0 -0
- /zrb/runner/{web_controller → web_route}/__init__.py +0 -0
- /zrb/runner/{web_controller/group_info_page → web_route/home_page}/__init__.py +0 -0
- /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +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-1.0.0a20.dist-info → zrb-1.0.0b1.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a20.dist-info → zrb-1.0.0b1.dist-info}/entry_points.txt +0 -0
@@ -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
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
<!doctype html>
|
2
2
|
<html lang="en">
|
3
|
+
|
3
4
|
<head>
|
4
5
|
<meta charset="utf-8">
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
@@ -9,6 +10,7 @@
|
|
9
10
|
<title>Zrb</title>
|
10
11
|
<link rel="stylesheet" href="/static/common.css">
|
11
12
|
</head>
|
13
|
+
|
12
14
|
<body>
|
13
15
|
<header class="container">
|
14
16
|
<hgroup>
|
@@ -26,8 +28,12 @@
|
|
26
28
|
</hgroup>
|
27
29
|
</header>
|
28
30
|
<main class="container">
|
31
|
+
<article>
|
29
32
|
<h2>{error_status_code}</h2>
|
30
33
|
<p>{error_message}</p>
|
34
|
+
</article>
|
31
35
|
</main>
|
32
36
|
</body>
|
37
|
+
<script src="/refresh-token.js"></script>
|
38
|
+
|
33
39
|
</html>
|
@@ -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
|
+
)
|
@@ -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
|
+
)
|
@@ -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
|
+
)
|
@@ -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 (
|
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
|
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__)
|
@@ -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")
|
zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py}
RENAMED
@@ -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.
|
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
|
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)
|
@@ -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
|
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");
|
@@ -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.
|
4
|
+
CURRENT_SESSION.startPolling();
|
5
5
|
}
|
6
6
|
// set maxStartDate to today
|
7
7
|
const tomorrow = new Date();
|
@@ -16,8 +16,6 @@ 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
|
|
@@ -63,11 +61,12 @@ submitTaskForm.addEventListener("input", async function(event) {
|
|
63
61
|
} catch (error) {
|
64
62
|
console.error("Error during fetch:", error);
|
65
63
|
}
|
66
|
-
})
|
64
|
+
});
|
67
65
|
|
68
66
|
|
69
67
|
function openPastSessionDialog(event) {
|
70
68
|
event.preventDefault();
|
69
|
+
PAST_SESSION.startPolling();
|
71
70
|
const dialog = document.getElementById("past-session-dialog")
|
72
71
|
dialog.showModal();
|
73
72
|
}
|
@@ -75,6 +74,7 @@ function openPastSessionDialog(event) {
|
|
75
74
|
|
76
75
|
function closePastSessionDialog(event) {
|
77
76
|
event.preventDefault();
|
77
|
+
PAST_SESSION.stopPolling();
|
78
78
|
const dialog = document.getElementById("past-session-dialog")
|
79
79
|
dialog.close();
|
80
80
|
}
|
@@ -109,8 +109,7 @@ async function submitNewSessionForm(event) {
|
|
109
109
|
const data = await response.json();
|
110
110
|
cfg.SESSION_NAME = data.session_name;
|
111
111
|
history.pushState(null, "", `${cfg.CURRENT_URL}${cfg.SESSION_NAME}`);
|
112
|
-
await
|
113
|
-
await CURRENT_SESSION.pollCurrentSession();
|
112
|
+
await CURRENT_SESSION.startPolling();
|
114
113
|
} else {
|
115
114
|
console.error("Error:", response);
|
116
115
|
}
|