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.

Files changed (85) hide show
  1. fastgenerateapi/__init__.py +2 -2
  2. fastgenerateapi/__version__.py +1 -1
  3. fastgenerateapi/api_view/base_view.py +17 -7
  4. fastgenerateapi/api_view/create_view.py +1 -1
  5. fastgenerateapi/api_view/delete_filter_view.py +1 -1
  6. fastgenerateapi/api_view/delete_tree_view.py +3 -3
  7. fastgenerateapi/api_view/delete_view.py +3 -3
  8. fastgenerateapi/api_view/get_all_view.py +21 -17
  9. fastgenerateapi/api_view/get_one_view.py +1 -1
  10. fastgenerateapi/api_view/get_relation_view.py +1 -1
  11. fastgenerateapi/api_view/get_tree_view.py +1 -1
  12. fastgenerateapi/api_view/mixin/base_mixin.py +11 -7
  13. fastgenerateapi/api_view/mixin/dbmodel_mixin.py +30 -20
  14. fastgenerateapi/api_view/mixin/response_mixin.py +79 -32
  15. fastgenerateapi/api_view/mixin/tool_mixin.py +1 -357
  16. fastgenerateapi/api_view/mixin/utils/__init__.py +0 -0
  17. fastgenerateapi/api_view/mixin/utils/docx_util.py +399 -0
  18. fastgenerateapi/api_view/mixin/utils/file_util.py +30 -0
  19. fastgenerateapi/api_view/mixin/utils/pdf_util.py +76 -0
  20. fastgenerateapi/api_view/mixin/utils/xlsx_util.py +336 -0
  21. fastgenerateapi/api_view/mixin/utils/zip_util.py +50 -0
  22. fastgenerateapi/api_view/switch_view.py +11 -11
  23. fastgenerateapi/api_view/update_relation_view.py +3 -3
  24. fastgenerateapi/api_view/update_view.py +1 -1
  25. fastgenerateapi/cache/cache_decorator.py +1 -1
  26. fastgenerateapi/controller/filter_controller.py +68 -26
  27. fastgenerateapi/controller/router_controller.py +9 -9
  28. fastgenerateapi/controller/rpc_controller.py +1 -1
  29. fastgenerateapi/controller/ws_controller.py +1 -1
  30. fastgenerateapi/deps/filter_params_deps.py +34 -4
  31. fastgenerateapi/deps/paginator_deps.py +4 -4
  32. fastgenerateapi/deps/tree_params_deps.py +4 -4
  33. fastgenerateapi/example/models.py +3 -2
  34. fastgenerateapi/example/schemas.py +1 -1
  35. fastgenerateapi/example/views.py +1 -1
  36. fastgenerateapi/fastapi_utils/__init__.py +0 -0
  37. fastgenerateapi/fastapi_utils/all.py +5 -0
  38. fastgenerateapi/fastapi_utils/param_utils.py +37 -0
  39. fastgenerateapi/fastapi_utils/response_utils.py +344 -0
  40. fastgenerateapi/model/__init__.py +0 -0
  41. fastgenerateapi/model/base_model.py +56 -0
  42. fastgenerateapi/my_fields/enum_field.py +8 -8
  43. fastgenerateapi/my_fields/pk_field.py +1 -1
  44. fastgenerateapi/my_fields/soft_delete_field.py +4 -3
  45. fastgenerateapi/my_fields/validator.py +60 -0
  46. fastgenerateapi/pydantic_utils/base_model.py +54 -21
  47. fastgenerateapi/pydantic_utils/base_settings.py +16 -0
  48. fastgenerateapi/pydantic_utils/json_encoders.py +2 -1
  49. fastgenerateapi/schemas_factory/common_function.py +1 -1
  50. fastgenerateapi/schemas_factory/common_schema_factory.py +4 -4
  51. fastgenerateapi/schemas_factory/create_schema_factory.py +4 -4
  52. fastgenerateapi/schemas_factory/filter_schema_factory.py +6 -6
  53. fastgenerateapi/schemas_factory/get_all_schema_factory.py +5 -5
  54. fastgenerateapi/schemas_factory/get_one_schema_factory.py +4 -3
  55. fastgenerateapi/schemas_factory/get_relation_schema_factory.py +3 -3
  56. fastgenerateapi/schemas_factory/get_tree_schema_factory.py +3 -3
  57. fastgenerateapi/schemas_factory/response_factory.py +5 -10
  58. fastgenerateapi/schemas_factory/sql_get_all_schema_factory.py +3 -3
  59. fastgenerateapi/schemas_factory/update_schema_factory.py +4 -4
  60. fastgenerateapi/settings/__init__.py +6 -0
  61. fastgenerateapi/settings/all_settings.py +91 -0
  62. fastgenerateapi/settings/{settings.py → app_settings.py} +31 -28
  63. fastgenerateapi/settings/db_settings.py +69 -0
  64. fastgenerateapi/settings/file_settings.py +24 -0
  65. fastgenerateapi/settings/jwt_settings.py +23 -0
  66. fastgenerateapi/settings/otlp_settings.py +69 -0
  67. fastgenerateapi/settings/redis_settings.py +16 -0
  68. fastgenerateapi/settings/sms_settings.py +25 -0
  69. fastgenerateapi/settings/system_settings.py +30 -0
  70. fastgenerateapi/utils/auto_discover.py +61 -0
  71. fastgenerateapi/utils/file_utils.py +76 -0
  72. fastgenerateapi/utils/pwd_utils.py +49 -0
  73. fastgenerateapi/utils/ramdom_utils.py +48 -0
  74. fastgenerateapi/utils/snowflake.py +23 -20
  75. fastgenerateapi/utils/str_util.py +120 -0
  76. fastgenerateapi/utils/swagger_to_js.py +26 -0
  77. {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/METADATA +61 -24
  78. fastgenerateapi-1.1.6.dist-info/RECORD +109 -0
  79. {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/WHEEL +1 -1
  80. {fastgenerateapi-0.0.27.dist-info → fastgenerateapi-1.1.6.dist-info}/top_level.txt +1 -0
  81. script/__init__.py +2 -0
  82. fastgenerateapi/settings/register_settings.py +0 -6
  83. fastgenerateapi/utils/parse_str.py +0 -36
  84. fastgenerateapi-0.0.27.dist-info/RECORD +0 -82
  85. {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 BaseSettings, Field, BaseModel
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='page', description="当前页字段")
9
- PAGE_SIZE_FIELD: Optional[str] = Field(default='page_size', description="每页数量字段")
10
- TOTAL_SIZE_FIELD: Optional[str] = Field(default='total', description="统计数量字段")
11
- DETERMINE_WHETHER_PAGE_FIELD: Optional[str] = Field(default='no_page', description="判断是否分页字段")
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='map', description="树状查询方式{'sql': sql循环查询,'map': 内存查询}")
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='create', description="创建路由后缀字段")
32
- ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default='get_one', description="获取一个路由后缀字段")
33
- ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default='get_all', description="获取列表路由后缀字段")
34
- ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default='get_tree', description="获取树状数据路由后缀字段")
35
- ROUTER_UPDATE_SUFFIX_FIELD: Optional[str] = Field(default='update', description="修改路由后缀字段")
36
- ROUTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete', description="删除路由后缀字段")
37
- ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete_tree', description="递归删除路由后缀字段")
38
- ROUTER_FILTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete_filter', description="递归删除路由后缀字段")
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='', description="函数转换路由时:前缀添加字段")
42
- RESTFUL_GET_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
43
- RESTFUL_POST_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
44
- RESTFUL_POST_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
45
- RESTFUL_PUT_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
46
- RESTFUL_PUT_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
47
- RESTFUL_DELETE_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
48
- RESTFUL_DELETE_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
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="message", description="消息返回字段")
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='list', description="列表页返回字段")
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 = 'APP_'
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
+