zrb 1.7.4__py3-none-any.whl → 1.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. zrb/__init__.py +2 -2
  2. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +3 -3
  3. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +3 -3
  4. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +3 -3
  5. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +3 -3
  6. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +3 -3
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +3 -3
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +3 -3
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +3 -3
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +3 -3
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +3 -3
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +3 -3
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +3 -3
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +3 -3
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +3 -3
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +3 -3
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +3 -3
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +3 -3
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +3 -3
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +3 -3
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +3 -3
  22. zrb/config.py +30 -0
  23. zrb/llm_config.py +2 -2
  24. zrb/runner/cli.py +2 -3
  25. zrb/runner/web_app.py +17 -14
  26. zrb/runner/web_auth_config.py +189 -0
  27. zrb/runner/web_route/docs_route.py +4 -2
  28. zrb/runner/web_route/error_page/serve_default_404.py +3 -3
  29. zrb/runner/web_route/error_page/show_error_page.py +2 -1
  30. zrb/runner/web_route/error_page/view.html +22 -39
  31. zrb/runner/web_route/home_page/home_page_route.py +5 -4
  32. zrb/runner/web_route/login_api_route.py +4 -4
  33. zrb/runner/web_route/login_page/login_page_route.py +11 -6
  34. zrb/runner/web_route/logout_api_route.py +4 -4
  35. zrb/runner/web_route/logout_page/logout_page_route.py +11 -6
  36. zrb/runner/web_route/node_page/group/show_group_page.py +2 -1
  37. zrb/runner/web_route/node_page/node_page_route.py +3 -3
  38. zrb/runner/web_route/node_page/task/show_task_page.py +2 -1
  39. zrb/runner/web_route/refresh_token_api_route.py +5 -5
  40. zrb/runner/web_route/static/global_template.html +6 -3
  41. zrb/runner/web_route/static/resources/pico-css/pico.amber.min.css +4 -0
  42. zrb/runner/web_route/static/resources/pico-css/pico.blue.min.css +4 -0
  43. zrb/runner/web_route/static/resources/pico-css/pico.cyan.min.css +4 -0
  44. zrb/runner/web_route/static/resources/pico-css/pico.fuchsia.min.css +4 -0
  45. zrb/runner/web_route/static/resources/pico-css/pico.green.min.css +4 -0
  46. zrb/runner/web_route/static/resources/pico-css/pico.grey.min.css +4 -0
  47. zrb/runner/web_route/static/resources/pico-css/pico.indigo.min.css +4 -0
  48. zrb/runner/web_route/static/resources/pico-css/pico.jade.min.css +4 -0
  49. zrb/runner/web_route/static/resources/pico-css/pico.lime.min.css +4 -0
  50. zrb/runner/web_route/static/resources/pico-css/pico.min.css +4 -0
  51. zrb/runner/web_route/static/resources/pico-css/pico.orange.min.css +4 -0
  52. zrb/runner/web_route/static/resources/pico-css/pico.pink.min.css +4 -0
  53. zrb/runner/web_route/static/resources/pico-css/pico.pumpkin.min.css +4 -0
  54. zrb/runner/web_route/static/resources/pico-css/pico.purple.min.css +4 -0
  55. zrb/runner/web_route/static/resources/pico-css/pico.red.min.css +4 -0
  56. zrb/runner/web_route/static/resources/pico-css/pico.sand.min.css +4 -0
  57. zrb/runner/web_route/static/resources/pico-css/pico.slate.min.css +4 -0
  58. zrb/runner/web_route/static/resources/pico-css/pico.violet.min.css +4 -0
  59. zrb/runner/web_route/static/resources/pico-css/pico.yellow.min.css +4 -0
  60. zrb/runner/web_route/static/resources/pico-css/pico.zinc.min.css +4 -0
  61. zrb/runner/web_route/static/static_route.py +3 -3
  62. zrb/runner/web_route/task_input_api_route.py +3 -3
  63. zrb/runner/web_route/task_session_api_route.py +4 -4
  64. zrb/runner/web_util/cookie.py +6 -6
  65. zrb/runner/web_util/token.py +18 -18
  66. zrb/runner/web_util/user.py +17 -15
  67. zrb/util/string/format.py +11 -14
  68. {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/METADATA +1 -1
  69. {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/RECORD +71 -53
  70. zrb/runner/web_config/config.py +0 -91
  71. zrb/runner/web_config/config_factory.py +0 -15
  72. zrb/runner/web_route/static/resources/pico.min.css +0 -4
  73. {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/WHEEL +0 -0
  74. {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,189 @@
1
+ from typing import Callable
2
+
3
+ from zrb.config import CFG
4
+ from zrb.runner.web_schema.user import User
5
+ from zrb.task.any_task import AnyTask
6
+
7
+
8
+ class WebAuthConfig:
9
+ def __init__(
10
+ self,
11
+ port: int | None = None,
12
+ secret_key: str | None = None,
13
+ access_token_expire_minutes: int | None = None,
14
+ refresh_token_expire_minutes: int | None = None,
15
+ access_token_cookie_name: str | None = None,
16
+ refresh_token_cookie_name: str | None = None,
17
+ enable_auth: bool | None = None,
18
+ super_admin_username: str | None = None,
19
+ super_admin_password: str | None = None,
20
+ guest_username: str | None = None,
21
+ guest_accessible_tasks: list[AnyTask | str] = [],
22
+ find_user_by_username: Callable[[str], User | None] | None = None,
23
+ ):
24
+ self._port = port
25
+ self._secret_key = secret_key
26
+ self._access_token_expire_minutes = access_token_expire_minutes
27
+ self._refresh_token_expire_minutes = refresh_token_expire_minutes
28
+ self._access_token_cookie_name = access_token_cookie_name
29
+ self._refresh_token_cookie_name = refresh_token_cookie_name
30
+ self._enable_auth = enable_auth
31
+ self._super_admin_username = super_admin_username
32
+ self._super_admin_password = super_admin_password
33
+ self._guest_username = guest_username
34
+ self._user_list = []
35
+ self._guest_accessible_tasks = guest_accessible_tasks
36
+ self._find_user_by_username = find_user_by_username
37
+
38
+ @property
39
+ def port(self) -> int:
40
+ if self._port is not None:
41
+ return self._port
42
+ return CFG.WEB_HTTP_PORT
43
+
44
+ @property
45
+ def secret_key(self) -> str:
46
+ if self._secret_key is not None:
47
+ return self._secret_key
48
+ return CFG.WEB_SECRET_KEY
49
+
50
+ @property
51
+ def access_token_expire_minutes(self) -> int:
52
+ if self._access_token_expire_minutes is not None:
53
+ return self._access_token_expire_minutes
54
+ return CFG.WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES
55
+
56
+ @property
57
+ def refresh_token_expire_minutes(self) -> int:
58
+ if self._refresh_token_expire_minutes is not None:
59
+ return self._refresh_token_expire_minutes
60
+ return CFG.WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES
61
+
62
+ @property
63
+ def access_token_cookie_name(self) -> str:
64
+ if self._access_token_cookie_name is not None:
65
+ return self._access_token_cookie_name
66
+ return CFG.WEB_ACCESS_TOKEN_COOKIE_NAME
67
+
68
+ @property
69
+ def refresh_token_cookie_name(self) -> str:
70
+ if self._refresh_token_cookie_name is not None:
71
+ return self._refresh_token_cookie_name
72
+ return CFG.WEB_REFRESH_TOKEN_COOKIE_NAME
73
+
74
+ @property
75
+ def enable_auth(self) -> bool:
76
+ if self._enable_auth is not None:
77
+ return self._enable_auth
78
+ return CFG.WEB_ENABLE_AUTH
79
+
80
+ @property
81
+ def super_admin_username(self) -> str:
82
+ if self._super_admin_username is not None:
83
+ return self._super_admin_username
84
+ return CFG.WEB_SUPER_ADMIN_USERNAME
85
+
86
+ @property
87
+ def super_admin_password(self) -> str:
88
+ if self._super_admin_password is not None:
89
+ return self._super_admin_password
90
+ return CFG.WEB_SUPER_ADMIN_PASSWORD
91
+
92
+ @property
93
+ def guest_username(self) -> str:
94
+ if self._guest_username is not None:
95
+ return self._guest_username
96
+ return CFG.WEB_GUEST_USERNAME
97
+
98
+ @property
99
+ def guest_accessible_tasks(self) -> list[AnyTask | str]:
100
+ return self._guest_accessible_tasks
101
+
102
+ @property
103
+ def default_user(self) -> User:
104
+ if self.enable_auth:
105
+ return User(
106
+ username=self.guest_username,
107
+ password="",
108
+ is_guest=True,
109
+ accessible_tasks=self.guest_accessible_tasks,
110
+ )
111
+ return User(
112
+ username=self.guest_username,
113
+ password="",
114
+ is_guest=True,
115
+ is_super_admin=True,
116
+ )
117
+
118
+ @property
119
+ def super_admin(self) -> User:
120
+ return User(
121
+ username=self.super_admin_username,
122
+ password=self.super_admin_password,
123
+ is_super_admin=True,
124
+ )
125
+
126
+ @property
127
+ def user_list(self) -> list[User]:
128
+ if not self.enable_auth:
129
+ return [self.default_user]
130
+ return self._user_list + [self.super_admin, self.default_user]
131
+
132
+ def set_port(self, port: int):
133
+ self._port = port
134
+
135
+ def set_secret_key(self, secret_key: str):
136
+ self._secret_key = secret_key
137
+
138
+ def set_access_token_expire_minutes(self, minutes: int):
139
+ self._access_token_expire_minutes = minutes
140
+
141
+ def set_refresh_token_expire_minutes(self, minutes: int):
142
+ self._refresh_token_expire_minutes = minutes
143
+
144
+ def set_access_token_cookie_name(self, name: str):
145
+ self._access_token_cookie_name = name
146
+
147
+ def set_refresh_token_cookie_name(self, name: str):
148
+ self._refresh_token_cookie_name = name
149
+
150
+ def set_enable_auth(self, enable: bool):
151
+ self._enable_auth = enable
152
+
153
+ def set_super_admin_username(self, username: str):
154
+ self._super_admin_username = username
155
+
156
+ def set_super_admin_password(self, password: str):
157
+ self._super_admin_password = password
158
+
159
+ def set_guest_username(self, username: str):
160
+ self._guest_username = username
161
+
162
+ def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
163
+ self._guest_accessible_tasks = tasks
164
+
165
+ def set_find_user_by_username(
166
+ self, find_user_by_username: Callable[[str], User | None]
167
+ ):
168
+ self._find_user_by_username = find_user_by_username
169
+
170
+ def append_user(self, user: User):
171
+ duplicates = [
172
+ existing_user
173
+ for existing_user in self.user_list
174
+ if existing_user.username == user.username
175
+ ]
176
+ if len(duplicates) > 0:
177
+ raise ValueError(f"User already exists {user.username}")
178
+ self._user_list.append(user)
179
+
180
+ def find_user_by_username(self, username: str) -> User | None:
181
+ user = None
182
+ if self._find_user_by_username is not None:
183
+ user = self._find_user_by_username(username)
184
+ if user is None:
185
+ user = next((u for u in self.user_list if u.username == username), None)
186
+ return user
187
+
188
+
189
+ web_auth_config = WebAuthConfig()
@@ -1,5 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ from zrb.config import CFG
4
+
3
5
  if TYPE_CHECKING:
4
6
  # We want fastapi to only be loaded when necessary to decrease footprint
5
7
  from fastapi import FastAPI
@@ -12,6 +14,6 @@ def serve_docs(app: "FastAPI") -> None:
12
14
  async def swagger_ui_html():
13
15
  return get_swagger_ui_html(
14
16
  openapi_url="/openapi.json",
15
- title="Zrb",
16
- swagger_favicon_url="/static/favicon-32x32.png",
17
+ title=CFG.WEB_TITLE,
18
+ swagger_favicon_url=CFG.WEB_FAVICON_PATH,
17
19
  )
@@ -1,7 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
- from zrb.runner.web_config.config import WebConfig
4
+ from zrb.runner.web_auth_config import WebAuthConfig
5
5
  from zrb.runner.web_route.error_page.show_error_page import show_error_page
6
6
  from zrb.runner.web_util.user import get_user_from_request
7
7
 
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
  def serve_default_404(
14
14
  app: "FastAPI",
15
15
  root_group: AnyGroup,
16
- web_config: WebConfig,
16
+ web_auth_config: WebAuthConfig,
17
17
  ) -> None:
18
18
  from fastapi import Request
19
19
  from fastapi.exception_handlers import http_exception_handler
@@ -24,5 +24,5 @@ def serve_default_404(
24
24
  if request.url.path.startswith("/api"):
25
25
  # Re-raise the exception to let FastAPI handle it
26
26
  return await http_exception_handler(request, exc)
27
- user = await get_user_from_request(web_config, request)
27
+ user = await get_user_from_request(web_auth_config, request)
28
28
  return show_error_page(user, root_group, 404, "Not found")
@@ -21,7 +21,8 @@ def show_error_page(user: User, root_group: AnyGroup, status_code: int, message:
21
21
  fstring_format(
22
22
  _GLOBAL_TEMPLATE,
23
23
  {
24
- "web_title": CFG.WEB_TITLE,
24
+ "CFG": CFG,
25
+ "root_group": root_group,
25
26
  "content": fstring_format(
26
27
  _VIEW_TEMPLATE,
27
28
  {
@@ -1,39 +1,22 @@
1
- <!doctype html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="utf-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <meta name="color-scheme" content="light dark">
8
- <link rel="stylesheet" href="/static/pico.min.css">
9
- <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
10
- <title>Zrb</title>
11
- <link rel="stylesheet" href="/static/common.css">
12
- </head>
13
-
14
- <body>
15
- <header class="container">
16
- <hgroup>
17
- <h1>{name}</h1>
18
- <p>{description}</p>
19
- <nav>
20
- <ul>
21
- <li><a href="/">🏠 Home</a></li>
22
- <li><a href="/docs">💻 API Documentation</a></li>
23
- </ul>
24
- <ul>
25
- <li>{auth_link}</li>
26
- </ul>
27
- </nav>
28
- </hgroup>
29
- </header>
30
- <main class="container">
31
- <article>
32
- <h2>{error_status_code}</h2>
33
- <p>{error_message}</p>
34
- </article>
35
- </main>
36
- </body>
37
- <script src="/refresh-token.js"></script>
38
-
39
- </html>
1
+ <header class="container">
2
+ <hgroup>
3
+ <h1>{name}</h1>
4
+ <p>{description}</p>
5
+ <nav>
6
+ <ul>
7
+ <li><a href="/">🏠 Home</a></li>
8
+ <li><a href="/docs">💻 API Documentation</a></li>
9
+ </ul>
10
+ <ul>
11
+ <li>{auth_link}</li>
12
+ </ul>
13
+ </nav>
14
+ </hgroup>
15
+ </header>
16
+ <main class="container">
17
+ <article>
18
+ <h2>{error_status_code}</h2>
19
+ <p>{error_message}</p>
20
+ </article>
21
+ </main>
22
+ <script src="/refresh-token.js"></script>
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  from zrb.config import CFG
5
5
  from zrb.group.any_group import AnyGroup
6
- from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_auth_config import WebAuthConfig
7
7
  from zrb.runner.web_util.html import (
8
8
  get_html_auth_link,
9
9
  get_html_subgroup_info,
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
21
21
  def serve_home_page(
22
22
  app: "FastAPI",
23
23
  root_group: AnyGroup,
24
- web_config: WebConfig,
24
+ web_auth_config: WebAuthConfig,
25
25
  ) -> None:
26
26
  from fastapi import Request
27
27
  from fastapi.responses import HTMLResponse
@@ -40,7 +40,7 @@ def serve_home_page(
40
40
  web_jargon = (
41
41
  CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
42
42
  )
43
- user = await get_user_from_request(web_config, request)
43
+ user = await get_user_from_request(web_auth_config, request)
44
44
  group_info = get_html_subgroup_info(user, "/ui/", root_group)
45
45
  task_info = get_html_subtask_info(user, "/ui/", root_group)
46
46
  auth_link = get_html_auth_link(user)
@@ -48,7 +48,8 @@ def serve_home_page(
48
48
  fstring_format(
49
49
  _GLOBAL_TEMPLATE,
50
50
  {
51
- "web_title": web_title,
51
+ "CFG": CFG,
52
+ "root_group": root_group,
52
53
  "content": fstring_format(
53
54
  _VIEW_TEMPLATE,
54
55
  {
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Annotated
2
2
 
3
- from zrb.runner.web_config.config import WebConfig
3
+ from zrb.runner.web_auth_config import WebAuthConfig
4
4
  from zrb.runner.web_util.cookie import set_auth_cookie
5
5
  from zrb.runner.web_util.token import generate_tokens_by_credentials
6
6
 
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
9
9
  from fastapi import FastAPI
10
10
 
11
11
 
12
- def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
12
+ def serve_login_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
13
13
  from fastapi import Depends, Response
14
14
  from fastapi.responses import JSONResponse
15
15
  from fastapi.security import OAuth2PasswordRequestForm
@@ -19,7 +19,7 @@ def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
19
19
  response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
20
20
  ):
21
21
  token = generate_tokens_by_credentials(
22
- web_config=web_config,
22
+ web_auth_config=web_auth_config,
23
23
  username=form_data.username,
24
24
  password=form_data.password,
25
25
  )
@@ -27,5 +27,5 @@ def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
27
27
  return JSONResponse(
28
28
  content={"detail": "Incorrect username or password"}, status_code=400
29
29
  )
30
- set_auth_cookie(web_config, response, token)
30
+ set_auth_cookie(web_auth_config, response, token)
31
31
  return token
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  from zrb.config import CFG
5
5
  from zrb.group.any_group import AnyGroup
6
- from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_auth_config import WebAuthConfig
7
7
  from zrb.runner.web_util.html import get_html_auth_link
8
8
  from zrb.runner.web_util.user import get_user_from_request
9
9
  from zrb.util.file import read_file
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  def serve_login_page(
18
18
  app: "FastAPI",
19
19
  root_group: AnyGroup,
20
- web_config: WebConfig,
20
+ web_auth_config: WebAuthConfig,
21
21
  ) -> None:
22
22
  from fastapi import Request
23
23
  from fastapi.responses import HTMLResponse
@@ -29,18 +29,23 @@ def serve_login_page(
29
29
  os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
30
30
  )
31
31
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
32
- user = await get_user_from_request(web_config, request)
32
+ user = await get_user_from_request(web_auth_config, request)
33
+ web_title = CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name
34
+ web_jargon = (
35
+ CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
36
+ )
33
37
  auth_link = get_html_auth_link(user)
34
38
  return HTMLResponse(
35
39
  fstring_format(
36
40
  _GLOBAL_TEMPLATE,
37
41
  {
38
- "web_title": CFG.WEB_TITLE,
42
+ "CFG": CFG,
43
+ "root_group": root_group,
39
44
  "content": fstring_format(
40
45
  _VIEW_TEMPLATE,
41
46
  {
42
- "name": root_group.name,
43
- "description": root_group.description,
47
+ "name": web_title,
48
+ "description": web_jargon,
44
49
  "auth_link": auth_link,
45
50
  },
46
51
  ),
@@ -1,18 +1,18 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from zrb.runner.web_config.config import WebConfig
3
+ from zrb.runner.web_auth_config import WebAuthConfig
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  # We want fastapi to only be loaded when necessary to decrease footprint
7
7
  from fastapi import FastAPI
8
8
 
9
9
 
10
- def serve_logout_api(app: "FastAPI", web_config: WebConfig) -> None:
10
+ def serve_logout_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
11
11
  from fastapi import Response
12
12
 
13
13
  @app.get("/api/v1/logout")
14
14
  @app.post("/api/v1/logout")
15
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)
16
+ response.delete_cookie(web_auth_config.access_token_cookie_name)
17
+ response.delete_cookie(web_auth_config.refresh_token_cookie_name)
18
18
  return {"message": "Logout successful"}
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  from zrb.config import CFG
5
5
  from zrb.group.any_group import AnyGroup
6
- from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_auth_config import WebAuthConfig
7
7
  from zrb.runner.web_util.html import get_html_auth_link
8
8
  from zrb.runner.web_util.user import get_user_from_request
9
9
  from zrb.util.file import read_file
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  def serve_logout_page(
18
18
  app: "FastAPI",
19
19
  root_group: AnyGroup,
20
- web_config: WebConfig,
20
+ web_auth_config: WebAuthConfig,
21
21
  ) -> None:
22
22
  from fastapi import Request
23
23
  from fastapi.responses import HTMLResponse
@@ -29,18 +29,23 @@ def serve_logout_page(
29
29
  os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
30
30
  )
31
31
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
32
- user = await get_user_from_request(web_config, request)
32
+ user = await get_user_from_request(web_auth_config, request)
33
+ web_title = CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name
34
+ web_jargon = (
35
+ CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
36
+ )
33
37
  auth_link = get_html_auth_link(user)
34
38
  return HTMLResponse(
35
39
  fstring_format(
36
40
  _GLOBAL_TEMPLATE,
37
41
  {
38
- "web_title": CFG.WEB_TITLE,
42
+ "CFG": CFG,
43
+ "root_group": root_group,
39
44
  "content": fstring_format(
40
45
  _VIEW_TEMPLATE,
41
46
  {
42
- "name": root_group.name,
43
- "description": root_group.description,
47
+ "name": web_title,
48
+ "description": web_jargon,
44
49
  "auth_link": auth_link,
45
50
  "user": user,
46
51
  },
@@ -36,7 +36,8 @@ def show_group_page(user: User, root_group: AnyGroup, group: AnyGroup, url: str)
36
36
  fstring_format(
37
37
  _GLOBAL_TEMPLATE,
38
38
  {
39
- "web_title": f"{web_title} | {group.name}",
39
+ "CFG": CFG,
40
+ "root_group": root_group,
40
41
  "content": fstring_format(
41
42
  _VIEW_TEMPLATE,
42
43
  {
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  from zrb.context.shared_context import SharedContext
5
5
  from zrb.group.any_group import AnyGroup
6
- from zrb.runner.web_config.config import WebConfig
6
+ from zrb.runner.web_auth_config import WebAuthConfig
7
7
  from zrb.runner.web_route.error_page.show_error_page import show_error_page
8
8
  from zrb.runner.web_route.node_page.group.show_group_page import show_group_page
9
9
  from zrb.runner.web_route.node_page.task.show_task_page import show_task_page
@@ -20,14 +20,14 @@ if TYPE_CHECKING:
20
20
  def serve_node_page(
21
21
  app: "FastAPI",
22
22
  root_group: AnyGroup,
23
- web_config: WebConfig,
23
+ web_auth_config: WebAuthConfig,
24
24
  ) -> None:
25
25
  from fastapi import Request
26
26
  from fastapi.responses import HTMLResponse
27
27
 
28
28
  @app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
29
29
  async def ui_page(path: str, request: Request) -> HTMLResponse:
30
- user = await get_user_from_request(web_config, request)
30
+ user = await get_user_from_request(web_auth_config, request)
31
31
  # Avoid capturing '/ui' itself
32
32
  if not path:
33
33
  return show_error_page(user, root_group, 422, "Undefined path")
@@ -64,7 +64,8 @@ def show_task_page(
64
64
  fstring_format(
65
65
  _GLOBAL_TEMPLATE,
66
66
  {
67
- "web_title": f"{web_title} | {task.name}",
67
+ "CFG": CFG,
68
+ "root_group": root_group,
68
69
  "content": fstring_format(
69
70
  _VIEW_TEMPLATE,
70
71
  {
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from zrb.runner.web_config.config import WebConfig
3
+ from zrb.runner.web_auth_config import WebAuthConfig
4
4
  from zrb.runner.web_schema.token import RefreshTokenRequest
5
5
  from zrb.runner.web_util.cookie import set_auth_cookie
6
6
  from zrb.runner.web_util.token import regenerate_tokens
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
10
10
  from fastapi import FastAPI
11
11
 
12
12
 
13
- def serve_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
13
+ def serve_refresh_token_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
14
14
  from fastapi import Cookie, Response
15
15
  from fastapi.responses import JSONResponse
16
16
 
@@ -19,7 +19,7 @@ def serve_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
19
19
  response: Response,
20
20
  body: RefreshTokenRequest = None,
21
21
  refresh_token_cookie: str = Cookie(
22
- None, alias=web_config.refresh_token_cookie_name
22
+ None, alias=web_auth_config.refresh_token_cookie_name
23
23
  ),
24
24
  ):
25
25
  # Try to get the refresh token from the request body first
@@ -33,6 +33,6 @@ def serve_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
33
33
  content={"detail": "Refresh token not provided"}, status_code=401
34
34
  )
35
35
  # Get token
36
- new_token = regenerate_tokens(web_config, refresh_token)
37
- set_auth_cookie(web_config, response, new_token)
36
+ new_token = regenerate_tokens(web_auth_config, refresh_token)
37
+ set_auth_cookie(web_auth_config, response, new_token)
38
38
  return new_token
@@ -4,10 +4,11 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/static/pico.min.css">
8
- <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
- <title>{web_title}</title>
7
+ <link rel="stylesheet" href="/static/pico-css/pico{'.' + CFG.WEB_COLOR if CFG.WEB_COLOR != '' else ''}.min.css">
8
+ <link rel="icon" href="{CFG.WEB_FAVICON_PATH}" sizes="32x32" type="image/png">
9
+ <title>{CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name}</title>
10
10
  <link rel="stylesheet" href="/static/common.css">
11
+ {'\n'.join(['<link rel="stylesheet" href="' + path + '">' for path in CFG.WEB_CSS_PATH])}
11
12
  </head>
12
13
  <body>
13
14
  <div class="theme-switcher">
@@ -24,4 +25,6 @@
24
25
  </body>
25
26
  <script src="/refresh-token.js"></script>
26
27
  <script src="/static/common.js"></script>
28
+ {'\n'.join(['<script src="' + path + '"></script>' for path in CFG.WEB_JS_PATH])}
29
+
27
30
  </html>