mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve?
```
admin> list services;
command: list services;
Listing all services
+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+
| extra | host | id | name | port | service_type | status |
+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+
| {} | 0.0.0.0 | 0 | ragflow_0 | 9380 | ragflow_server | Timeout |
| {'meta_type': 'mysql', 'password': 'infini_rag_flow', 'username': 'root'} | localhost | 1 | mysql | 5455 | meta_data | Alive |
| {'password': 'infini_rag_flow', 'store_type': 'minio', 'user': 'rag_flow'} | localhost | 2 | minio | 9000 | file_store | Alive |
| {'password': 'infini_rag_flow', 'retrieval_type': 'elasticsearch', 'username': 'elastic'} | localhost | 3 | elasticsearch | 1200 | retrieval | Alive |
| {'db_name': 'default_db', 'retrieval_type': 'infinity'} | localhost | 4 | infinity | 23817 | retrieval | Timeout |
| {'database': 1, 'mq_type': 'redis', 'password': 'infini_rag_flow'} | localhost | 5 | redis | 6379 | message_queue | Alive |
+-------------------------------------------------------------------------------------------+-----------+----+---------------+-------+----------------+---------+
admin>
Use '\q' to quit
admin>
```
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Signed-off-by: Jin Hai <haijin.chn@gmail.com>
224 lines
8.4 KiB
Python
224 lines
8.4 KiB
Python
#
|
|
# Copyright 2025 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.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
|
|
import re
|
|
from werkzeug.security import check_password_hash
|
|
from api.db import ActiveEnum
|
|
from api.db.services import UserService
|
|
from api.db.joint_services.user_account_service import create_new_user, delete_user_data
|
|
from api.db.services.canvas_service import UserCanvasService
|
|
from api.db.services.user_service import TenantService
|
|
from api.db.services.knowledgebase_service import KnowledgebaseService
|
|
from api.utils.crypt import decrypt
|
|
from api.utils import health_utils
|
|
|
|
from api.common.exceptions import AdminException, UserAlreadyExistsError, UserNotFoundError
|
|
from config import SERVICE_CONFIGS
|
|
|
|
|
|
class UserMgr:
|
|
@staticmethod
|
|
def get_all_users():
|
|
users = UserService.get_all_users()
|
|
result = []
|
|
for user in users:
|
|
result.append({'email': user.email, 'nickname': user.nickname, 'create_date': user.create_date,
|
|
'is_active': user.is_active})
|
|
return result
|
|
|
|
@staticmethod
|
|
def get_user_details(username):
|
|
# use email to query
|
|
users = UserService.query_user_by_email(username)
|
|
result = []
|
|
for user in users:
|
|
result.append({
|
|
'email': user.email,
|
|
'language': user.language,
|
|
'last_login_time': user.last_login_time,
|
|
'is_authenticated': user.is_authenticated,
|
|
'is_active': user.is_active,
|
|
'is_anonymous': user.is_anonymous,
|
|
'login_channel': user.login_channel,
|
|
'status': user.status,
|
|
'is_superuser': user.is_superuser,
|
|
'create_date': user.create_date,
|
|
'update_date': user.update_date
|
|
})
|
|
return result
|
|
|
|
@staticmethod
|
|
def create_user(username, password, role="user") -> dict:
|
|
# Validate the email address
|
|
if not re.match(r"^[\w\._-]+@([\w_-]+\.)+[\w-]{2,}$", username):
|
|
raise AdminException(f"Invalid email address: {username}!")
|
|
# Check if the email address is already used
|
|
if UserService.query(email=username):
|
|
raise UserAlreadyExistsError(username)
|
|
# Construct user info data
|
|
user_info_dict = {
|
|
"email": username,
|
|
"nickname": "", # ask user to edit it manually in settings.
|
|
"password": decrypt(password),
|
|
"login_channel": "password",
|
|
"is_superuser": role == "admin",
|
|
}
|
|
return create_new_user(user_info_dict)
|
|
|
|
@staticmethod
|
|
def delete_user(username):
|
|
# use email to delete
|
|
user_list = UserService.query_user_by_email(username)
|
|
if not user_list:
|
|
raise UserNotFoundError(username)
|
|
if len(user_list) > 1:
|
|
raise AdminException(f"Exist more than 1 user: {username}!")
|
|
usr = user_list[0]
|
|
return delete_user_data(usr.id)
|
|
|
|
@staticmethod
|
|
def update_user_password(username, new_password) -> str:
|
|
# use email to find user. check exist and unique.
|
|
user_list = UserService.query_user_by_email(username)
|
|
if not user_list:
|
|
raise UserNotFoundError(username)
|
|
elif len(user_list) > 1:
|
|
raise AdminException(f"Exist more than 1 user: {username}!")
|
|
# check new_password different from old.
|
|
usr = user_list[0]
|
|
psw = decrypt(new_password)
|
|
if check_password_hash(usr.password, psw):
|
|
return "Same password, no need to update!"
|
|
# update password
|
|
UserService.update_user_password(usr.id, psw)
|
|
return "Password updated successfully!"
|
|
|
|
@staticmethod
|
|
def update_user_activate_status(username, activate_status: str):
|
|
# use email to find user. check exist and unique.
|
|
user_list = UserService.query_user_by_email(username)
|
|
if not user_list:
|
|
raise UserNotFoundError(username)
|
|
elif len(user_list) > 1:
|
|
raise AdminException(f"Exist more than 1 user: {username}!")
|
|
# check activate status different from new
|
|
usr = user_list[0]
|
|
# format activate_status before handle
|
|
_activate_status = activate_status.lower()
|
|
target_status = {
|
|
'on': ActiveEnum.ACTIVE.value,
|
|
'off': ActiveEnum.INACTIVE.value,
|
|
}.get(_activate_status)
|
|
if not target_status:
|
|
raise AdminException(f"Invalid activate_status: {activate_status}")
|
|
if target_status == usr.is_active:
|
|
return f"User activate status is already {_activate_status}!"
|
|
# update is_active
|
|
UserService.update_user(usr.id, {"is_active": target_status})
|
|
return f"Turn {_activate_status} user activate status successfully!"
|
|
|
|
|
|
class UserServiceMgr:
|
|
|
|
@staticmethod
|
|
def get_user_datasets(username):
|
|
# use email to find user.
|
|
user_list = UserService.query_user_by_email(username)
|
|
if not user_list:
|
|
raise UserNotFoundError(username)
|
|
elif len(user_list) > 1:
|
|
raise AdminException(f"Exist more than 1 user: {username}!")
|
|
# find tenants
|
|
usr = user_list[0]
|
|
tenants = TenantService.get_joined_tenants_by_user_id(usr.id)
|
|
tenant_ids = [m["tenant_id"] for m in tenants]
|
|
# filter permitted kb and owned kb
|
|
return KnowledgebaseService.get_all_kb_by_tenant_ids(tenant_ids, usr.id)
|
|
|
|
@staticmethod
|
|
def get_user_agents(username):
|
|
# use email to find user.
|
|
user_list = UserService.query_user_by_email(username)
|
|
if not user_list:
|
|
raise UserNotFoundError(username)
|
|
elif len(user_list) > 1:
|
|
raise AdminException(f"Exist more than 1 user: {username}!")
|
|
# find tenants
|
|
usr = user_list[0]
|
|
tenants = TenantService.get_joined_tenants_by_user_id(usr.id)
|
|
tenant_ids = [m["tenant_id"] for m in tenants]
|
|
# filter permitted agents and owned agents
|
|
res = UserCanvasService.get_all_agents_by_tenant_ids(tenant_ids, usr.id)
|
|
return [{
|
|
'title': r['title'],
|
|
'permission': r['permission'],
|
|
'canvas_type': r['canvas_type'],
|
|
'canvas_category': r['canvas_category']
|
|
} for r in res]
|
|
|
|
|
|
class ServiceMgr:
|
|
|
|
@staticmethod
|
|
def get_all_services():
|
|
result = []
|
|
configs = SERVICE_CONFIGS.configs
|
|
for service_id, config in enumerate(configs):
|
|
config_dict = config.to_dict()
|
|
service_detail = None
|
|
try:
|
|
service_detail = ServiceMgr.get_service_details(service_id)
|
|
if service_detail['alive']:
|
|
config_dict['status'] = 'Alive'
|
|
else:
|
|
config_dict['status'] = 'Timeout'
|
|
except Exception:
|
|
config_dict['status'] = 'Timeout'
|
|
result.append(config_dict)
|
|
return result
|
|
|
|
@staticmethod
|
|
def get_services_by_type(service_type_str: str):
|
|
raise AdminException("get_services_by_type: not implemented")
|
|
|
|
@staticmethod
|
|
def get_service_details(service_id: int):
|
|
service_id = int(service_id)
|
|
configs = SERVICE_CONFIGS.configs
|
|
service_config_mapping = {
|
|
c.id: {
|
|
'name': c.name,
|
|
'detail_func_name': c.detail_func_name
|
|
} for c in configs
|
|
}
|
|
service_info = service_config_mapping.get(service_id, {})
|
|
if not service_info:
|
|
raise AdminException(f"Invalid service_id: {service_id}")
|
|
|
|
detail_func = getattr(health_utils, service_info.get('detail_func_name'))
|
|
res = detail_func()
|
|
res.update({'service_name': service_info.get('name')})
|
|
return res
|
|
|
|
@staticmethod
|
|
def shutdown_service(service_id: int):
|
|
raise AdminException("shutdown_service: not implemented")
|
|
|
|
@staticmethod
|
|
def restart_service(service_id: int):
|
|
raise AdminException("restart_service: not implemented")
|