|
@@ -32,6 +32,7 @@ from models.account import (
|
|
|
TenantStatus,
|
|
|
)
|
|
|
from models.model import DifySetup
|
|
|
+from services.billing_service import BillingService
|
|
|
from services.errors.account import (
|
|
|
AccountAlreadyInTenantError,
|
|
|
AccountLoginError,
|
|
@@ -50,6 +51,8 @@ from services.errors.account import (
|
|
|
)
|
|
|
from services.errors.workspace import WorkSpaceNotAllowedCreateError
|
|
|
from services.feature_service import FeatureService
|
|
|
+from tasks.delete_account_task import delete_account_task
|
|
|
+from tasks.mail_account_deletion_task import send_account_deletion_verification_code
|
|
|
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
|
|
@@ -70,6 +73,9 @@ class AccountService:
|
|
|
email_code_login_rate_limiter = RateLimiter(
|
|
|
prefix="email_code_login_rate_limit", max_attempts=1, time_window=60 * 1
|
|
|
)
|
|
|
+ email_code_account_deletion_rate_limiter = RateLimiter(
|
|
|
+ prefix="email_code_account_deletion_rate_limit", max_attempts=1, time_window=60 * 1
|
|
|
+ )
|
|
|
LOGIN_MAX_ERROR_LIMITS = 5
|
|
|
|
|
|
@staticmethod
|
|
@@ -201,6 +207,15 @@ class AccountService:
|
|
|
from controllers.console.error import AccountNotFound
|
|
|
|
|
|
raise AccountNotFound()
|
|
|
+
|
|
|
+ if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(email):
|
|
|
+ raise AccountRegisterError(
|
|
|
+ description=(
|
|
|
+ "This email account has been deleted within the past "
|
|
|
+ "30 days and is temporarily unavailable for new account registration"
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
account = Account()
|
|
|
account.email = email
|
|
|
account.name = name
|
|
@@ -240,6 +255,42 @@ class AccountService:
|
|
|
|
|
|
return account
|
|
|
|
|
|
+ @staticmethod
|
|
|
+ def generate_account_deletion_verification_code(account: Account) -> tuple[str, str]:
|
|
|
+ code = "".join([str(random.randint(0, 9)) for _ in range(6)])
|
|
|
+ token = TokenManager.generate_token(
|
|
|
+ account=account, token_type="account_deletion", additional_data={"code": code}
|
|
|
+ )
|
|
|
+ return token, code
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def send_account_deletion_verification_email(cls, account: Account, code: str):
|
|
|
+ email = account.email
|
|
|
+ if cls.email_code_account_deletion_rate_limiter.is_rate_limited(email):
|
|
|
+ from controllers.console.auth.error import EmailCodeAccountDeletionRateLimitExceededError
|
|
|
+
|
|
|
+ raise EmailCodeAccountDeletionRateLimitExceededError()
|
|
|
+
|
|
|
+ send_account_deletion_verification_code.delay(to=email, code=code)
|
|
|
+
|
|
|
+ cls.email_code_account_deletion_rate_limiter.increment_rate_limit(email)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def verify_account_deletion_code(token: str, code: str) -> bool:
|
|
|
+ token_data = TokenManager.get_token_data(token, "account_deletion")
|
|
|
+ if token_data is None:
|
|
|
+ return False
|
|
|
+
|
|
|
+ if token_data["code"] != code:
|
|
|
+ return False
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def delete_account(account: Account) -> None:
|
|
|
+ """Delete account. This method only adds a task to the queue for deletion."""
|
|
|
+ delete_account_task.delay(account.id)
|
|
|
+
|
|
|
@staticmethod
|
|
|
def link_account_integrate(provider: str, open_id: str, account: Account) -> None:
|
|
|
"""Link account integrate"""
|
|
@@ -379,6 +430,7 @@ class AccountService:
|
|
|
def send_email_code_login_email(
|
|
|
cls, account: Optional[Account] = None, email: Optional[str] = None, language: Optional[str] = "en-US"
|
|
|
):
|
|
|
+ email = account.email if account else email
|
|
|
if email is None:
|
|
|
raise ValueError("Email must be provided.")
|
|
|
if cls.email_code_login_rate_limiter.is_rate_limited(email):
|
|
@@ -408,6 +460,14 @@ class AccountService:
|
|
|
|
|
|
@classmethod
|
|
|
def get_user_through_email(cls, email: str):
|
|
|
+ if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(email):
|
|
|
+ raise AccountRegisterError(
|
|
|
+ description=(
|
|
|
+ "This email account has been deleted within the past "
|
|
|
+ "30 days and is temporarily unavailable for new account registration"
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
account = db.session.query(Account).filter(Account.email == email).first()
|
|
|
if not account:
|
|
|
return None
|
|
@@ -824,6 +884,10 @@ class RegisterService:
|
|
|
db.session.commit()
|
|
|
except WorkSpaceNotAllowedCreateError:
|
|
|
db.session.rollback()
|
|
|
+ except AccountRegisterError as are:
|
|
|
+ db.session.rollback()
|
|
|
+ logging.exception("Register failed")
|
|
|
+ raise are
|
|
|
except Exception as e:
|
|
|
db.session.rollback()
|
|
|
logging.exception("Register failed")
|