mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
feat(代码生成器): 重构代码生成模板并增强安全性
重构代码生成模板结构,将模板文件移动到标准目录 添加文件覆盖控制和安全路径检查,防止生成到项目外 优化生成逻辑,支持跳过已存在文件并返回统计信息 完善模板内容,增加分页查询和更多功能实现细节
This commit is contained in:
@@ -222,10 +222,6 @@ async def gen_code_local_controller(
|
||||
返回:
|
||||
- JSONResponse: 包含生成结果的JSON响应
|
||||
"""
|
||||
from app.config.setting import settings
|
||||
if not settings.allow_overwrite:
|
||||
logger.error('【系统预设】不允许生成文件覆盖到本地')
|
||||
return ErrorResponse(msg='【系统预设】不允许生成文件覆盖到本地')
|
||||
result = await GenTableService.generate_code_service(auth, table_name)
|
||||
logger.info('生成代码到指定路径成功')
|
||||
return SuccessResponse(msg="生成代码到指定路径成功", data=result)
|
||||
|
||||
@@ -408,18 +408,35 @@ class GenTableService:
|
||||
"""
|
||||
env = Jinja2TemplateInitializerUtil.init_jinja2()
|
||||
render_info = await cls.__get_gen_render_info(auth, table_name)
|
||||
gen_table_schema = render_info[3]
|
||||
skipped = 0
|
||||
for template in render_info[0]:
|
||||
try:
|
||||
render_content = await env.get_template(template).render_async(**render_info[2])
|
||||
gen_path = cls.__get_gen_path(render_info[3], template)
|
||||
gen_path = cls.__get_gen_path(gen_table_schema, template)
|
||||
if gen_path:
|
||||
# 只允许写入到项目根目录及其子目录
|
||||
project_root = os.path.realpath(str(settings.BASE_DIR.parent))
|
||||
target_path = os.path.realpath(gen_path)
|
||||
if not target_path.startswith(project_root):
|
||||
raise CustomException(msg='生成路径不允许,请选择项目目录内路径')
|
||||
|
||||
os.makedirs(os.path.dirname(gen_path), exist_ok=True)
|
||||
|
||||
# 覆盖控制:存在且不允许覆盖则跳过
|
||||
if os.path.exists(gen_path) and not settings.allow_overwrite:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
with open(gen_path, 'w', encoding='utf-8') as f:
|
||||
f.write(render_content)
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f'渲染模板失败,表名:{render_info[3].table_name},详细错误信息:{str(e)}')
|
||||
raise CustomException(msg=f'渲染模板失败,表名:{gen_table_schema.table_name},详细错误信息:{str(e)}')
|
||||
|
||||
return SuccessResponse(msg='生成代码成功')
|
||||
msg = '生成代码成功'
|
||||
if skipped:
|
||||
msg += f'(已跳过 {skipped} 个已存在文件)'
|
||||
return SuccessResponse(msg=msg)
|
||||
|
||||
@classmethod
|
||||
async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) -> bytes:
|
||||
@@ -682,13 +699,14 @@ class GenTableService:
|
||||
- Optional[str]: 生成的文件路径,若失败则返回None。
|
||||
"""
|
||||
try:
|
||||
gen_path = gen_table.gen_path or ""
|
||||
gen_path = (gen_table.gen_path or '').strip()
|
||||
file_name = Jinja2TemplateUtil.get_file_name(template, gen_table)
|
||||
# 修复:检查文件名是否为空
|
||||
if not file_name:
|
||||
return None
|
||||
if gen_path == '/':
|
||||
return os.path.join(os.getcwd(), GEN_PATH, file_name)
|
||||
# 默认写入到项目根目录(backend的上一级)
|
||||
project_root = str(settings.BASE_DIR.parent)
|
||||
if gen_path in ['', '/']:
|
||||
return os.path.join(project_root, file_name)
|
||||
else:
|
||||
return os.path.join(gen_path, file_name)
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user