| from pydantic import computed_field | |||||
| from pydantic_settings import BaseSettings, SettingsConfigDict | from pydantic_settings import BaseSettings, SettingsConfigDict | ||||
| from configs.deploy import DeploymentConfig | from configs.deploy import DeploymentConfig | ||||
| # ignore extra attributes | # ignore extra attributes | ||||
| extra='ignore', | extra='ignore', | ||||
| ) | ) | ||||
| CODE_MAX_NUMBER: int = 9223372036854775807 | |||||
| CODE_MIN_NUMBER: int = -9223372036854775808 | |||||
| CODE_MAX_STRING_LENGTH: int = 80000 | |||||
| CODE_MAX_STRING_ARRAY_LENGTH: int = 30 | |||||
| CODE_MAX_OBJECT_ARRAY_LENGTH: int = 30 | |||||
| CODE_MAX_NUMBER_ARRAY_LENGTH: int = 1000 | |||||
| HTTP_REQUEST_MAX_CONNECT_TIMEOUT: int = 300 | |||||
| HTTP_REQUEST_MAX_READ_TIMEOUT: int = 600 | |||||
| HTTP_REQUEST_MAX_WRITE_TIMEOUT: int = 600 | |||||
| HTTP_REQUEST_NODE_MAX_BINARY_SIZE: int = 1024 * 1024 * 10 | |||||
| @computed_field | |||||
| def HTTP_REQUEST_NODE_READABLE_MAX_BINARY_SIZE(self) -> str: | |||||
| return f'{self.HTTP_REQUEST_NODE_MAX_BINARY_SIZE / 1024 / 1024:.2f}MB' | |||||
| HTTP_REQUEST_NODE_MAX_TEXT_SIZE: int = 1024 * 1024 | |||||
| @computed_field | |||||
| def HTTP_REQUEST_NODE_READABLE_MAX_TEXT_SIZE(self) -> str: | |||||
| return f'{self.HTTP_REQUEST_NODE_MAX_TEXT_SIZE / 1024 / 1024:.2f}MB' | |||||
| SSRF_PROXY_HTTP_URL: str | None = None | |||||
| SSRF_PROXY_HTTPS_URL: str | None = None |
| from flask_restful import Resource | from flask_restful import Resource | ||||
| from werkzeug.exceptions import Forbidden | from werkzeug.exceptions import Forbidden | ||||
| from configs import dify_config | |||||
| from controllers.console import api | from controllers.console import api | ||||
| from libs.login import login_required | from libs.login import login_required | ||||
| from libs.oauth_data_source import NotionOAuth | from libs.oauth_data_source import NotionOAuth | ||||
| def get_oauth_providers(): | def get_oauth_providers(): | ||||
| with current_app.app_context(): | with current_app.app_context(): | ||||
| notion_oauth = NotionOAuth(client_id=current_app.config.get('NOTION_CLIENT_ID'), | |||||
| client_secret=current_app.config.get( | |||||
| 'NOTION_CLIENT_SECRET'), | |||||
| redirect_uri=current_app.config.get( | |||||
| 'CONSOLE_API_URL') + '/console/api/oauth/data-source/callback/notion') | |||||
| if not dify_config.NOTION_CLIENT_ID or not dify_config.NOTION_CLIENT_SECRET: | |||||
| return {} | |||||
| notion_oauth = NotionOAuth(client_id=dify_config.NOTION_CLIENT_ID, | |||||
| client_secret=dify_config.NOTION_CLIENT_SECRET, | |||||
| redirect_uri=dify_config.CONSOLE_API_URL + '/console/api/oauth/data-source/callback/notion') | |||||
| OAUTH_PROVIDERS = { | OAUTH_PROVIDERS = { | ||||
| 'notion': notion_oauth | 'notion': notion_oauth | ||||
| print(vars(oauth_provider)) | print(vars(oauth_provider)) | ||||
| if not oauth_provider: | if not oauth_provider: | ||||
| return {'error': 'Invalid provider'}, 400 | return {'error': 'Invalid provider'}, 400 | ||||
| if current_app.config.get('NOTION_INTEGRATION_TYPE') == 'internal': | |||||
| internal_secret = current_app.config.get('NOTION_INTERNAL_SECRET') | |||||
| if dify_config.NOTION_INTEGRATION_TYPE == 'internal': | |||||
| internal_secret = dify_config.NOTION_INTERNAL_SECRET | |||||
| if not internal_secret: | |||||
| return {'error': 'Internal secret is not set'}, | |||||
| oauth_provider.save_internal_access_token(internal_secret) | oauth_provider.save_internal_access_token(internal_secret) | ||||
| return { 'data': '' } | return { 'data': '' } | ||||
| else: | else: | ||||
| if 'code' in request.args: | if 'code' in request.args: | ||||
| code = request.args.get('code') | code = request.args.get('code') | ||||
| return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&code={code}') | |||||
| return redirect(f'{dify_config.CONSOLE_WEB_URL}?type=notion&code={code}') | |||||
| elif 'error' in request.args: | elif 'error' in request.args: | ||||
| error = request.args.get('error') | error = request.args.get('error') | ||||
| return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&error={error}') | |||||
| return redirect(f'{dify_config.CONSOLE_WEB_URL}?type=notion&error={error}') | |||||
| else: | else: | ||||
| return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&error=Access denied') | |||||
| return redirect(f'{dify_config.CONSOLE_WEB_URL}?type=notion&error=Access denied') | |||||
| class OAuthDataSourceBinding(Resource): | class OAuthDataSourceBinding(Resource): |
| from typing import cast | from typing import cast | ||||
| import flask_login | import flask_login | ||||
| from flask import current_app, request | |||||
| from flask import request | |||||
| from flask_restful import Resource, reqparse | from flask_restful import Resource, reqparse | ||||
| import services | import services | ||||
| class ResetPasswordApi(Resource): | class ResetPasswordApi(Resource): | ||||
| @setup_required | @setup_required | ||||
| def get(self): | def get(self): | ||||
| parser = reqparse.RequestParser() | |||||
| parser.add_argument('email', type=email, required=True, location='json') | |||||
| args = parser.parse_args() | |||||
| # parser = reqparse.RequestParser() | |||||
| # parser.add_argument('email', type=email, required=True, location='json') | |||||
| # args = parser.parse_args() | |||||
| # import mailchimp_transactional as MailchimpTransactional | # import mailchimp_transactional as MailchimpTransactional | ||||
| # from mailchimp_transactional.api_client import ApiClientError | # from mailchimp_transactional.api_client import ApiClientError | ||||
| account = {'email': args['email']} | |||||
| # account = {'email': args['email']} | |||||
| # account = AccountService.get_by_email(args['email']) | # account = AccountService.get_by_email(args['email']) | ||||
| # if account is None: | # if account is None: | ||||
| # raise ValueError('Email not found') | # raise ValueError('Email not found') | ||||
| # AccountService.update_password(account, new_password) | # AccountService.update_password(account, new_password) | ||||
| # todo: Send email | # todo: Send email | ||||
| MAILCHIMP_API_KEY = current_app.config['MAILCHIMP_TRANSACTIONAL_API_KEY'] | |||||
| # MAILCHIMP_API_KEY = current_app.config['MAILCHIMP_TRANSACTIONAL_API_KEY'] | |||||
| # mailchimp = MailchimpTransactional(MAILCHIMP_API_KEY) | # mailchimp = MailchimpTransactional(MAILCHIMP_API_KEY) | ||||
| message = { | |||||
| 'from_email': 'noreply@example.com', | |||||
| 'to': [{'email': account.email}], | |||||
| 'subject': 'Reset your Dify password', | |||||
| 'html': """ | |||||
| <p>Dear User,</p> | |||||
| <p>The Dify team has generated a new password for you, details as follows:</p> | |||||
| <p><strong>{new_password}</strong></p> | |||||
| <p>Please change your password to log in as soon as possible.</p> | |||||
| <p>Regards,</p> | |||||
| <p>The Dify Team</p> | |||||
| """ | |||||
| } | |||||
| # message = { | |||||
| # 'from_email': 'noreply@example.com', | |||||
| # 'to': [{'email': account['email']}], | |||||
| # 'subject': 'Reset your Dify password', | |||||
| # 'html': """ | |||||
| # <p>Dear User,</p> | |||||
| # <p>The Dify team has generated a new password for you, details as follows:</p> | |||||
| # <p><strong>{new_password}</strong></p> | |||||
| # <p>Please change your password to log in as soon as possible.</p> | |||||
| # <p>Regards,</p> | |||||
| # <p>The Dify Team</p> | |||||
| # """ | |||||
| # } | |||||
| # response = mailchimp.messages.send({ | # response = mailchimp.messages.send({ | ||||
| # 'message': message, | # 'message': message, |
| from flask import current_app, redirect, request | from flask import current_app, redirect, request | ||||
| from flask_restful import Resource | from flask_restful import Resource | ||||
| from configs import dify_config | |||||
| from constants.languages import languages | from constants.languages import languages | ||||
| from extensions.ext_database import db | from extensions.ext_database import db | ||||
| from libs.helper import get_remote_ip | from libs.helper import get_remote_ip | ||||
| def get_oauth_providers(): | def get_oauth_providers(): | ||||
| with current_app.app_context(): | with current_app.app_context(): | ||||
| github_oauth = GitHubOAuth(client_id=current_app.config.get('GITHUB_CLIENT_ID'), | |||||
| client_secret=current_app.config.get( | |||||
| 'GITHUB_CLIENT_SECRET'), | |||||
| redirect_uri=current_app.config.get( | |||||
| 'CONSOLE_API_URL') + '/console/api/oauth/authorize/github') | |||||
| google_oauth = GoogleOAuth(client_id=current_app.config.get('GOOGLE_CLIENT_ID'), | |||||
| client_secret=current_app.config.get( | |||||
| 'GOOGLE_CLIENT_SECRET'), | |||||
| redirect_uri=current_app.config.get( | |||||
| 'CONSOLE_API_URL') + '/console/api/oauth/authorize/google') | |||||
| OAUTH_PROVIDERS = { | |||||
| 'github': github_oauth, | |||||
| 'google': google_oauth | |||||
| } | |||||
| if not dify_config.GITHUB_CLIENT_ID or not dify_config.GITHUB_CLIENT_SECRET: | |||||
| github_oauth = None | |||||
| else: | |||||
| github_oauth = GitHubOAuth( | |||||
| client_id=dify_config.GITHUB_CLIENT_ID, | |||||
| client_secret=dify_config.GITHUB_CLIENT_SECRET, | |||||
| redirect_uri=dify_config.CONSOLE_API_URL + '/console/api/oauth/authorize/github', | |||||
| ) | |||||
| if not dify_config.GOOGLE_CLIENT_ID or not dify_config.GOOGLE_CLIENT_SECRET: | |||||
| google_oauth = None | |||||
| else: | |||||
| google_oauth = GoogleOAuth( | |||||
| client_id=dify_config.GOOGLE_CLIENT_ID, | |||||
| client_secret=dify_config.GOOGLE_CLIENT_SECRET, | |||||
| redirect_uri=dify_config.CONSOLE_API_URL + '/console/api/oauth/authorize/google', | |||||
| ) | |||||
| OAUTH_PROVIDERS = {'github': github_oauth, 'google': google_oauth} | |||||
| return OAUTH_PROVIDERS | return OAUTH_PROVIDERS | ||||
| token = oauth_provider.get_access_token(code) | token = oauth_provider.get_access_token(code) | ||||
| user_info = oauth_provider.get_user_info(token) | user_info = oauth_provider.get_user_info(token) | ||||
| except requests.exceptions.HTTPError as e: | except requests.exceptions.HTTPError as e: | ||||
| logging.exception( | |||||
| f"An error occurred during the OAuth process with {provider}: {e.response.text}") | |||||
| logging.exception(f'An error occurred during the OAuth process with {provider}: {e.response.text}') | |||||
| return {'error': 'OAuth process failed'}, 400 | return {'error': 'OAuth process failed'}, 400 | ||||
| account = _generate_account(provider, user_info) | account = _generate_account(provider, user_info) | ||||
| token = AccountService.login(account, ip_address=get_remote_ip(request)) | token = AccountService.login(account, ip_address=get_remote_ip(request)) | ||||
| return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?console_token={token}') | |||||
| return redirect(f'{dify_config.CONSOLE_WEB_URL}?console_token={token}') | |||||
| def _get_account_by_openid_or_email(provider: str, user_info: OAuthUserInfo) -> Optional[Account]: | def _get_account_by_openid_or_email(provider: str, user_info: OAuthUserInfo) -> Optional[Account]: | ||||
| # Create account | # Create account | ||||
| account_name = user_info.name if user_info.name else 'Dify' | account_name = user_info.name if user_info.name else 'Dify' | ||||
| account = RegisterService.register( | account = RegisterService.register( | ||||
| email=user_info.email, | |||||
| name=account_name, | |||||
| password=None, | |||||
| open_id=user_info.id, | |||||
| provider=provider | |||||
| email=user_info.email, name=account_name, password=None, open_id=user_info.id, provider=provider | |||||
| ) | ) | ||||
| # Set interface language | # Set interface language |
| import os | |||||
| import requests | import requests | ||||
| from configs import dify_config | |||||
| from models.api_based_extension import APIBasedExtensionPoint | from models.api_based_extension import APIBasedExtensionPoint | ||||
| try: | try: | ||||
| # proxy support for security | # proxy support for security | ||||
| proxies = None | proxies = None | ||||
| if os.environ.get("SSRF_PROXY_HTTP_URL") and os.environ.get("SSRF_PROXY_HTTPS_URL"): | |||||
| if dify_config.SSRF_PROXY_HTTP_URL and dify_config.SSRF_PROXY_HTTPS_URL: | |||||
| proxies = { | proxies = { | ||||
| 'http': os.environ.get("SSRF_PROXY_HTTP_URL"), | |||||
| 'https': os.environ.get("SSRF_PROXY_HTTPS_URL"), | |||||
| 'http': dify_config.SSRF_PROXY_HTTP_URL, | |||||
| 'https': dify_config.SSRF_PROXY_HTTPS_URL, | |||||
| } | } | ||||
| response = requests.request( | response = requests.request( |
| import logging | import logging | ||||
| import os | |||||
| import time | import time | ||||
| from enum import Enum | from enum import Enum | ||||
| from threading import Lock | from threading import Lock | ||||
| from pydantic import BaseModel | from pydantic import BaseModel | ||||
| from yarl import URL | from yarl import URL | ||||
| from configs import dify_config | |||||
| from core.helper.code_executor.entities import CodeDependency | from core.helper.code_executor.entities import CodeDependency | ||||
| from core.helper.code_executor.javascript.javascript_transformer import NodeJsTemplateTransformer | from core.helper.code_executor.javascript.javascript_transformer import NodeJsTemplateTransformer | ||||
| from core.helper.code_executor.jinja2.jinja2_transformer import Jinja2TemplateTransformer | from core.helper.code_executor.jinja2.jinja2_transformer import Jinja2TemplateTransformer | ||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| # Code Executor | # Code Executor | ||||
| CODE_EXECUTION_ENDPOINT = os.environ.get('CODE_EXECUTION_ENDPOINT', 'http://sandbox:8194') | |||||
| CODE_EXECUTION_API_KEY = os.environ.get('CODE_EXECUTION_API_KEY', 'dify-sandbox') | |||||
| CODE_EXECUTION_ENDPOINT = dify_config.CODE_EXECUTION_ENDPOINT | |||||
| CODE_EXECUTION_API_KEY = dify_config.CODE_EXECUTION_API_KEY | |||||
| CODE_EXECUTION_TIMEOUT= (10, 60) | CODE_EXECUTION_TIMEOUT= (10, 60) | ||||
| http_client: httpx.Client | None = None, | http_client: httpx.Client | None = None, | ||||
| custom_headers: Mapping[str, str] | None = None | custom_headers: Mapping[str, str] | None = None | ||||
| ) -> None: | ) -> None: | ||||
| # if api_key is None: | |||||
| # api_key = os.environ.get("ZHIPUAI_API_KEY") | |||||
| if api_key is None: | if api_key is None: | ||||
| raise ZhipuAIError("未提供api_key,请通过参数或环境变量提供") | |||||
| raise ZhipuAIError("No api_key provided, please provide it through parameters or environment variables") | |||||
| self.api_key = api_key | self.api_key = api_key | ||||
| if base_url is None: | if base_url is None: |
| import os | |||||
| from typing import Optional, Union, cast | from typing import Optional, Union, cast | ||||
| from configs import dify_config | |||||
| from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor, CodeLanguage | from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor, CodeLanguage | ||||
| from core.helper.code_executor.code_node_provider import CodeNodeProvider | from core.helper.code_executor.code_node_provider import CodeNodeProvider | ||||
| from core.helper.code_executor.javascript.javascript_code_provider import JavascriptCodeProvider | from core.helper.code_executor.javascript.javascript_code_provider import JavascriptCodeProvider | ||||
| from core.workflow.nodes.code.entities import CodeNodeData | from core.workflow.nodes.code.entities import CodeNodeData | ||||
| from models.workflow import WorkflowNodeExecutionStatus | from models.workflow import WorkflowNodeExecutionStatus | ||||
| MAX_NUMBER = int(os.environ.get('CODE_MAX_NUMBER', '9223372036854775807')) | |||||
| MIN_NUMBER = int(os.environ.get('CODE_MIN_NUMBER', '-9223372036854775808')) | |||||
| MAX_NUMBER = dify_config.CODE_MAX_NUMBER | |||||
| MIN_NUMBER = dify_config.CODE_MIN_NUMBER | |||||
| MAX_PRECISION = 20 | MAX_PRECISION = 20 | ||||
| MAX_DEPTH = 5 | MAX_DEPTH = 5 | ||||
| MAX_STRING_LENGTH = int(os.environ.get('CODE_MAX_STRING_LENGTH', '80000')) | |||||
| MAX_STRING_ARRAY_LENGTH = int(os.environ.get('CODE_MAX_STRING_ARRAY_LENGTH', '30')) | |||||
| MAX_OBJECT_ARRAY_LENGTH = int(os.environ.get('CODE_MAX_OBJECT_ARRAY_LENGTH', '30')) | |||||
| MAX_NUMBER_ARRAY_LENGTH = int(os.environ.get('CODE_MAX_NUMBER_ARRAY_LENGTH', '1000')) | |||||
| MAX_STRING_LENGTH = dify_config.CODE_MAX_STRING_LENGTH | |||||
| MAX_STRING_ARRAY_LENGTH = dify_config.CODE_MAX_STRING_ARRAY_LENGTH | |||||
| MAX_OBJECT_ARRAY_LENGTH = dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH | |||||
| MAX_NUMBER_ARRAY_LENGTH = dify_config.CODE_MAX_NUMBER_ARRAY_LENGTH | |||||
| class CodeNode(BaseNode): | class CodeNode(BaseNode): |
| import os | |||||
| from typing import Literal, Optional, Union | from typing import Literal, Optional, Union | ||||
| from pydantic import BaseModel, ValidationInfo, field_validator | from pydantic import BaseModel, ValidationInfo, field_validator | ||||
| from configs import dify_config | |||||
| from core.workflow.entities.base_node_data_entities import BaseNodeData | from core.workflow.entities.base_node_data_entities import BaseNodeData | ||||
| MAX_CONNECT_TIMEOUT = int(os.environ.get('HTTP_REQUEST_MAX_CONNECT_TIMEOUT', '300')) | |||||
| MAX_READ_TIMEOUT = int(os.environ.get('HTTP_REQUEST_MAX_READ_TIMEOUT', '600')) | |||||
| MAX_WRITE_TIMEOUT = int(os.environ.get('HTTP_REQUEST_MAX_WRITE_TIMEOUT', '600')) | |||||
| MAX_CONNECT_TIMEOUT = dify_config.HTTP_REQUEST_MAX_CONNECT_TIMEOUT | |||||
| MAX_READ_TIMEOUT = dify_config.HTTP_REQUEST_MAX_READ_TIMEOUT | |||||
| MAX_WRITE_TIMEOUT = dify_config.HTTP_REQUEST_MAX_WRITE_TIMEOUT | |||||
| class HttpRequestNodeAuthorizationConfig(BaseModel): | class HttpRequestNodeAuthorizationConfig(BaseModel): |
| import json | import json | ||||
| import os | |||||
| from copy import deepcopy | from copy import deepcopy | ||||
| from random import randint | from random import randint | ||||
| from typing import Any, Optional, Union | from typing import Any, Optional, Union | ||||
| import httpx | import httpx | ||||
| import core.helper.ssrf_proxy as ssrf_proxy | import core.helper.ssrf_proxy as ssrf_proxy | ||||
| from configs import dify_config | |||||
| from core.workflow.entities.variable_entities import VariableSelector | from core.workflow.entities.variable_entities import VariableSelector | ||||
| from core.workflow.entities.variable_pool import ValueType, VariablePool | from core.workflow.entities.variable_pool import ValueType, VariablePool | ||||
| from core.workflow.nodes.http_request.entities import ( | from core.workflow.nodes.http_request.entities import ( | ||||
| ) | ) | ||||
| from core.workflow.utils.variable_template_parser import VariableTemplateParser | from core.workflow.utils.variable_template_parser import VariableTemplateParser | ||||
| MAX_BINARY_SIZE = int(os.environ.get('HTTP_REQUEST_NODE_MAX_BINARY_SIZE', 1024 * 1024 * 10)) # 10MB | |||||
| READABLE_MAX_BINARY_SIZE = f'{MAX_BINARY_SIZE / 1024 / 1024:.2f}MB' | |||||
| MAX_TEXT_SIZE = int(os.environ.get('HTTP_REQUEST_NODE_MAX_TEXT_SIZE', 1024 * 1024)) # 1MB | |||||
| READABLE_MAX_TEXT_SIZE = f'{MAX_TEXT_SIZE / 1024 / 1024:.2f}MB' | |||||
| MAX_BINARY_SIZE = dify_config.HTTP_REQUEST_NODE_MAX_BINARY_SIZE | |||||
| READABLE_MAX_BINARY_SIZE = dify_config.HTTP_REQUEST_NODE_READABLE_MAX_BINARY_SIZE | |||||
| MAX_TEXT_SIZE = dify_config.HTTP_REQUEST_NODE_MAX_TEXT_SIZE | |||||
| READABLE_MAX_TEXT_SIZE = dify_config.HTTP_REQUEST_NODE_READABLE_MAX_TEXT_SIZE | |||||
| class HttpExecutorResponse: | class HttpExecutorResponse: |