zrb 0.0.51__py3-none-any.whl → 0.0.53__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/builtin/generator/fastapp/add.py +3 -2
- zrb/builtin/generator/fastapp/template/_automate/snake_app_name/cmd/start.sh +1 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/.gitignore +1 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/config.py +14 -5
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/.env.local +4 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/.gitignore +1 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/README.md +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/package-lock.json +22 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/package.json +3 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/playwright.config.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/postcss.config.js +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.css +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.d.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/app.html +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/index.test.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/helper.ts +146 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/store.ts +4 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/type.ts +10 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Menu.svelte +19 -17
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Navigation.svelte +64 -7
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/helper.ts +18 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/type.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/app.ts +9 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/navData.ts +6 -17
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/error/helper.ts +12 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+error.svelte +8 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.js +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.svelte +6 -5
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+page.svelte +2 -8
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/static/favicon.png +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/static/logo.png +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/svelte.config.js +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tailwind.config.js +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tests/test.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/tsconfig.json +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/vite.config.ts +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/__init__.py +8 -4
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/access_token_scheme.py +14 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/access_token_util.py +17 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/bearer_token_scheme.py +5 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/model/user_model.py +9 -4
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/refresh_token_util.py +17 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/__init__.py +14 -9
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_scheme/oauth2_bearer_token_scheme.py → access_token/scheme.py} +11 -11
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/access_token/util.py +69 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/authorizer/rpc_authorizer.py +4 -2
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_util/jwt_token_util.py → refresh_token/util.py} +18 -6
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/group/api.py +9 -7
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/group/rpc.py +4 -4
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/permission/api.py +9 -7
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/permission/rpc.py +4 -4
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/api.py +27 -16
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/model.py +44 -19
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/rpc.py +19 -11
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/schema/request.py +10 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/schema/token.py +7 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/requirements.txt +1 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/template.env +7 -3
- zrb/builtin/generator/fastapp_crud/add.py +7 -21
- zrb/builtin/generator/fastapp_crud/add_navigation.py +32 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/+page.svelte +121 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/delete/[id]/+page.svelte +75 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/delete/[id]/+page.ts +5 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/detail/[id]/+page.svelte +54 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/detail/[id]/+page.ts +5 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/new/+page.svelte +52 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/update/[id]/+page.svelte +78 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/frontend/src/routes/kebab-module-name/kebab-entity-name/update/[id]/+page.ts +5 -0
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/module/snake_module_name/entity/snake_entity_name/api.py +9 -7
- zrb/builtin/generator/fastapp_crud/template/src/kebab-app-name/src/module/snake_module_name/entity/snake_entity_name/rpc.py +4 -4
- zrb/builtin/generator/fastapp_field/add.py +203 -4
- zrb/helper/util.py +4 -0
- zrb/task/resource_maker.py +2 -1
- {zrb-0.0.51.dist-info → zrb-0.0.53.dist-info}/METADATA +6 -5
- {zrb-0.0.51.dist-info → zrb-0.0.53.dist-info}/RECORD +66 -52
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/config.ts +0 -4
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/about/+page.svelte +0 -2
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/greetings/[slug]/+page.js +0 -6
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/greetings/[slug]/+page.svelte +0 -5
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/sample/+page.svelte +0 -37
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/sample/delete/[id]/+page.svelte +0 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/token_scheme.py +0 -11
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/token_util.py +0 -17
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/token_scheme/token_sheme.py +0 -5
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/token_util/token_util.py +0 -13
- /zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_scheme/__init__.py → access_token/_init_.py} +0 -0
- /zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/core/{token_util/__init__.py → refresh_token/_init_.py} +0 -0
- {zrb-0.0.51.dist-info → zrb-0.0.53.dist-info}/LICENSE +0 -0
- {zrb-0.0.51.dist-info → zrb-0.0.53.dist-info}/WHEEL +0 -0
- {zrb-0.0.51.dist-info → zrb-0.0.53.dist-info}/entry_points.txt +0 -0
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/model.py
CHANGED
@@ -4,8 +4,10 @@ from module.auth.schema.user import (
|
|
4
4
|
User, UserData, UserResult, UserLogin
|
5
5
|
)
|
6
6
|
from module.auth.entity.user.repo import UserRepo
|
7
|
-
from module.auth.schema.token import
|
8
|
-
|
7
|
+
from module.auth.schema.token import (
|
8
|
+
AccessTokenData, RefreshTokenData, TokenResponse
|
9
|
+
)
|
10
|
+
from module.auth.core import AccessTokenUtil, RefreshTokenUtil
|
9
11
|
from module.auth.entity.permission.model import PermissionModel
|
10
12
|
|
11
13
|
|
@@ -18,16 +20,20 @@ class UserModel(
|
|
18
20
|
self,
|
19
21
|
repo: UserRepo,
|
20
22
|
permission_model: PermissionModel,
|
21
|
-
|
22
|
-
|
23
|
+
access_token_util: AccessTokenUtil,
|
24
|
+
access_token_expire_seconds: int,
|
25
|
+
refresh_token_util: RefreshTokenUtil,
|
26
|
+
refresh_token_expire_seconds: int,
|
23
27
|
guest_user: User,
|
24
28
|
admin_user: Optional[User] = None,
|
25
29
|
admin_user_password: str = ''
|
26
30
|
):
|
27
31
|
self.repo = repo
|
28
32
|
self.permission_model = permission_model
|
29
|
-
self.
|
30
|
-
self.
|
33
|
+
self.access_token_util = access_token_util
|
34
|
+
self.access_token_expire_seconds = access_token_expire_seconds
|
35
|
+
self.refresh_token_util = refresh_token_util
|
36
|
+
self.refresh_token_expire_seconds = refresh_token_expire_seconds
|
31
37
|
self.guest_user = guest_user
|
32
38
|
self.admin_user = admin_user
|
33
39
|
self.admin_user_pasword = admin_user_password
|
@@ -116,21 +122,44 @@ class UserModel(
|
|
116
122
|
return False
|
117
123
|
return id == self.admin_user.id
|
118
124
|
|
119
|
-
async def
|
125
|
+
async def create_auth_token(self, user_login: UserLogin) -> TokenResponse:
|
120
126
|
user = await self._get_user_by_user_login(user_login)
|
121
|
-
return
|
127
|
+
return TokenResponse(
|
128
|
+
access_token=self._get_access_token(user),
|
129
|
+
refresh_token=self._get_refresh_token(user),
|
130
|
+
token_type='bearer'
|
131
|
+
)
|
122
132
|
|
123
|
-
async def
|
124
|
-
|
125
|
-
|
133
|
+
async def refresh_auth_token(
|
134
|
+
self, refresh_token: str, access_token: str
|
135
|
+
) -> TokenResponse:
|
136
|
+
access_token_data = self.access_token_util.decode(
|
137
|
+
access_token, parse_expired_token=True
|
138
|
+
)
|
139
|
+
refresh_token_data = self.refresh_token_util.decode(refresh_token)
|
140
|
+
if access_token_data.user_id != refresh_token_data.user_id:
|
141
|
+
raise ValueError('Unmatch refresh and access token')
|
142
|
+
user = await self.get_by_id(refresh_token_data.user_id)
|
143
|
+
return TokenResponse(
|
144
|
+
access_token=self._get_access_token(user),
|
145
|
+
refresh_token=self._get_refresh_token(user),
|
146
|
+
token_type='bearer'
|
147
|
+
)
|
126
148
|
|
127
|
-
def
|
128
|
-
|
149
|
+
def _get_access_token(self, user: User) -> str:
|
150
|
+
access_token_data = AccessTokenData(
|
129
151
|
user_id=user.id,
|
130
152
|
username=user.username,
|
131
|
-
expire_seconds=self.
|
153
|
+
expire_seconds=self.access_token_expire_seconds
|
132
154
|
)
|
133
|
-
return self.
|
155
|
+
return self.access_token_util.encode(access_token_data)
|
156
|
+
|
157
|
+
def _get_refresh_token(self, user: User) -> str:
|
158
|
+
refresh_token_data = RefreshTokenData(
|
159
|
+
user_id=user.id,
|
160
|
+
expire_seconds=self.refresh_token_expire_seconds
|
161
|
+
)
|
162
|
+
return self.refresh_token_util.encode(refresh_token_data)
|
134
163
|
|
135
164
|
async def _get_user_by_user_login(self, user_login: UserLogin) -> User:
|
136
165
|
if user_login.identity == '':
|
@@ -146,7 +175,3 @@ class UserModel(
|
|
146
175
|
):
|
147
176
|
return self.admin_user
|
148
177
|
return await self.repo.get_by_user_login(user_login)
|
149
|
-
|
150
|
-
async def _get_user_by_token(self, token: str) -> User:
|
151
|
-
token_data = self.token_util.decode(token)
|
152
|
-
return await self.get_by_id(token_data.user_id)
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/entity/user/rpc.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
from typing import Any, Mapping
|
1
|
+
from typing import Any, Mapping, List, Union
|
2
2
|
from logging import Logger
|
3
3
|
from core.messagebus import Publisher
|
4
4
|
from core.rpc import Caller, Server
|
5
5
|
from core.repo import SearchFilter
|
6
6
|
from module.auth.component.model.user_model import user_model
|
7
7
|
from module.auth.schema.user import UserData, UserLogin
|
8
|
-
from module.auth.schema.token import
|
8
|
+
from module.auth.schema.token import AccessTokenData
|
9
9
|
|
10
10
|
|
11
11
|
def register_rpc(
|
@@ -32,20 +32,28 @@ def register_rpc(
|
|
32
32
|
|
33
33
|
@rpc_server.register('auth_is_user_authorized')
|
34
34
|
async def is_user_having_permission(
|
35
|
-
id: str,
|
35
|
+
id: str, permission_name: Union[str, List[str]]
|
36
36
|
) -> Mapping[str, bool]:
|
37
37
|
'''
|
38
38
|
Used by RPC Authenticator
|
39
39
|
'''
|
40
|
-
|
40
|
+
if isinstance(permission_name, str):
|
41
|
+
return await user_model.is_authorized(id, permission_name)
|
42
|
+
return await user_model.is_authorized(id, *permission_name)
|
41
43
|
|
42
44
|
@rpc_server.register('auth_create_token')
|
43
|
-
async def create_token(login_data: Mapping[str, str]) -> str:
|
44
|
-
|
45
|
+
async def create_token(login_data: Mapping[str, str]) -> Mapping[str, str]:
|
46
|
+
result = await user_model.create_auth_token(UserLogin(**login_data))
|
47
|
+
return result.dict()
|
45
48
|
|
46
49
|
@rpc_server.register('auth_refresh_token')
|
47
|
-
async def refresh_token(
|
48
|
-
|
50
|
+
async def refresh_token(
|
51
|
+
refresh_token: str, access_token: str
|
52
|
+
) -> Mapping[str, str]:
|
53
|
+
result = await user_model.refresh_auth_token(
|
54
|
+
refresh_token, access_token
|
55
|
+
)
|
56
|
+
return result.dict()
|
49
57
|
|
50
58
|
@rpc_server.register('auth_get_user')
|
51
59
|
async def get(
|
@@ -77,7 +85,7 @@ def register_rpc(
|
|
77
85
|
data: Mapping[str, Any],
|
78
86
|
user_token_data: Mapping[str, Any]
|
79
87
|
) -> Mapping[str, Any]:
|
80
|
-
user_token_data =
|
88
|
+
user_token_data = AccessTokenData(**user_token_data)
|
81
89
|
data['created_by'] = user_token_data.user_id
|
82
90
|
data['updated_by'] = user_token_data.user_id
|
83
91
|
row = await user_model.insert(
|
@@ -91,7 +99,7 @@ def register_rpc(
|
|
91
99
|
data: Mapping[str, Any],
|
92
100
|
user_token_data: Mapping[str, Any]
|
93
101
|
) -> Mapping[str, Any]:
|
94
|
-
user_token_data =
|
102
|
+
user_token_data = AccessTokenData(**user_token_data)
|
95
103
|
data['updated_by'] = user_token_data.user_id
|
96
104
|
row = await user_model.update(
|
97
105
|
id=id, data=UserData(**data)
|
@@ -103,6 +111,6 @@ def register_rpc(
|
|
103
111
|
id: str,
|
104
112
|
user_token_data: Mapping[str, Any]
|
105
113
|
) -> Mapping[str, Any]:
|
106
|
-
user_token_data =
|
114
|
+
user_token_data = AccessTokenData(**user_token_data)
|
107
115
|
row = await user_model.delete(id=id)
|
108
116
|
return row.dict()
|
@@ -1,12 +1,18 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
2
|
|
3
3
|
|
4
|
-
class
|
4
|
+
class AccessTokenData(BaseModel):
|
5
5
|
user_id: str
|
6
6
|
username: str
|
7
7
|
expire_seconds: int
|
8
8
|
|
9
9
|
|
10
|
+
class RefreshTokenData(BaseModel):
|
11
|
+
user_id: str
|
12
|
+
expire_seconds: int
|
13
|
+
|
14
|
+
|
10
15
|
class TokenResponse(BaseModel):
|
11
16
|
access_token: str
|
17
|
+
refresh_token: str
|
12
18
|
token_type: str
|
@@ -7,10 +7,14 @@ APP_MAX_NOT_READY=10
|
|
7
7
|
|
8
8
|
PUBLIC_BRAND=PascalAppName
|
9
9
|
PUBLIC_TITLE=PascalAppName
|
10
|
-
|
10
|
+
PUBLIC_AUTH_ACCESS_TOKEN_COOKIE_KEY=access_token
|
11
|
+
PUBLIC_AUTH_REFRESH_TOKEN_COOKIE_KEY=refresh_token
|
12
|
+
|
13
|
+
APP_AUTH_ACCESS_TOKEN_EXPIRE_SECONDS=300
|
14
|
+
APP_AUTH_ACCESS_TOKEN_TYPE=jwt
|
15
|
+
APP_AUTH_REFRESH_TOKEN_EXPIRE_SECONDS=21600
|
16
|
+
APP_AUTH_REFRESH_TOKEN_TYPE=jwt
|
11
17
|
|
12
|
-
APP_AUTH_TOKEN_EXPIRE_SECONDS=300
|
13
|
-
APP_AUTH_TOKEN_TYPE=jwt
|
14
18
|
APP_AUTH_JWT_TOKEN_SECRET_KEY=Alch3mist
|
15
19
|
APP_AUTH_JWT_TOKEN_ALGORITHM=HS256
|
16
20
|
|
@@ -5,15 +5,16 @@ from ....task.decorator import python_task
|
|
5
5
|
from ....task.cmd_task import CmdTask
|
6
6
|
from ....task.resource_maker import ResourceMaker
|
7
7
|
from ....runner import runner
|
8
|
+
from ....helper import util
|
9
|
+
from ....helper.codemod.add_import_module import add_import_module
|
10
|
+
from ....helper.codemod.append_code_to_function import append_code_to_function
|
11
|
+
from ....helper.file.text import read_text_file_async, write_text_file_async
|
8
12
|
from .._common.input import (
|
9
13
|
project_dir_input, app_name_input, module_name_input, entity_name_input,
|
10
14
|
plural_entity_name_input, main_column_name_input
|
11
15
|
)
|
12
16
|
from .._common.helper import validate_project_dir
|
13
|
-
from
|
14
|
-
from ....helper.codemod.add_import_module import add_import_module
|
15
|
-
from ....helper.codemod.append_code_to_function import append_code_to_function
|
16
|
-
from ....helper.file.text import read_text_file_async, write_text_file_async
|
17
|
+
from .add_navigation import create_add_navigation_task
|
17
18
|
|
18
19
|
import asyncio
|
19
20
|
import os
|
@@ -128,26 +129,11 @@ prepare_codemod = CmdTask(
|
|
128
129
|
]
|
129
130
|
)
|
130
131
|
|
131
|
-
|
132
|
-
nav_file_path = '{{input.project_dir}}/src/{{util.to_kebab_case(input.app_name)}}/src/frontend/src/lib/config/navData.ts' # noqa
|
133
|
-
var_name = 'navData'
|
134
|
-
title = '{{util.to_pascal_case(input.entity_name)}}'
|
135
|
-
url = '{{util.to_kebab_case(input.module_name)}}/{{util.to_kebab_case(input.entity_name)}}' # noqa
|
136
|
-
permission = ''
|
137
|
-
add_navigation = CmdTask(
|
138
|
-
name='add-navigation',
|
139
|
-
inputs=[
|
140
|
-
project_dir_input,
|
141
|
-
app_name_input,
|
142
|
-
module_name_input,
|
143
|
-
entity_name_input,
|
144
|
-
],
|
132
|
+
add_navigation = create_add_navigation_task(
|
145
133
|
upstreams=[
|
146
134
|
copy_resource,
|
147
135
|
prepare_codemod,
|
148
|
-
]
|
149
|
-
retry=0,
|
150
|
-
cmd=f'node {codemod_dir}/dist/addNav.js "{nav_file_path}" "{var_name}" "{title}" "{url}" "{permission}"' # noqa
|
136
|
+
]
|
151
137
|
)
|
152
138
|
|
153
139
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
from typing import List
|
2
|
+
from ....task.task import Task
|
3
|
+
from ....task.cmd_task import CmdTask
|
4
|
+
from .._common.input import (
|
5
|
+
project_dir_input, app_name_input, module_name_input, entity_name_input,
|
6
|
+
)
|
7
|
+
|
8
|
+
import os
|
9
|
+
|
10
|
+
current_dir = os.path.dirname(__file__)
|
11
|
+
codemod_dir = os.path.join(current_dir, 'nodejs', 'codemod')
|
12
|
+
|
13
|
+
nav_config_file_path = '{{input.project_dir}}/src/{{util.to_kebab_case(input.app_name)}}/src/frontend/src/lib/config/navData.ts' # noqa
|
14
|
+
nav_var_name = 'navData'
|
15
|
+
nav_title = '{{util.to_pascal_case(input.entity_name)}}'
|
16
|
+
nav_url = '/{{util.to_kebab_case(input.module_name)}}/{{util.to_kebab_case(input.entity_name)}}' # noqa
|
17
|
+
nav_permission = '{{util.to_snake_case(input.module_name)}}:{{util.to_snake_case(input.entity_name)}}:get' # noqa
|
18
|
+
|
19
|
+
|
20
|
+
def create_add_navigation_task(upstreams: List[Task]) -> Task:
|
21
|
+
return CmdTask(
|
22
|
+
name='add-navigation',
|
23
|
+
inputs=[
|
24
|
+
project_dir_input,
|
25
|
+
app_name_input,
|
26
|
+
module_name_input,
|
27
|
+
entity_name_input,
|
28
|
+
],
|
29
|
+
upstreams=upstreams,
|
30
|
+
retry=0,
|
31
|
+
cmd=f'node {codemod_dir}/dist/addNav.js "{nav_config_file_path}" "{nav_var_name}" "{nav_title}" "{nav_url}" "{nav_permission}"' # noqa
|
32
|
+
)
|
@@ -0,0 +1,121 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import axios from 'axios';
|
3
|
+
import { onMount } from 'svelte';
|
4
|
+
import { ensureAccessToken } from '$lib/auth/helper';
|
5
|
+
import { getErrorMessage } from '$lib/error/helper';
|
6
|
+
|
7
|
+
let limit: number = 5;
|
8
|
+
let pageIndex: number = 0;
|
9
|
+
let count: number = 0;
|
10
|
+
let keyword: string = '';
|
11
|
+
let isAlertVisible: boolean = false;
|
12
|
+
let errorMessage: string = '';
|
13
|
+
|
14
|
+
let limitOptions: Array<number> = [5, 10, 30, 50, 100];
|
15
|
+
let pageIndexes: Array<number> = [];
|
16
|
+
let rows: Array<any> = [];
|
17
|
+
|
18
|
+
onMount(async() => {
|
19
|
+
await loadRows();
|
20
|
+
});
|
21
|
+
|
22
|
+
async function loadRows() {
|
23
|
+
const accessToken = await ensureAccessToken();
|
24
|
+
try {
|
25
|
+
const encodedKeyword = encodeURIComponent(keyword);
|
26
|
+
const offset = limit * pageIndex
|
27
|
+
const response = await axios.get(
|
28
|
+
`/api/v1/kebab-module-name/kebab-plural-entity-name?limit=${limit}&offset=${offset}&keyword=${encodedKeyword}`,
|
29
|
+
{headers: {Authorization: `Bearer ${accessToken}`}}
|
30
|
+
);
|
31
|
+
if (response?.status == 200 && response?.data) {
|
32
|
+
count = response.data.count;
|
33
|
+
rows = response.data.data;
|
34
|
+
pageIndexes = [];
|
35
|
+
for (let i = 0; i*limit < count; i++) {
|
36
|
+
pageIndexes.push(i);
|
37
|
+
}
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
errorMessage = 'Unknown error';
|
41
|
+
} catch(error) {
|
42
|
+
console.error(error);
|
43
|
+
errorMessage = getErrorMessage(error);
|
44
|
+
}
|
45
|
+
isAlertVisible = true;
|
46
|
+
}
|
47
|
+
|
48
|
+
async function onPaginationClick(index: number) {
|
49
|
+
pageIndex = index;
|
50
|
+
await loadRows();
|
51
|
+
}
|
52
|
+
</script>
|
53
|
+
|
54
|
+
<h1 class="text-3xl">Book</h1>
|
55
|
+
<div class="overflow-x-auto">
|
56
|
+
|
57
|
+
<div class="flex items-center mb-5 mt-5">
|
58
|
+
<label for="limit" class="mr-2">Row per page:</label>
|
59
|
+
<select id="limit" class="select select-bordered mr-2" bind:value={limit} on:change={loadRows}>
|
60
|
+
{#each limitOptions as limitOption}
|
61
|
+
{#if limitOption == limit }
|
62
|
+
<option selected>{limitOption}</option>
|
63
|
+
{:else}
|
64
|
+
<option>{limitOption}</option>
|
65
|
+
{/if}
|
66
|
+
{/each}
|
67
|
+
</select>
|
68
|
+
<input type="text" placeholder="Search..." class="input input-bordered mr-2" bind:value={keyword} />
|
69
|
+
<button class="btn btn-primary btn-square" on:click={loadRows}>
|
70
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
71
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
72
|
+
</svg>
|
73
|
+
</button>
|
74
|
+
<a class="btn ml-5" href="./new">New</a>
|
75
|
+
</div>
|
76
|
+
|
77
|
+
<div class="alert alert-error shadow-lg mt-5 mb-5 {isAlertVisible? 'visible': 'hidden'}">
|
78
|
+
<div>
|
79
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
80
|
+
<span>{errorMessage}</span>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
<table class="table w-full">
|
85
|
+
<!-- head -->
|
86
|
+
<thead>
|
87
|
+
<tr>
|
88
|
+
<th></th>
|
89
|
+
<th>Human readable column name</th>
|
90
|
+
<!-- DON'T DELETE: insert new column header here-->
|
91
|
+
<th></th>
|
92
|
+
</tr>
|
93
|
+
</thead>
|
94
|
+
<tbody>
|
95
|
+
{#each rows as row}
|
96
|
+
<tr>
|
97
|
+
<th>{row.id}</th>
|
98
|
+
<td>{row.snake_column_name}</td>
|
99
|
+
<!-- DON'T DELETE: insert new column here-->
|
100
|
+
<td>
|
101
|
+
<a class="btn" href="./detail/{row.id}">Detail</a>
|
102
|
+
<a class="btn" href="./update/{row.id}">Update</a>
|
103
|
+
<a class="btn btn-accent" href="./delete/{row.id}">Delete</a>
|
104
|
+
</td>
|
105
|
+
</tr>
|
106
|
+
{/each}
|
107
|
+
</tbody>
|
108
|
+
</table>
|
109
|
+
|
110
|
+
<div class="btn-group justify-center w-full">
|
111
|
+
{#each pageIndexes as pageIndexOption}
|
112
|
+
{#if pageIndexOption == pageIndex}
|
113
|
+
<button class="btn btn-disabled">{pageIndexOption + 1}</button>
|
114
|
+
{:else}
|
115
|
+
<button class="btn" on:click={async () => await onPaginationClick(pageIndexOption)}>
|
116
|
+
{pageIndexOption + 1}
|
117
|
+
</button>
|
118
|
+
{/if}
|
119
|
+
{/each}
|
120
|
+
</div>
|
121
|
+
</div>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import axios from 'axios';
|
3
|
+
import { onMount } from 'svelte';
|
4
|
+
import { goto } from '$app/navigation';
|
5
|
+
import { ensureAccessToken } from '$lib/auth/helper';
|
6
|
+
import { getErrorMessage } from '$lib/error/helper';
|
7
|
+
|
8
|
+
export let data: {id?: string} = {};
|
9
|
+
|
10
|
+
let row: any = {};
|
11
|
+
let isAlertVisible: boolean = false;
|
12
|
+
let errorMessage: string = '';
|
13
|
+
|
14
|
+
onMount(async() => {
|
15
|
+
await loadRow();
|
16
|
+
});
|
17
|
+
|
18
|
+
async function loadRow() {
|
19
|
+
const accessToken = await ensureAccessToken();
|
20
|
+
try {
|
21
|
+
const response = await axios.get(
|
22
|
+
`/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
|
23
|
+
{headers: {Authorization: `Bearer ${accessToken}`}}
|
24
|
+
);
|
25
|
+
if (response?.status == 200 && response?.data) {
|
26
|
+
row = response.data;
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
errorMessage = 'Unknown error';
|
30
|
+
} catch(error) {
|
31
|
+
console.error(error);
|
32
|
+
errorMessage = getErrorMessage(error);
|
33
|
+
}
|
34
|
+
isAlertVisible = true;
|
35
|
+
}
|
36
|
+
|
37
|
+
async function onDeleteClick() {
|
38
|
+
const accessToken = await ensureAccessToken();
|
39
|
+
try {
|
40
|
+
const response = await axios.delete(
|
41
|
+
`/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
|
42
|
+
{headers: {Authorization: `Bearer ${accessToken}`}}
|
43
|
+
);
|
44
|
+
if (response?.status == 200 && response?.data) {
|
45
|
+
await goto('../../');
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
errorMessage = 'Unknown error';
|
49
|
+
} catch(error) {
|
50
|
+
console.error(error);
|
51
|
+
errorMessage = getErrorMessage(error);
|
52
|
+
}
|
53
|
+
isAlertVisible = true;
|
54
|
+
}
|
55
|
+
</script>
|
56
|
+
<h1 class="text-3xl">Book</h1>
|
57
|
+
|
58
|
+
<form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
|
59
|
+
<h2 class="text-xl font-bold mb-4">Delete Book {data.id}</h2>
|
60
|
+
<div class="mb-4">
|
61
|
+
<label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
|
62
|
+
<span id="kebab-column-name">{row.snake_column_name}</span>
|
63
|
+
</div>
|
64
|
+
<!-- DON'T DELETE: insert new field here-->
|
65
|
+
<a href="#top" class="btn btn-accent" on:click={onDeleteClick}>Delete</a>
|
66
|
+
<a href="../../" class="btn">Cancel</a>
|
67
|
+
|
68
|
+
<div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
|
69
|
+
<div>
|
70
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
71
|
+
<span>{errorMessage}</span>
|
72
|
+
</div>
|
73
|
+
</div>
|
74
|
+
|
75
|
+
</form>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import axios from 'axios';
|
3
|
+
import { onMount } from 'svelte';
|
4
|
+
import { ensureAccessToken } from '$lib/auth/helper';
|
5
|
+
import { getErrorMessage } from '$lib/error/helper';
|
6
|
+
|
7
|
+
export let data: {id?: string} = {};
|
8
|
+
|
9
|
+
let row: any = {};
|
10
|
+
let isAlertVisible: boolean = false;
|
11
|
+
let errorMessage: string = '';
|
12
|
+
|
13
|
+
onMount(async() => {
|
14
|
+
await loadRow();
|
15
|
+
});
|
16
|
+
|
17
|
+
async function loadRow() {
|
18
|
+
const accessToken = await ensureAccessToken();
|
19
|
+
try {
|
20
|
+
const response = await axios.get(
|
21
|
+
`/api/v1/kebab-module-name/kebab-plural-entity-name/${data.id}`,
|
22
|
+
{headers: {Authorization: `Bearer ${accessToken}`}}
|
23
|
+
);
|
24
|
+
if (response?.status == 200 && response?.data) {
|
25
|
+
row = response.data;
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
errorMessage = 'Unknown error';
|
29
|
+
} catch(error) {
|
30
|
+
console.error(error);
|
31
|
+
errorMessage = getErrorMessage(error);
|
32
|
+
}
|
33
|
+
isAlertVisible = true;
|
34
|
+
}
|
35
|
+
</script>
|
36
|
+
<h1 class="text-3xl">Book</h1>
|
37
|
+
|
38
|
+
<form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
|
39
|
+
<h2 class="text-xl font-bold mb-4">Show Book {data.id}</h2>
|
40
|
+
<div class="mb-4">
|
41
|
+
<label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
|
42
|
+
<span id="kebab-column-name">{row.snake_column_name}</span>
|
43
|
+
</div>
|
44
|
+
<!-- DON'T DELETE: insert new field here-->
|
45
|
+
<a href="../../" class="btn btn-primary">Show others</a>
|
46
|
+
|
47
|
+
<div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
|
48
|
+
<div>
|
49
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
50
|
+
<span>{errorMessage}</span>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
</form>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import axios from 'axios';
|
3
|
+
import { goto } from '$app/navigation';
|
4
|
+
import { ensureAccessToken } from '$lib/auth/helper';
|
5
|
+
import { getErrorMessage } from '$lib/error/helper';
|
6
|
+
|
7
|
+
let row: any = {}
|
8
|
+
let isAlertVisible: boolean = false;
|
9
|
+
let isSaving: boolean = false;
|
10
|
+
let errorMessage: string = '';
|
11
|
+
|
12
|
+
async function onSaveClick() {
|
13
|
+
isSaving = true
|
14
|
+
const accessToken = await ensureAccessToken();
|
15
|
+
try {
|
16
|
+
const response = await axios.post(
|
17
|
+
'/api/v1/kebab-module-name/kebab-plural-entity-name', row, {headers: {Authorization: `Bearer ${accessToken}`}}
|
18
|
+
);
|
19
|
+
if (response?.status == 200) {
|
20
|
+
await goto('../');
|
21
|
+
return;
|
22
|
+
}
|
23
|
+
errorMessage = 'Unknown error';
|
24
|
+
} catch(error) {
|
25
|
+
console.error(error);
|
26
|
+
errorMessage = getErrorMessage(error);
|
27
|
+
}
|
28
|
+
isAlertVisible = true;
|
29
|
+
isSaving = false;
|
30
|
+
}
|
31
|
+
</script>
|
32
|
+
|
33
|
+
<h1 class="text-3xl">Book</h1>
|
34
|
+
|
35
|
+
<form class="max-w-md mx-auto bg-gray-100 p-6 rounded-md mt-5 mb-5">
|
36
|
+
<h2 class="text-xl font-bold mb-4">New Book</h2>
|
37
|
+
<div class="mb-4">
|
38
|
+
<label class="block text-gray-700 font-bold mb-2" for="kebab-column-name">Human readable column name</label>
|
39
|
+
<input type="text" class="input w-full" id="kebab-column-name" placeholder="Human readable column name" bind:value={row.snake_column_name}>
|
40
|
+
</div>
|
41
|
+
<!-- DON'T DELETE: insert new field here-->
|
42
|
+
<a href="#top" class="btn btn-primary {isSaving ? 'btn-disabled': '' }" on:click={onSaveClick}>Save</a>
|
43
|
+
<a href="../" class="btn">Cancel</a>
|
44
|
+
|
45
|
+
<div class="alert alert-error shadow-lg mt-5 {isAlertVisible? 'visible': 'hidden'}">
|
46
|
+
<div>
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
48
|
+
<span>{errorMessage}</span>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
</form>
|