fastgenerateapi 0.0.27__py2.py3-none-any.whl → 1.1.6__py2.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.
Potentially problematic release.
This version of fastgenerateapi might be problematic. Click here for more details.
- fastgenerateapi/__init__.py +2 -2
- fastgenerateapi/__version__.py +1 -1
- fastgenerateapi/api_view/base_view.py +17 -7
- fastgenerateapi/api_view/create_view.py +1 -1
- fastgenerateapi/api_view/delete_filter_view.py +1 -1
- fastgenerateapi/api_view/delete_tree_view.py +3 -3
- fastgenerateapi/api_view/delete_view.py +3 -3
- fastgenerateapi/api_view/get_all_view.py +21 -17
- fastgenerateapi/api_view/get_one_view.py +1 -1
- fastgenerateapi/api_view/get_relation_view.py +1 -1
- fastgenerateapi/api_view/get_tree_view.py +1 -1
- fastgenerateapi/api_view/mixin/base_mixin.py +11 -7
- fastgenerateapi/api_view/mixin/dbmodel_mixin.py +30 -20
- fastgenerateapi/api_view/mixin/response_mixin.py +79 -32
- fastgenerateapi/api_view/mixin/tool_mixin.py +1 -357
- fastgenerateapi/api_view/mixin/utils/__init__.py +0 -0
- fastgenerateapi/api_view/mixin/utils/docx_util.py +399 -0
- fastgenerateapi/api_view/mixin/utils/file_util.py +30 -0
- fastgenerateapi/api_view/mixin/utils/pdf_util.py +76 -0
- fastgenerateapi/api_view/mixin/utils/xlsx_util.py +336 -0
- fastgenerateapi/api_view/mixin/utils/zip_util.py +50 -0
- fastgenerateapi/api_view/switch_view.py +11 -11
- fastgenerateapi/api_view/update_relation_view.py +3 -3
- fastgenerateapi/api_view/update_view.py +1 -1
- fastgenerateapi/cache/cache_decorator.py +1 -1
- fastgenerateapi/controller/filter_controller.py +68 -26
- fastgenerateapi/controller/router_controller.py +9 -9
- fastgenerateapi/controller/rpc_controller.py +1 -1
- fastgenerateapi/controller/ws_controller.py +1 -1
- fastgenerateapi/deps/filter_params_deps.py +34 -4
- fastgenerateapi/deps/paginator_deps.py +4 -4
- fastgenerateapi/deps/tree_params_deps.py +4 -4
- fastgenerateapi/example/models.py +3 -2
- fastgenerateapi/example/schemas.py +1 -1
- fastgenerateapi/example/views.py +1 -1
- fastgenerateapi/fastapi_utils/__init__.py +0 -0
- fastgenerateapi/fastapi_utils/all.py +5 -0
- fastgenerateapi/fastapi_utils/param_utils.py +37 -0
- fastgenerateapi/fastapi_utils/response_utils.py +344 -0
- fastgenerateapi/model/__init__.py +0 -0
- fastgenerateapi/model/base_model.py +56 -0
- fastgenerateapi/my_fields/enum_field.py +8 -8
- fastgenerateapi/my_fields/pk_field.py +1 -1
- fastgenerateapi/my_fields/soft_delete_field.py +4 -3
- fastgenerateapi/my_fields/validator.py +60 -0
- fastgenerateapi/pydantic_utils/base_model.py +54 -21
- fastgenerateapi/pydantic_utils/base_settings.py +16 -0
- fastgenerateapi/pydantic_utils/json_encoders.py +2 -1
- fastgenerateapi/schemas_factory/common_function.py +1 -1
- fastgenerateapi/schemas_factory/common_schema_factory.py +4 -4
- fastgenerateapi/schemas_factory/create_schema_factory.py +4 -4
- fastgenerateapi/schemas_factory/filter_schema_factory.py +6 -6
- fastgenerateapi/schemas_factory/get_all_schema_factory.py +5 -5
- fastgenerateapi/schemas_factory/get_one_schema_factory.py +4 -3
- fastgenerateapi/schemas_factory/get_relation_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/get_tree_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/response_factory.py +5 -10
- fastgenerateapi/schemas_factory/sql_get_all_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/update_schema_factory.py +4 -4
- fastgenerateapi/settings/__init__.py +6 -0
- fastgenerateapi/settings/all_settings.py +91 -0
- fastgenerateapi/settings/{settings.py → app_settings.py} +31 -28
- fastgenerateapi/settings/db_settings.py +69 -0
- fastgenerateapi/settings/file_settings.py +24 -0
- fastgenerateapi/settings/jwt_settings.py +23 -0
- fastgenerateapi/settings/otlp_settings.py +69 -0
- fastgenerateapi/settings/redis_settings.py +16 -0
- fastgenerateapi/settings/sms_settings.py +25 -0
- fastgenerateapi/settings/system_settings.py +30 -0
- fastgenerateapi/utils/auto_discover.py +61 -0
- fastgenerateapi/utils/file_utils.py +76 -0
- fastgenerateapi/utils/pwd_utils.py +49 -0
- fastgenerateapi/utils/ramdom_utils.py +48 -0
- fastgenerateapi/utils/snowflake.py +23 -20
- fastgenerateapi/utils/str_util.py +120 -0
- fastgenerateapi/utils/swagger_to_js.py +26 -0
- {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/METADATA +61 -24
- fastgenerateapi-1.1.6.dist-info/RECORD +109 -0
- {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/WHEEL +1 -1
- {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/top_level.txt +1 -0
- script/__init__.py +2 -0
- fastgenerateapi/settings/register_settings.py +0 -6
- fastgenerateapi/utils/parse_str.py +0 -36
- fastgenerateapi-0.0.27.dist-info/RECORD +0 -82
- {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/LICENSE +0 -0
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from fastgenerateapi.pydantic_utils.base_settings import BaseSettings
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class AppSettings(BaseSettings):
|
|
8
|
+
# 字段配置 老版本(pydantic.utils.to_lower_camel)
|
|
9
|
+
ALIAS_GENERATOR: Optional[str] = Field(default="pydantic.alias_generators.to_snake", description="序列化参数命名方法路径")
|
|
10
|
+
|
|
7
11
|
# 分页对应字段以及配置默认值
|
|
8
|
-
CURRENT_PAGE_FIELD: Optional[str] = Field(default=
|
|
9
|
-
PAGE_SIZE_FIELD: Optional[str] = Field(default=
|
|
10
|
-
TOTAL_SIZE_FIELD: Optional[str] = Field(default=
|
|
11
|
-
DETERMINE_WHETHER_PAGE_FIELD: Optional[str] = Field(default=
|
|
12
|
+
CURRENT_PAGE_FIELD: Optional[str] = Field(default="page", description="当前页字段")
|
|
13
|
+
PAGE_SIZE_FIELD: Optional[str] = Field(default="page_size", description="每页数量字段")
|
|
14
|
+
TOTAL_SIZE_FIELD: Optional[str] = Field(default="total", description="统计数量字段")
|
|
15
|
+
DETERMINE_WHETHER_PAGE_FIELD: Optional[str] = Field(default="no_page", description="判断是否分页字段")
|
|
12
16
|
DETERMINE_PAGE_BOOL_VALUE: Optional[bool] = Field(default=True, description="分页对应的布尔值")
|
|
13
17
|
DEFAULT_WHETHER_PAGE: Optional[bool] = Field(default=False, description="默认是否分页")
|
|
14
18
|
DEFAULT_PAGE_SIZE: Optional[int] = Field(default=10, description="默认每页数量")
|
|
15
19
|
DEFAULT_MAX_PAGE_SIZE: Optional[int] = Field(default=200, description="默认最大每页数量")
|
|
16
20
|
|
|
17
21
|
# 方法优化项
|
|
18
|
-
METHOD_TREE_CHOICE: Optional[str] = Field(default=
|
|
22
|
+
METHOD_TREE_CHOICE: Optional[str] = Field(default="map", description="树状查询方式{sql: sql循环查询,map: 内存查询}")
|
|
19
23
|
|
|
20
24
|
# 缓存配置参数
|
|
21
25
|
CACHE_GET_ONE_WHETHER_OPEN: Optional[bool] = Field(default=False, description="查询详情是否打开缓存")
|
|
@@ -28,24 +32,24 @@ class AppSettings(BaseSettings):
|
|
|
28
32
|
# 路由后缀字段是否添加以及配置默认值
|
|
29
33
|
ROUTER_WHETHER_UNDERLINE_TO_STRIKE: Optional[bool] = Field(default=False, description="路由是否下划线转中划线")
|
|
30
34
|
ROUTER_WHETHER_ADD_SUFFIX: Optional[bool] = Field(default=True, description="增删改查路由是否添加后缀")
|
|
31
|
-
ROUTER_CREATE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
32
|
-
ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
33
|
-
ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
34
|
-
ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
35
|
-
ROUTER_UPDATE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
36
|
-
ROUTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
37
|
-
ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
38
|
-
ROUTER_FILTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default=
|
|
35
|
+
ROUTER_CREATE_SUFFIX_FIELD: Optional[str] = Field(default="create", description="创建路由后缀字段")
|
|
36
|
+
ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default="detail", description="获取一个路由后缀字段") # get_one
|
|
37
|
+
ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default="list", description="获取列表路由后缀字段") # get_all
|
|
38
|
+
ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default="tree", description="获取树状数据路由后缀字段") # get_tree
|
|
39
|
+
ROUTER_UPDATE_SUFFIX_FIELD: Optional[str] = Field(default="update", description="修改路由后缀字段")
|
|
40
|
+
ROUTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default="delete", description="删除路由后缀字段")
|
|
41
|
+
ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default="delete_tree", description="递归删除路由后缀字段")
|
|
42
|
+
ROUTER_FILTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default="delete_filter", description="递归删除路由后缀字段")
|
|
39
43
|
|
|
40
44
|
# 函数转换路由时,默认添加字段,(遵循restful规范时,get路由处理方案)
|
|
41
|
-
RESTFUL_GET_ROUTER_ADD_PREFIX: Optional[str] = Field(default=
|
|
42
|
-
RESTFUL_GET_ROUTER_ADD_SUFFIX: Optional[str] = Field(default=
|
|
43
|
-
RESTFUL_POST_ROUTER_ADD_PREFIX: Optional[str] = Field(default=
|
|
44
|
-
RESTFUL_POST_ROUTER_ADD_SUFFIX: Optional[str] = Field(default=
|
|
45
|
-
RESTFUL_PUT_ROUTER_ADD_PREFIX: Optional[str] = Field(default=
|
|
46
|
-
RESTFUL_PUT_ROUTER_ADD_SUFFIX: Optional[str] = Field(default=
|
|
47
|
-
RESTFUL_DELETE_ROUTER_ADD_PREFIX: Optional[str] = Field(default=
|
|
48
|
-
RESTFUL_DELETE_ROUTER_ADD_SUFFIX: Optional[str] = Field(default=
|
|
45
|
+
RESTFUL_GET_ROUTER_ADD_PREFIX: Optional[str] = Field(default="", description="函数转换路由时:前缀添加字段")
|
|
46
|
+
RESTFUL_GET_ROUTER_ADD_SUFFIX: Optional[str] = Field(default="", description="函数转换路由时:后缀pk前添加字段")
|
|
47
|
+
RESTFUL_POST_ROUTER_ADD_PREFIX: Optional[str] = Field(default="", description="函数转换路由时:前缀添加字段")
|
|
48
|
+
RESTFUL_POST_ROUTER_ADD_SUFFIX: Optional[str] = Field(default="", description="函数转换路由时:后缀pk前添加字段")
|
|
49
|
+
RESTFUL_PUT_ROUTER_ADD_PREFIX: Optional[str] = Field(default="", description="函数转换路由时:前缀添加字段")
|
|
50
|
+
RESTFUL_PUT_ROUTER_ADD_SUFFIX: Optional[str] = Field(default="", description="函数转换路由时:后缀pk前添加字段")
|
|
51
|
+
RESTFUL_DELETE_ROUTER_ADD_PREFIX: Optional[str] = Field(default="", description="函数转换路由时:前缀添加字段")
|
|
52
|
+
RESTFUL_DELETE_ROUTER_ADD_SUFFIX: Optional[str] = Field(default="", description="函数转换路由时:后缀pk前添加字段")
|
|
49
53
|
|
|
50
54
|
# 分布式id
|
|
51
55
|
WORKER_ID: Optional[int] = Field(default=1, description="数据中心(机器区域)ID")
|
|
@@ -67,20 +71,19 @@ class AppSettings(BaseSettings):
|
|
|
67
71
|
CODE_SUCCESS_DEFAULT_VALUE: Optional[int] = Field(default=200, description="code成功返回值")
|
|
68
72
|
CODE_FAIL_DEFAULT_VALUE: Optional[int] = Field(default=-1, description="code失败返回值")
|
|
69
73
|
SUCCESS_RESPONSE_FIELD: Optional[bool] = Field(default=True, description="success返回字段")
|
|
70
|
-
MESSAGE_RESPONSE_FIELD: Optional[str] = Field(default="
|
|
74
|
+
MESSAGE_RESPONSE_FIELD: Optional[str] = Field(default="msg", description="消息返回字段")
|
|
71
75
|
DATA_RESPONSE_FIELD: Optional[str] = Field(default="data", description="数据返回字段")
|
|
72
|
-
LIST_RESPONSE_FIELD: Optional[str] = Field(default=
|
|
76
|
+
LIST_RESPONSE_FIELD: Optional[str] = Field(default="list", description="列表页返回字段")
|
|
73
77
|
|
|
74
78
|
# GetAll 筛选是否双下划线转单下划线
|
|
75
79
|
FILTER_UNDERLINE_WHETHER_DOUBLE_TO_SINGLE: Optional[bool] = Field(default=True, description="筛选是否双下划线转单下划线")
|
|
76
80
|
SCHEMAS_UNDERLINE_WHETHER_DOUBLE_TO_SINGLE: Optional[bool] = Field(default=True, description="序列化字段是否双下划线转单下划线")
|
|
77
81
|
|
|
78
82
|
class Config:
|
|
79
|
-
env_prefix =
|
|
83
|
+
env_prefix = "APP_"
|
|
80
84
|
env_file = "./.env"
|
|
81
85
|
case_sensitive = True
|
|
86
|
+
extra = 'allow'
|
|
87
|
+
|
|
82
88
|
|
|
83
89
|
|
|
84
|
-
class SettingsModel(BaseModel):
|
|
85
|
-
# 系统配置
|
|
86
|
-
app_settings: AppSettings = AppSettings()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DBSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
Database Settings
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
TYPE: Optional[str] = Field(default='mysql', description="数据库类型")
|
|
13
|
+
HOST: Optional[str] = Field(default='127.0.0.1', description="数据库域名")
|
|
14
|
+
PORT: Optional[str] = Field(default='3306', description="数据库端口")
|
|
15
|
+
DATABASE: Optional[str] = Field(default='admin', description="数据库名")
|
|
16
|
+
USERNAME: Optional[str] = Field(default='root', description="数据库用户名")
|
|
17
|
+
PASSWORD: Optional[str] = Field(default='', description="数据库密码")
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def dsn(self):
|
|
21
|
+
return f"{self.TYPE.lower()}://{self.USERNAME}:{self.PASSWORD}@{self.HOST}:{self.PORT}/{self.DATABASE}"
|
|
22
|
+
|
|
23
|
+
class Config:
|
|
24
|
+
env_prefix = 'DB_'
|
|
25
|
+
env_file = "./.env"
|
|
26
|
+
case_sensitive = True
|
|
27
|
+
extra = 'allow'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PostgresqlSettings(DBSettings):
|
|
31
|
+
"""
|
|
32
|
+
Postgresql Settings
|
|
33
|
+
"""
|
|
34
|
+
TYPE: Optional[str] = Field(default='postgres', description="数据库类型")
|
|
35
|
+
|
|
36
|
+
class Config:
|
|
37
|
+
env_prefix = 'Postgresql_'
|
|
38
|
+
env_file = "./.env"
|
|
39
|
+
case_sensitive = True
|
|
40
|
+
extra = 'allow'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MySQLSettings(DBSettings):
|
|
44
|
+
"""
|
|
45
|
+
MySQL Settings
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
class Config:
|
|
49
|
+
env_prefix = 'MYSQL_'
|
|
50
|
+
env_file = "./.env"
|
|
51
|
+
case_sensitive = True
|
|
52
|
+
extra = 'allow'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class LocalSettings(DBSettings):
|
|
56
|
+
"""
|
|
57
|
+
MySQL Settings
|
|
58
|
+
"""
|
|
59
|
+
TYPE: str = Field(..., description="数据库类型")
|
|
60
|
+
|
|
61
|
+
class Config:
|
|
62
|
+
env_prefix = 'LOCAL_'
|
|
63
|
+
env_file = "./.env"
|
|
64
|
+
case_sensitive = True
|
|
65
|
+
extra = 'allow'
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileServerSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
文件配置
|
|
10
|
+
"""
|
|
11
|
+
FILE_SERVER: Optional[str] = Field(default='http://localhost:8001', description="文件服务域名", title="file Server Domain")
|
|
12
|
+
FILE_URL: Optional[str] = Field(default='/static/', description="文件路径前缀", title="file url prefix")
|
|
13
|
+
FILE_ROOT: Optional[str] = Field(default='static', description="文件储存路径", title="file storage path")
|
|
14
|
+
|
|
15
|
+
class Config:
|
|
16
|
+
env_prefix = 'FileServer_'
|
|
17
|
+
env_file = "./.env"
|
|
18
|
+
case_sensitive = True
|
|
19
|
+
extra = 'allow'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JWTSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
jwt配置
|
|
10
|
+
"""
|
|
11
|
+
SECRET_KEY: Optional[str] = Field(..., description="认证密钥", title="JWT SECRET KEY")
|
|
12
|
+
ALGORITHM: Optional[str] = Field(default='HS256', description="认证密钥", title="JWT ALGORITHM")
|
|
13
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: Optional[int] = Field(default=30, description="过期时间 (分钟)", title="JWT Access Token")
|
|
14
|
+
REFRESH_TOKEN_EXPIRE_MINUTES: Optional[int] = Field(default=60, description="过期时间(分钟)", title="JWT Refresh Token")
|
|
15
|
+
|
|
16
|
+
class Config:
|
|
17
|
+
env_prefix = 'JWT_'
|
|
18
|
+
env_file = "./.env"
|
|
19
|
+
case_sensitive = True
|
|
20
|
+
extra = 'allow'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from fastapi import FastAPI
|
|
5
|
+
from opentelemetry import metrics, trace
|
|
6
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
8
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
9
|
+
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
|
|
10
|
+
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
|
|
11
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
12
|
+
from opentelemetry.sdk.metrics._internal.export import PeriodicExportingMetricReader
|
|
13
|
+
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
|
|
14
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
15
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
16
|
+
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
from pydantic_settings import BaseSettings
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OpenTelemetrySettings(BaseSettings):
|
|
22
|
+
"""
|
|
23
|
+
跟踪和指标配置
|
|
24
|
+
"""
|
|
25
|
+
ENDPOINT: Optional[str] = Field(default=None, description="访问接口", example="http://127.0.0.1")
|
|
26
|
+
AUTHORIZATION: Optional[str] = Field(default=None, description="认证字符串")
|
|
27
|
+
|
|
28
|
+
def setup_open_telemetry(self, app: FastAPI, service_name: str):
|
|
29
|
+
if not self.ENDPOINT:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
with httpx.Client() as client:
|
|
33
|
+
health_check = client.get(f"{self.ENDPOINT}/healthz", timeout=3).json()
|
|
34
|
+
if health_check["status"] != "ok":
|
|
35
|
+
raise Exception("otlp服务异常")
|
|
36
|
+
resource = Resource(attributes={
|
|
37
|
+
SERVICE_NAME: service_name
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
tracer_provider = TracerProvider(resource=resource)
|
|
41
|
+
headers = {
|
|
42
|
+
"Authorization": self.AUTHORIZATION} if self.AUTHORIZATION else {}
|
|
43
|
+
exporter = OTLPMetricExporter(
|
|
44
|
+
endpoint=f"{self.ENDPOINT}/api/default/v1/metrics",
|
|
45
|
+
headers=headers,
|
|
46
|
+
)
|
|
47
|
+
meter_provider = MeterProvider([PeriodicExportingMetricReader(exporter)], resource=resource)
|
|
48
|
+
metrics.set_meter_provider(meter_provider)
|
|
49
|
+
processor = BatchSpanProcessor(
|
|
50
|
+
OTLPSpanExporter(
|
|
51
|
+
endpoint=f"{self.ENDPOINT}/api/default/v1/traces",
|
|
52
|
+
headers=headers
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
tracer_provider.add_span_processor(processor)
|
|
56
|
+
trace.set_tracer_provider(tracer_provider)
|
|
57
|
+
FastAPIInstrumentor.instrument_app(
|
|
58
|
+
app,
|
|
59
|
+
tracer_provider=tracer_provider,
|
|
60
|
+
meter_provider=meter_provider,
|
|
61
|
+
)
|
|
62
|
+
TortoiseORMInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
63
|
+
HTTPXClientInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
64
|
+
|
|
65
|
+
class Config:
|
|
66
|
+
env_prefix = 'OTLP_'
|
|
67
|
+
env_file = "./.env"
|
|
68
|
+
case_sensitive = True
|
|
69
|
+
extra = 'allow'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RedisSettings(BaseSettings):
|
|
8
|
+
URL: Optional[str] = Field(default="redis://127.0.0.1", description="IP地址")
|
|
9
|
+
PORT: Optional[int] = Field(default=6379, description="映射端口")
|
|
10
|
+
PASSWORD: Optional[str] = Field(default="", description="密码")
|
|
11
|
+
|
|
12
|
+
class Config:
|
|
13
|
+
env_prefix = 'REDIS_'
|
|
14
|
+
env_file = "./.env"
|
|
15
|
+
case_sensitive = True
|
|
16
|
+
extra = 'allow'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SmsSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
短信配置
|
|
10
|
+
Sms Settings
|
|
11
|
+
"""
|
|
12
|
+
CACHE_BACKEND: Optional[str] = Field(default="redis", description="缓存类型", title="cache_backend(redis, inmemory)")
|
|
13
|
+
CACHE_BACKEND_DSN: Optional[str] = Field(default="redis://localhost:6379/1", description="缓存dsn", title="cache_backend_dsn")
|
|
14
|
+
IS_LIMIT_CODE_FREQUENCY: Optional[bool] = Field(default=True, description="是否限制频率", title="code frequency check")
|
|
15
|
+
CHECK_CODE_RESEND_TIME: Optional[int] = Field(default=1, description="验证码重新发送时间", title="check code resend time (minute)")
|
|
16
|
+
DEFAULT_CODE: Optional[int] = Field(default="654123", description="默认验证码", title="sms default code")
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
env_prefix = 'Sms_'
|
|
20
|
+
env_file = "./.env"
|
|
21
|
+
case_sensitive = True
|
|
22
|
+
extra = 'allow'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SystemSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
System Settings
|
|
10
|
+
"""
|
|
11
|
+
NAME: str = Field(default='fastgenerateapi', description="项目名称")
|
|
12
|
+
HOST: Optional[str] = Field(default='127.0.0.1', description="本地运行host")
|
|
13
|
+
PORT: Optional[int] = Field(default=8001, description="本地运行port")
|
|
14
|
+
DEBUG: bool = Field(default=True, description="是否开启调试模式,本地修改自动重载")
|
|
15
|
+
|
|
16
|
+
# BASE_DIR: Union[PathType, str, None]
|
|
17
|
+
DOMAIN: Optional[str] = Field(default="http://127.0.0.1:8001", description="服务域名(对外暴露的域名地址包含协议)")
|
|
18
|
+
FIELD_SECRET: Optional[str] = Field(default=None, description="字段加密密钥")
|
|
19
|
+
|
|
20
|
+
# 分布式id
|
|
21
|
+
WORKER_ID: Optional[int] = Field(default='1', description="数据中心(机器区域)ID")
|
|
22
|
+
DATACENTER_ID: Optional[int] = Field(default='1', description="机器ID")
|
|
23
|
+
|
|
24
|
+
class Config:
|
|
25
|
+
env_prefix = ''
|
|
26
|
+
env_file = "./.env"
|
|
27
|
+
case_sensitive = True
|
|
28
|
+
extra = 'allow'
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_path_list(path: str, match_prefix: str, prefix: str = "") -> List[str]:
|
|
8
|
+
files = []
|
|
9
|
+
prefix = prefix + "." + os.path.basename(path) if prefix else os.path.basename(path)
|
|
10
|
+
for item in Path(path).iterdir():
|
|
11
|
+
if item.is_file():
|
|
12
|
+
if item.name.startswith(match_prefix) and item.name.endswith(".py"):
|
|
13
|
+
files.append(prefix+"."+item.name[:-3])
|
|
14
|
+
elif item.is_dir():
|
|
15
|
+
if item.name.startswith(match_prefix):
|
|
16
|
+
for s_item in Path(os.path.join(path, item.name)).iterdir():
|
|
17
|
+
if s_item.is_file() and s_item.name.startswith(match_prefix):
|
|
18
|
+
files.append(prefix + "." + item.name + "." + s_item.name[:-3])
|
|
19
|
+
else:
|
|
20
|
+
files += get_path_list(os.path.join(path, item.name), match_prefix, prefix)
|
|
21
|
+
|
|
22
|
+
return files
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def discover_models(
|
|
26
|
+
base_path="apps", # 模块路径
|
|
27
|
+
include: Optional[List[str]] = None, # 包含的模型
|
|
28
|
+
exclude: Optional[List[str]] = None, # 排除的模型
|
|
29
|
+
) -> List[str]:
|
|
30
|
+
"""
|
|
31
|
+
自动发现models,并生成tortoise-orm所需要的字符串
|
|
32
|
+
会自动添加 aerich.models,如不需要请在 exclude 参数排除
|
|
33
|
+
"""
|
|
34
|
+
models_list = ["aerich.models"] + get_path_list(base_path, "models")
|
|
35
|
+
return include + [model for model in models_list if model not in exclude] if exclude else models_list
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def discover_routes(
|
|
39
|
+
app, # Fastapi 生成的实体类
|
|
40
|
+
base_path="apps", # 模块路径
|
|
41
|
+
prefix="", # 路由前缀
|
|
42
|
+
include: Optional[List[str]] = None, # 包含的路由
|
|
43
|
+
exclude: Optional[List[str]] = None, # 排除的路由
|
|
44
|
+
):
|
|
45
|
+
"""自动发现路由,并注册"""
|
|
46
|
+
paths = include if include else []
|
|
47
|
+
|
|
48
|
+
for path in paths + get_path_list(base_path, "router"):
|
|
49
|
+
if exclude and path in exclude:
|
|
50
|
+
continue
|
|
51
|
+
module = importlib.import_module(path)
|
|
52
|
+
if hasattr(module, "router"):
|
|
53
|
+
app.include_router(prefix=prefix, router=getattr(module, "router"))
|
|
54
|
+
return app
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == '__main__':
|
|
58
|
+
# print(discover_models("../../modules"))
|
|
59
|
+
print(get_path_list("../../modules", "router"))
|
|
60
|
+
|
|
61
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
|
|
3
|
+
type_dict = {
|
|
4
|
+
# '424D': 'bmp',
|
|
5
|
+
'FFD8FF': 'jpg',
|
|
6
|
+
# '2E524D46': 'rm',
|
|
7
|
+
# '4D546864': 'mid',
|
|
8
|
+
'89504E47': 'png',
|
|
9
|
+
'47494638': 'gif',
|
|
10
|
+
'49492A00': 'tif',
|
|
11
|
+
# '41433130': 'dwg',
|
|
12
|
+
# '38425053': 'psd',
|
|
13
|
+
# '2142444E': 'pst',
|
|
14
|
+
# 'FF575043': 'wpd',
|
|
15
|
+
# 'AC9EBD8F': 'qdf',
|
|
16
|
+
# 'E3828596': 'pwl',
|
|
17
|
+
'504B0304': 'zip',
|
|
18
|
+
'52617221': 'rar',
|
|
19
|
+
'57415645': 'wav',
|
|
20
|
+
'41564920': 'avi',
|
|
21
|
+
'2E7261FD': 'ram',
|
|
22
|
+
'000001BA': 'mpg',
|
|
23
|
+
'000001B3': 'mpg',
|
|
24
|
+
'6D6F6F76': 'mov',
|
|
25
|
+
# '7B5C727466': 'rtf',
|
|
26
|
+
'3C3F786D6C': 'xml',
|
|
27
|
+
'68746D6C3E': 'html',
|
|
28
|
+
'D0CF11E0': 'doc/xls',
|
|
29
|
+
'255044462D312E': 'pdf',
|
|
30
|
+
'CFAD12FEC5FD746F': 'dbx',
|
|
31
|
+
# '3026B2758E66CF11': 'asf',
|
|
32
|
+
'5374616E64617264204A': 'mdb',
|
|
33
|
+
# '252150532D41646F6265': 'ps/eps',
|
|
34
|
+
# '44656C69766572792D646174653A': 'eml'
|
|
35
|
+
}
|
|
36
|
+
max_len = len(max(type_dict, key=len)) // 2
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def get_filetype(file):
|
|
40
|
+
# 读取二进制文件开头一定的长度
|
|
41
|
+
if isinstance(file, str):
|
|
42
|
+
filename = file
|
|
43
|
+
with open(filename, 'rb') as f:
|
|
44
|
+
byte = f.read(max_len)
|
|
45
|
+
else:
|
|
46
|
+
filename = file.filename
|
|
47
|
+
byte = await file.read(max_len)
|
|
48
|
+
await file.seek(0)
|
|
49
|
+
# 解析为元组
|
|
50
|
+
byte_list = struct.unpack('B' * max_len, byte)
|
|
51
|
+
# 转为16进制
|
|
52
|
+
code = ''.join([('%X' % each).zfill(2) for each in byte_list])
|
|
53
|
+
# 根据标识符筛选判断文件格式
|
|
54
|
+
result = list(filter(lambda x: code.startswith(x), type_dict))
|
|
55
|
+
nametype = filename.split('.')[-1]
|
|
56
|
+
if result:
|
|
57
|
+
filetype = type_dict[result[0]]
|
|
58
|
+
if filetype == "zip":
|
|
59
|
+
filetype = nametype if nametype in ['xlsx', 'docx'] else "zip"
|
|
60
|
+
elif filetype == "doc/xls":
|
|
61
|
+
filetype = nametype if nametype in ['doc', 'xls'] else None
|
|
62
|
+
elif filetype == "jpg":
|
|
63
|
+
filetype = nametype if nametype in ['jpg', 'jpeg'] else None
|
|
64
|
+
else:
|
|
65
|
+
filetype = filetype if filetype == nametype else None
|
|
66
|
+
else:
|
|
67
|
+
filetype = nametype if nametype in ['mp3', 'mp4', 'txt'] else None
|
|
68
|
+
return filetype
|
|
69
|
+
|
|
70
|
+
file_type = {
|
|
71
|
+
"image": ["jpg", "png", "gif", "tif"],
|
|
72
|
+
"document": ["zip", "rar", "wav", "xml", "html", "doc", "xls", "pdf", "dbx", "mdb"],
|
|
73
|
+
"voice": ["mp3", "amr"],
|
|
74
|
+
"video": ["wav", "avi", "ram", "mpg", "mov", "mp4"],
|
|
75
|
+
}
|
|
76
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from passlib.hash import pbkdf2_sha256, md5_crypt
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_random_salt(length=64):
|
|
7
|
+
"""
|
|
8
|
+
生成随机salt字符串,可指定长度
|
|
9
|
+
:param length: int
|
|
10
|
+
:return: salt: str
|
|
11
|
+
"""
|
|
12
|
+
seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&*="
|
|
13
|
+
sa = []
|
|
14
|
+
for i in range(length):
|
|
15
|
+
sa.append(random.choice(seed))
|
|
16
|
+
salt = ''.join(sa)
|
|
17
|
+
return salt
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_password_hash(password: str, salt: str) -> str:
|
|
21
|
+
"""
|
|
22
|
+
根据传入的password与salt参数生成hash值
|
|
23
|
+
:param password:
|
|
24
|
+
:param salt:
|
|
25
|
+
:return:
|
|
26
|
+
"""
|
|
27
|
+
custom_pbkdf2 = pbkdf2_sha256.using(salt=salt.encode('utf-8'), rounds=1000)
|
|
28
|
+
return custom_pbkdf2.hash(password)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_password(password: str, salt: str, password_hash: str) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
password 校验
|
|
34
|
+
:param password:
|
|
35
|
+
:param salt:
|
|
36
|
+
:param password_hash:
|
|
37
|
+
:return:
|
|
38
|
+
"""
|
|
39
|
+
validating_password_hash = create_password_hash(password, salt)
|
|
40
|
+
return validating_password_hash == password_hash
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# if __name__ == '__main__':
|
|
44
|
+
# # salt = make_random_salt(length=60)
|
|
45
|
+
# salt = "lpiZRQwWoj&*wh29Jovk$uA3YGP$TSQYpM4ooy1z3zGuNT=vmLpb&Ves"
|
|
46
|
+
# hash_str = create_password_hash('123456132312312312312321', salt)
|
|
47
|
+
# print(hash_str)
|
|
48
|
+
# print(len(hash_str))
|
|
49
|
+
# print(validate_password(password='123456132312312312312321', salt=salt, password_hash=hash_str))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import random
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_code(n=4):
|
|
7
|
+
"""
|
|
8
|
+
生成随机验证码,数字和字母的概率各一半
|
|
9
|
+
:param n: 验证码长度
|
|
10
|
+
:return: str
|
|
11
|
+
"""
|
|
12
|
+
s = ''
|
|
13
|
+
for i in range(n):
|
|
14
|
+
ret_num = random.randint(0, 9)
|
|
15
|
+
ret_alpha = chr(random.randint(65, 90))
|
|
16
|
+
result = random.choice([ret_num, ret_alpha])
|
|
17
|
+
s += str(result)
|
|
18
|
+
return s
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def random_str() -> str:
|
|
22
|
+
"""
|
|
23
|
+
唯一随机字符串
|
|
24
|
+
:return: str
|
|
25
|
+
"""
|
|
26
|
+
only = hashlib.md5(str(uuid.uuid1()).encode(encoding='UTF-8')).hexdigest()
|
|
27
|
+
return str(only)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_code_number(n: int = 6) -> str:
|
|
31
|
+
"""
|
|
32
|
+
随机数字
|
|
33
|
+
:param n: 长度
|
|
34
|
+
:return: str
|
|
35
|
+
"""
|
|
36
|
+
code = ""
|
|
37
|
+
for i in range(n):
|
|
38
|
+
ch = chr(random.randrange(ord('0'), ord('9') + 1))
|
|
39
|
+
code += ch
|
|
40
|
+
|
|
41
|
+
return code
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == '__main__':
|
|
45
|
+
print(get_code())
|
|
46
|
+
print(random_str())
|
|
47
|
+
print(get_code_number(6))
|
|
48
|
+
|