zrb 0.0.52__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/template/_automate/snake_app_name/cmd/start.sh +1 -1
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/.gitignore +1 -1
- 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 +16 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/package.json +2 -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 +0 -0
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/type.ts +3 -3
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Menu.svelte +19 -16
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/components/navigation/Navigation.svelte +14 -11
- 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 +4 -3
- 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 +25 -15
- 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 +2 -2
- 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/builtin/generator/project/template/.gitignore +1 -1
- zrb/builtin/generator/simple_python_app/template/src/kebab-app-name/src/.gitignore +1 -1
- zrb/helper/util.py +4 -0
- zrb/task/resource_maker.py +2 -1
- {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/METADATA +3 -3
- {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/RECORD +64 -56
- zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/auth.ts +0 -83
- 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/lib/cookie/cookie.ts +0 -19
- 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.52.dist-info → zrb-0.0.53.dist-info}/LICENSE +0 -0
- {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/WHEEL +0 -0
- {zrb-0.0.52.dist-info → zrb-0.0.53.dist-info}/entry_points.txt +0 -0
@@ -3,4 +3,4 @@ echo "Activate venv"
|
|
3
3
|
source venv/bin/activate
|
4
4
|
|
5
5
|
echo "Start app"
|
6
|
-
uvicorn main:app --host {{env.get("APP_HOST", "0.0.0.0")}} --port {{env.get("APP_PORT", "8080")}} {{ "--reload" if util.to_boolean(env.get("APP_RELOAD", "true")) else "" }}
|
6
|
+
uvicorn main:app --host {{env.get("APP_HOST", "0.0.0.0")}} --port {{env.get("APP_PORT", "8080")}} {{ "--reload --reload-include index.html" if util.to_boolean(env.get("APP_RELOAD", "true")) else "" }}
|
@@ -13,13 +13,22 @@ app_host = os.getenv('APP_HOST', '0.0.0.0')
|
|
13
13
|
app_port = int(os.getenv('APP_PORT', '8080'))
|
14
14
|
app_reload = str_to_boolean(os.getenv('APP_RELOAD', 'true'))
|
15
15
|
app_max_not_ready = int(os.getenv('APP_MAX_NOT_READY', '10'))
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
app_auth_access_token_cookie_key = os.getenv(
|
18
|
+
'PUBLIC_AUTH_ACCESS_TOKEN_COOKIE_KEY', 'access_token'
|
19
|
+
)
|
20
|
+
app_auth_refresh_token_cookie_key = os.getenv(
|
21
|
+
'PUBLIC_AUTH_REFRESH_TOKEN_COOKIE_KEY', 'refresh_token'
|
18
22
|
)
|
19
|
-
|
20
|
-
|
23
|
+
|
24
|
+
app_auth_access_token_type = os.getenv('APP_AUTH_ACCESS_TOKEN_TYPE', 'jwt')
|
25
|
+
app_auth_access_token_expire_seconds = int(os.getenv(
|
26
|
+
'APP_AUTH_ACCESS_TOKEN_EXPIRE_SECONDS', '300'
|
27
|
+
))
|
28
|
+
app_auth_refresh_token_type = os.getenv('APP_AUTH_REFRESH_TOKEN_TYPE', 'jwt')
|
29
|
+
app_auth_refresh_token_expire_seconds = int(os.getenv(
|
30
|
+
'APP_AUTH_REFRESH_TOKEN_EXPIRE_SECONDS', '86400'
|
21
31
|
))
|
22
|
-
app_auth_token_type = os.getenv('APP_AUTH_TOKEN_TYPE', 'jwt')
|
23
32
|
app_auth_jwt_token_secret_key = os.getenv(
|
24
33
|
'APP_AUTH_JWT_TOKEN_SECRET_KEY', 'secret'
|
25
34
|
)
|
File without changes
|
@@ -10,6 +10,7 @@
|
|
10
10
|
"dependencies": {
|
11
11
|
"axios": "^1.4.0",
|
12
12
|
"daisyui": "^2.51.6",
|
13
|
+
"js-cookie": "^3.0.5",
|
13
14
|
"jwt-decode": "^3.1.2",
|
14
15
|
"process": "^0.11.10"
|
15
16
|
},
|
@@ -18,6 +19,7 @@
|
|
18
19
|
"@sveltejs/adapter-auto": "^2.0.0",
|
19
20
|
"@sveltejs/adapter-static": "^2.0.1",
|
20
21
|
"@sveltejs/kit": "^1.5.0",
|
22
|
+
"@types/js-cookie": "^3.0.3",
|
21
23
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
22
24
|
"@typescript-eslint/parser": "^5.45.0",
|
23
25
|
"autoprefixer": "^10.4.14",
|
@@ -352,6 +354,12 @@
|
|
352
354
|
"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==",
|
353
355
|
"dev": true
|
354
356
|
},
|
357
|
+
"node_modules/@types/js-cookie": {
|
358
|
+
"version": "3.0.3",
|
359
|
+
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.3.tgz",
|
360
|
+
"integrity": "sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==",
|
361
|
+
"dev": true
|
362
|
+
},
|
355
363
|
"node_modules/@types/json-schema": {
|
356
364
|
"version": "7.0.11",
|
357
365
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
@@ -1851,6 +1859,14 @@
|
|
1851
1859
|
"jiti": "bin/jiti.js"
|
1852
1860
|
}
|
1853
1861
|
},
|
1862
|
+
"node_modules/js-cookie": {
|
1863
|
+
"version": "3.0.5",
|
1864
|
+
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
1865
|
+
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
1866
|
+
"engines": {
|
1867
|
+
"node": ">=14"
|
1868
|
+
}
|
1869
|
+
},
|
1854
1870
|
"node_modules/js-sdsl": {
|
1855
1871
|
"version": "4.4.0",
|
1856
1872
|
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
|
@@ -19,6 +19,7 @@
|
|
19
19
|
"@sveltejs/adapter-auto": "^2.0.0",
|
20
20
|
"@sveltejs/adapter-static": "^2.0.1",
|
21
21
|
"@sveltejs/kit": "^1.5.0",
|
22
|
+
"@types/js-cookie": "^3.0.3",
|
22
23
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
23
24
|
"@typescript-eslint/parser": "^5.45.0",
|
24
25
|
"autoprefixer": "^10.4.14",
|
@@ -40,6 +41,7 @@
|
|
40
41
|
"dependencies": {
|
41
42
|
"axios": "^1.4.0",
|
42
43
|
"daisyui": "^2.51.6",
|
44
|
+
"js-cookie": "^3.0.5",
|
43
45
|
"jwt-decode": "^3.1.2",
|
44
46
|
"process": "^0.11.10"
|
45
47
|
}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,146 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
import jwt_decode from 'jwt-decode';
|
3
|
+
import { authAccessTokenCookieKey, authRefreshTokenCookieKey, isAuthorizedApiUrl, loginApiUrl, refreshTokenApiUrl } from '../config/app';
|
4
|
+
import { userIdStore, userNameStore } from './store';
|
5
|
+
import type { AccessTokenData } from './type';
|
6
|
+
import Cookies from 'js-cookie';
|
7
|
+
|
8
|
+
|
9
|
+
export async function getAuthorization(permissions: string[]): Promise<{[key: string]: boolean}> {
|
10
|
+
try {
|
11
|
+
const accessToken = await ensureAccessToken();
|
12
|
+
if (accessToken == '') {
|
13
|
+
return {};
|
14
|
+
}
|
15
|
+
const response = await axios.post(
|
16
|
+
isAuthorizedApiUrl,
|
17
|
+
{permission_names: permissions},
|
18
|
+
{
|
19
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
20
|
+
}
|
21
|
+
);
|
22
|
+
if (response && response.status == 200 && response.data) {
|
23
|
+
return response.data;
|
24
|
+
}
|
25
|
+
throw new Error('Invalid response');
|
26
|
+
} catch(error) {
|
27
|
+
console.error(error);
|
28
|
+
}
|
29
|
+
return {};
|
30
|
+
}
|
31
|
+
|
32
|
+
export async function initAuthStore() {
|
33
|
+
try {
|
34
|
+
const accessToken = await ensureAccessToken();
|
35
|
+
if (accessToken) {
|
36
|
+
setAuthStoreByAccessToken(accessToken);
|
37
|
+
}
|
38
|
+
} catch(error) {
|
39
|
+
console.error(error);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export async function ensureAccessToken(): Promise<string> {
|
44
|
+
try {
|
45
|
+
const oldAccessToken = getOldAccessToken();
|
46
|
+
if (oldAccessToken) {
|
47
|
+
const oldAccessTokenData: AccessTokenData = decodeAccessToken(oldAccessToken);
|
48
|
+
const { expireAt } = oldAccessTokenData;
|
49
|
+
const now = new Date();
|
50
|
+
if (now.getTime()/1000 < expireAt) {
|
51
|
+
return oldAccessToken;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
const oldRefreshToken = getOldRefreshToken();
|
55
|
+
if (oldRefreshToken) {
|
56
|
+
const response = await axios.post(
|
57
|
+
refreshTokenApiUrl,
|
58
|
+
{access_token: oldAccessToken},
|
59
|
+
{
|
60
|
+
headers: { Authorization: `Bearer ${oldRefreshToken}` }
|
61
|
+
}
|
62
|
+
);
|
63
|
+
if (response && response.status == 200 && response.data && response.data.access_token && response.data.refresh_token) {
|
64
|
+
const newAccessToken: string = response.data.access_token;
|
65
|
+
const newRefreshToken: string = response.data.refresh_token;
|
66
|
+
saveToken(newAccessToken, newRefreshToken);
|
67
|
+
setAuthStoreByAccessToken(newAccessToken);
|
68
|
+
return newAccessToken;
|
69
|
+
}
|
70
|
+
throw new Error('Invalid refresh-token response');
|
71
|
+
}
|
72
|
+
throw new Error('Cannot refresh token');
|
73
|
+
} catch(error) {
|
74
|
+
logout();
|
75
|
+
throw(error);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
function getOldRefreshToken(): string | null {
|
80
|
+
return localStorage.getItem(authRefreshTokenCookieKey);
|
81
|
+
}
|
82
|
+
|
83
|
+
function getOldAccessToken(): string | undefined {
|
84
|
+
return Cookies.get(authAccessTokenCookieKey);
|
85
|
+
}
|
86
|
+
|
87
|
+
export async function login(identity: string, password: string): Promise<boolean> {
|
88
|
+
try {
|
89
|
+
const response = await axios.post(loginApiUrl, {identity, password});
|
90
|
+
if (response && response.status == 200 && response.data && response.data.access_token && response.data.refresh_token) {
|
91
|
+
const accessToken: string = response.data.access_token;
|
92
|
+
const refreshToken: string = response.data.refresh_token;
|
93
|
+
saveToken(accessToken, refreshToken);
|
94
|
+
setAuthStoreByAccessToken(accessToken);
|
95
|
+
return true;
|
96
|
+
}
|
97
|
+
} catch(error) {
|
98
|
+
console.error(error);
|
99
|
+
}
|
100
|
+
logout();
|
101
|
+
return false;
|
102
|
+
}
|
103
|
+
|
104
|
+
function saveToken(accessToken: string, refreshToken: string) {
|
105
|
+
Cookies.set(authAccessTokenCookieKey, accessToken);
|
106
|
+
localStorage.setItem(authRefreshTokenCookieKey, refreshToken);
|
107
|
+
}
|
108
|
+
|
109
|
+
export function logout() {
|
110
|
+
Cookies.remove(authAccessTokenCookieKey);
|
111
|
+
localStorage.removeItem(authRefreshTokenCookieKey);
|
112
|
+
unsetAuthStore();
|
113
|
+
}
|
114
|
+
|
115
|
+
function unsetAuthStore() {
|
116
|
+
setAuthStore('', '');
|
117
|
+
}
|
118
|
+
|
119
|
+
function setAuthStoreByAccessToken(accessToken: string) {
|
120
|
+
const tokenData = decodeAccessToken(accessToken);
|
121
|
+
setAuthStore(tokenData.sub.userId, tokenData.sub.userName);
|
122
|
+
}
|
123
|
+
|
124
|
+
function setAuthStore(newUserId: string, newUserName: string) {
|
125
|
+
userIdStore.set(newUserId);
|
126
|
+
userNameStore.set(newUserName);
|
127
|
+
}
|
128
|
+
|
129
|
+
function decodeAccessToken(accessToken: string): AccessTokenData {
|
130
|
+
const jwtTokenData: {
|
131
|
+
exp: number,
|
132
|
+
sub: {
|
133
|
+
user_id: string,
|
134
|
+
username: string,
|
135
|
+
expire_seconds: number
|
136
|
+
}
|
137
|
+
} = jwt_decode(accessToken);
|
138
|
+
return {
|
139
|
+
sub: {
|
140
|
+
userId: jwtTokenData.sub.user_id,
|
141
|
+
userName: jwtTokenData.sub.username,
|
142
|
+
expireSeconds: jwtTokenData.sub.expire_seconds,
|
143
|
+
},
|
144
|
+
expireAt: jwtTokenData.exp
|
145
|
+
}
|
146
|
+
}
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/auth/store.ts
CHANGED
File without changes
|
@@ -1,9 +1,9 @@
|
|
1
|
-
export interface
|
2
|
-
sub:
|
1
|
+
export interface AccessTokenData {
|
2
|
+
sub: AccessTokenDataSub;
|
3
3
|
expireAt: number;
|
4
4
|
}
|
5
5
|
|
6
|
-
export interface
|
6
|
+
export interface AccessTokenDataSub {
|
7
7
|
userId: string;
|
8
8
|
userName: string;
|
9
9
|
expireSeconds: number;
|
@@ -1,20 +1,23 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import type {SingleNavData} from './type';
|
3
|
-
export let
|
3
|
+
export let singleNavData: SingleNavData;
|
4
|
+
export let authorization: {[permission: string]: boolean} = {};
|
4
5
|
</script>
|
5
|
-
{#if
|
6
|
-
|
7
|
-
|
8
|
-
<
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
{#if (!singleNavData.permission || singleNavData.permission == '' || (authorization[singleNavData.permission]))}
|
7
|
+
{#if 'submenus' in singleNavData && singleNavData.submenus}
|
8
|
+
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
9
|
+
<li tabindex="0">
|
10
|
+
<a href="#top">
|
11
|
+
{singleNavData.title}
|
12
|
+
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"/></svg>
|
13
|
+
</a>
|
14
|
+
<ul class="p-2 bg-base-100">
|
15
|
+
{#each singleNavData.submenus as submenu}
|
16
|
+
<svelte:self singleNavData={submenu} authorization={authorization} />
|
17
|
+
{/each}
|
18
|
+
</ul>
|
19
|
+
</li>
|
20
|
+
{:else}
|
21
|
+
<li><a href={singleNavData.url} class="px-4">{singleNavData.title}</a></li>
|
22
|
+
{/if}
|
20
23
|
{/if}
|
@@ -3,31 +3,34 @@
|
|
3
3
|
import { onMount } from 'svelte';
|
4
4
|
import Menu from './Menu.svelte';
|
5
5
|
import type { SingleNavData } from './type';
|
6
|
-
import { login, logout
|
6
|
+
import { getAuthorization, initAuthStore, login, logout } from '../../auth/helper';
|
7
7
|
import { userIdStore } from '../../auth/store';
|
8
|
+
import { getNavDataPermissions } from './helper';
|
8
9
|
|
9
10
|
export let logo: string;
|
10
11
|
export let brand: string;
|
11
|
-
export let
|
12
|
+
export let navData: SingleNavData[];
|
12
13
|
export let loginTitle: string = 'Login';
|
13
14
|
export let logoutTitle: string = 'Logout';
|
14
|
-
|
15
|
-
|
15
|
+
|
16
|
+
const navDataPermissions = getNavDataPermissions(navData);
|
16
17
|
|
17
18
|
let identity: string;
|
18
19
|
let password: string;
|
19
|
-
|
20
20
|
let userId = '';
|
21
|
-
|
21
|
+
let authorization: {[key: string]: boolean} = {};
|
22
|
+
|
23
|
+
userIdStore.subscribe(async (value) => {
|
22
24
|
userId = value;
|
25
|
+
authorization = await getAuthorization(navDataPermissions);
|
23
26
|
});
|
24
27
|
|
25
28
|
onMount(async() => {
|
26
|
-
await
|
29
|
+
await initAuthStore();
|
27
30
|
});
|
28
31
|
|
29
32
|
async function onLoginClick() {
|
30
|
-
const loginSuccess = await login(
|
33
|
+
const loginSuccess = await login(identity, password);
|
31
34
|
if (loginSuccess) {
|
32
35
|
await goto('/');
|
33
36
|
return;
|
@@ -41,15 +44,15 @@
|
|
41
44
|
}
|
42
45
|
</script>
|
43
46
|
|
44
|
-
<div class="navbar sticky top-0 bg-base-100">
|
47
|
+
<div class="navbar sticky top-0 bg-base-100 z-50">
|
45
48
|
<div class="flex-1">
|
46
49
|
<img class="h-8 mr-3" src={logo} alt="Logo">
|
47
50
|
<a href="/" class="btn btn-ghost normal-case text-xl">{brand}</a>
|
48
51
|
</div>
|
49
52
|
<div class="flex-none">
|
50
53
|
<ul class="menu menu-horizontal px-1">
|
51
|
-
{#each
|
52
|
-
<Menu
|
54
|
+
{#each navData as singleNavData}
|
55
|
+
<Menu singleNavData={singleNavData} authorization={authorization} />
|
53
56
|
{/each}
|
54
57
|
{#if userId == ''}
|
55
58
|
<li><a href="#login-modal" class="px-4">{loginTitle}</a></li>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import type { SingleNavData } from "./type";
|
2
|
+
|
3
|
+
export function getNavDataPermissions(navData: SingleNavData[]): string[] {
|
4
|
+
let permissions: string[] = [];
|
5
|
+
for (const singleNavData of navData) {
|
6
|
+
if (singleNavData.permission && singleNavData.permission != '') {
|
7
|
+
permissions.push(singleNavData.permission);
|
8
|
+
}
|
9
|
+
if (singleNavData.submenus) {
|
10
|
+
const subPermissions: string[] = getNavDataPermissions(singleNavData.submenus);
|
11
|
+
const uniqueSubPermissions = subPermissions.filter((value) => {
|
12
|
+
return permissions.indexOf(value) === -1
|
13
|
+
});
|
14
|
+
permissions = permissions.concat(uniqueSubPermissions)
|
15
|
+
}
|
16
|
+
}
|
17
|
+
return permissions
|
18
|
+
}
|
File without changes
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import {PUBLIC_BRAND, PUBLIC_TITLE, PUBLIC_AUTH_ACCESS_TOKEN_COOKIE_KEY, PUBLIC_AUTH_REFRESH_TOKEN_COOKIE_KEY} from '$env/static/public';
|
2
|
+
export const appBrand = PUBLIC_BRAND || 'PascalAppName';
|
3
|
+
export const appTitle = PUBLIC_TITLE || 'PascalAppName';
|
4
|
+
export const authAccessTokenCookieKey = PUBLIC_AUTH_ACCESS_TOKEN_COOKIE_KEY || 'access_token';
|
5
|
+
export const authRefreshTokenCookieKey = PUBLIC_AUTH_REFRESH_TOKEN_COOKIE_KEY || 'refresh_token';
|
6
|
+
|
7
|
+
export const loginApiUrl: string = '/api/v1/auth/login';
|
8
|
+
export const refreshTokenApiUrl: string = '/api/v1/auth/refresh-token';
|
9
|
+
export const isAuthorizedApiUrl: string = '/api/v1/auth/is-authorized';
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/config/navData.ts
CHANGED
@@ -1,25 +1,14 @@
|
|
1
1
|
import type { SingleNavData } from '../components/navigation/type'
|
2
2
|
|
3
3
|
export const navData: SingleNavData[] = [
|
4
|
-
{title:
|
4
|
+
{title: 'Home', url: '/'},
|
5
5
|
{
|
6
|
-
title:
|
7
|
-
url:
|
6
|
+
title: 'Auth',
|
7
|
+
url: '#',
|
8
8
|
submenus: [
|
9
|
-
{title:
|
10
|
-
{title:
|
11
|
-
{title:
|
9
|
+
{title: 'Permission', url: '/auth/permission', permission: 'auth:permission:get'},
|
10
|
+
{title: 'Group', url: '/auth/group', permission: 'auth:group:get'},
|
11
|
+
{title: 'User', url: '/auth/user', permission: 'auth:user:get'},
|
12
12
|
]
|
13
13
|
},
|
14
|
-
{title: "About", url: "/about"},
|
15
|
-
{title: "Greetings, Lord", url: "/greetings/Lord"},
|
16
|
-
{
|
17
|
-
title: "Test",
|
18
|
-
url: "#",
|
19
|
-
submenus: [
|
20
|
-
{title: "Sub 1", url: "/"},
|
21
|
-
{title: "Sub 2 long long title", url: "/about"}
|
22
|
-
]
|
23
|
-
},
|
24
|
-
{title: "Sample url", url: "/sample"},
|
25
14
|
]
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/lib/error/helper.ts
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
|
3
|
+
export function getErrorMessage(error: unknown): string {
|
4
|
+
if (axios.isAxiosError(error)) {
|
5
|
+
let errorMessage = error.message;
|
6
|
+
if (error.response?.data?.detail) {
|
7
|
+
errorMessage += JSON.stringify(error.response.data.detail);
|
8
|
+
}
|
9
|
+
return errorMessage;
|
10
|
+
}
|
11
|
+
return error + '';
|
12
|
+
}
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+error.svelte
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { page } from '$app/stores';
|
3
|
+
</script>
|
4
|
+
<div class="flex flex-col justify-center items-center">
|
5
|
+
<h1 class="text-5xl">{$page.status}</h1>
|
6
|
+
<p class="text-3xl">{$page.error?.message}</p>
|
7
|
+
<a class="btn btn-primary mt-5" href="/">Let's go home</a>
|
8
|
+
</div>
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.js
CHANGED
File without changes
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+layout.svelte
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
<script lang="ts">
|
2
|
-
|
2
|
+
import { onMount } from 'svelte';
|
3
3
|
import Navigation from '$lib/components/navigation/Navigation.svelte';
|
4
4
|
import { navData } from '$lib/config/navData';
|
5
|
-
import { appBrand, appTitle } from '$lib/config/
|
5
|
+
import { appBrand, appTitle } from '$lib/config/app';
|
6
6
|
import logo from '/static/logo.png';
|
7
|
+
import "../app.css";
|
7
8
|
</script>
|
8
9
|
|
9
10
|
<title>{appTitle}</title>
|
10
|
-
<Navigation
|
11
|
+
<Navigation navData={navData} logo={logo} brand={appBrand}></Navigation>
|
11
12
|
<div class="pl-10 pr-10">
|
12
13
|
<slot></slot>
|
13
14
|
</div>
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/frontend/src/routes/+page.svelte
CHANGED
@@ -1,8 +1,2 @@
|
|
1
|
-
<h1 class="text-3xl font-bold underline">Welcome to
|
2
|
-
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
3
|
-
|
4
|
-
<style lang="postcss">
|
5
|
-
:global(html) {
|
6
|
-
background-color: theme(colors.green.100);
|
7
|
-
}
|
8
|
-
</style>
|
1
|
+
<h1 class="text-3xl font-bold underline">Welcome to PascalAppName</h1>
|
2
|
+
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
zrb/builtin/generator/fastapp/template/src/kebab-app-name/src/module/auth/component/__init__.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
from module.auth.component.authorizer import authorizer
|
2
2
|
from module.auth.component.base import Base
|
3
3
|
from module.auth.component.password_hasher import password_hasher
|
4
|
-
from module.auth.component.
|
5
|
-
from module.auth.component.
|
4
|
+
from module.auth.component.access_token_scheme import access_token_scheme
|
5
|
+
from module.auth.component.access_token_util import access_token_util
|
6
|
+
from module.auth.component.bearer_token_scheme import bearer_token_scheme
|
7
|
+
from module.auth.component.refresh_token_util import refresh_token_util
|
6
8
|
from module.auth.component.user import (
|
7
9
|
admin_user, admin_user_password, guest_user
|
8
10
|
)
|
@@ -10,8 +12,10 @@ from module.auth.component.user import (
|
|
10
12
|
assert authorizer
|
11
13
|
assert Base
|
12
14
|
assert password_hasher
|
13
|
-
assert
|
14
|
-
assert
|
15
|
+
assert access_token_scheme
|
16
|
+
assert access_token_util
|
17
|
+
assert bearer_token_scheme
|
18
|
+
assert refresh_token_util
|
15
19
|
assert admin_user
|
16
20
|
assert admin_user_password
|
17
21
|
assert guest_user
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from module.auth.core import (
|
2
|
+
AccessTokenScheme, create_oauth2_bearer_access_token_scheme
|
3
|
+
)
|
4
|
+
from module.auth.component.access_token_util import access_token_util
|
5
|
+
from module.auth.component.user import guest_user
|
6
|
+
from config import app_auth_access_token_cookie_key
|
7
|
+
|
8
|
+
|
9
|
+
access_token_scheme: AccessTokenScheme = create_oauth2_bearer_access_token_scheme( # noqa
|
10
|
+
guest_user=guest_user,
|
11
|
+
access_token_util=access_token_util,
|
12
|
+
token_url='/api/v1/auth/login-oauth',
|
13
|
+
token_cookie_key=app_auth_access_token_cookie_key
|
14
|
+
)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from config import (
|
2
|
+
app_auth_access_token_type, app_auth_jwt_token_algorithm,
|
3
|
+
app_auth_jwt_token_secret_key
|
4
|
+
)
|
5
|
+
from module.auth.core import AccessTokenUtil, JWTAccessTokenUtil
|
6
|
+
|
7
|
+
|
8
|
+
def init_token_util() -> AccessTokenUtil:
|
9
|
+
if app_auth_access_token_type.lower() == 'jwt':
|
10
|
+
return JWTAccessTokenUtil(
|
11
|
+
secret_key=app_auth_jwt_token_secret_key,
|
12
|
+
algorithm=app_auth_jwt_token_algorithm
|
13
|
+
)
|
14
|
+
raise ValueError(f'Invalid auth token type: {app_auth_access_token_type}')
|
15
|
+
|
16
|
+
|
17
|
+
access_token_util = init_token_util()
|
@@ -1,7 +1,10 @@
|
|
1
|
-
from config import
|
1
|
+
from config import (
|
2
|
+
app_auth_admin_active, app_auth_access_token_expire_seconds,
|
3
|
+
app_auth_refresh_token_expire_seconds
|
4
|
+
)
|
2
5
|
from module.auth.component.repo.user_repo import user_repo
|
3
6
|
from module.auth.entity.user.model import UserModel
|
4
|
-
from module.auth.component import
|
7
|
+
from module.auth.component import access_token_util, refresh_token_util
|
5
8
|
from module.auth.component.user import (
|
6
9
|
admin_user, admin_user_password, guest_user
|
7
10
|
)
|
@@ -11,8 +14,10 @@ from module.auth.component.model.permission_model import permission_model
|
|
11
14
|
user_model: UserModel = UserModel(
|
12
15
|
repo=user_repo,
|
13
16
|
permission_model=permission_model,
|
14
|
-
|
15
|
-
|
17
|
+
access_token_util=access_token_util,
|
18
|
+
access_token_expire_seconds=app_auth_access_token_expire_seconds,
|
19
|
+
refresh_token_util=refresh_token_util,
|
20
|
+
refresh_token_expire_seconds=app_auth_refresh_token_expire_seconds,
|
16
21
|
guest_user=guest_user,
|
17
22
|
admin_user=admin_user if app_auth_admin_active else None,
|
18
23
|
admin_user_password=admin_user_password
|