init: RAG 知识库服务初始提交

- 后端 API(Flask + Gunicorn)
- RAG 引擎(混合检索 + 云端 Reranker + 引用溯源)
- 文档解析(MinerU + 多格式支持)
- Docker 生产部署配置
- 排除前端项目、敏感配置、模型文件
This commit is contained in:
lacerate551
2026-06-04 17:35:27 +08:00
commit 100d1a06eb
158 changed files with 64534 additions and 0 deletions

262
auth/gateway.py Normal file
View File

@@ -0,0 +1,262 @@
"""
网关认证模块 - 从网关注入的 Header 读取用户信息
## 使用方式
from auth.gateway import require_gateway_auth, get_current_user
@app.route('/protected')
@require_gateway_auth
def protected():
user = request.current_user # {"user_id": ..., "role": ..., ...}
...
## Header 规范(开发模式可选)
- X-User-ID: 用户唯一标识 (可选)
- X-User-Name: 用户名 (可选)
- X-User-Role: 用户角色 (可选)
- X-User-Department: 部门 (可选)
## 模式说明
开发模式 (DEV_MODE=true默认):
- 支持 mock token 模拟用户Authorization: Bearer mock-token-admin
- 无 Header 时自动使用开发测试用户
- 适用于前端测试和开发调试
生产模式 (DEV_MODE=false):
- 不需要 Header直接放行
- 权限由后端完全控制,通过 collections 参数传入
- RAG 服务完全无状态,只负责问答检索
"""
from functools import wraps
from flask import request, jsonify
from typing import Dict, Optional
import os
from pathlib import Path
from dotenv import load_dotenv
# 加载 .env 文件(从项目根目录)
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(env_path)
# ==================== 模拟用户数据(开发环境)====================
# 用于前端模拟登录测试,仅 DEV_MODE=true 时生效
MOCK_USERS = {
'admin': {
'user_id': 'admin001',
'password': 'admin123',
'role': 'admin',
'department': '管理部'
},
'admin2': {
'user_id': 'admin002',
'password': 'admin456',
'role': 'admin',
'department': '技术部'
},
'admin3': {
'user_id': 'admin003',
'password': 'admin789',
'role': 'admin',
'department': '运营部'
},
'manager': {
'user_id': 'manager001',
'password': 'manager123',
'role': 'manager',
'department': '财务部'
},
'user': {
'user_id': 'user001',
'password': 'test123',
'role': 'user',
'department': '技术部'
}
}
def require_gateway_auth(f):
"""
网关认证装饰器 - 从 Header 读取用户信息
开发模式 (DEV_MODE=true默认):
- 支持 mock token: Authorization: Bearer mock-token-admin
- 无 Header 时自动使用开发测试用户admin 角色)
生产模式 (DEV_MODE=false):
- 不需要 Header直接放行
- 用户信息设为默认值
- 权限由后端通过 collections 参数控制
"""
@wraps(f)
def decorated(*args, **kwargs):
# 开发模式开关(默认开启,生产环境设置 DEV_MODE=false
dev_mode = os.environ.get('DEV_MODE', 'true').lower() != 'false'
# 开发模式:支持 mock token
if dev_mode:
auth_header = request.headers.get('Authorization', '')
if auth_header.startswith('Bearer mock-token-'):
username = auth_header.replace('Bearer mock-token-', '')
mock_user = MOCK_USERS.get(username)
if mock_user:
request.current_user = {
"user_id": mock_user['user_id'],
"username": username,
"role": mock_user['role'],
"department": mock_user['department']
}
return f(*args, **kwargs)
# 从 Header 读取用户信息(可选)
user_id = request.headers.get('X-User-ID')
username = request.headers.get('X-User-Name', '')
role = request.headers.get('X-User-Role', 'user')
department = request.headers.get('X-User-Department', '')
# 如果有 Header使用 Header 中的用户信息
if user_id:
request.current_user = {
"user_id": user_id,
"username": username,
"role": role,
"department": department
}
return f(*args, **kwargs)
# 无 Header 时的默认处理
if dev_mode:
# 开发模式:使用默认测试用户
request.current_user = {
"user_id": "dev-user",
"username": "开发测试用户",
"role": "admin",
"department": "开发部"
}
else:
# 生产模式:使用默认用户(后端通过 collections 控制权限)
request.current_user = {
"user_id": "backend-caller",
"username": "后端调用",
"role": "user",
"department": ""
}
return f(*args, **kwargs)
return decorated
def get_current_user() -> Optional[Dict]:
"""
获取当前登录用户信息
Returns:
用户信息字典,未认证返回 None
"""
return getattr(request, 'current_user', None)
# ==================== 兼容旧代码 ====================
# 别名
require_auth = require_gateway_auth
def get_user_permissions(role: str):
"""
兼容旧代码 - 权限由后端管理,此函数仅返回默认值
"""
return ['public', 'internal', 'confidential']
def check_collection_permission(role: str, department: str, collection_name: str, operation: str = "read") -> bool:
"""
兼容旧代码 - 权限由后端管理,默认返回 True
"""
return True
def get_accessible_collections(role: str, department: str, operation: str = "read", all_collections=None):
"""
兼容旧代码 - 返回所有向量库
"""
if all_collections:
return all_collections
return ['public_kb']
def can_create_collection(role: str) -> bool:
"""兼容旧代码 - 权限由后端管理"""
return True
def can_delete_collection(role: str) -> bool:
"""兼容旧代码 - 权限由后端管理"""
return True
def require_role(*roles):
"""
兼容旧代码 - 权限由后端管理,此装饰器不再执行权限验证
"""
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
return f(*args, **kwargs)
return decorated
return decorator
def require_collection_permission(operation: str):
"""
兼容旧代码 - 权限由后端管理,此装饰器不再执行权限验证
"""
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
return f(*args, **kwargs)
return decorated
return decorator
def get_auth_manager():
"""兼容旧代码"""
return _FakeAuthManager()
class _FakeAuthManager:
"""兼容旧代码的假 AuthManager"""
@staticmethod
def get_user_permissions(role: str):
return ['public', 'internal', 'confidential']
@staticmethod
def get_accessible_collections(role: str, department: str):
return ['public_kb']
def is_admin() -> bool:
"""检查当前用户是否为管理员"""
user = get_current_user()
return user is not None and user.get('role') == 'admin'
def is_manager_or_above() -> bool:
"""检查当前用户是否为经理或以上级别"""
user = get_current_user()
return user is not None and user.get('role') in ('admin', 'manager')
def normalize_department_name(department: str) -> str:
"""兼容旧代码 - 部门名称标准化"""
if not department:
return ""
if department.replace("_", "").replace("-", "").isalnum() and department.isascii():
return department.lower()
return ""