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
@@ -67,8 +67,9 @@ copy_resource = ResourceMaker(
|
|
67
67
|
excludes=[
|
68
68
|
'*/deployment/venv',
|
69
69
|
'*/src/kebab-app-name/venv',
|
70
|
-
'*/src/kebab-app-name/frontend/node_modules',
|
71
|
-
'*/src/kebab-app-name/frontend/build',
|
70
|
+
'*/src/kebab-app-name/src/frontend/node_modules',
|
71
|
+
'*/src/kebab-app-name/src/frontend/build',
|
72
|
+
'*/src/kebab-app-name/src/frontend/.svelte-kit',
|
72
73
|
]
|
73
74
|
)
|
74
75
|
|
@@ -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,8 @@
|
|
10
10
|
"dependencies": {
|
11
11
|
"axios": "^1.4.0",
|
12
12
|
"daisyui": "^2.51.6",
|
13
|
+
"js-cookie": "^3.0.5",
|
14
|
+
"jwt-decode": "^3.1.2",
|
13
15
|
"process": "^0.11.10"
|
14
16
|
},
|
15
17
|
"devDependencies": {
|
@@ -17,6 +19,7 @@
|
|
17
19
|
"@sveltejs/adapter-auto": "^2.0.0",
|
18
20
|
"@sveltejs/adapter-static": "^2.0.1",
|
19
21
|
"@sveltejs/kit": "^1.5.0",
|
22
|
+
"@types/js-cookie": "^3.0.3",
|
20
23
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
21
24
|
"@typescript-eslint/parser": "^5.45.0",
|
22
25
|
"autoprefixer": "^10.4.14",
|
@@ -351,6 +354,12 @@
|
|
351
354
|
"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==",
|
352
355
|
"dev": true
|
353
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
|
+
},
|
354
363
|
"node_modules/@types/json-schema": {
|
355
364
|
"version": "7.0.11",
|
356
365
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
@@ -1850,6 +1859,14 @@
|
|
1850
1859
|
"jiti": "bin/jiti.js"
|
1851
1860
|
}
|
1852
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
|
+
},
|
1853
1870
|
"node_modules/js-sdsl": {
|
1854
1871
|
"version": "4.4.0",
|
1855
1872
|
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
|
@@ -1884,6 +1901,11 @@
|
|
1884
1901
|
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
1885
1902
|
"dev": true
|
1886
1903
|
},
|
1904
|
+
"node_modules/jwt-decode": {
|
1905
|
+
"version": "3.1.2",
|
1906
|
+
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
1907
|
+
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
1908
|
+
},
|
1887
1909
|
"node_modules/kleur": {
|
1888
1910
|
"version": "4.1.5",
|
1889
1911
|
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.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,8 @@
|
|
40
41
|
"dependencies": {
|
41
42
|
"axios": "^1.4.0",
|
42
43
|
"daisyui": "^2.51.6",
|
44
|
+
"js-cookie": "^3.0.5",
|
45
|
+
"jwt-decode": "^3.1.2",
|
43
46
|
"process": "^0.11.10"
|
44
47
|
}
|
45
48
|
}
|
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
|
+
}
|
@@ -1,21 +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
|
-
{:else}
|
20
|
-
|
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}
|
21
23
|
{/if}
|
@@ -1,23 +1,80 @@
|
|
1
1
|
<script lang="ts">
|
2
|
-
import
|
2
|
+
import { goto } from '$app/navigation';
|
3
|
+
import { onMount } from 'svelte';
|
3
4
|
import Menu from './Menu.svelte';
|
5
|
+
import type { SingleNavData } from './type';
|
6
|
+
import { getAuthorization, initAuthStore, login, logout } from '../../auth/helper';
|
7
|
+
import { userIdStore } from '../../auth/store';
|
8
|
+
import { getNavDataPermissions } from './helper';
|
9
|
+
|
4
10
|
export let logo: string;
|
5
11
|
export let brand: string;
|
6
|
-
export let
|
7
|
-
export let
|
12
|
+
export let navData: SingleNavData[];
|
13
|
+
export let loginTitle: string = 'Login';
|
14
|
+
export let logoutTitle: string = 'Logout';
|
15
|
+
|
16
|
+
const navDataPermissions = getNavDataPermissions(navData);
|
17
|
+
|
18
|
+
let identity: string;
|
19
|
+
let password: string;
|
20
|
+
let userId = '';
|
21
|
+
let authorization: {[key: string]: boolean} = {};
|
22
|
+
|
23
|
+
userIdStore.subscribe(async (value) => {
|
24
|
+
userId = value;
|
25
|
+
authorization = await getAuthorization(navDataPermissions);
|
26
|
+
});
|
27
|
+
|
28
|
+
onMount(async() => {
|
29
|
+
await initAuthStore();
|
30
|
+
});
|
31
|
+
|
32
|
+
async function onLoginClick() {
|
33
|
+
const loginSuccess = await login(identity, password);
|
34
|
+
if (loginSuccess) {
|
35
|
+
await goto('/');
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
alert('salah');
|
39
|
+
}
|
40
|
+
|
41
|
+
async function onLogoutClick() {
|
42
|
+
logout();
|
43
|
+
await goto('/');
|
44
|
+
}
|
8
45
|
</script>
|
9
46
|
|
10
|
-
<div class="navbar bg-base-100">
|
47
|
+
<div class="navbar sticky top-0 bg-base-100 z-50">
|
11
48
|
<div class="flex-1">
|
12
49
|
<img class="h-8 mr-3" src={logo} alt="Logo">
|
13
50
|
<a href="/" class="btn btn-ghost normal-case text-xl">{brand}</a>
|
14
51
|
</div>
|
15
52
|
<div class="flex-none">
|
16
53
|
<ul class="menu menu-horizontal px-1">
|
17
|
-
{#each
|
18
|
-
<Menu
|
54
|
+
{#each navData as singleNavData}
|
55
|
+
<Menu singleNavData={singleNavData} authorization={authorization} />
|
19
56
|
{/each}
|
57
|
+
{#if userId == ''}
|
58
|
+
<li><a href="#login-modal" class="px-4">{loginTitle}</a></li>
|
59
|
+
{:else}
|
60
|
+
<li><a href="#top" class="px-4" on:click={onLogoutClick}>{logoutTitle}</a></li>
|
61
|
+
{/if}
|
20
62
|
</ul>
|
21
63
|
</div>
|
22
64
|
</div>
|
23
|
-
|
65
|
+
|
66
|
+
<div class="modal" id="login-modal">
|
67
|
+
<form class="modal-box">
|
68
|
+
<div class="mb-6">
|
69
|
+
<label class="block text-gray-700 font-bold mb-2" for="identity">Identity</label>
|
70
|
+
<input class="w-full px-3 py-2 border rounded-lg text-gray-700 focus:outline-none focus:shadow-outline" id="identity" type="text" placeholder="Enter your username/email/phone" bind:value={identity} />
|
71
|
+
</div>
|
72
|
+
<div class="mb-6">
|
73
|
+
<label class="block text-gray-700 font-bold mb-2" for="password">Password</label>
|
74
|
+
<input class="w-full px-3 py-2 border rounded-lg text-gray-700 focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="Enter your password" bind:value={password} />
|
75
|
+
</div>
|
76
|
+
<div class="modal-action">
|
77
|
+
<a href="#top" class="btn btn-primary" on:click={onLoginClick}>Sign in</a>
|
78
|
+
</div>
|
79
|
+
</form>
|
80
|
+
</div>
|
@@ -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
|
-
import {navData} from '$lib/config/navData';
|
5
|
-
import {appBrand, appTitle
|
6
|
-
import logo from '
|
4
|
+
import { navData } from '$lib/config/navData';
|
5
|
+
import { appBrand, appTitle } from '$lib/config/app';
|
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>
|
Binary file
|
Binary file
|
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
|
+
)
|