mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 13:12:24 +00:00
Update plugins and adapt to new features (#1040)
* Update plugins and adapt to new features * Update plugin versions
This commit is contained in:
@@ -1,22 +1,38 @@
|
||||
# Code Generator
|
||||
|
||||
代码生成器插件,生成通用业务代码
|
||||
|
||||
> [!TIP]
|
||||
> 当前版本仅包含后端代码生成
|
||||
|
||||
> [!WARNING]
|
||||
> 由于 jinja2 在渲染模版时,文本方式输出可能存在格式问题,所以 `preview` 接口可能无法直观预览代码,这是为前端进行的预设
|
||||
|
||||
## 全局配置
|
||||
|
||||
在 `backend/core/conf.py` 中添加以下内容:
|
||||
|
||||
```python
|
||||
##################################################
|
||||
# [ Plugin ] code_generator
|
||||
##################################################
|
||||
# 基础配置(in plugin.toml)
|
||||
CODE_GENERATOR_DOWNLOAD_ZIP_FILENAME: str
|
||||
```
|
||||
|
||||
## 简介
|
||||
|
||||
代码生成器使用 api 调用实现,包含两个模组,设计可能存在缺陷,相关问题请直接提交 issues
|
||||
代码生成器使用 API 调用实现,包含两个模组,设计可能存在缺陷,相关问题请直接提交 issues
|
||||
|
||||
### 1. 代码生成业务
|
||||
### 代码生成业务
|
||||
|
||||
包含代码生成的相关配置,详情查看:`generator/model/gen_business.py`
|
||||
包含代码生成的相关配置,详情查看:`code_generator/model/gen_business.py`
|
||||
|
||||
### 2. 代码生成模型列
|
||||
### 代码生成模型列
|
||||
|
||||
包含代码生成所需要的模型列信息,就像正常定义模型列一样,目前支持的功能有限
|
||||
|
||||
## 使(食)用
|
||||
## 使用方式
|
||||
|
||||
1. 启动后端服务,打开 swagger 文档直接操作
|
||||
2. 通过第三方 api 调试工具发送接口请求
|
||||
@@ -24,17 +40,13 @@
|
||||
|
||||
接口参数基本都有说明,请注意查看
|
||||
|
||||
### F. 纯手动模式
|
||||
|
||||
不推荐(手动创建业务接口被标记为「已弃用」)
|
||||
### 手动模式
|
||||
|
||||
1. 通过创建业务接口手动添加一项业务数据
|
||||
2. 通过模型创建接口手动添加模型列
|
||||
3. 访问 `preview`(预览),`generate`(磁盘写入),`download`(下载)接口,执行后端代码生成相应工作
|
||||
|
||||
### S. 自动模式
|
||||
|
||||
推荐
|
||||
### 自动模式
|
||||
|
||||
1. 访问 `tables` 接口,获取数据库表名列表
|
||||
2. 通过 `import` 接口,导入数据库已有的数据库表数据,将自动创建业务表数据和模型表数据
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
[plugin]
|
||||
summary = '代码生成'
|
||||
version = '0.1.0'
|
||||
version = '0.1.1'
|
||||
description = '生成通用业务代码'
|
||||
author = 'wu-clan'
|
||||
tags = ['other']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
router = ['v1']
|
||||
|
||||
[settings]
|
||||
CODE_GENERATOR_DOWNLOAD_ZIP_FILENAME = 'fba_generator'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
## 参数配置
|
||||
# Config
|
||||
|
||||
内置插件,可直接使用
|
||||
参数配置插件,通常用于动态配置系统参数和前端工程数据展示
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
[plugin]
|
||||
summary = '参数配置'
|
||||
version = '0.0.2'
|
||||
description = '通常用于动态配置系统参数/前端工程数据展示'
|
||||
description = '通常用于动态配置系统参数和前端工程数据展示'
|
||||
author = 'wu-clan'
|
||||
tags = ['other']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
extend = 'admin'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
## 字典管理
|
||||
# Dict
|
||||
|
||||
内置插件,可直接使用
|
||||
数据字典插件,通常用于约束前端工程数据展示
|
||||
|
||||
@@ -3,6 +3,8 @@ summary = '数据字典'
|
||||
version = '0.0.8'
|
||||
description = '通常用于约束前端工程数据展示'
|
||||
author = 'wu-clan'
|
||||
tags = ['other']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
extend = 'admin'
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# [ Plugin ] email
|
||||
EMAIL_USERNAME=''
|
||||
EMAIL_PASSWORD=''
|
||||
@@ -1,5 +1,23 @@
|
||||
## 参数配置
|
||||
# Email
|
||||
|
||||
默认使用本地电子邮件配置
|
||||
电子邮件插件,提供邮件发送功能,支持验证码、通知等场景
|
||||
|
||||
支持通过 `config 插件` 动态配置电子邮件参数,当动态配置 `EMAIL_STATUS` 为 `1` 时,将自动应用动态配置
|
||||
## 全局配置
|
||||
|
||||
在 `backend/core/conf.py` 中添加以下内容:
|
||||
|
||||
```python
|
||||
##################################################
|
||||
# [ Plugin ] email
|
||||
##################################################
|
||||
# .env
|
||||
EMAIL_USERNAME: str
|
||||
EMAIL_PASSWORD: str
|
||||
|
||||
# 基础配置(in plugin.toml)
|
||||
EMAIL_HOST: str
|
||||
EMAIL_PORT: int
|
||||
EMAIL_SSL: bool
|
||||
EMAIL_CAPTCHA_REDIS_PREFIX: str
|
||||
EMAIL_CAPTCHA_EXPIRE_SECONDS: int
|
||||
```
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
[plugin]
|
||||
summary = '电子邮件'
|
||||
version = '0.0.2'
|
||||
description = '发送电子邮件,例如验证码、通知等'
|
||||
version = '0.0.3'
|
||||
description = '提供邮件发送功能,支持验证码、通知等场景'
|
||||
author = 'wu-clan'
|
||||
tags = ['other']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
router = ['v1']
|
||||
|
||||
[settings]
|
||||
EMAIL_HOST = 'smtp.qq.com'
|
||||
EMAIL_PORT = 465
|
||||
EMAIL_SSL = true
|
||||
EMAIL_CAPTCHA_REDIS_PREFIX = 'fba:email:captcha'
|
||||
EMAIL_CAPTCHA_EXPIRE_SECONDS = 180 # 3 分钟
|
||||
|
||||
@@ -13,13 +13,43 @@ from starlette.concurrency import run_in_threadpool
|
||||
from backend.common.exception import errors
|
||||
from backend.common.log import log
|
||||
from backend.core.conf import settings
|
||||
from backend.core.path_conf import PLUGIN_DIR
|
||||
from backend.core.path_conf import ENV_FILE_PATH, PLUGIN_DIR
|
||||
from backend.database.redis import redis_client
|
||||
from backend.plugin.requirements import install_requirements_async
|
||||
from backend.utils.locks import acquire_distributed_reload_lock
|
||||
from backend.utils.pattern_validate import is_git_url
|
||||
|
||||
|
||||
async def _append_env_example(plugin_path: anyio.Path) -> None:
|
||||
"""
|
||||
追加主 .env 文件
|
||||
|
||||
:param plugin_path: 插件目录路径
|
||||
:return:
|
||||
"""
|
||||
env_example_path = plugin_path / '.env.example'
|
||||
if not await env_example_path.exists():
|
||||
return
|
||||
|
||||
async with await open_file(env_example_path, mode='r', encoding='utf-8') as f:
|
||||
env_example_content = await f.read()
|
||||
|
||||
if not env_example_content.strip():
|
||||
return
|
||||
|
||||
env_path = anyio.Path(ENV_FILE_PATH)
|
||||
existing_content = ''
|
||||
if await env_path.exists():
|
||||
async with await open_file(env_path, mode='r', encoding='utf-8') as f:
|
||||
existing_content = await f.read()
|
||||
|
||||
separator = '\n' if existing_content and not existing_content.endswith('\n') else ''
|
||||
new_content = f'{existing_content}{separator}{env_example_content}'
|
||||
|
||||
async with await open_file(env_path, mode='w', encoding='utf-8') as f:
|
||||
await f.write(new_content)
|
||||
|
||||
|
||||
async def install_zip_plugin(file: UploadFile | str) -> str:
|
||||
"""
|
||||
安装 ZIP 插件
|
||||
@@ -72,6 +102,7 @@ async def install_zip_plugin(file: UploadFile | str) -> str:
|
||||
members.append(member)
|
||||
await run_in_threadpool(zf.extractall, full_plugin_path, members)
|
||||
|
||||
await _append_env_example(full_plugin_path)
|
||||
await install_requirements_async(plugin_dir_name)
|
||||
await redis_client.set(f'{settings.PLUGIN_REDIS_PREFIX}:changed', 'ture')
|
||||
|
||||
@@ -100,6 +131,7 @@ async def install_git_plugin(repo_url: str) -> str:
|
||||
log.error(f'插件安装失败: {e}')
|
||||
raise errors.ServerError(msg='插件安装失败,请稍后重试') from e
|
||||
|
||||
await _append_env_example(path)
|
||||
await install_requirements_async(repo_name)
|
||||
await redis_client.set(f'{settings.PLUGIN_REDIS_PREFIX}:changed', 'ture')
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
## 通知公告
|
||||
# Notice
|
||||
|
||||
内置插件,可直接使用
|
||||
通知公告插件,用于发布系统内部通知、公告
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
[plugin]
|
||||
summary = '通知公告'
|
||||
version = '0.0.2'
|
||||
description = '发布系统内部通知、公告'
|
||||
description = '用于发布系统内部通知、公告'
|
||||
author = 'wu-clan'
|
||||
tags = ['other']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
extend = 'admin'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# [ Plugin ] oauth2
|
||||
OAUTH2_GITHUB_CLIENT_ID='test'
|
||||
OAUTH2_GITHUB_CLIENT_SECRET='test'
|
||||
OAUTH2_GOOGLE_CLIENT_ID='test'
|
||||
OAUTH2_GOOGLE_CLIENT_SECRET='test'
|
||||
@@ -1,3 +1,26 @@
|
||||
## OAuth2
|
||||
# OAuth2
|
||||
|
||||
内置插件,可直接使用
|
||||
OAuth 2.0 第三方登录插件,支持 GitHub、Google 等社交平台登录
|
||||
|
||||
## 全局配置
|
||||
|
||||
在 `backend/core/conf.py` 中添加以下内容:
|
||||
|
||||
```python
|
||||
##################################################
|
||||
# [ Plugin ] oauth2
|
||||
##################################################
|
||||
# .env
|
||||
OAUTH2_GITHUB_CLIENT_ID: str
|
||||
OAUTH2_GITHUB_CLIENT_SECRET: str
|
||||
OAUTH2_GOOGLE_CLIENT_ID: str
|
||||
OAUTH2_GOOGLE_CLIENT_SECRET: str
|
||||
|
||||
# 基础配置(in plugin.toml)
|
||||
OAUTH2_STATE_REDIS_PREFIX: str
|
||||
OAUTH2_STATE_EXPIRE_SECONDS: int
|
||||
OAUTH2_GITHUB_REDIRECT_URI: str
|
||||
OAUTH2_GOOGLE_REDIRECT_URI: str
|
||||
OAUTH2_FRONTEND_LOGIN_REDIRECT_URI: str
|
||||
OAUTH2_FRONTEND_BINDING_REDIRECT_URI: str
|
||||
```
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
[plugin]
|
||||
summary = 'OAuth 2.0'
|
||||
version = '0.0.10'
|
||||
description = '通过 OAuth 2.0 的方式登录系统'
|
||||
version = '0.0.11'
|
||||
description = '支持 GitHub、Google 等社交平台登录'
|
||||
author = 'wu-clan'
|
||||
tags = ['auth']
|
||||
database = ['mysql', 'pgsql']
|
||||
|
||||
[app]
|
||||
router = ['v1']
|
||||
|
||||
[settings]
|
||||
OAUTH2_STATE_REDIS_PREFIX = 'fba:oauth2:state'
|
||||
OAUTH2_STATE_EXPIRE_SECONDS = 180 # 3 分钟
|
||||
OAUTH2_GITHUB_REDIRECT_URI = 'http://127.0.0.1:8000/api/v1/oauth2/github/callback'
|
||||
OAUTH2_GOOGLE_REDIRECT_URI = 'http://127.0.0.1:8000/api/v1/oauth2/google/callback'
|
||||
OAUTH2_FRONTEND_LOGIN_REDIRECT_URI = 'http://localhost:5173/oauth2/callback'
|
||||
OAUTH2_FRONTEND_BINDING_REDIRECT_URI = 'http://localhost:5173/profile'
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
import rtoml
|
||||
|
||||
from pydantic.fields import FieldInfo
|
||||
from pydantic_core import PydanticUndefinedType
|
||||
from pydantic_settings import PydanticBaseSettingsSource
|
||||
|
||||
from backend.core.path_conf import PLUGIN_DIR
|
||||
@@ -36,4 +37,13 @@ class PluginSettingsSource(PydanticBaseSettingsSource):
|
||||
plugin_settings = config.get('settings', {})
|
||||
merged_settings.update(plugin_settings)
|
||||
|
||||
return merged_settings
|
||||
filtered_settings: dict[str, Any] = {}
|
||||
for key, value in merged_settings.items():
|
||||
field_info = self.settings_cls.model_fields.get(key)
|
||||
if field_info is not None:
|
||||
if isinstance(field_info.default, PydanticUndefinedType):
|
||||
filtered_settings[key] = value
|
||||
else:
|
||||
filtered_settings[key] = value
|
||||
|
||||
return filtered_settings
|
||||
|
||||
Reference in New Issue
Block a user