第一次提交

This commit is contained in:
rjb
2026-01-19 00:09:36 +08:00
parent de4b5059e9
commit 6674060f2f
191 changed files with 40940 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
# Core package
from app.core.config import settings
from app.core.database import get_db, Base
__all__ = ["settings", "get_db", "Base"]

View File

@@ -0,0 +1,23 @@
"""
Celery 应用配置
"""
from celery import Celery
from app.core.config import settings
celery_app = Celery(
"aiagent",
broker=settings.REDIS_URL,
backend=settings.REDIS_URL,
include=["app.tasks.workflow_tasks", "app.tasks.agent_tasks"]
)
celery_app.conf.update(
task_serializer="json",
accept_content=["json"],
result_serializer="json",
timezone="Asia/Shanghai",
enable_utc=True,
task_track_started=True,
task_time_limit=30 * 60, # 30分钟
task_soft_time_limit=25 * 60, # 25分钟
)

View File

@@ -0,0 +1,47 @@
"""
应用配置
"""
from pydantic_settings import BaseSettings
from typing import List
class Settings(BaseSettings):
"""应用设置"""
# 应用基本信息
APP_NAME: str = "低代码智能体平台"
APP_VERSION: str = "1.0.0"
DEBUG: bool = True
SECRET_KEY: str = "dev-secret-key-change-in-production"
# 数据库配置MySQL
DATABASE_URL: str = "mysql+pymysql://root:!Rjb12191@gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com:24936/agent_db?charset=utf8mb4"
# Redis配置
REDIS_URL: str = "redis://localhost:6379/0"
# CORS配置支持字符串或列表
CORS_ORIGINS: str = "http://localhost:3000,http://127.0.0.1:3000,http://localhost:8038,http://101.43.95.130:8038"
# OpenAI配置
OPENAI_API_KEY: str = ""
OPENAI_BASE_URL: str = "https://api.openai.com/v1"
# DeepSeek配置
DEEPSEEK_API_KEY: str = ""
DEEPSEEK_BASE_URL: str = "https://api.deepseek.com"
# Anthropic配置
ANTHROPIC_API_KEY: str = ""
# JWT配置
JWT_SECRET_KEY: str = "dev-jwt-secret-key-change-in-production"
JWT_ALGORITHM: str = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()

View File

@@ -0,0 +1,45 @@
"""
数据库配置
"""
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
# 创建数据库引擎MySQL
engine = create_engine(
settings.DATABASE_URL,
pool_pre_ping=True,
pool_size=10,
max_overflow=20,
echo=settings.DEBUG # 开发环境显示SQL
)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建基础模型类
Base = declarative_base()
def get_db():
"""获取数据库会话"""
db = SessionLocal()
try:
yield db
finally:
db.close()
def init_db():
"""初始化数据库,创建所有表"""
# 导入所有模型,确保它们被注册
import app.models.user
import app.models.workflow
import app.models.agent
import app.models.execution
import app.models.model_config
import app.models.workflow_template
import app.models.permission
import app.models.alert_rule
Base.metadata.create_all(bind=engine)

View File

