shaapi 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. shaapi/__init__.py +3 -0
  2. shaapi/cli.py +97 -0
  3. shaapi/generator.py +114 -0
  4. shaapi/template/.dockerignore +37 -0
  5. shaapi/template/.env.template +59 -0
  6. shaapi/template/.gitattributes +12 -0
  7. shaapi/template/.gitignore +170 -0
  8. shaapi/template/.gitlab-ci.yml +89 -0
  9. shaapi/template/Dockerfile +59 -0
  10. shaapi/template/LICENSE +21 -0
  11. shaapi/template/README.md +206 -0
  12. shaapi/template/backend/.gitignore +164 -0
  13. shaapi/template/backend/__init__.py +0 -0
  14. shaapi/template/backend/alembic/README +1 -0
  15. shaapi/template/backend/alembic/env.py +102 -0
  16. shaapi/template/backend/alembic/script.py.mako +26 -0
  17. shaapi/template/backend/alembic/versions/2026_06_08_1024-64524c63b666_initial.py +143 -0
  18. shaapi/template/backend/alembic.ini +117 -0
  19. shaapi/template/backend/app/__init__.py +55 -0
  20. shaapi/template/backend/app/admin/__init__.py +1 -0
  21. shaapi/template/backend/app/admin/api/v1/__init__.py +0 -0
  22. shaapi/template/backend/app/admin/api/v1/auth.py +59 -0
  23. shaapi/template/backend/app/admin/api/v1/casbin.py +218 -0
  24. shaapi/template/backend/app/admin/api/v1/login_log.py +63 -0
  25. shaapi/template/backend/app/admin/api/v1/opera_log.py +61 -0
  26. shaapi/template/backend/app/admin/api/v1/role.py +108 -0
  27. shaapi/template/backend/app/admin/api/v1/user.py +47 -0
  28. shaapi/template/backend/app/admin/schema/casbin_rule.py +45 -0
  29. shaapi/template/backend/app/admin/schema/login_log.py +36 -0
  30. shaapi/template/backend/app/admin/schema/opera_log.py +43 -0
  31. shaapi/template/backend/app/admin/schema/role.py +36 -0
  32. shaapi/template/backend/app/admin/schema/sso.py +37 -0
  33. shaapi/template/backend/app/admin/schema/token.py +74 -0
  34. shaapi/template/backend/app/admin/schema/user.py +93 -0
  35. shaapi/template/backend/app/admin/service/auth_service.py +233 -0
  36. shaapi/template/backend/app/admin/service/casbin_service.py +135 -0
  37. shaapi/template/backend/app/admin/service/login_log_service.py +62 -0
  38. shaapi/template/backend/app/admin/service/opera_log_service.py +31 -0
  39. shaapi/template/backend/app/admin/service/role_service.py +79 -0
  40. shaapi/template/backend/app/admin/service/secure_token_service.py +60 -0
  41. shaapi/template/backend/app/admin/service/user_service.py +153 -0
  42. shaapi/template/backend/app/api.py +11 -0
  43. shaapi/template/backend/common/__init__.py +0 -0
  44. shaapi/template/backend/common/cloud_storage/__init__.py +11 -0
  45. shaapi/template/backend/common/cloud_storage/cloud_storage.py +180 -0
  46. shaapi/template/backend/common/dataclasses.py +52 -0
  47. shaapi/template/backend/common/email_conf/email.py +105 -0
  48. shaapi/template/backend/common/enums.py +144 -0
  49. shaapi/template/backend/common/exception/__init__.py +0 -0
  50. shaapi/template/backend/common/exception/errors.py +87 -0
  51. shaapi/template/backend/common/exception/exception_handler.py +280 -0
  52. shaapi/template/backend/common/log.py +123 -0
  53. shaapi/template/backend/common/model.py +68 -0
  54. shaapi/template/backend/common/pagination.py +83 -0
  55. shaapi/template/backend/common/response/__init__.py +0 -0
  56. shaapi/template/backend/common/response/response_code.py +158 -0
  57. shaapi/template/backend/common/response/response_schema.py +110 -0
  58. shaapi/template/backend/common/schema.py +144 -0
  59. shaapi/template/backend/common/security/jwt.py +203 -0
  60. shaapi/template/backend/common/security/rbac.py +98 -0
  61. shaapi/template/backend/common/security/sec_token.py +6 -0
  62. shaapi/template/backend/common/socketio/action.py +11 -0
  63. shaapi/template/backend/common/socketio/server.py +50 -0
  64. shaapi/template/backend/common/sso/base.py +69 -0
  65. shaapi/template/backend/common/sso/google.py +127 -0
  66. shaapi/template/backend/core/conf.py +208 -0
  67. shaapi/template/backend/core/path_conf.py +24 -0
  68. shaapi/template/backend/core/registrar.py +195 -0
  69. shaapi/template/backend/crud/__init__.py +1 -0
  70. shaapi/template/backend/crud/crud_base.py +35 -0
  71. shaapi/template/backend/crud/crud_casbin.py +46 -0
  72. shaapi/template/backend/crud/crud_login_log.py +58 -0
  73. shaapi/template/backend/crud/crud_opera_log.py +58 -0
  74. shaapi/template/backend/crud/crud_role.py +128 -0
  75. shaapi/template/backend/crud/crud_user.py +267 -0
  76. shaapi/template/backend/database/__init__.py +0 -0
  77. shaapi/template/backend/database/db_postgres.py +125 -0
  78. shaapi/template/backend/database/db_redis.py +62 -0
  79. shaapi/template/backend/entrypoint-api.sh +19 -0
  80. shaapi/template/backend/lang/en/app.py +18 -0
  81. shaapi/template/backend/lang/en/auth.py +10 -0
  82. shaapi/template/backend/lang/fr/app.py +18 -0
  83. shaapi/template/backend/lang/fr/auth.py +10 -0
  84. shaapi/template/backend/main.py +54 -0
  85. shaapi/template/backend/middleware/__init__.py +1 -0
  86. shaapi/template/backend/middleware/access_middleware.py +19 -0
  87. shaapi/template/backend/middleware/i18n_middleware.py +19 -0
  88. shaapi/template/backend/middleware/jwt_auth_middleware.py +73 -0
  89. shaapi/template/backend/middleware/opera_log_middleware.py +179 -0
  90. shaapi/template/backend/middleware/state_middleware.py +26 -0
  91. shaapi/template/backend/models/__init__.py +10 -0
  92. shaapi/template/backend/models/associations.py +20 -0
  93. shaapi/template/backend/models/casbin_rule.py +30 -0
  94. shaapi/template/backend/models/login_log.py +28 -0
  95. shaapi/template/backend/models/opera_log.py +36 -0
  96. shaapi/template/backend/models/role.py +27 -0
  97. shaapi/template/backend/models/user.py +30 -0
  98. shaapi/template/backend/seeder/json/admin.json +15 -0
  99. shaapi/template/backend/seeder/json/user.json +15 -0
  100. shaapi/template/backend/seeder/run.py +34 -0
  101. shaapi/template/backend/static/ip2region.xdb +0 -0
  102. shaapi/template/backend/templates/build/meet.html +169 -0
  103. shaapi/template/backend/templates/build/new_account.html +373 -0
  104. shaapi/template/backend/templates/build/reset-password.html +170 -0
  105. shaapi/template/backend/templates/build/test_email.html +25 -0
  106. shaapi/template/backend/templates/build/welcome-one-1.html +160 -0
  107. shaapi/template/backend/templates/build/welcome-one.html +178 -0
  108. shaapi/template/backend/templates/build/welcome-two.html +234 -0
  109. shaapi/template/backend/templates/index.html +0 -0
  110. shaapi/template/backend/templates/src/new_account.mjml +15 -0
  111. shaapi/template/backend/templates/src/reset_password.mjml +19 -0
  112. shaapi/template/backend/templates/src/test_email.mjml +11 -0
  113. shaapi/template/backend/templates/ws/ws.html +70 -0
  114. shaapi/template/backend/utils/demo_site.py +18 -0
  115. shaapi/template/backend/utils/encrypt.py +108 -0
  116. shaapi/template/backend/utils/health_check.py +34 -0
  117. shaapi/template/backend/utils/prometheus.py +135 -0
  118. shaapi/template/backend/utils/request_parse.py +110 -0
  119. shaapi/template/backend/utils/serializers.py +75 -0
  120. shaapi/template/backend/utils/timezone.py +51 -0
  121. shaapi/template/backend/utils/trace_id.py +7 -0
  122. shaapi/template/backend/utils/translator.py +28 -0
  123. shaapi/template/devops/scripts/deploy.sh +7 -0
  124. shaapi/template/devops/scripts/setup_env.sh +62 -0
  125. shaapi/template/docker-compose.monitoring.yml +63 -0
  126. shaapi/template/docker-compose.override.yml +12 -0
  127. shaapi/template/docker-compose.yml +90 -0
  128. shaapi/template/docker-run.sh +99 -0
  129. shaapi/template/etc/dashboards/fastapi-observability.json +1044 -0
  130. shaapi/template/etc/dashboards.yaml +10 -0
  131. shaapi/template/etc/grafana/datasource.yml +79 -0
  132. shaapi/template/etc/prometheus/prometheus.yml +52 -0
  133. shaapi/template/package-lock.json +2102 -0
  134. shaapi/template/package.json +16 -0
  135. shaapi/template/pyproject.toml +78 -0
  136. shaapi/template/uv.lock +2866 -0
  137. shaapi-0.1.0.dist-info/METADATA +92 -0
  138. shaapi-0.1.0.dist-info/RECORD +141 -0
  139. shaapi-0.1.0.dist-info/WHEEL +4 -0
  140. shaapi-0.1.0.dist-info/entry_points.txt +2 -0
  141. shaapi-0.1.0.dist-info/licenses/LICENCE +21 -0
