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.
- shaapi/__init__.py +3 -0
- shaapi/cli.py +97 -0
- shaapi/generator.py +114 -0
- shaapi/template/.dockerignore +37 -0
- shaapi/template/.env.template +59 -0
- shaapi/template/.gitattributes +12 -0
- shaapi/template/.gitignore +170 -0
- shaapi/template/.gitlab-ci.yml +89 -0
- shaapi/template/Dockerfile +59 -0
- shaapi/template/LICENSE +21 -0
- shaapi/template/README.md +206 -0
- shaapi/template/backend/.gitignore +164 -0
- shaapi/template/backend/__init__.py +0 -0
- shaapi/template/backend/alembic/README +1 -0
- shaapi/template/backend/alembic/env.py +102 -0
- shaapi/template/backend/alembic/script.py.mako +26 -0
- shaapi/template/backend/alembic/versions/2026_06_08_1024-64524c63b666_initial.py +143 -0
- shaapi/template/backend/alembic.ini +117 -0
- shaapi/template/backend/app/__init__.py +55 -0
- shaapi/template/backend/app/admin/__init__.py +1 -0
- shaapi/template/backend/app/admin/api/v1/__init__.py +0 -0
- shaapi/template/backend/app/admin/api/v1/auth.py +59 -0
- shaapi/template/backend/app/admin/api/v1/casbin.py +218 -0
- shaapi/template/backend/app/admin/api/v1/login_log.py +63 -0
- shaapi/template/backend/app/admin/api/v1/opera_log.py +61 -0
- shaapi/template/backend/app/admin/api/v1/role.py +108 -0
- shaapi/template/backend/app/admin/api/v1/user.py +47 -0
- shaapi/template/backend/app/admin/schema/casbin_rule.py +45 -0
- shaapi/template/backend/app/admin/schema/login_log.py +36 -0
- shaapi/template/backend/app/admin/schema/opera_log.py +43 -0
- shaapi/template/backend/app/admin/schema/role.py +36 -0
- shaapi/template/backend/app/admin/schema/sso.py +37 -0
- shaapi/template/backend/app/admin/schema/token.py +74 -0
- shaapi/template/backend/app/admin/schema/user.py +93 -0
- shaapi/template/backend/app/admin/service/auth_service.py +233 -0
- shaapi/template/backend/app/admin/service/casbin_service.py +135 -0
- shaapi/template/backend/app/admin/service/login_log_service.py +62 -0
- shaapi/template/backend/app/admin/service/opera_log_service.py +31 -0
- shaapi/template/backend/app/admin/service/role_service.py +79 -0
- shaapi/template/backend/app/admin/service/secure_token_service.py +60 -0
- shaapi/template/backend/app/admin/service/user_service.py +153 -0
- shaapi/template/backend/app/api.py +11 -0
- shaapi/template/backend/common/__init__.py +0 -0
- shaapi/template/backend/common/cloud_storage/__init__.py +11 -0
- shaapi/template/backend/common/cloud_storage/cloud_storage.py +180 -0
- shaapi/template/backend/common/dataclasses.py +52 -0
- shaapi/template/backend/common/email_conf/email.py +105 -0
- shaapi/template/backend/common/enums.py +144 -0
- shaapi/template/backend/common/exception/__init__.py +0 -0
- shaapi/template/backend/common/exception/errors.py +87 -0
- shaapi/template/backend/common/exception/exception_handler.py +280 -0
- shaapi/template/backend/common/log.py +123 -0
- shaapi/template/backend/common/model.py +68 -0
- shaapi/template/backend/common/pagination.py +83 -0
- shaapi/template/backend/common/response/__init__.py +0 -0
- shaapi/template/backend/common/response/response_code.py +158 -0
- shaapi/template/backend/common/response/response_schema.py +110 -0
- shaapi/template/backend/common/schema.py +144 -0
- shaapi/template/backend/common/security/jwt.py +203 -0
- shaapi/template/backend/common/security/rbac.py +98 -0
- shaapi/template/backend/common/security/sec_token.py +6 -0
- shaapi/template/backend/common/socketio/action.py +11 -0
- shaapi/template/backend/common/socketio/server.py +50 -0
- shaapi/template/backend/common/sso/base.py +69 -0
- shaapi/template/backend/common/sso/google.py +127 -0
- shaapi/template/backend/core/conf.py +208 -0
- shaapi/template/backend/core/path_conf.py +24 -0
- shaapi/template/backend/core/registrar.py +195 -0
- shaapi/template/backend/crud/__init__.py +1 -0
- shaapi/template/backend/crud/crud_base.py +35 -0
- shaapi/template/backend/crud/crud_casbin.py +46 -0
- shaapi/template/backend/crud/crud_login_log.py +58 -0
- shaapi/template/backend/crud/crud_opera_log.py +58 -0
- shaapi/template/backend/crud/crud_role.py +128 -0
- shaapi/template/backend/crud/crud_user.py +267 -0
- shaapi/template/backend/database/__init__.py +0 -0
- shaapi/template/backend/database/db_postgres.py +125 -0
- shaapi/template/backend/database/db_redis.py +62 -0
- shaapi/template/backend/entrypoint-api.sh +19 -0
- shaapi/template/backend/lang/en/app.py +18 -0
- shaapi/template/backend/lang/en/auth.py +10 -0
- shaapi/template/backend/lang/fr/app.py +18 -0
- shaapi/template/backend/lang/fr/auth.py +10 -0
- shaapi/template/backend/main.py +54 -0
- shaapi/template/backend/middleware/__init__.py +1 -0
- shaapi/template/backend/middleware/access_middleware.py +19 -0
- shaapi/template/backend/middleware/i18n_middleware.py +19 -0
- shaapi/template/backend/middleware/jwt_auth_middleware.py +73 -0
- shaapi/template/backend/middleware/opera_log_middleware.py +179 -0
- shaapi/template/backend/middleware/state_middleware.py +26 -0
- shaapi/template/backend/models/__init__.py +10 -0
- shaapi/template/backend/models/associations.py +20 -0
- shaapi/template/backend/models/casbin_rule.py +30 -0
- shaapi/template/backend/models/login_log.py +28 -0
- shaapi/template/backend/models/opera_log.py +36 -0
- shaapi/template/backend/models/role.py +27 -0
- shaapi/template/backend/models/user.py +30 -0
- shaapi/template/backend/seeder/json/admin.json +15 -0
- shaapi/template/backend/seeder/json/user.json +15 -0
- shaapi/template/backend/seeder/run.py +34 -0
- shaapi/template/backend/static/ip2region.xdb +0 -0
- shaapi/template/backend/templates/build/meet.html +169 -0
- shaapi/template/backend/templates/build/new_account.html +373 -0
- shaapi/template/backend/templates/build/reset-password.html +170 -0
- shaapi/template/backend/templates/build/test_email.html +25 -0
- shaapi/template/backend/templates/build/welcome-one-1.html +160 -0
- shaapi/template/backend/templates/build/welcome-one.html +178 -0
- shaapi/template/backend/templates/build/welcome-two.html +234 -0
- shaapi/template/backend/templates/index.html +0 -0
- shaapi/template/backend/templates/src/new_account.mjml +15 -0
- shaapi/template/backend/templates/src/reset_password.mjml +19 -0
- shaapi/template/backend/templates/src/test_email.mjml +11 -0
- shaapi/template/backend/templates/ws/ws.html +70 -0
- shaapi/template/backend/utils/demo_site.py +18 -0
- shaapi/template/backend/utils/encrypt.py +108 -0
- shaapi/template/backend/utils/health_check.py +34 -0
- shaapi/template/backend/utils/prometheus.py +135 -0
- shaapi/template/backend/utils/request_parse.py +110 -0
- shaapi/template/backend/utils/serializers.py +75 -0
- shaapi/template/backend/utils/timezone.py +51 -0
- shaapi/template/backend/utils/trace_id.py +7 -0
- shaapi/template/backend/utils/translator.py +28 -0
- shaapi/template/devops/scripts/deploy.sh +7 -0
- shaapi/template/devops/scripts/setup_env.sh +62 -0
- shaapi/template/docker-compose.monitoring.yml +63 -0
- shaapi/template/docker-compose.override.yml +12 -0
- shaapi/template/docker-compose.yml +90 -0
- shaapi/template/docker-run.sh +99 -0
- shaapi/template/etc/dashboards/fastapi-observability.json +1044 -0
- shaapi/template/etc/dashboards.yaml +10 -0
- shaapi/template/etc/grafana/datasource.yml +79 -0
- shaapi/template/etc/prometheus/prometheus.yml +52 -0
- shaapi/template/package-lock.json +2102 -0
- shaapi/template/package.json +16 -0
- shaapi/template/pyproject.toml +78 -0
- shaapi/template/uv.lock +2866 -0
- shaapi-0.1.0.dist-info/METADATA +92 -0
- shaapi-0.1.0.dist-info/RECORD +141 -0
- shaapi-0.1.0.dist-info/WHEEL +4 -0
- shaapi-0.1.0.dist-info/entry_points.txt +2 -0
- shaapi-0.1.0.dist-info/licenses/LICENCE +21 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
|
|
3
|
+
from asgi_correlation_id import CorrelationIdMiddleware
|
|
4
|
+
from fastapi import Depends, FastAPI, APIRouter
|
|
5
|
+
from fastapi_limiter import FastAPILimiter
|
|
6
|
+
from fastapi_pagination import add_pagination
|
|
7
|
+
from starlette.middleware.authentication import AuthenticationMiddleware
|
|
8
|
+
|
|
9
|
+
from backend.common.exception.exception_handler import register_exception
|
|
10
|
+
from backend.common.log import set_customize_logfile, setup_logging
|
|
11
|
+
from backend.core.conf import settings
|
|
12
|
+
from backend.core.path_conf import STATIC_DIR
|
|
13
|
+
from backend.database.db_postgres import create_table
|
|
14
|
+
from backend.database.db_redis import redis_client
|
|
15
|
+
from backend.middleware.jwt_auth_middleware import JwtAuthMiddleware
|
|
16
|
+
from backend.middleware.opera_log_middleware import OperaLogMiddleware
|
|
17
|
+
from backend.middleware.state_middleware import StateMiddleware
|
|
18
|
+
from backend.middleware.i18n_middleware import I18nMiddleware
|
|
19
|
+
from backend.utils.demo_site import demo_site
|
|
20
|
+
from backend.utils.health_check import ensure_unique_route_names, http_limit_callback
|
|
21
|
+
from backend.utils.serializers import MsgSpecJSONResponse
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@asynccontextmanager
|
|
27
|
+
async def register_init(app: FastAPI):
|
|
28
|
+
"""
|
|
29
|
+
Start initialization
|
|
30
|
+
|
|
31
|
+
:return:
|
|
32
|
+
"""
|
|
33
|
+
# Creating database tables (dev convenience). In production rely on Alembic
|
|
34
|
+
# migrations and set DB_AUTO_CREATE=false.
|
|
35
|
+
if settings.DB_AUTO_CREATE:
|
|
36
|
+
await create_table()
|
|
37
|
+
# Connecting to redis
|
|
38
|
+
await redis_client.open()
|
|
39
|
+
# Initialize limiter
|
|
40
|
+
await FastAPILimiter.init(
|
|
41
|
+
redis=redis_client,
|
|
42
|
+
prefix=settings.REQUEST_LIMITER_REDIS_PREFIX,
|
|
43
|
+
http_callback=http_limit_callback,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
yield
|
|
47
|
+
|
|
48
|
+
# Closing a redis connection
|
|
49
|
+
await redis_client.close()
|
|
50
|
+
# Close limiter
|
|
51
|
+
await FastAPILimiter.close()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def register_app(router: APIRouter, name: str = "Parse name" ) -> FastAPI:
|
|
55
|
+
# FastAPI
|
|
56
|
+
app = FastAPI(
|
|
57
|
+
title=f"[{settings.ENVIRONMENT.upper()}] {settings.FASTAPI_TITLE} {name.upper()}",
|
|
58
|
+
contact={
|
|
59
|
+
"name": "shaapi",
|
|
60
|
+
"email": "shalomtehe219@gmail.com",
|
|
61
|
+
},
|
|
62
|
+
version=settings.FASTAPI_VERSION,
|
|
63
|
+
description=settings.FASTAPI_DESCRIPTION,
|
|
64
|
+
docs_url=f"{settings.FASTAPI_DOCS_URL}",
|
|
65
|
+
redoc_url=f"{settings.FASTAPI_REDOCS_URL}",
|
|
66
|
+
openapi_url=f"{settings.FASTAPI_OPENAPI_URL}",
|
|
67
|
+
default_response_class=MsgSpecJSONResponse,
|
|
68
|
+
# NOTE: the lifespan is owned by the parent app (see backend/main.py).
|
|
69
|
+
# Starlette does NOT run the lifespan of mounted sub-applications, so
|
|
70
|
+
# shared resources (DB tables, Redis, rate limiter) are initialized once
|
|
71
|
+
# on the parent instead.
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# log (computing)
|
|
75
|
+
register_logger()
|
|
76
|
+
|
|
77
|
+
# Static files
|
|
78
|
+
register_static_file(app)
|
|
79
|
+
|
|
80
|
+
# middleware
|
|
81
|
+
register_middleware(app, name)
|
|
82
|
+
|
|
83
|
+
# routing (in computer networks)
|
|
84
|
+
register_router(app, router)
|
|
85
|
+
|
|
86
|
+
# tab window (in a web browser etc)
|
|
87
|
+
register_page(app)
|
|
88
|
+
|
|
89
|
+
# Global Exception Handling
|
|
90
|
+
register_exception(app)
|
|
91
|
+
return app
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def register_logger() -> None:
|
|
95
|
+
"""
|
|
96
|
+
System Log
|
|
97
|
+
|
|
98
|
+
:return:
|
|
99
|
+
"""
|
|
100
|
+
setup_logging()
|
|
101
|
+
set_customize_logfile()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def register_static_file(app: FastAPI):
|
|
105
|
+
"""
|
|
106
|
+
Static file interaction development model, production using nginx static resource service
|
|
107
|
+
|
|
108
|
+
:param app:
|
|
109
|
+
:return:
|
|
110
|
+
"""
|
|
111
|
+
if settings.FASTAPI_STATIC_FILES:
|
|
112
|
+
import os
|
|
113
|
+
|
|
114
|
+
from fastapi.staticfiles import StaticFiles
|
|
115
|
+
|
|
116
|
+
if not os.path.exists(STATIC_DIR):
|
|
117
|
+
os.mkdir(STATIC_DIR)
|
|
118
|
+
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def register_middleware(app: FastAPI, name: str = "Parse name"):
|
|
122
|
+
"""
|
|
123
|
+
Middleware, execution order from bottom to top
|
|
124
|
+
|
|
125
|
+
:param app:
|
|
126
|
+
:return:
|
|
127
|
+
"""
|
|
128
|
+
# Metrics middleware (opt-in observability)
|
|
129
|
+
if settings.OBSERVABILITY_ENABLED:
|
|
130
|
+
from backend.utils.prometheus import PrometheusMiddleware
|
|
131
|
+
|
|
132
|
+
app.add_middleware(PrometheusMiddleware, app_name=settings.APP_NAME)
|
|
133
|
+
|
|
134
|
+
# Opera log (required)
|
|
135
|
+
app.add_middleware(OperaLogMiddleware)
|
|
136
|
+
|
|
137
|
+
# I18N translation middleware
|
|
138
|
+
app.add_middleware(I18nMiddleware)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
app.add_middleware(
|
|
142
|
+
AuthenticationMiddleware, backend=JwtAuthMiddleware(), on_error=JwtAuthMiddleware.auth_exception_handler
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Access log
|
|
146
|
+
if settings.MIDDLEWARE_ACCESS:
|
|
147
|
+
from backend.middleware.access_middleware import AccessMiddleware
|
|
148
|
+
|
|
149
|
+
app.add_middleware(AccessMiddleware)
|
|
150
|
+
|
|
151
|
+
# State
|
|
152
|
+
app.add_middleware(StateMiddleware)
|
|
153
|
+
|
|
154
|
+
# Trace ID (required)
|
|
155
|
+
app.add_middleware(CorrelationIdMiddleware, validator=False)
|
|
156
|
+
|
|
157
|
+
# CORS: Always at the end
|
|
158
|
+
if settings.MIDDLEWARE_CORS:
|
|
159
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
160
|
+
|
|
161
|
+
app.add_middleware(
|
|
162
|
+
CORSMiddleware,
|
|
163
|
+
allow_origins=settings.CORS_ALLOWED_ORIGINS,
|
|
164
|
+
allow_credentials=True,
|
|
165
|
+
allow_methods=["*"],
|
|
166
|
+
allow_headers=["*"],
|
|
167
|
+
expose_headers=settings.CORS_EXPOSE_HEADERS,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def register_router(app: FastAPI, router: APIRouter):
|
|
172
|
+
"""
|
|
173
|
+
routing
|
|
174
|
+
|
|
175
|
+
:param app: FastAPI
|
|
176
|
+
:return:
|
|
177
|
+
"""
|
|
178
|
+
dependencies = [Depends(demo_site)] if settings.DEMO_MODE else None
|
|
179
|
+
|
|
180
|
+
# API
|
|
181
|
+
app.include_router(router, dependencies=dependencies)
|
|
182
|
+
|
|
183
|
+
# Extra
|
|
184
|
+
ensure_unique_route_names(app)
|
|
185
|
+
# simplify_operation_ids(app)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def register_page(app: FastAPI):
|
|
189
|
+
"""
|
|
190
|
+
paging search
|
|
191
|
+
|
|
192
|
+
:param app:
|
|
193
|
+
:return:
|
|
194
|
+
"""
|
|
195
|
+
add_pagination(app)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from sqlalchemy import select
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
4
|
+
from sqlalchemy.orm import selectinload
|
|
5
|
+
from sqlalchemy.sql import Select
|
|
6
|
+
from sqlalchemy_crud_plus.utils import parse_filters
|
|
7
|
+
from sqlalchemy_crud_plus.types import Model
|
|
8
|
+
from sqlalchemy_crud_plus import CRUDPlus
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CRUDBase(CRUDPlus[Model]):
|
|
14
|
+
|
|
15
|
+
def get_with_relationship(self, stmt: Select, populates: list[str] = [], sub_populates: list[Any] = []) -> Select:
|
|
16
|
+
for populate in populates:
|
|
17
|
+
stmt = stmt.options(selectinload(getattr(self.model, populate)), *sub_populates)
|
|
18
|
+
|
|
19
|
+
return stmt
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def select_model_by_column(self, session: AsyncSession, populates: list[str] = [], sub_populates: list[Any] = [], **kwargs) -> Model | None:
|
|
23
|
+
"""
|
|
24
|
+
Query by column
|
|
25
|
+
|
|
26
|
+
:param session: The SQLAlchemy async session.
|
|
27
|
+
:param kwargs: Query expressions.
|
|
28
|
+
:return:
|
|
29
|
+
"""
|
|
30
|
+
filters = parse_filters(self.model, **kwargs)
|
|
31
|
+
stmt = select(self.model).where(*filters)
|
|
32
|
+
stmt = self.get_with_relationship(stmt, populates=populates, sub_populates=sub_populates)
|
|
33
|
+
query = await session.execute(stmt)
|
|
34
|
+
return query.scalars().first()
|
|
35
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import Select
|
|
4
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
5
|
+
from backend.crud.crud_base import CRUDBase
|
|
6
|
+
|
|
7
|
+
from backend.models import CasbinRule
|
|
8
|
+
from backend.app.admin.schema.casbin_rule import DeleteAllPoliciesParam
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CRUDCasbin(CRUDBase[CasbinRule]):
|
|
12
|
+
async def get_list(self, ptype: str, sub: str) -> Select:
|
|
13
|
+
"""
|
|
14
|
+
Getting a list of casbin policies
|
|
15
|
+
|
|
16
|
+
:param ptype:
|
|
17
|
+
:param sub:
|
|
18
|
+
:return:
|
|
19
|
+
"""
|
|
20
|
+
return await self.select_order('id', 'desc', ptype=ptype, v0__like=f'%{sub}%')
|
|
21
|
+
|
|
22
|
+
async def delete_policies_by_sub(self, db: AsyncSession, sub: DeleteAllPoliciesParam) -> int:
|
|
23
|
+
"""
|
|
24
|
+
Delete all P casbin policies for the role
|
|
25
|
+
|
|
26
|
+
:param db:
|
|
27
|
+
:param sub:
|
|
28
|
+
:return:
|
|
29
|
+
"""
|
|
30
|
+
where_list = [sub.role]
|
|
31
|
+
if sub.uuid:
|
|
32
|
+
where_list.append(sub.uuid)
|
|
33
|
+
return await self.delete_model_by_column(db, allow_multiple=True, v0__mor={'eq': where_list})
|
|
34
|
+
|
|
35
|
+
async def delete_groups_by_uuid(self, db: AsyncSession, uuid: UUID) -> int:
|
|
36
|
+
"""
|
|
37
|
+
Delete all G casbin policies for a user
|
|
38
|
+
|
|
39
|
+
:param db:
|
|
40
|
+
:param uuid:
|
|
41
|
+
:return:
|
|
42
|
+
"""
|
|
43
|
+
return await self.delete_model_by_column(db, allow_multiple=True, v0=str(uuid))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
casbin_dao: CRUDCasbin = CRUDCasbin(CasbinRule)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from sqlalchemy import Select
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlalchemy_crud_plus import CRUDPlus
|
|
4
|
+
|
|
5
|
+
from backend.models import LoginLog
|
|
6
|
+
from backend.app.admin.schema.login_log import CreateLoginLogParam
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CRUDLoginLog(CRUDPlus[LoginLog]):
|
|
10
|
+
async def get_list(self, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
|
11
|
+
"""
|
|
12
|
+
Get the list of login logs
|
|
13
|
+
|
|
14
|
+
:param username:
|
|
15
|
+
:param status:
|
|
16
|
+
:param ip:
|
|
17
|
+
:return:
|
|
18
|
+
"""
|
|
19
|
+
filters = {}
|
|
20
|
+
if username is not None:
|
|
21
|
+
filters.update(username__like=f'%{username}%')
|
|
22
|
+
if status is not None:
|
|
23
|
+
filters.update(status=status)
|
|
24
|
+
if ip is not None:
|
|
25
|
+
filters.update(ip__like=f'%{ip}%')
|
|
26
|
+
return await self.select_order('created_time', 'desc', **filters)
|
|
27
|
+
|
|
28
|
+
async def create(self, db: AsyncSession, obj_in: CreateLoginLogParam) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Create login logs
|
|
31
|
+
|
|
32
|
+
:param db:
|
|
33
|
+
:param obj_in:
|
|
34
|
+
:return:
|
|
35
|
+
"""
|
|
36
|
+
await self.create_model(db, obj_in, commit=True)
|
|
37
|
+
|
|
38
|
+
async def delete(self, db: AsyncSession, pk: list[int]) -> int:
|
|
39
|
+
"""
|
|
40
|
+
Delete login logs
|
|
41
|
+
|
|
42
|
+
:param db:
|
|
43
|
+
:param pk:
|
|
44
|
+
:return:
|
|
45
|
+
"""
|
|
46
|
+
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk)
|
|
47
|
+
|
|
48
|
+
async def delete_all(self, db: AsyncSession) -> int:
|
|
49
|
+
"""
|
|
50
|
+
Delete all login logs
|
|
51
|
+
|
|
52
|
+
:param db:
|
|
53
|
+
:return:
|
|
54
|
+
"""
|
|
55
|
+
return await self.delete_model_by_column(db, allow_multiple=True)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
login_log_dao: CRUDLoginLog = CRUDLoginLog(LoginLog)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from sqlalchemy import Select
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from backend.crud.crud_base import CRUDBase
|
|
4
|
+
|
|
5
|
+
from backend.models import OperaLog
|
|
6
|
+
from backend.app.admin.schema.opera_log import CreateOperaLogParam
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CRUDOperaLogDao(CRUDBase[OperaLog]):
|
|
10
|
+
async def get_list(self, username: str | None = None, status: int | None = None, ip: str | None = None) -> Select:
|
|
11
|
+
"""
|
|
12
|
+
Getting a list of operation logs
|
|
13
|
+
|
|
14
|
+
:param username:
|
|
15
|
+
:param status:
|
|
16
|
+
:param ip:
|
|
17
|
+
:return:
|
|
18
|
+
"""
|
|
19
|
+
filters = {}
|
|
20
|
+
if username is not None:
|
|
21
|
+
filters.update(username=f'%{username}%')
|
|
22
|
+
if status is not None:
|
|
23
|
+
filters.update(status=status)
|
|
24
|
+
if ip is not None:
|
|
25
|
+
filters.update(ip=f'%{ip}%')
|
|
26
|
+
return await self.select_order('created_time', 'desc', **filters)
|
|
27
|
+
|
|
28
|
+
async def create(self, db: AsyncSession, obj_in: CreateOperaLogParam) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Creating operation logs
|
|
31
|
+
|
|
32
|
+
:param db:
|
|
33
|
+
:param obj_in:
|
|
34
|
+
:return:
|
|
35
|
+
"""
|
|
36
|
+
await self.create_model(db, obj_in)
|
|
37
|
+
|
|
38
|
+
async def delete(self, db: AsyncSession, pk: list[int]) -> int:
|
|
39
|
+
"""
|
|
40
|
+
Delete operation log
|
|
41
|
+
|
|
42
|
+
:param db:
|
|
43
|
+
:param pk:
|
|
44
|
+
:return:
|
|
45
|
+
"""
|
|
46
|
+
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk)
|
|
47
|
+
|
|
48
|
+
async def delete_all(self, db: AsyncSession) -> int:
|
|
49
|
+
"""
|
|
50
|
+
Delete all operation logs
|
|
51
|
+
|
|
52
|
+
:param db:
|
|
53
|
+
:return:
|
|
54
|
+
"""
|
|
55
|
+
return await self.delete_model_by_column(db, allow_multiple=True)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
opera_log_dao: CRUDOperaLogDao = CRUDOperaLogDao(OperaLog)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from sqlalchemy import Sequence, desc, select
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlalchemy.sql import Select
|
|
4
|
+
from backend.crud.crud_base import CRUDBase
|
|
5
|
+
|
|
6
|
+
from backend.models import Role, Role
|
|
7
|
+
from backend.app.admin.schema.role import (
|
|
8
|
+
CreateRoleParam,
|
|
9
|
+
UpdateRoleParam,
|
|
10
|
+
)
|
|
11
|
+
from backend.models.user import User
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CRUDRole(CRUDBase[Role]):
|
|
15
|
+
async def get(self, db: AsyncSession, role_id: int) -> Role | None:
|
|
16
|
+
"""
|
|
17
|
+
Getting Roles
|
|
18
|
+
|
|
19
|
+
:param db:
|
|
20
|
+
:param role_id:
|
|
21
|
+
:return:
|
|
22
|
+
"""
|
|
23
|
+
return await self.select_model(db, role_id)
|
|
24
|
+
|
|
25
|
+
async def get(self, db, role_id: int) -> Role | None:
|
|
26
|
+
"""
|
|
27
|
+
Get role by id
|
|
28
|
+
|
|
29
|
+
:param db:
|
|
30
|
+
:param role_id:
|
|
31
|
+
:return:
|
|
32
|
+
"""
|
|
33
|
+
return await self.select_model(db, role_id)
|
|
34
|
+
|
|
35
|
+
async def get_all(self, db) -> Sequence[Role]:
|
|
36
|
+
"""
|
|
37
|
+
Get all roles
|
|
38
|
+
|
|
39
|
+
:param db:
|
|
40
|
+
:return:
|
|
41
|
+
"""
|
|
42
|
+
return await self.select_models(db)
|
|
43
|
+
|
|
44
|
+
async def get_user_roles(self, db, user_id: int) -> Sequence[Role]:
|
|
45
|
+
"""
|
|
46
|
+
Get user roles
|
|
47
|
+
|
|
48
|
+
:param db:
|
|
49
|
+
:param user_id:
|
|
50
|
+
:return:
|
|
51
|
+
"""
|
|
52
|
+
stmt = select(self.model).join(self.model.users).where(User.id == user_id)
|
|
53
|
+
roles = await db.execute(stmt)
|
|
54
|
+
return roles.scalars().all()
|
|
55
|
+
|
|
56
|
+
async def get_list(
|
|
57
|
+
self, name: str = None, data_scope: int = None, status: int = None
|
|
58
|
+
) -> Select:
|
|
59
|
+
"""
|
|
60
|
+
Get role list
|
|
61
|
+
|
|
62
|
+
:param name:
|
|
63
|
+
:param data_scope:
|
|
64
|
+
:param status:
|
|
65
|
+
:return:
|
|
66
|
+
"""
|
|
67
|
+
stmt = (
|
|
68
|
+
select(self.model)
|
|
69
|
+
.order_by(desc(self.model.created_time))
|
|
70
|
+
)
|
|
71
|
+
where_list = []
|
|
72
|
+
if name:
|
|
73
|
+
where_list.append(self.model.name.like(f"%{name}%"))
|
|
74
|
+
if data_scope:
|
|
75
|
+
where_list.append(self.model.data_scope == data_scope)
|
|
76
|
+
if status is not None:
|
|
77
|
+
where_list.append(self.model.status == status)
|
|
78
|
+
if where_list:
|
|
79
|
+
stmt = stmt.where(*where_list)
|
|
80
|
+
return stmt
|
|
81
|
+
|
|
82
|
+
async def get_by_name(self, db, name: str) -> Role | None:
|
|
83
|
+
"""
|
|
84
|
+
Get role by name
|
|
85
|
+
|
|
86
|
+
:param db:
|
|
87
|
+
:param name:
|
|
88
|
+
:return:
|
|
89
|
+
"""
|
|
90
|
+
roles = await self.select_model_by_column(db, name=name)
|
|
91
|
+
|
|
92
|
+
return roles
|
|
93
|
+
|
|
94
|
+
async def create(self, db, obj_in: CreateRoleParam) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Creating roles
|
|
97
|
+
|
|
98
|
+
:param db:
|
|
99
|
+
:param obj_in:
|
|
100
|
+
:return:
|
|
101
|
+
"""
|
|
102
|
+
await self.create_model(db, obj_in)
|
|
103
|
+
|
|
104
|
+
async def update(self, db, role_id: int, obj_in: UpdateRoleParam) -> int:
|
|
105
|
+
"""
|
|
106
|
+
Updating roles
|
|
107
|
+
|
|
108
|
+
:param db:
|
|
109
|
+
:param role_id:
|
|
110
|
+
:param obj_in:
|
|
111
|
+
:return:
|
|
112
|
+
"""
|
|
113
|
+
return await self.update_model(db, role_id, obj_in)
|
|
114
|
+
|
|
115
|
+
async def delete(self, db, role_id: list[int]) -> int:
|
|
116
|
+
"""
|
|
117
|
+
Delete roles
|
|
118
|
+
|
|
119
|
+
:param db:
|
|
120
|
+
:param role_id:
|
|
121
|
+
:return:
|
|
122
|
+
"""
|
|
123
|
+
return await self.delete_model_by_column(
|
|
124
|
+
db, allow_multiple=True, id__in=role_id
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
role_dao: CRUDRole = CRUDRole(Role)
|