diff --git a/api/apps/tenant_app.py b/api/apps/restful_apis/tenant_api.py similarity index 59% rename from api/apps/tenant_app.py rename to api/apps/restful_apis/tenant_api.py index be6305e891..4d45337cb0 100644 --- a/api/apps/tenant_app.py +++ b/api/apps/restful_apis/tenant_api.py @@ -1,5 +1,5 @@ # -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# Copyright 2026 The InfiniFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,48 +13,56 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import logging import asyncio +import logging + +from api.apps import current_user, login_required from api.db import UserTenantRole from api.db.db_models import UserTenant -from api.db.services.user_service import UserTenantService, UserService - +from api.db.services.user_service import UserService, UserTenantService +from api.utils.api_utils import ( + get_data_error_result, + get_json_result, + get_request_json, + server_error_response, + validate_request, +) +from api.utils.web_utils import send_invite_email +from common import settings from common.constants import RetCode, StatusEnum from common.misc_utils import get_uuid from common.time_utils import delta_seconds -from api.utils.api_utils import get_data_error_result, get_json_result, get_request_json, server_error_response, validate_request -from api.utils.web_utils import send_invite_email -from common import settings -from api.apps import login_required, current_user -@manager.route("//user/list", methods=["GET"]) # noqa: F821 +@manager.route("/tenants//users", methods=["GET"]) # noqa: F821 @login_required def user_list(tenant_id): if current_user.id != tenant_id: return get_json_result( data=False, - message='No authorization.', - code=RetCode.AUTHENTICATION_ERROR) + message="No authorization.", + code=RetCode.AUTHENTICATION_ERROR, + ) try: users = UserTenantService.get_by_tenant_id(tenant_id) - for u in users: - u["delta_seconds"] = delta_seconds(str(u["update_date"])) + for user in users: + user["delta_seconds"] = delta_seconds(str(user["update_date"])) return get_json_result(data=users) - except Exception as e: - return server_error_response(e) + except Exception as exc: + return server_error_response(exc) -@manager.route('//user', methods=['POST']) # noqa: F821 +@manager.route("/tenants//users", methods=["POST"]) # noqa: F821 @login_required @validate_request("email") async def create(tenant_id): if current_user.id != tenant_id: return get_json_result( data=False, - message='No authorization.', - code=RetCode.AUTHENTICATION_ERROR) + message="No authorization.", + code=RetCode.AUTHENTICATION_ERROR, + ) req = await get_request_json() invite_user_email = req["email"] @@ -71,7 +79,8 @@ async def create(tenant_id): if user_tenant_role == UserTenantRole.OWNER: return get_data_error_result(message=f"{invite_user_email} is the owner of the team.") return get_data_error_result( - message=f"{invite_user_email} is in the team, but the role: {user_tenant_role} is invalid.") + message=f"{invite_user_email} is in the team, but the role: {user_tenant_role} is invalid." + ) UserTenantService.save( id=get_uuid(), @@ -79,10 +88,10 @@ async def create(tenant_id): tenant_id=tenant_id, invited_by=current_user.id, role=UserTenantRole.INVITE, - status=StatusEnum.VALID.value) + status=StatusEnum.VALID.value, + ) try: - user_name = "" _, user = UserService.get_by_id(current_user.id) if user: @@ -93,52 +102,62 @@ async def create(tenant_id): to_email=invite_user_email, invite_url=settings.MAIL_FRONTEND_URL, tenant_id=tenant_id, - inviter=user_name or current_user.email + inviter=user_name or current_user.email, ) ) - except Exception as e: - logging.exception(f"Failed to send invite email to {invite_user_email}: {e}") - return get_json_result(data=False, message="Failed to send invite email.", code=RetCode.SERVER_ERROR) - usr = invite_users[0].to_dict() - usr = {k: v for k, v in usr.items() if k in ["id", "avatar", "email", "nickname"]} + except Exception as exc: + logging.exception(f"Failed to send invite email to {invite_user_email}: {exc}") + return get_json_result( + data=False, + message="Failed to send invite email.", + code=RetCode.SERVER_ERROR, + ) - return get_json_result(data=usr) + user = invite_users[0].to_dict() + user = {k: v for k, v in user.items() if k in ["id", "avatar", "email", "nickname"]} + return get_json_result(data=user) -@manager.route('//user/', methods=['DELETE']) # noqa: F821 +@manager.route("/tenants//users", methods=["DELETE"]) # noqa: F821 @login_required -def rm(tenant_id, user_id): +@validate_request("user_id") +async def rm(tenant_id): + req = await get_request_json() + user_id = req["user_id"] if current_user.id != tenant_id and current_user.id != user_id: return get_json_result( data=False, - message='No authorization.', - code=RetCode.AUTHENTICATION_ERROR) + message="No authorization.", + code=RetCode.AUTHENTICATION_ERROR, + ) try: UserTenantService.filter_delete([UserTenant.tenant_id == tenant_id, UserTenant.user_id == user_id]) return get_json_result(data=True) - except Exception as e: - return server_error_response(e) + except Exception as exc: + return server_error_response(exc) -@manager.route("/list", methods=["GET"]) # noqa: F821 +@manager.route("/tenants", methods=["GET"]) # noqa: F821 @login_required def tenant_list(): try: users = UserTenantService.get_tenants_by_user_id(current_user.id) - for u in users: - u["delta_seconds"] = delta_seconds(str(u["update_date"])) + for user in users: + user["delta_seconds"] = delta_seconds(str(user["update_date"])) return get_json_result(data=users) - except Exception as e: - return server_error_response(e) + except Exception as exc: + return server_error_response(exc) -@manager.route("/agree/", methods=["PUT"]) # noqa: F821 +@manager.route("/tenants/", methods=["PATCH"]) # noqa: F821 @login_required def agree(tenant_id): try: - UserTenantService.filter_update([UserTenant.tenant_id == tenant_id, UserTenant.user_id == current_user.id], - {"role": UserTenantRole.NORMAL}) + UserTenantService.filter_update( + [UserTenant.tenant_id == tenant_id, UserTenant.user_id == current_user.id], + {"role": UserTenantRole.NORMAL}, + ) return get_json_result(data=True) - except Exception as e: - return server_error_response(e) + except Exception as exc: + return server_error_response(exc) diff --git a/sdk/python/test.py b/sdk/python/test.py new file mode 100644 index 0000000000..c670033194 --- /dev/null +++ b/sdk/python/test.py @@ -0,0 +1,17 @@ +from .ragflow_sdk import RAGFlow + +rag_object = RAGFlow(api_key="ragflow-FDfRECsXDRagsKPxb_EfZdDPcmngavSgYEzbU_Blgq4", base_url="http://localhost:9222") +assistant = rag_object.get_agent("b0bc46e43dfc11f1b4ff84ba59bc54d9") +session = assistant.create_session() + +print("\n==================== Miss R =====================\n") +print("Hello. What can I do for you?") + +while True: + question = input("\n==================== User =====================\n> ") + print("\n==================== Miss R =====================\n") + + cont = "" + for ans in session.ask(question, stream=True): + print(ans.content[len(cont):], end='', flush=True) + cont = ans.content diff --git a/test/testcases/test_web_api/test_user_app/test_tenant_app_unit.py b/test/testcases/test_web_api/test_user_app/test_tenant_app_unit.py index b94a579db1..cafe5576e3 100644 --- a/test/testcases/test_web_api/test_user_app/test_tenant_app_unit.py +++ b/test/testcases/test_web_api/test_user_app/test_tenant_app_unit.py @@ -180,7 +180,7 @@ def _load_tenant_module(monkeypatch): common_pkg.settings = settings_mod sys.modules.pop("test_tenant_app_unit_module", None) - module_path = repo_root / "api" / "apps" / "tenant_app.py" + module_path = repo_root / "api" / "apps" / "restful_apis" / "tenant_api.py" spec = importlib.util.spec_from_file_location("test_tenant_app_unit_module", module_path) module = importlib.util.module_from_spec(spec) module.manager = _DummyManager() @@ -268,20 +268,21 @@ def test_rm_and_tenant_list_matrix_unit(monkeypatch): module = _load_tenant_module(monkeypatch) module.current_user.id = "outsider" - res = module.rm("tenant-1", "user-2") + _set_request_json(monkeypatch, module, {"user_id": "user-2"}) + res = _run(module.rm("tenant-1")) assert res["code"] == module.RetCode.AUTHENTICATION_ERROR, res assert res["message"] == "No authorization.", res module.current_user.id = "tenant-1" deleted = [] monkeypatch.setattr(module.UserTenantService, "filter_delete", lambda conditions: deleted.append(conditions) or True) - res = module.rm("tenant-1", "user-2") + res = _run(module.rm("tenant-1")) assert res["code"] == 0, res assert res["data"] is True, res assert deleted, "filter_delete should be called" monkeypatch.setattr(module.UserTenantService, "filter_delete", lambda _conditions: (_ for _ in ()).throw(RuntimeError("rm boom"))) - res = module.rm("tenant-1", "user-2") + res = _run(module.rm("tenant-1")) assert res["code"] == 100, res assert "rm boom" in res["message"], res diff --git a/web/src/services/user-service.ts b/web/src/services/user-service.ts index 7f95201915..09d7d682d5 100644 --- a/web/src/services/user-service.ts +++ b/web/src/services/user-service.ts @@ -139,11 +139,14 @@ export const deleteTenantUser = ({ }: { tenantId: string; userId: string; -}) => request.delete(api.deleteTenantUser(tenantId, userId)); +}) => + request.delete(api.deleteTenantUser(tenantId), { + data: { userId }, + }); export const listTenant = () => request.get(api.listTenant); export const agreeTenant = (tenantId: string) => - request.put(api.agreeTenant(tenantId)); + request.patch(api.agreeTenant(tenantId)); export default userService; diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 9e07517d0d..171ebdd468 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -16,13 +16,13 @@ export default { loginChannel: (channel: string) => `${webAPI}/user/login/${channel}`, // team - addTenantUser: (tenantId: string) => `${webAPI}/tenant/${tenantId}/user`, + addTenantUser: (tenantId: string) => `${restAPIv1}/tenants/${tenantId}/users`, listTenantUser: (tenantId: string) => - `${webAPI}/tenant/${tenantId}/user/list`, - deleteTenantUser: (tenantId: string, userId: string) => - `${webAPI}/tenant/${tenantId}/user/${userId}`, - listTenant: `${webAPI}/tenant/list`, - agreeTenant: (tenantId: string) => `${webAPI}/tenant/agree/${tenantId}`, + `${restAPIv1}/tenants/${tenantId}/users`, + deleteTenantUser: (tenantId: string) => + `${restAPIv1}/tenants/${tenantId}/users`, + listTenant: `${restAPIv1}/tenants`, + agreeTenant: (tenantId: string) => `${restAPIv1}/tenants/${tenantId}`, // llm model factoriesList: `${webAPI}/llm/factories`,