1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
| # Console API Blueprint配置
bp = Blueprint("console", __name__, url_prefix="/console/api")
api = ExternalApi(
bp,
version="1.0",
title="Console API",
description="Console management APIs for app configuration, monitoring, and administration",
)
# Console命名空间
console_ns = Namespace("console", description="Console management API operations", path="/")
class ConsoleAPIRouter:
"""
Console API路由管理器
负责组织和注册Console API的所有路由
"""
def __init__(self, api: ExternalApi):
"""
初始化Console API路由
Args:
api: ExternalApi实例
"""
self.api = api
self._register_core_routes()
self._register_app_routes()
self._register_dataset_routes()
self._register_workspace_routes()
self._register_auth_routes()
def _register_core_routes(self):
"""注册核心路由"""
# 系统管理路由
self.api.add_resource(SetupApi, "/setup")
self.api.add_resource(PingApi, "/ping")
self.api.add_resource(VersionApi, "/version")
self.api.add_resource(FeatureApi, "/features")
# 文件管理路由
self.api.add_resource(FileApi, "/files/upload")
self.api.add_resource(FilePreviewApi, "/files/<uuid:file_id>/preview")
self.api.add_resource(FileSupportTypeApi, "/files/support-type")
# 远程文件路由
self.api.add_resource(RemoteFileInfoApi, "/remote-files/<path:url>")
self.api.add_resource(RemoteFileUploadApi, "/remote-files/upload")
def _register_app_routes(self):
"""注册应用管理路由"""
# 应用CRUD操作
from .app import app, workflow, message, conversation
# 应用基础管理
self.api.add_resource(AppListApi, "/apps")
self.api.add_resource(AppApi, "/apps/<uuid:app_id>")
self.api.add_resource(AppCreateApi, "/apps")
# 工作流管理
self.api.add_resource(WorkflowListApi, "/apps/<uuid:app_id>/workflows")
self.api.add_resource(WorkflowApi, "/apps/<uuid:app_id>/workflows/<uuid:workflow_id>")
# 消息和对话管理
self.api.add_resource(MessageListApi, "/apps/<uuid:app_id>/messages")
self.api.add_resource(ConversationListApi, "/apps/<uuid:app_id>/conversations")
def _register_dataset_routes(self):
"""注册数据集管理路由"""
from .datasets import datasets, datasets_document, datasets_segments
# 数据集CRUD
self.api.add_resource(DatasetListApi, "/datasets")
self.api.add_resource(DatasetApi, "/datasets/<uuid:dataset_id>")
# 文档管理
self.api.add_resource(DocumentListApi, "/datasets/<uuid:dataset_id>/documents")
self.api.add_resource(DocumentApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>")
# 段落管理
self.api.add_resource(SegmentListApi, "/datasets/<uuid:dataset_id>/segments")
def _register_workspace_routes(self):
"""注册工作空间管理路由"""
from .workspace import workspace, account, model_providers
# 工作空间管理
self.api.add_resource(WorkspaceApi, "/workspaces/current")
self.api.add_resource(AccountApi, "/account")
# 模型提供者配置
self.api.add_resource(ModelProviderListApi, "/workspaces/current/model-providers")
self.api.add_resource(ModelProviderApi, "/workspaces/current/model-providers/<string:provider>")
def _register_auth_routes(self):
"""注册认证相关路由"""
from .auth import login, oauth, forgot_password
# 用户认证
self.api.add_resource(LoginApi, "/login")
self.api.add_resource(LogoutApi, "/logout")
self.api.add_resource(ForgotPasswordApi, "/forgot-password")
# OAuth认证
self.api.add_resource(OAuthApi, "/oauth")
@console_ns.route("/apps")
class AppListApi(Resource):
"""
应用列表API
提供应用的分页查询、过滤和管理功能
"""
@api.doc("list_apps")
@api.doc(description="获取应用列表,支持分页和多种过滤条件")
@api.expect(
api.parser()
.add_argument("page", type=int, location="args", help="页码 (1-99999)", default=1)
.add_argument("limit", type=int, location="args", help="页大小 (1-100)", default=20)
.add_argument(
"mode", type=str, location="args",
choices=["completion", "chat", "advanced-chat", "workflow", "agent-chat", "channel", "all"],
default="all", help="应用模式过滤"
)
.add_argument("name", type=str, location="args", help="按应用名称过滤")
.add_argument("tag_ids", type=str, location="args", help="逗号分隔的标签ID")
.add_argument("is_created_by_me", type=bool, location="args", help="只显示我创建的应用")
)
@api.response(200, "Success", app_pagination_fields)
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def get(self):
"""
获取应用列表
支持多维度过滤和分页查询
Returns:
分页的应用列表,包含应用基本信息和统计数据
"""
# 参数解析和验证
parser = reqparse.RequestParser()
parser.add_argument("page", type=inputs.int_range(1, 99999), default=1)
parser.add_argument("limit", type=inputs.int_range(1, 100), default=20)
parser.add_argument("mode", type=str, choices=ALLOWED_APP_MODES, default="all")
parser.add_argument("name", type=str)
parser.add_argument("tag_ids", type=self._parse_uuid_list)
parser.add_argument("is_created_by_me", type=inputs.boolean)
args = parser.parse_args()
# 业务逻辑调用
app_service = AppService()
app_pagination = app_service.get_paginate_apps(
user_id=current_user.id,
tenant_id=current_user.current_tenant_id,
args=args
)
# 返回格式化结果
if not app_pagination:
return {
"data": [],
"total": 0,
"page": 1,
"limit": 20,
"has_more": False
}
return marshal(app_pagination, app_pagination_fields)
@api.doc("create_app")
@api.doc(description="创建新应用")
@api.expect(app_create_fields)
@api.response(201, "应用创建成功", app_detail_fields)
@api.response(400, "请求参数错误")
@setup_required
@login_required
@account_initialization_required
@cloud_edition_billing_resource_check("apps")
def post(self):
"""
创建新应用
支持多种应用类型的创建
Returns:
创建的应用详细信息
"""
# 参数解析
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, help="应用名称")
parser.add_argument("icon", type=str, help="应用图标")
parser.add_argument("icon_background", type=str, help="图标背景色")
parser.add_argument(
"mode", type=str, required=True,
choices=ALLOW_CREATE_APP_MODES,
help="应用模式"
)
parser.add_argument(
"description", type=self._validate_description_length,
help="应用描述"
)
args = parser.parse_args()
# 创建应用
try:
app_service = AppService()
app = app_service.create_app(
tenant_id=current_user.current_tenant_id,
app_name=args["name"],
app_mode=args["mode"],
icon=args.get("icon"),
icon_background=args.get("icon_background"),
description=args.get("description"),
account=current_user
)
return marshal(app, app_detail_fields), 201
except ValueError as e:
raise BadRequest(str(e))
except Exception as e:
logger.exception("创建应用失败")
raise InternalServerError("应用创建失败")
def _validate_description_length(self, description: str) -> str:
"""
验证描述长度
Args:
description: 应用描述
Returns:
str: 验证后的描述
Raises:
ValueError: 描述长度超限
"""
if description and len(description) > 400:
raise ValueError("应用描述不能超过400个字符")
return description
def _parse_uuid_list(self, value: str) -> list[str]:
"""
解析UUID列表
Args:
value: 逗号分隔的UUID字符串
Returns:
list[str]: UUID列表
Raises:
ValueError: UUID格式错误
"""
try:
return [str(uuid.UUID(v)) for v in value.split(",")]
except ValueError:
abort(400, message="标签ID格式错误")
@console_ns.route("/apps/<uuid:app_id>")
class AppApi(Resource):
"""
单个应用API
提供应用的详细信息查询、更新和删除功能
"""
@api.doc("get_app")
@api.doc(description="获取应用详细信息")
@api.response(200, "Success", app_detail_fields_with_site)
@api.response(404, "应用不存在")
@setup_required
@login_required
@account_initialization_required
@get_app_model
def get(self, app_model: App):
"""
获取应用详细信息
包含应用配置、统计数据和站点信息
Args:
app_model: 应用模型实例(通过装饰器注入)
Returns:
应用详细信息,包含所有配置和统计数据
"""
try:
# 获取应用服务
app_service = AppService()
# 获取应用详细信息
app_detail = app_service.get_app_detail(
app_id=app_model.id,
user_id=current_user.id,
tenant_id=current_user.current_tenant_id
)
# 获取应用站点信息
site_info = app_service.get_app_site_info(app_model.id)
# 合并信息
app_data = marshal(app_detail, app_detail_fields)
if site_info:
app_data.update(marshal(site_info, site_fields))
return app_data, 200
except Exception as e:
logger.exception(f"获取应用 {app_model.id} 详情失败")
raise InternalServerError("获取应用信息失败")
@api.doc("update_app")
@api.doc(description="更新应用信息")
@api.expect(app_update_fields)
@api.response(200, "更新成功", app_detail_fields)
@setup_required
@login_required
@account_initialization_required
@get_app_model
def put(self, app_model: App):
"""
更新应用信息
支持应用名称、描述、图标等基础信息的更新
Args:
app_model: 应用模型实例
Returns:
更新后的应用信息
"""
# 参数解析
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, help="应用名称")
parser.add_argument("description", type=self._validate_description, help="应用描述")
parser.add_argument("icon", type=str, help="应用图标")
parser.add_argument("icon_background", type=str, help="图标背景色")
args = parser.parse_args()
try:
# 更新应用
app_service = AppService()
updated_app = app_service.update_app(
app_id=app_model.id,
tenant_id=current_user.current_tenant_id,
account=current_user,
**args
)
return marshal(updated_app, app_detail_fields), 200
except ValueError as e:
raise BadRequest(str(e))
except Exception as e:
logger.exception(f"更新应用 {app_model.id} 失败")
raise InternalServerError("更新应用失败")
@api.doc("delete_app")
@api.doc(description="删除应用")
@api.response(204, "删除成功")
@api.response(404, "应用不存在")
@setup_required
@login_required
@account_initialization_required
@get_app_model
def delete(self, app_model: App):
"""
删除应用
软删除应用,保留历史数据
Args:
app_model: 应用模型实例
"""
try:
app_service = AppService()
app_service.delete_app(
app_id=app_model.id,
user_id=current_user.id,
tenant_id=current_user.current_tenant_id
)
return "", 204
except Exception as e:
logger.exception(f"删除应用 {app_model.id} 失败")
raise InternalServerError("删除应用失败")
class ConsoleAuthMiddleware:
"""
Console API认证中间件
处理用户会话认证和权限验证
"""
@staticmethod
def setup_required(f):
"""
系统初始化检查装饰器
确保系统已完成初始设置
"""
def decorated(*args, **kwargs):
# 检查系统是否已初始化
if not SystemSetupService.is_setup_completed():
return {"message": "系统未完成初始化设置"}, 412
return f(*args, **kwargs)
return decorated
@staticmethod
def login_required(f):
"""
登录认证装饰器
确保用户已登录
"""
def decorated(*args, **kwargs):
if not current_user.is_authenticated:
return {"message": "未登录或会话已过期"}, 401
return f(*args, **kwargs)
return decorated
@staticmethod
def account_initialization_required(f):
"""
账户初始化检查装饰器
确保用户账户已完成初始化
"""
def decorated(*args, **kwargs):
if not current_user.is_initialized:
return {"message": "账户未完成初始化"}, 400
return f(*args, **kwargs)
return decorated
@staticmethod
def enterprise_license_required(f):
"""
企业许可证检查装饰器
检查企业功能的许可证状态
"""
def decorated(*args, **kwargs):
if not EnterpriseService.check_license_valid():
return {"message": "需要有效的企业许可证"}, 403
return f(*args, **kwargs)
return decorated
@staticmethod
def cloud_edition_billing_resource_check(resource_type: str):
"""
云版本计费资源检查装饰器
检查租户的资源使用限额
Args:
resource_type: 资源类型(如"apps", "datasets"等)
"""
def decorator(f):
def decorated(*args, **kwargs):
# 检查资源配额
if not BillingService.check_resource_quota(
tenant_id=current_user.current_tenant_id,
resource_type=resource_type
):
return {"message": f"已达到{resource_type}资源配额限制"}, 429
return f(*args, **kwargs)
return decorated
return decorator
@staticmethod
def admin_required(f):
"""
管理员权限检查装饰器
确保当前用户具有管理员权限
"""
def decorated(*args, **kwargs):
if not current_user.is_admin:
return {"message": "需要管理员权限"}, 403
return f(*args, **kwargs)
return decorated
|