mirror of
https://github.com/YFGaia/dify-plus.git
synced 2026-06-04 10:14:00 +08:00
17832f2424
本次提交整合了多个功能改进和问题修复: 主要功能: - 批量工作流处理功能完善,支持 Excel 上传和进度跟踪 - 管理中心反向代理和转发配置优化 - 用户同步添加互斥锁,防止并发问题 - 计费系统和额度显示优化 - AI 绘图功能扩展 前端改进: - 文本生成应用显示修复 - 批量任务进度展示优化 - 按钮样式和 CSS 优化,禁止换行 - 多语言支持完善(新增印尼语等) - 构建镜像逻辑优化 - 批量处理进度管理器实现 后端改进: - Docker Compose 配置升级 - 队列任务和 Worker Pool 优化 - Admin API 初始化和验证逻辑改进 - 数据库迁移和初始化完善 - 静态变量处理优化 - URL 签名助手实现 - Celery 扩展优化 - 代码和导入包问题修复(idea 自动调整代码位置) 技术改进: - 兼容性修复 (flask-restx, jschardet) - 钉钉 Web API 版本更新 - 代码格式化和导入包问题修复 - 日志处理优化 - 工作流循环管理优化 Docker 相关: - Nginx 配置更新 - 容器启动脚本优化 - 镜像构建流程改进 - docker-compose.dify-plus.yaml 大幅更新 管理后台: - 工作流批量处理 API 实现 - 工作池初始化 - 批量工作流服务实现 - 转发扩展配置 - 用户服务扩展
202 lines
8.6 KiB
Python
202 lines
8.6 KiB
Python
import json
|
||
import logging
|
||
import secrets
|
||
import time
|
||
|
||
import requests
|
||
from alibabacloud_dingtalk.oauth2_1_0 import models as dingtalkoauth_2__1__0_models
|
||
from alibabacloud_dingtalk.oauth2_1_0.client import Client as dingtalkoauth2_1_0Client
|
||
from alibabacloud_tea_openapi import models as open_api_models
|
||
from alibabacloud_tea_util.client import Client as UtilClient
|
||
from flask import request
|
||
from pypinyin import lazy_pinyin
|
||
|
||
from configs import dify_config
|
||
from extensions.ext_database import db
|
||
from libs.helper import extract_remote_ip
|
||
from models.account import Account
|
||
from models.system_extend import SystemIntegrationClassify, SystemIntegrationExtend
|
||
from services.account_service import AccountService, RegisterService, TenantService
|
||
from services.account_service_extend import TenantExtendService
|
||
|
||
logger = logging.getLogger(__name__)
|
||
DINGTALK_ACCOUNT_TOKEN = { "time": 0, "token": "" }
|
||
|
||
|
||
class DingTalkService:
|
||
@classmethod
|
||
def create_client(cls) -> dingtalkoauth2_1_0Client:
|
||
"""
|
||
使用 Token 初始化账号Client
|
||
@return: Client
|
||
@throws Exception
|
||
"""
|
||
config = open_api_models.Config()
|
||
config.protocol = "https"
|
||
config.region_id = "central"
|
||
return dingtalkoauth2_1_0Client(config)
|
||
|
||
@classmethod
|
||
def get_user_token(cls, code: str) -> (str, str):
|
||
# get token
|
||
client = cls.create_client()
|
||
integration: SystemIntegrationExtend = (
|
||
db.session.query(SystemIntegrationExtend).filter(
|
||
SystemIntegrationExtend.status == True,
|
||
SystemIntegrationExtend.classify == SystemIntegrationClassify.SYSTEM_INTEGRATION_DINGTALK).first()
|
||
)
|
||
if integration is None:
|
||
return "", "尚未配置钉钉登录"
|
||
get_access_token_request = dingtalkoauth_2__1__0_models.GetUserTokenRequest(
|
||
client_secret=integration.decodeSecret(),
|
||
client_id=integration.app_key,
|
||
grant_type="authorization_code",
|
||
code=code,
|
||
)
|
||
#
|
||
response = client.get_user_token(get_access_token_request)
|
||
if response.status_code == 200:
|
||
return response.body.access_token, ""
|
||
else:
|
||
return "", response.body
|
||
|
||
@classmethod
|
||
def get_access_token(cls) -> (str, str):
|
||
global DINGTALK_ACCOUNT_TOKEN
|
||
if DINGTALK_ACCOUNT_TOKEN["time"] > time.time():
|
||
return DINGTALK_ACCOUNT_TOKEN["token"], ""
|
||
integration: SystemIntegrationExtend = (
|
||
db.session.query(SystemIntegrationExtend).filter(
|
||
SystemIntegrationExtend.status == True,
|
||
SystemIntegrationExtend.classify == SystemIntegrationClassify.SYSTEM_INTEGRATION_DINGTALK).first()
|
||
)
|
||
if integration is None:
|
||
return "", "尚未配置钉钉登录"
|
||
# get token
|
||
client = cls.create_client()
|
||
get_access_token_request = dingtalkoauth_2__1__0_models.GetAccessTokenRequest(
|
||
app_secret=integration.decodeSecret(),
|
||
app_key=integration.app_key,
|
||
)
|
||
try:
|
||
token_request = client.get_access_token(get_access_token_request)
|
||
if token_request.status_code == 200:
|
||
DINGTALK_ACCOUNT_TOKEN["token"] = token_request.body.access_token
|
||
DINGTALK_ACCOUNT_TOKEN["time"] = int(time.time()) + 3600
|
||
return token_request.body.access_token, ""
|
||
else:
|
||
return "", token_request.body
|
||
except Exception as err:
|
||
if not UtilClient.empty(err.code) and not UtilClient.empty(err.message):
|
||
# err 中含有 code 和 message 属性,可帮助开发定位问题
|
||
return "", f"Failed to retrieve token:${err.code}, {err.message}"
|
||
return "", "Failed to retrieve token"
|
||
|
||
@classmethod
|
||
def auto_create_user(cls, userid: str) -> (str, str):
|
||
dingTalkToken, err = cls.get_access_token()
|
||
responses = requests.post(
|
||
f'https://oapi.dingtalk.com/topapi/v2/user/get?access_token={dingTalkToken}',
|
||
json={ "userid": userid },
|
||
)
|
||
# Check the response status code
|
||
if responses.status_code != 200:
|
||
return "", f"Request for user information failed, status code: {responses.status_code}"
|
||
reqs = responses.json()
|
||
if reqs["errcode"] != 0:
|
||
return "", "Request for user information failed: " + userid + " " + json.dumps(reqs)
|
||
# Check if the user exists
|
||
username = reqs["result"]['name']
|
||
email = f"{''.join(lazy_pinyin(username))}@{dify_config.EMAIL_DOMAIN}"
|
||
if "email" in reqs["result"] and len(reqs["result"]["email"]):
|
||
email = reqs["result"]["email"]
|
||
account: Account = (
|
||
db.session.query(Account).filter(Account.email == email).first()
|
||
)
|
||
if account is None:
|
||
# registered user
|
||
try:
|
||
# generate random password
|
||
new_password = secrets.token_urlsafe(16)
|
||
account = RegisterService.register(
|
||
email=email,
|
||
name=username,
|
||
password=new_password,
|
||
language=dify_config.DEFAULT_LANGUAGE,
|
||
)
|
||
except EOFError as a:
|
||
return "", f"register user error: {str(a)}, info {json.loads(reqs)}"
|
||
|
||
tenant_extend_service = TenantExtendService
|
||
super_admin_id = tenant_extend_service.get_super_admin_id().id
|
||
super_admin_tenant_id = tenant_extend_service.get_super_admin_tenant_id().id
|
||
if super_admin_id and super_admin_tenant_id:
|
||
isCreate = TenantExtendService.create_default_tenant_member_if_not_exist(
|
||
super_admin_tenant_id, account.id
|
||
)
|
||
if isCreate:
|
||
TenantService.switch_tenant(account, super_admin_tenant_id)
|
||
# token jwt
|
||
token = AccountService.login(account, ip_address=extract_remote_ip(request))
|
||
return token, ""
|
||
|
||
@classmethod
|
||
def user_third_party(cls, code: str) -> (str, str):
|
||
userToken, err = cls.get_user_token(code)
|
||
|
||
if err != "":
|
||
return "", f"Failed to obtain token: {err}"
|
||
response = requests.get(
|
||
"https://api.dingtalk.com/v1.0/contact/users/me",
|
||
headers={ "x-acs-dingtalk-access-token": userToken },
|
||
)
|
||
# Check the response status code
|
||
if response.status_code != 200:
|
||
return "", f"Request failed, status code: {response.status_code}, msg: {response.text}"
|
||
# Print the response content
|
||
req = response.json()
|
||
if "statusCode" in req.keys() and req["statusCode"] != 200:
|
||
return "", f"Request failed, msg: {req.message}"
|
||
# 提取userid
|
||
dingTalkToken, err = cls.get_access_token()
|
||
unionIdResponse = requests.post(
|
||
f"https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token={dingTalkToken}",
|
||
json={ "unionid": req["unionId"] }
|
||
)
|
||
# Check the response status code
|
||
if unionIdResponse.status_code != 200:
|
||
return "", f"unionIdResponse failed, status code: {unionIdResponse.status_code}, msg: {unionIdResponse.text}"
|
||
# Print the response content
|
||
unionIdReq = unionIdResponse.json()
|
||
if unionIdReq["errcode"] != 0:
|
||
return "", f"Request failed, msg: {unionIdReq['errmsg']}"
|
||
|
||
token, err = cls.auto_create_user(unionIdReq["result"]["userid"])
|
||
if len(err) > 0:
|
||
return "", "Request failed: " + err
|
||
|
||
return f"{dify_config.CONSOLE_WEB_URL}/explore/apps-center-extend?console_token={token.access_token}&&refresh_token={token.refresh_token}", ""
|
||
|
||
@classmethod
|
||
def get_user_info(cls, code: str) -> (str, str):
|
||
host = "https://oapi.dingtalk.com/topapi/v2/user"
|
||
token, err = cls.get_access_token()
|
||
if err != "":
|
||
return "", f"Failed to obtain token: {err}"
|
||
response = requests.post(
|
||
f"{host}/getuserinfo?access_token={token}",
|
||
json={ "code": code },
|
||
)
|
||
# Check the response status code
|
||
if response.status_code != 200:
|
||
return "", f"Request failed, status code: {response.status_code}"
|
||
# Print the response content
|
||
req = response.json()
|
||
if req["errcode"] != 0:
|
||
return "", "Request failed: " + req["errmsg"]
|
||
token, err = cls.auto_create_user(req["result"]["userid"])
|
||
if len(err) != 0:
|
||
return "", "Request failed: " + err
|
||
|
||
return f"{dify_config.CONSOLE_WEB_URL}/explore/apps-center-extend?console_token={token.access_token}&&refresh_token={token.refresh_token}", ""
|