```
feat: 添加飞书用户模块和订阅功能支持 - 新增feishu_users模块用于处理飞书用户身份验证和权限管理 - 新增subscriptions模块用于处理订阅相关功能 - 新增personalization模块用于个性化服务 - 在alembic迁移配置中注册新的模型模块 - 在API路由器中添加feishu_users和subscriptions路由 - 实现事件调度服务的改进,包括错误处理和状态更新优化 - 添加飞书命令处理的权限检查机制 - 实现飞书应用票据事件处理 - 改进审计日志记录功能 ```
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
@@ -23,6 +24,13 @@ from app.modules.legacy_mysql.constants import (
|
||||
|
||||
from app.modules.legacy_mysql.services.common import FORBIDDEN_SQL_TOKENS, _normalize_sql, _query_name_text, _row_to_dict
|
||||
|
||||
_SQL_COMMENT_MARKERS = ("--", "#", "/*", "*/")
|
||||
_SQL_QUOTED_CONTENT_PATTERN = re.compile(
|
||||
r"""'(?:''|\\.|[^'])*'|"(?:""|\\.|[^"])*"|`(?:``|[^`])*`""",
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
_SQL_WORD_PATTERN = re.compile(r"[a-z_]+")
|
||||
|
||||
|
||||
class LegacyQueryMixin:
|
||||
@staticmethod
|
||||
@@ -36,13 +44,27 @@ class LegacyQueryMixin:
|
||||
|
||||
@staticmethod
|
||||
def _ensure_readonly(sql: str) -> None:
|
||||
stripped = sql.strip().lower()
|
||||
if not stripped.startswith(LEGACY_SELECT_PREFIX):
|
||||
stripped = sql.strip()
|
||||
scrubbed = _SQL_QUOTED_CONTENT_PATTERN.sub(" ", stripped)
|
||||
if any(marker in scrubbed for marker in _SQL_COMMENT_MARKERS):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=LegacyQueryError.SQL_COMMENTS_NOT_ALLOWED,
|
||||
)
|
||||
statement = scrubbed.rstrip()
|
||||
if statement.endswith(LEGACY_SQL_TRAILING_TERMINATOR):
|
||||
statement = statement[:-1].rstrip()
|
||||
if LEGACY_SQL_TRAILING_TERMINATOR in statement:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=LegacyQueryError.SINGLE_STATEMENT_REQUIRED,
|
||||
)
|
||||
if not re.match(rf"^{LEGACY_SELECT_PREFIX}\b", statement, flags=re.IGNORECASE):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=LegacyQueryError.ONLY_SELECT_ALLOWED,
|
||||
)
|
||||
tokens = {token.strip(" ;,\n\t") for token in stripped.replace("(", " ").split()}
|
||||
tokens = set(_SQL_WORD_PATTERN.findall(statement.lower()))
|
||||
if tokens & FORBIDDEN_SQL_TOKENS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -117,9 +139,10 @@ class LegacyQueryMixin:
|
||||
engine = self._ensure_engine()
|
||||
params = dict(params or {})
|
||||
try:
|
||||
params[LegacyResponseKey.LIMIT] = bounded_limit(
|
||||
limit_value = bounded_limit(
|
||||
params.get(LegacyResponseKey.LIMIT, limit)
|
||||
)
|
||||
params[LegacyResponseKey.LIMIT] = limit_value
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
@@ -130,7 +153,10 @@ class LegacyQueryMixin:
|
||||
limited_sql = f"{sql.rstrip(LEGACY_SQL_TRAILING_TERMINATOR)}{LEGACY_LIMIT_CLAUSE}"
|
||||
with engine.connect() as conn:
|
||||
result = conn.execute(text(limited_sql), params)
|
||||
rows = [_row_to_dict(row) for row in result.mappings().all()]
|
||||
rows = [
|
||||
_row_to_dict(row)
|
||||
for row in result.mappings().fetchmany(limit_value)
|
||||
]
|
||||
columns = list(rows[0].keys()) if rows else []
|
||||
return {
|
||||
LegacyResponseKey.COLUMNS: columns,
|
||||
|
||||
Reference in New Issue
Block a user