@@ -0,0 +1,75 @@
"""
全局错误处理器
"""
import logging
import traceback
from fastapi import Request, status
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from sqlalchemy.exc import SQLAlchemyError
from app.core.exceptions import BaseAPIException
logger = logging.getLogger(__name__)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""处理验证错误"""
errors = []
for error in exc.errors():
field = ".".join(str(loc) for loc in error.get("loc", []))
errors.append({
"field": field,
"message": error.get("msg"),
"type": error.get("type")
})
logger.warning(f"验证错误: {errors}")
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"error": "VALIDATION_ERROR",
"message": "请求参数验证失败",
"details": errors
}
)
async def api_exception_handler(request: Request, exc: BaseAPIException):
"""处理自定义API异常"""
logger.error(f"API异常: {exc.detail} (错误码: {exc.error_code})")
return JSONResponse(
status_code=exc.status_code,
content={
"error": exc.error_code or "API_ERROR",
"message": exc.detail
}
)
async def sqlalchemy_exception_handler(request: Request, exc: SQLAlchemyError):
"""处理数据库错误"""
logger.error(f"数据库错误: {str(exc)}", exc_info=True)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": "DATABASE_ERROR",
"message": "数据库操作失败,请稍后重试"
}
)
async def general_exception_handler(request: Request, exc: Exception):
"""处理通用异常"""
logger.error(f"未处理的异常: {str(exc)}", exc_info=True)
logger.error(f"异常堆栈: {traceback.format_exc()}")
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": "INTERNAL_ERROR",
"message": "服务器内部错误,请稍后重试"
}
)

View File

@@ -0,0 +1,86 @@
"""
自定义异常类
"""
from fastapi import HTTPException, status
class BaseAPIException(HTTPException):
"""基础API异常"""
def __init__(self, status_code: int, detail: str, error_code: str = None):
super().__init__(status_code=status_code, detail=detail)
self.error_code = error_code
class ValidationError(BaseAPIException):
"""验证错误"""
def __init__(self, detail: str):
super().__init__(
status_code=status.HTTP_400_BAD_REQUEST,
detail=detail,
error_code="VALIDATION_ERROR"
)
class NotFoundError(BaseAPIException):
"""资源未找到错误"""
def __init__(self, resource: str, resource_id: str = None):
detail = f"{resource}不存在"
if resource_id:
detail += f": {resource_id}"
super().__init__(
status_code=status.HTTP_404_NOT_FOUND,
detail=detail,
error_code="NOT_FOUND"
)
class UnauthorizedError(BaseAPIException):
"""未授权错误"""
def __init__(self, detail: str = "未授权访问"):
super().__init__(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=detail,
error_code="UNAUTHORIZED"
)
class ForbiddenError(BaseAPIException):
"""禁止访问错误"""
def __init__(self, detail: str = "无权访问此资源"):
super().__init__(
status_code=status.HTTP_403_FORBIDDEN,
detail=detail,
error_code="FORBIDDEN"
)
class ConflictError(BaseAPIException):
"""资源冲突错误"""
def __init__(self, detail: str):
super().__init__(
status_code=status.HTTP_409_CONFLICT,
detail=detail,
error_code="CONFLICT"
)
class InternalServerError(BaseAPIException):
"""内部服务器错误"""
def __init__(self, detail: str = "服务器内部错误"):
super().__init__(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=detail,
error_code="INTERNAL_ERROR"
)
class WorkflowExecutionError(BaseAPIException):
"""工作流执行错误"""
def __init__(self, detail: str, node_id: str = None):
if node_id:
detail = f"节点 {node_id} 执行失败: {detail}"
super().__init__(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=detail,
error_code="WORKFLOW_EXECUTION_ERROR"
)

View File

@@ -0,0 +1,55 @@
"""
安全相关功能密码加密、JWT等
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
import bcrypt
from app.core.config import settings
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""验证密码"""
try:
# bcrypt限制密码长度最多72字节
password_bytes = plain_password.encode('utf-8')
if len(password_bytes) > 72:
password_bytes = password_bytes[:72]
return bcrypt.checkpw(password_bytes, hashed_password.encode('utf-8'))
except Exception:
return False
def get_password_hash(password: str) -> str:
"""获取密码哈希"""
# bcrypt限制密码长度最多72字节
password_bytes = password.encode('utf-8')
if len(password_bytes) > 72:
password_bytes = password_bytes[:72]
# 生成盐并哈希密码
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode('utf-8')
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""创建访问令牌"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY or settings.SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
return encoded_jwt
def decode_access_token(token: str) -> Optional[dict]:
"""解码访问令牌"""
try:
payload = jwt.decode(token, settings.JWT_SECRET_KEY or settings.SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
return payload
except JWTError:
return None