dify/api/services/entities/account_entities.py
非法操作 ef92cb7c65
refactor(api): complete account education boundary (#41153)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 01:48:12 +00:00

204 lines
4.9 KiB
Python

"""Framework-neutral contracts for Console account use cases."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from enum import StrEnum
@dataclass(frozen=True, slots=True)
class AccountSnapshot:
id: str
name: str
email: str
avatar: str | None
is_password_set: bool
interface_language: str | None
interface_theme: str | None
timezone: str | None
last_login_at: datetime | None
last_login_ip: str | None
status: str
initialized_at: datetime | None
created_at: datetime
@dataclass(frozen=True, slots=True)
class AccountProfileChanges:
name: str | None = None
avatar: str | None = None
interface_language: str | None = None
interface_theme: str | None = None
timezone: str | None = None
def has_changes(self) -> bool:
return any(
value is not None
for value in (
self.name,
self.avatar,
self.interface_language,
self.interface_theme,
self.timezone,
)
)
@dataclass(frozen=True, slots=True)
class AccountCredentials:
password_hash: str | None
password_salt: str | None
@dataclass(frozen=True, slots=True)
class AccountPasswordDigest:
password_hash: str
password_salt: str
@dataclass(frozen=True, slots=True)
class AccountIntegrationSnapshot:
provider: str
created_at: datetime
@dataclass(frozen=True, slots=True)
class AccountIntegrationStatus:
provider: str
created_at: datetime | None
is_bound: bool
@dataclass(frozen=True, slots=True)
class AccountInitialization:
interface_language: str
interface_theme: str
timezone: str
initialized_at: datetime
class AccountInitializationStatus(StrEnum):
INITIALIZED = "initialized"
ACCOUNT_NOT_FOUND = "account_not_found"
ALREADY_INITIALIZED = "already_initialized"
INVALID_INVITATION = "invalid_invitation"
@dataclass(frozen=True, slots=True)
class AccountInitializationResult:
status: AccountInitializationStatus
account: AccountSnapshot | None = None
@dataclass(frozen=True, slots=True)
class AccountDeletionChallenge:
token: str
code: str
@dataclass(frozen=True, slots=True)
class ChangeEmailVerification:
email: str
token: str
class AccountEmailResetStatus(StrEnum):
UPDATED = "updated"
ACCOUNT_NOT_FOUND = "account_not_found"
EMAIL_CHANGED = "email_changed"
EMAIL_IN_USE = "email_in_use"
@dataclass(frozen=True, slots=True)
class AccountEmailResetResult:
status: AccountEmailResetStatus
account: AccountSnapshot | None = None
class AccountChangeEmailPhase(StrEnum):
OLD_EMAIL = "old_email"
OLD_EMAIL_VERIFIED = "old_email_verified"
NEW_EMAIL = "new_email"
NEW_EMAIL_VERIFIED = "new_email_verified"
@dataclass(frozen=True, slots=True)
class AccountChangeEmailToken:
account_id: str
email: str
old_email: str
code: str
def is_bound_to_account(self, account_id: str) -> bool:
return self.account_id == account_id
@dataclass(frozen=True, slots=True)
class AccountChangeEmailOldEmailToken(AccountChangeEmailToken):
phase = AccountChangeEmailPhase.OLD_EMAIL
def promote(self) -> AccountChangeEmailOldEmailVerifiedToken:
return AccountChangeEmailOldEmailVerifiedToken(
account_id=self.account_id,
email=self.email,
old_email=self.old_email,
code=self.code,
)
@dataclass(frozen=True, slots=True)
class AccountChangeEmailOldEmailVerifiedToken(AccountChangeEmailToken):
phase = AccountChangeEmailPhase.OLD_EMAIL_VERIFIED
@dataclass(frozen=True, slots=True)
class AccountChangeEmailNewEmailToken(AccountChangeEmailToken):
phase = AccountChangeEmailPhase.NEW_EMAIL
def promote(self) -> AccountChangeEmailNewEmailVerifiedToken:
return AccountChangeEmailNewEmailVerifiedToken(
account_id=self.account_id,
email=self.email,
old_email=self.old_email,
code=self.code,
)
@dataclass(frozen=True, slots=True)
class AccountChangeEmailNewEmailVerifiedToken(AccountChangeEmailToken):
phase = AccountChangeEmailPhase.NEW_EMAIL_VERIFIED
type AccountChangeEmailTokenData = (
AccountChangeEmailOldEmailToken
| AccountChangeEmailOldEmailVerifiedToken
| AccountChangeEmailNewEmailToken
| AccountChangeEmailNewEmailVerifiedToken
)
@dataclass(frozen=True, slots=True)
class AccountEducationVerification:
token: str | None
@dataclass(frozen=True, slots=True)
class AccountEducationActivation:
message: str
@dataclass(frozen=True, slots=True)
class AccountEducationStatus:
result: bool | None
is_student: bool | None
expire_at: datetime | None
allow_refresh: bool | None
@dataclass(frozen=True, slots=True)
class AccountEducationAutocomplete:
data: tuple[str, ...]
curr_page: int | None
has_next: bool | None