impl phone login logic

pull/21891/head
ytqh 1 year ago
parent a6da58b3a1
commit 2c9cbf11fe

@ -44,14 +44,15 @@ def initialize_extensions(app: DifyApp):
ext_login,
ext_mail,
ext_migrate,
ext_phone_sms,
ext_proxy_fix,
ext_redis,
ext_sentry,
ext_set_secretkey,
ext_storage,
ext_swagger,
ext_timezone,
ext_warnings,
ext_swagger
)
extensions = [
@ -70,12 +71,13 @@ def initialize_extensions(app: DifyApp):
ext_celery,
ext_login,
ext_mail,
ext_phone_sms,
ext_hosting_provider,
ext_sentry,
ext_proxy_fix,
ext_blueprints,
ext_commands,
ext_swagger
ext_swagger,
]
for ext in extensions:
short_name = ext.__name__.split(".")[-1]

@ -1,3 +1,5 @@
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
@ -17,9 +19,9 @@ class DeploymentConfig(BaseSettings):
default=False,
)
DEBUG_CODE_FOR_LOGIN: str = Field(
DEBUG_CODE_FOR_LOGIN: Optional[str] = Field(
description="Default code for login",
default="",
default=None,
)
EDITION: str = Field(

@ -586,6 +586,32 @@ class MailConfig(BaseSettings):
)
class PhoneSmsConfig(BaseSettings):
"""
Configuration for phone SMS services
"""
ALIYUN_ACCESS_KEY_ID: Optional[str] = Field(
description="Aliyun access key id",
default=None,
)
ALIYUN_ACCESS_KEY_SECRET: Optional[str] = Field(
description="Aliyun access key secret",
default=None,
)
ALIYUN_SIGN_NAME: Optional[str] = Field(
description="Aliyun sign name",
default=None,
)
ALIYUN_TEMPLATE_CODE: Optional[str] = Field(
description="Aliyun template code",
default=None,
)
class RagEtlConfig(BaseSettings):
"""
Configuration for RAG ETL processes
@ -809,6 +835,7 @@ class FeatureConfig(
ModelLoadBalanceConfig,
ModerationConfig,
MultiModalTransferConfig,
PhoneSmsConfig,
PositionConfig,
RagEtlConfig,
SecurityConfig,

@ -0,0 +1,75 @@
import json
import logging
from typing import Optional
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
from configs import dify_config
from dify_app import DifyApp
from flask import Flask
class PhoneSms:
def __init__(self):
self._client: Optional[Dysmsapi20170525Client] = None
def init_app(self, app: Flask):
if not dify_config.ALIYUN_ACCESS_KEY_ID or not dify_config.ALIYUN_ACCESS_KEY_SECRET:
logging.warning("ALIYUN_ACCESS_KEY_ID and ALIYUN_ACCESS_KEY_SECRET must be set")
return
if not dify_config.ALIYUN_SIGN_NAME or not dify_config.ALIYUN_TEMPLATE_CODE:
logging.warning("ALIYUN_SIGN_NAME and ALIYUN_TEMPLATE_CODE must be set")
return
self._client = self._create_client(dify_config.ALIYUN_ACCESS_KEY_ID, dify_config.ALIYUN_ACCESS_KEY_SECRET)
self._sign_name = dify_config.ALIYUN_SIGN_NAME
self._template_code = dify_config.ALIYUN_TEMPLATE_CODE
def is_inited(self) -> bool:
return self._client is not None
def _create_client(self, id: str, secret: str) -> Dysmsapi20170525Client:
"""
使用AK&SK初始化账号Client
@return: Client
@throws Exception
"""
# 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
# 建议使用更安全的 STS 方式更多鉴权访问方式请参见https://help.aliyun.com/document_detail/378659.html。
config = open_api_models.Config(
# 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。,
access_key_id=id,
# 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。,
access_key_secret=secret,
)
# Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.endpoint = f'dysmsapi.aliyuncs.com'
return Dysmsapi20170525Client(config)
def send_sms(self, phone_numbers: str, code: str) -> None:
if not self._client:
raise ValueError("PhoneSms client is not initialized")
send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
phone_numbers=phone_numbers,
sign_name=self._sign_name,
template_code=self._template_code,
template_param=json.dumps({"code": code}),
)
response = self._client.send_sms_with_options(send_sms_request, util_models.RuntimeOptions())
if response.body.code != 'OK':
raise Exception(response.body.message)
def init_app(app: DifyApp):
phone_sms.init_app(app)
app.extensions["phone_sms"] = phone_sms
phone_sms = PhoneSms()

36
api/poetry.lock generated

@ -197,7 +197,7 @@ version = "0.3.6"
description = "The alibabacloud credentials module of alibabaCloud Python SDK."
optional = false
python-versions = ">=3.6"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_credentials-0.3.6.tar.gz", hash = "sha256:caa82cf258648dcbe1ca14aeba50ba21845567d6ac3cd48d318e0a445fff7f96"},
]
@ -205,13 +205,31 @@ files = [
[package.dependencies]
alibabacloud-tea = ">=0.3.9"
[[package]]
name = "alibabacloud-dysmsapi20170525"
version = "3.1.1"
description = "Alibaba Cloud Dysmsapi (20170525) SDK Library for Python"
optional = false
python-versions = ">=3.6"
groups = ["main"]
files = [
{file = "alibabacloud_dysmsapi20170525-3.1.1-py3-none-any.whl", hash = "sha256:a6f02476f4475e66fab680ce96281d73dc5ae506e5197190c54232eda6afd191"},
{file = "alibabacloud_dysmsapi20170525-3.1.1.tar.gz", hash = "sha256:5b0440dab0195afa1e42f97ce3ba9ce445f2afca2d0de873688248abc9592854"},
]
[package.dependencies]
alibabacloud-endpoint-util = ">=0.0.3,<1.0.0"
alibabacloud-openapi-util = ">=0.2.2,<1.0.0"
alibabacloud-tea-openapi = ">=0.3.12,<1.0.0"
alibabacloud-tea-util = ">=0.3.13,<1.0.0"
[[package]]
name = "alibabacloud-endpoint-util"
version = "0.0.3"
description = "The endpoint-util module of alibabaCloud Python SDK."
optional = false
python-versions = "*"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_endpoint_util-0.0.3.tar.gz", hash = "sha256:8c0efb76fdcc3af4ca716ef24bbce770201a3f83f98c0afcf81655f684b9c7d2"},
]
@ -225,7 +243,7 @@ version = "0.0.2"
description = "Alibaba Cloud Gateway SPI SDK Library for Python"
optional = false
python-versions = ">=3.6"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_gateway_spi-0.0.2.tar.gz", hash = "sha256:f932c8ba67291531dfbee6ca521dcf3523eb4ff93512bf0aaf135f2d4fc4704d"},
]
@ -261,7 +279,7 @@ version = "0.2.2"
description = "Aliyun Tea OpenApi Library for Python"
optional = false
python-versions = "*"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_openapi_util-0.2.2.tar.gz", hash = "sha256:ebbc3906f554cb4bf8f513e43e8a33e8b6a3d4a0ef13617a0e14c3dda8ef52a8"},
]
@ -326,7 +344,7 @@ version = "0.4.0"
description = "The tea module of alibabaCloud Python SDK."
optional = false
python-versions = ">=3.7"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud-tea-0.4.0.tar.gz", hash = "sha256:bdf72d747723bab190331b3c8593109fe2807504469bc0147f78c8c4945ed396"},
{file = "alibabacloud_tea-0.4.0-py3-none-any.whl", hash = "sha256:59fae5765e6654f884e130233df6fb61ca0fbe01a29ed0755a1cf099a3d4d863"},
@ -356,7 +374,7 @@ version = "0.3.12"
description = "Alibaba Cloud openapi SDK Library for Python"
optional = false
python-versions = ">=3.6"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_tea_openapi-0.3.12.tar.gz", hash = "sha256:2e14809f357438e62c1ef4976a7655110dd54a75bbfa7d905fa3798355cfd974"},
]
@ -374,7 +392,7 @@ version = "0.3.13"
description = "The tea-util module of alibabaCloud Python SDK."
optional = false
python-versions = ">=3.6"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_tea_util-0.3.13.tar.gz", hash = "sha256:8cbdfd2a03fbbf622f901439fa08643898290dd40e1d928347f6346e43f63c90"},
]
@ -388,7 +406,7 @@ version = "0.0.2"
description = "The tea-xml module of alibabaCloud Python SDK."
optional = false
python-versions = "*"
groups = ["vdb"]
groups = ["main", "vdb"]
files = [
{file = "alibabacloud_tea_xml-0.0.2.tar.gz", hash = "sha256:f0135e8148fd7d9c1f029db161863f37f144f837c280cba16c2edeb2f9c549d8"},
]
@ -11828,4 +11846,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.11,<3.13"
content-hash = "b26521378e2b5423ccebc7b1af0890275ddbc3dbcea748de74b83efd1c06e877"
content-hash = "97dd07fa06f04b556689dace7e0821f44aa7b6ad2fa4fba3da24ad0941bba63c"

@ -112,6 +112,7 @@ zhipuai = "~2.1.5"
# required by main implementations
############################################################
flasgger = "^0.9.7.1"
alibabacloud-dysmsapi20170525 = "^3.1.1"
[tool.poetry.group.indirect.dependencies]
kaleido = "0.2.1"
rank-bm25 = "~0.2.2"

@ -55,6 +55,7 @@ from tasks.mail_account_deletion_task import send_account_deletion_verification_
from tasks.mail_email_code_login import send_email_code_login_mail_task
from tasks.mail_invite_member_task import send_invite_member_mail_task
from tasks.mail_reset_password_task import send_reset_password_mail_task
from tasks.phone_sms_code_login import send_phone_sms_code_login_task
from werkzeug.exceptions import Unauthorized
@ -676,7 +677,7 @@ class AccountService:
if cls.phone_code_login_rate_limiter.is_rate_limited(phone) and not DeploymentConfig().DEBUG:
raise Exception("Phone verification code rate limit exceeded")
if dify_config.DEBUG_CODE_FOR_LOGIN and dify_config.DEBUG_CODE_FOR_LOGIN != "":
if dify_config.DEBUG_CODE_FOR_LOGIN:
code = dify_config.DEBUG_CODE_FOR_LOGIN
else:
code = "".join([str(random.randint(0, 9)) for _ in range(6)])
@ -689,11 +690,10 @@ class AccountService:
additional_data={"code": code, "phone": phone},
)
# Here you would typically send an SMS with the code
# For now we'll just assume the SMS sending service exists
# send_phone_code_login_sms_task.delay(to=phone, code=code)
# Log SMS sending in production environment
if dify_config.DEBUG_CODE_FOR_LOGIN:
logging.info(f"Mock Code, Skip sending phone verification code to {phone}")
else:
send_phone_sms_code_login_task.delay(phone=phone, code=code)
logging.info(f"Phone verification code sent to {phone}")
cls.phone_code_login_rate_limiter.increment_rate_limit(phone)

@ -0,0 +1,36 @@
import logging
import time
import click
from celery import shared_task # type: ignore
from extensions.ext_phone_sms import phone_sms
from flask import render_template
@shared_task(queue="phone_sms")
def send_phone_sms_code_login_task(phone: str, code: str):
"""
Async Send email code login mail
:param language: Language in which the email should be sent (e.g., 'en', 'zh')
:param to: Recipient email address
:param code: Email code to be included in the email
"""
if not phone_sms.is_inited():
return
logging.info(click.style(f"Start phone sms code login mail to {phone}", fg="green"))
start_at = time.perf_counter()
# send email code login mail using different languages
try:
phone_sms.send_sms(phone, code)
end_at = time.perf_counter()
logging.info(
click.style(
f"Send phone sms code login mail to {phone} succeeded: latency: {end_at - start_at}",
fg="green",
)
)
except Exception as e:
logging.exception(f"Send phone sms code login mail to {phone} failed: {e}")

@ -950,3 +950,9 @@ NEED_USER_PROFILE_GENERATION_APP_IDS=
USER_PROFILE_GENERATE_TASK_INTERVAL=5
USER_MEMORY_GENERATION_APP_ID=
USER_HEALTH_SUMMARY_GENERATION_APP_ID=
# Phone SMS Code Login
ALIYUN_ACCESS_KEY_ID=
ALIYUN_ACCESS_KEY_SECRET=
ALIYUN_SIGN_NAME=
ALIYUN_TEMPLATE_CODE=

@ -401,6 +401,10 @@ x-shared-env: &shared-api-worker-env
USER_PROFILE_GENERATE_TASK_INTERVAL: ${USER_PROFILE_GENERATE_TASK_INTERVAL:-5}
USER_MEMORY_GENERATION_APP_ID: ${USER_MEMORY_GENERATION_APP_ID:-}
USER_HEALTH_SUMMARY_GENERATION_APP_ID: ${USER_HEALTH_SUMMARY_GENERATION_APP_ID:-}
ALIYUN_ACCESS_KEY_ID: ${ALIYUN_ACCESS_KEY_ID:-}
ALIYUN_ACCESS_KEY_SECRET: ${ALIYUN_ACCESS_KEY_SECRET:-}
ALIYUN_SIGN_NAME: ${ALIYUN_SIGN_NAME:-}
ALIYUN_TEMPLATE_CODE: ${ALIYUN_TEMPLATE_CODE:-}
services:
# API service

Loading…
Cancel
Save