@@ -0,0 +1,117 @@
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts
5
+ # Use forward slashes (/) also on windows to provide an os agnostic path
6
+ script_location = alembic
7
+
8
+ # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
9
+ # Uncomment the line below if you want the files to be prepended with date and time
10
+ # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
11
+ # for all available tokens
12
+ file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
13
+
14
+ # sys.path path, will be prepended to sys.path if present.
15
+ # defaults to the current working directory.
16
+ prepend_sys_path = .
17
+
18
+ # timezone to use when rendering the date within the migration file
19
+ # as well as the filename.
20
+ # If specified, requires the python>=3.9 or backports.zoneinfo library.
21
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
22
+ # string value is passed to ZoneInfo()
23
+ # leave blank for localtime
24
+ # timezone =
25
+
26
+ # max length of characters to apply to the "slug" field
27
+ # truncate_slug_length = 40
28
+
29
+ # set to 'true' to run the environment during
30
+ # the 'revision' command, regardless of autogenerate
31
+ # revision_environment = false
32
+
33
+ # set to 'true' to allow .pyc and .pyo files without
34
+ # a source .py file to be detected as revisions in the
35
+ # versions/ directory
36
+ # sourceless = false
37
+
38
+ # version location specification; This defaults
39
+ # to alembic/versions. When using multiple version
40
+ # directories, initial revisions must be specified with --version-path.
41
+ # The path separator used here should be the separator specified by "version_path_separator" below.
42
+ # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
43
+
44
+ # version path separator; As mentioned above, this is the character used to split
45
+ # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
46
+ # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
47
+ # Valid values for version_path_separator are:
48
+ #
49
+ # version_path_separator = :
50
+ # version_path_separator = ;
51
+ # version_path_separator = space
52
+ # version_path_separator = newline
53
+ version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
54
+
55
+ # set to 'true' to search source files recursively
56
+ # in each "version_locations" directory
57
+ # new in Alembic version 1.10
58
+ # recursive_version_locations = false
59
+
60
+ # the output encoding used when revision files
61
+ # are written from script.py.mako
62
+ # output_encoding = utf-8
63
+
64
+ sqlalchemy.url = driver://user:pass@localhost/dbname
65
+
66
+
67
+ [post_write_hooks]
68
+ # post_write_hooks defines scripts or Python functions that are run
69
+ # on newly generated revision scripts. See the documentation for further
70
+ # detail and examples
71
+
72
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
73
+ # hooks = black
74
+ # black.type = console_scripts
75
+ # black.entrypoint = black
76
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
77
+
78
+ # lint with attempts to fix using "ruff" - use the exec runner, execute a binary
79
+ # hooks = ruff
80
+ # ruff.type = exec
81
+ # ruff.executable = %(here)s/.venv/bin/ruff
82
+ # ruff.options = --fix REVISION_SCRIPT_FILENAME
83
+
84
+ # Logging configuration
85
+ [loggers]
86
+ keys = root,sqlalchemy,alembic
87
+
88
+ [handlers]
89
+ keys = console
90
+
91
+ [formatters]
92
+ keys = generic
93
+
94
+ [logger_root]
95
+ level = WARNING
96
+ handlers = console
97
+ qualname =
98
+
99
+ [logger_sqlalchemy]
100
+ level = WARNING
101
+ handlers =
102
+ qualname = sqlalchemy.engine
103
+
104
+ [logger_alembic]
105
+ level = INFO
106
+ handlers =
107
+ qualname = alembic
108
+
109
+ [handler_console]
110
+ class = StreamHandler
111
+ args = (sys.stderr,)
112
+ level = NOTSET
113
+ formatter = generic
114
+
115
+ [formatter_generic]
116
+ format = %(levelname)-5.5s [%(name)s] %(message)s
117
+ datefmt = %H:%M:%S
@@ -0,0 +1,55 @@
1
+ import os
2
+ import importlib
3
+ from typing import Iterator
4
+ import types
5
+ import logging
6
+
7
+ logging.basicConfig(level=logging.DEBUG)
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class Handlers:
11
+ handlers_base_path = ('backend', 'app')
12
+ ignored = ('__init__.py', '__pycache__','crud','schema','service')
13
+ paths = ('admin/api/v1', 'client/api/v1', 'mentor/api/v1', 'company/api/v1')
14
+ # include_dirs = ["v1"]
15
+
16
+ @classmethod
17
+ def __all_module_paths(cls) -> list:
18
+ module_paths = []
19
+
20
+ for path in cls.paths:
21
+ handlers_path = f"{os.path.join(os.getcwd(), *cls.handlers_base_path)}/{path}/".replace("backend/backend","backend")
22
+ for root, dirs, files in os.walk(handlers_path):
23
+ for file in files:
24
+ if file.endswith('.py') and file not in cls.ignored:
25
+ relative_path = os.path.relpath(os.path.join(root, file), handlers_path)
26
+ module_paths.append(os.path.join(path, relative_path))
27
+
28
+
29
+
30
+
31
+ return module_paths
32
+
33
+ @classmethod
34
+ def __module_namespace(cls, module_path: str) -> str:
35
+ # Normalize both separators so module resolution works on Windows
36
+ # (os.sep == '\\') and Linux (os.sep == '/') alike.
37
+ module_path = module_path.replace(os.sep, '.').replace('/', '.').replace('.py', '')
38
+ namespace = '%s.%s' % ('.'.join(cls.handlers_base_path), module_path)
39
+ return namespace
40
+
41
+ @classmethod
42
+ def iterator(cls) -> Iterator[types.ModuleType]:
43
+ for module_path in cls.__all_module_paths():
44
+ module_name = cls.__module_namespace(module_path)
45
+ try:
46
+ handler = importlib.import_module(module_name)
47
+ yield handler
48
+ except Exception as e:
49
+ logger.error(f"Erreur lors de l'importation de {module_name}: {e}")
50
+
51
+ @classmethod
52
+ def modules(cls) -> map:
53
+ return map(
54
+ lambda module_path: cls.__module_namespace(module_path), cls.__all_module_paths()
55
+ )
File without changes
@@ -0,0 +1,59 @@
1
+ from fastapi import APIRouter, Request, Response
2
+ from starlette.background import BackgroundTasks
3
+
4
+ from backend.app.admin.schema.user import (
5
+ GetCurrentUserInfoDetail,
6
+ # UpdatePasswordParam,
7
+ UserLoginSchema,
8
+ UserRegister
9
+ )
10
+ from backend.app.admin.service.auth_service import auth_service
11
+ # from backend.app.user.service.user_service import user_service
12
+ from backend.common.response.response_schema import ResponseModel, response_base
13
+ from backend.common.security.jwt import DependsJwtAuth
14
+
15
+ router = APIRouter(prefix="/auth", tags=["User Auth"])
16
+
17
+
18
+ @router.post('/register', summary='registered user')
19
+ async def register_user(request: Request, response: Response, obj: UserRegister, background_tasks: BackgroundTasks) -> ResponseModel:
20
+ data = await auth_service.register(request=request, response=response, obj=obj, background_tasks=background_tasks)
21
+
22
+ return response_base.success(request=request, data=data)
23
+
24
+
25
+ @router.post(
26
+ '/login',
27
+ summary='user login',
28
+ description='json format, only supports debugging in third-party api tools, e.g. postman.',
29
+ # dependencies=[Depends(RateLimiter(times=5, minutes=1))],
30
+ )
31
+ async def user_login(
32
+ request: Request, response: Response, obj: UserLoginSchema, background_tasks: BackgroundTasks
33
+ ) -> ResponseModel:
34
+ data = await auth_service.login(request=request, response=response, obj=obj, background_tasks=background_tasks)
35
+ return response_base.success(request=request, data=data)
36
+
37
+
38
+ @router.post('/token/new', summary='Create a new token', dependencies=[DependsJwtAuth])
39
+ async def create_new_token(request: Request, response: Response) -> ResponseModel:
40
+ data = await auth_service.new_token(request=request, response=response)
41
+ return response_base.success(request=request, data=data)
42
+
43
+
44
+ @router.post('/logout', summary='user logout', dependencies=[DependsJwtAuth])
45
+ async def user_logout(request: Request, response: Response) -> ResponseModel:
46
+ await auth_service.logout(request=request, response=response)
47
+ return response_base.success(request=request)
48
+
49
+
50
+ @router.get('/me', summary='Get connected user profile', dependencies=[DependsJwtAuth], response_model_exclude={'password'})
51
+ async def get_current_user(request: Request) -> ResponseModel:
52
+ data = GetCurrentUserInfoDetail(**request.user.model_dump())
53
+ return response_base.success(request=request, data=data)
54
+
55
+
56
+
57
+
58
+
59
+
@@ -0,0 +1,218 @@
1
+ from typing import Annotated
2
+ from uuid import UUID
3
+
4
+ from fastapi import APIRouter, Query, Request
5
+
6
+ from backend.app.admin.schema.casbin_rule import (
7
+ CreatePolicyParam,
8
+ CreateUserRoleParam,
9
+ DeleteAllPoliciesParam,
10
+ DeletePolicyParam,
11
+ DeleteUserRoleParam,
12
+ GetPolicyListDetails,
13
+ UpdatePolicyParam,
14
+ )
15
+ from backend.app.admin.service.casbin_service import casbin_service
16
+ from backend.common.pagination import DependsPagination, paging_data
17
+ from backend.common.response.response_schema import ResponseModel, response_base
18
+ from backend.common.security.jwt import DependsJwtAuth
19
+ from backend.common.security.rbac import DependsRBAC
20
+ from backend.database.db_postgres import CurrentSession
21
+
22
+ router = APIRouter(prefix='/casbin', tags=["Casbin"])
23
+
24
+
25
+ @router.get(
26
+ '/',
27
+ summary='(Fuzzy condition) Get all permission policies with pagination',
28
+ dependencies=[
29
+ DependsJwtAuth,
30
+ DependsPagination,
31
+ ],
32
+ )
33
+ async def get_pagination_casbin(
34
+ request: Request,
35
+ db: CurrentSession,
36
+ ptype: Annotated[str | None, Query(description='Policy type, p / g')] = None,
37
+ sub: Annotated[str | None, Query(description='User UUID / Role')] = None,
38
+ ) :
39
+ casbin_select = await casbin_service.get_casbin_list(ptype=ptype, sub=sub)
40
+ page_data = await paging_data(db, casbin_select, GetPolicyListDetails)
41
+ return response_base.success(request=request, data=page_data)
42
+
43
+ @router.get('/policies', summary='Get all P permission policies', dependencies=[DependsJwtAuth])
44
+ async def get_all_policies(request: Request, role: Annotated[int | None, Query(description='Role ID')] = None) -> ResponseModel:
45
+ policies = await casbin_service.get_policy_list(role=role)
46
+ return response_base.success(request=request, data=policies)
47
+
48
+
49
+ @router.post(
50
+ '/policy',
51
+ summary='Add P permission policy',
52
+ dependencies=[
53
+ DependsRBAC,
54
+ ],
55
+ )
56
+ async def create_policy(request: Request, p: CreatePolicyParam) -> ResponseModel:
57
+ """
58
+ P Policy:
59
+
60
+ - It is recommended to add role-based access control, which needs to be combined with adding G policies to actually have access rights. Suitable for configuring global interface access policies.<br>
61
+ **Format**: Role + Access Path + Access Method
62
+
63
+ - If adding user-based access control, there is no need to add G policies to actually have access rights. Suitable for configuring specific user interface access policies.<br>
64
+ **Format**: User UUID + Access Path + Access Method
65
+ """
66
+ data = await casbin_service.create_policy(p=p)
67
+ return response_base.success(request=request, data=data)
68
+
69
+
70
+ @router.post(
71
+ '/policies',
72
+ summary='Add multiple P permission policies',
73
+ dependencies=[
74
+ DependsRBAC,
75
+ ],
76
+ )
77
+ async def create_policies(request: Request, ps: list[CreatePolicyParam]) -> ResponseModel:
78
+ data = await casbin_service.create_policies(ps=ps)
79
+ return response_base.success(request=request, data=data)
80
+
81
+
82
+ @router.put(
83
+ '/policy',
84
+ summary='Update P permission policy',
85
+ dependencies=[
86
+ DependsRBAC,
87
+ ],
88
+ )
89
+ async def update_policy(request: Request, old: UpdatePolicyParam, new: UpdatePolicyParam) -> ResponseModel:
90
+ data = await casbin_service.update_policy(old=old, new=new)
91
+ return response_base.success(request=request, data=data)
92
+
93
+
94
+ @router.put(
95
+ '/policies',
96
+ summary='Update multiple P permission policies',
97
+ dependencies=[
98
+ DependsRBAC,
99
+ ],
100
+ )
101
+ async def update_policies(request: Request, old: list[UpdatePolicyParam], new: list[UpdatePolicyParam]) -> ResponseModel:
102
+ data = await casbin_service.update_policies(old=old, new=new)
103
+ return response_base.success(request=request, data=data)
104
+
105
+
106
+ @router.delete(
107
+ '/policy',
108
+ summary='Delete P permission policy',
109
+ dependencies=[
110
+ DependsRBAC,
111
+ ],
112
+ )
113
+ async def delete_policy(request: Request, p: DeletePolicyParam) -> ResponseModel:
114
+ data = await casbin_service.delete_policy(p=p)
115
+ return response_base.success(request=request, data=data)
116
+
117
+
118
+ @router.delete(
119
+ '/policies',
120
+ summary='Delete multiple P permission policies',
121
+ dependencies=[
122
+ DependsRBAC,
123
+ ],
124
+ )
125
+ async def delete_policies(request: Request, ps: list[DeletePolicyParam]) -> ResponseModel:
126
+ data = await casbin_service.delete_policies(ps=ps)
127
+ return response_base.success(request=request, data=data)
128
+
129
+
130
+ @router.delete(
131
+ '/policies/all',
132
+ summary='Delete all P permission policies',
133
+ dependencies=[
134
+ DependsRBAC,
135
+ ],
136
+ )
137
+ async def delete_all_policies(request: Request, sub: DeleteAllPoliciesParam) -> ResponseModel:
138
+ count = await casbin_service.delete_all_policies(sub=sub)
139
+ if count > 0:
140
+ return response_base.success(request=request )
141
+ return response_base.fail(request=request)
142
+
143
+
144
+ @router.get('/groups', summary='Get all G permission policies', dependencies=[DependsJwtAuth])
145
+ async def get_all_groups(request: Request) -> ResponseModel:
146
+ data = await casbin_service.get_group_list()
147
+ return response_base.success(request=request, data=data)
148
+
149
+
150
+ @router.post(
151
+ '/group',
152
+ summary='Add G permission policy',
153
+ dependencies=[
154
+ DependsRBAC,
155
+ ],
156
+ )
157
+ async def create_group(request: Request, g: CreateUserRoleParam) -> ResponseModel:
158
+ """
159
+ G Policy (**Depends on P policy**):
160
+
161
+ - If you add role-based access control in the P policy, you also need to add group-based access control in the G policy to actually have access rights.<br>
162
+ **Format**: User UUID + Role
163
+
164
+ - If you add user-based access control in the P policy, there is no need to add the corresponding G policy to have access rights.<br>
165
+ However, you will not have all the permissions of the user role, but only the specific access rights added by the corresponding P policy.
166
+ """
167
+ data = await casbin_service.create_group(g=g)
168
+ return response_base.success(request=request, data=data)
169
+
170
+
171
+ @router.post(
172
+ '/groups',
173
+ summary='Add multiple G permission policies',
174
+ dependencies=[
175
+ DependsRBAC,
176
+ ],
177
+ )
178
+ async def create_groups(request: Request, gs: list[CreateUserRoleParam]) -> ResponseModel:
179
+ data = await casbin_service.create_groups(gs=gs)
180
+ return response_base.success(request=request, data=data)
181
+
182
+
183
+ @router.delete(
184
+ '/group',
185
+ summary='Delete G permission policy',
186
+ dependencies=[
187
+ DependsRBAC,
188
+ ],
189
+ )
190
+ async def delete_group(request: Request, g: DeleteUserRoleParam) -> ResponseModel:
191
+ data = await casbin_service.delete_group(g=g)
192
+ return response_base.success(request=request, data=data)
193
+
194
+
195
+ @router.delete(
196
+ '/groups',
197
+ summary='Delete multiple G permission policies',
198
+ dependencies=[
199
+ DependsRBAC,
200
+ ],
201
+ )
202
+ async def delete_groups(request: Request, gs: list[DeleteUserRoleParam]) -> ResponseModel:
203
+ data = await casbin_service.delete_groups(gs=gs)
204
+ return response_base.success(request=request, data=data)
205
+
206
+
207
+ @router.delete(
208
+ '/groups/all',
209
+ summary='Delete all G permission policies',
210
+ dependencies=[
211
+ DependsRBAC,
212
+ ],
213
+ )
214
+ async def delete_all_groups(request: Request, uuid: Annotated[UUID, Query(...)]) -> ResponseModel:
215
+ count = await casbin_service.delete_all_groups(uuid=uuid)
216
+ if count > 0:
217
+ return response_base.success(request=request)
218
+ return response_base.fail(request=request)
@@ -0,0 +1,63 @@
1
+ from typing import Annotated
2
+
3
+ from fastapi import APIRouter, Query, Request
4
+
5
+ from backend.app.admin.schema.login_log import GetLoginLogListDetails
6
+ from backend.app.admin.service.login_log_service import login_log_service
7
+ from backend.common.pagination import DependsPagination, paging_data
8
+ from backend.common.response.response_schema import ResponseModel, response_base
9
+ from backend.common.security.jwt import DependsJwtAuth
10
+ from backend.common.security.rbac import DependsRBAC
11
+ from backend.database.db_postgres import CurrentSession
12
+
13
+ router = APIRouter(prefix="/login_log", tags=["Login log"] )
14
+
15
+
16
+ @router.get(
17
+ '/',
18
+ summary='(Fuzzy Criteria) Paging for Login Logs',
19
+ dependencies=[
20
+ DependsJwtAuth,
21
+ DependsPagination,
22
+ ],
23
+ )
24
+ async def get_pagination_login_logs(
25
+ request: Request,
26
+ db: CurrentSession,
27
+ username: Annotated[str | None, Query()] = None,
28
+ status: Annotated[int | None, Query()] = None,
29
+ ip: Annotated[str | None, Query()] = None,
30
+ ) -> ResponseModel:
31
+ log_select = await login_log_service.get_select(username=username, status=status, ip=ip)
32
+ page_data = await paging_data(db, log_select, GetLoginLogListDetails)
33
+ return response_base.success(request=request, data=page_data)
34
+
35
+
36
+ @router.delete(
37
+ '/',
38
+ summary='(Batch) Delete Login Logs',
39
+ dependencies=[
40
+ DependsRBAC,
41
+ DependsJwtAuth,
42
+
43
+ ],
44
+ )
45
+ async def delete_login_log(request: Request, pk: Annotated[list[int], Query(...)]) -> ResponseModel:
46
+ count = await login_log_service.delete(pk=pk)
47
+ if count > 0:
48
+ return response_base.success(request=request)
49
+ return response_base.fail(request=request)
50
+
51
+
52
+ @router.delete(
53
+ '/all',
54
+ summary='Empty login log',
55
+ dependencies=[
56
+ DependsRBAC,
57
+ ],
58
+ )
59
+ async def delete_all_login_logs(request: Request) -> ResponseModel:
60
+ count = await login_log_service.delete_all()
61
+ if count > 0:
62
+ return response_base.success(request=request)
63
+ return response_base.fail(request=request)
@@ -0,0 +1,61 @@
1
+ from typing import Annotated
2
+
3
+ from fastapi import APIRouter, Query, Request
4
+
5
+ from backend.app.admin.schema.opera_log import GetOperaLogListDetails
6
+ from backend.app.admin.service.opera_log_service import opera_log_service
7
+ from backend.common.pagination import DependsPagination, paging_data
8
+ from backend.common.response.response_schema import ResponseModel, response_base
9
+ from backend.common.security.jwt import DependsJwtAuth
10
+ from backend.common.security.rbac import DependsRBAC
11
+ from backend.database.db_postgres import CurrentSession
12
+
13
+ router = APIRouter(prefix="/opera_log", tags=["Operation Log"])
14
+
15
+
16
+ @router.get(
17
+ '/',
18
+ summary='(Fuzzy Condition) Paging for Operation Logs',
19
+ dependencies=[
20
+ DependsJwtAuth,
21
+ DependsPagination,
22
+ ],
23
+ )
24
+ async def get_pagination_opera_logs(
25
+ request: Request,
26
+ db: CurrentSession,
27
+ username: Annotated[str | None, Query()] = None,
28
+ status: Annotated[int | None, Query()] = None,
29
+ ip: Annotated[str | None, Query()] = None,
30
+ ) -> ResponseModel:
31
+ log_select = await opera_log_service.get_select(username=username, status=status, ip=ip)
32
+ page_data = await paging_data(db, log_select, GetOperaLogListDetails)
33
+ return response_base.success(request=request, data=page_data)
34
+
35
+
36
+ @router.delete(
37
+ '/',
38
+ summary='Delete (batch) operation logs',
39
+ dependencies=[
40
+ DependsRBAC,
41
+ ],
42
+ )
43
+ async def delete_opera_log(request: Request, pk: Annotated[list[int], Query(...)]) -> ResponseModel:
44
+ count = await opera_log_service.delete(pk=pk)
45
+ if count > 0:
46
+ return response_base.success(request=request)
47
+ return response_base.fail(request=request)
48
+
49
+
50
+ @router.delete(
51
+ '/all',
52
+ summary='Delete (batch) operation log',
53
+ dependencies=[
54
+ DependsRBAC,
55
+ ],
56
+ )
57
+ async def delete_all_opera_logs(request: Request) -> ResponseModel:
58
+ count = await opera_log_service.delete_all()
59
+ if count > 0:
60
+ return response_base.success(request=request)
61
+ return response_base.fail(request=request)
@@ -0,0 +1,108 @@
1
+ from typing import Annotated
2
+ from fastapi import APIRouter, Path, Query, Request
3
+ from backend.common.pagination import DependsPagination, paging_data
4
+ from backend.database.db_postgres import CurrentSession
5
+ from backend.utils.serializers import select_as_dict, select_list_serialize
6
+
7
+ from backend.app.admin.schema.role import GetRoleListDetails, UpdateRoleParam, CreateRoleParam
8
+ from backend.app.admin.service.role_service import role_service
9
+ from backend.common.response.response_schema import ResponseModel, response_base
10
+ from backend.common.security.jwt import DependsJwtAuth
11
+
12
+ router = APIRouter(prefix="/role", tags=["Role"])
13
+
14
+
15
+ @router.get("/all", summary="Get all roles", dependencies=[DependsJwtAuth])
16
+ async def get_all_roles(request: Request,) -> ResponseModel:
17
+ roles = await role_service.get_all()
18
+ data = select_list_serialize(roles)
19
+ return response_base.success(request=request, data=data)
20
+
21
+
22
+ @router.get("/{pk}/all", summary="Get all user roles", dependencies=[DependsJwtAuth])
23
+ async def get_user_all_roles(request: Request, pk: Annotated[int, Path(...)]) -> ResponseModel:
24
+ roles = await role_service.get_user_roles(pk=pk)
25
+ data = select_list_serialize(roles)
26
+ return response_base.success(request=request, data=data)
27
+
28
+
29
+ @router.get(
30
+ "/{pk}",
31
+ summary="Get role by ID",
32
+ dependencies=[DependsJwtAuth]
33
+ )
34
+ async def get_role(request: Request, pk: Annotated[int, Path(...)]) -> ResponseModel:
35
+ role = await role_service.get_by_id(role_id=pk)
36
+ data = GetRoleListDetails(**select_as_dict(role))
37
+ return response_base.success(request=request, data=data)
38
+
39
+
40
+ @router.get(
41
+ "/",
42
+ summary="Get roles pagination",
43
+ dependencies=[
44
+ DependsJwtAuth,
45
+ DependsPagination,
46
+ ],
47
+ )
48
+ async def get_pagination_roles(
49
+ request: Request,
50
+ db: CurrentSession,
51
+ name: Annotated[str | None, Query()] = None,
52
+ data_scope: Annotated[int | None, Query()] = None,
53
+ status: Annotated[int | None, Query()] = None,
54
+ ) -> ResponseModel:
55
+ role_select = await role_service.get_select(
56
+ name=name, data_scope=data_scope, status=status
57
+ )
58
+ page_data = await paging_data(db, role_select, GetRoleListDetails)
59
+ return response_base.success(request=request, data=page_data)
60
+
61
+
62
+ @router.post(
63
+ "/",
64
+ summary="Role creation",
65
+ dependencies=[
66
+ # Depends(RequestPermission('sys:role:add')),
67
+ # DependsRBAC,
68
+ DependsJwtAuth,
69
+ ],
70
+ )
71
+ async def create_role(request: Request, obj: CreateRoleParam) -> ResponseModel:
72
+ await role_service.create(obj=obj)
73
+ return response_base.success(request=request)
74
+
75
+
76
+ @router.put(
77
+ "/{pk}",
78
+ summary="Update role",
79
+ dependencies=[
80
+ # Depends(RequestPermission('sys:role:edit')),
81
+ # DependsRBAC,
82
+ DependsJwtAuth,
83
+ ],
84
+ )
85
+ async def update_role(
86
+ request: Request,
87
+ pk: Annotated[int, Path(...)], obj: UpdateRoleParam
88
+ ) -> ResponseModel:
89
+ count = await role_service.update(pk=pk, obj=obj)
90
+ if count > 0:
91
+ return response_base.success(request=request)
92
+ return response_base.fail(request=request)
93
+
94
+
95
+ @router.delete(
96
+ "/",
97
+ summary="Delete roles",
98
+ dependencies=[
99
+ # Depends(RequestPermission('sys:role:del')),
100
+ # DependsRBAC,
101
+ DependsJwtAuth,
102
+ ],
103
+ )
104
+ async def delete_role(request: Request, pk: Annotated[list[int], Query(...)]) -> ResponseModel:
105
+ count = await role_service.delete(pk=pk)
106
+ if count > 0:
107
+ return response_base.success(request=request)
108
+ return response_base.fail(request=request)