Spaces:
Running
Running
| """ | |
| SQLAlchemy ORM 模型定义 | |
| """ | |
| from datetime import datetime | |
| from typing import Optional, Dict, Any | |
| import json | |
| from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey | |
| from sqlalchemy.ext.declarative import declarative_base | |
| from sqlalchemy.types import TypeDecorator | |
| from sqlalchemy.orm import relationship | |
| Base = declarative_base() | |
| class JSONEncodedDict(TypeDecorator): | |
| """JSON 编码字典类型""" | |
| impl = Text | |
| def process_bind_param(self, value: Optional[Dict[str, Any]], dialect): | |
| if value is None: | |
| return None | |
| return json.dumps(value, ensure_ascii=False) | |
| def process_result_value(self, value: Optional[str], dialect): | |
| if value is None: | |
| return None | |
| return json.loads(value) | |
| class Account(Base): | |
| """已注册账号表""" | |
| __tablename__ = 'accounts' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| email = Column(String(255), nullable=False, unique=True, index=True) | |
| password = Column(String(255)) # 注册密码(明文存储) | |
| access_token = Column(Text) | |
| refresh_token = Column(Text) | |
| id_token = Column(Text) | |
| session_token = Column(Text) # 会话令牌(优先刷新方式) | |
| client_id = Column(String(255)) # OAuth Client ID | |
| account_id = Column(String(255)) | |
| workspace_id = Column(String(255)) | |
| email_service = Column(String(50), nullable=False) # 'tempmail', 'outlook', 'moe_mail' | |
| email_service_id = Column(String(255)) # 邮箱服务中的ID | |
| proxy_used = Column(String(255)) | |
| registered_at = Column(DateTime, default=datetime.utcnow) | |
| last_refresh = Column(DateTime) # 最后刷新时间 | |
| expires_at = Column(DateTime) # Token 过期时间 | |
| status = Column(String(20), default='active') # 'active', 'expired', 'banned', 'failed' | |
| extra_data = Column(JSONEncodedDict) # 额外信息存储 | |
| cpa_uploaded = Column(Boolean, default=False) # 是否已上传到 CPA | |
| cpa_uploaded_at = Column(DateTime) # 上传时间 | |
| source = Column(String(20), default='register') # 'register' 或 'login',区分账号来源 | |
| subscription_type = Column(String(20)) # None / 'plus' / 'team' | |
| subscription_at = Column(DateTime) # 订阅开通时间 | |
| cookies = Column(Text) # 完整 cookie 字符串,用于支付请求 | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| def to_dict(self) -> Dict[str, Any]: | |
| """转换为字典""" | |
| return { | |
| 'id': self.id, | |
| 'email': self.email, | |
| 'password': self.password, | |
| 'client_id': self.client_id, | |
| 'email_service': self.email_service, | |
| 'account_id': self.account_id, | |
| 'workspace_id': self.workspace_id, | |
| 'registered_at': self.registered_at.isoformat() if self.registered_at else None, | |
| 'last_refresh': self.last_refresh.isoformat() if self.last_refresh else None, | |
| 'expires_at': self.expires_at.isoformat() if self.expires_at else None, | |
| 'status': self.status, | |
| 'proxy_used': self.proxy_used, | |
| 'cpa_uploaded': self.cpa_uploaded, | |
| 'cpa_uploaded_at': self.cpa_uploaded_at.isoformat() if self.cpa_uploaded_at else None, | |
| 'source': self.source, | |
| 'subscription_type': self.subscription_type, | |
| 'subscription_at': self.subscription_at.isoformat() if self.subscription_at else None, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None | |
| } | |
| class EmailService(Base): | |
| """邮箱服务配置表""" | |
| __tablename__ = 'email_services' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| service_type = Column(String(50), nullable=False) # 'outlook', 'moe_mail' | |
| name = Column(String(100), nullable=False) | |
| config = Column(JSONEncodedDict, nullable=False) # 服务配置(加密存储) | |
| enabled = Column(Boolean, default=True) | |
| priority = Column(Integer, default=0) # 使用优先级 | |
| last_used = Column(DateTime) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class RegistrationTask(Base): | |
| """注册任务表""" | |
| __tablename__ = 'registration_tasks' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| task_uuid = Column(String(36), unique=True, nullable=False, index=True) # 任务唯一标识 | |
| status = Column(String(20), default='pending') # 'pending', 'running', 'completed', 'failed', 'cancelled' | |
| email_service_id = Column(Integer, ForeignKey('email_services.id'), index=True) # 使用的邮箱服务 | |
| proxy = Column(String(255)) # 使用的代理 | |
| logs = Column(Text) # 注册过程日志 | |
| result = Column(JSONEncodedDict) # 注册结果 | |
| error_message = Column(Text) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| started_at = Column(DateTime) | |
| completed_at = Column(DateTime) | |
| # 关系 | |
| email_service = relationship('EmailService') | |
| class Setting(Base): | |
| """系统设置表""" | |
| __tablename__ = 'settings' | |
| key = Column(String(100), primary_key=True) | |
| value = Column(Text) | |
| description = Column(Text) | |
| category = Column(String(50), default='general') # 'general', 'email', 'proxy', 'openai' | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class CpaService(Base): | |
| """CPA 服务配置表""" | |
| __tablename__ = 'cpa_services' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String(100), nullable=False) # 服务名称 | |
| api_url = Column(String(500), nullable=False) # API URL | |
| api_token = Column(Text, nullable=False) # API Token | |
| enabled = Column(Boolean, default=True) | |
| priority = Column(Integer, default=0) # 优先级 | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class Sub2ApiService(Base): | |
| """Sub2API 服务配置表""" | |
| __tablename__ = 'sub2api_services' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String(100), nullable=False) # 服务名称 | |
| api_url = Column(String(500), nullable=False) # API URL (host) | |
| api_key = Column(Text, nullable=False) # x-api-key | |
| enabled = Column(Boolean, default=True) | |
| priority = Column(Integer, default=0) # 优先级 | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class TeamManagerService(Base): | |
| """Team Manager 服务配置表""" | |
| __tablename__ = 'tm_services' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String(100), nullable=False) # 服务名称 | |
| api_url = Column(String(500), nullable=False) # API URL | |
| api_key = Column(Text, nullable=False) # X-API-Key | |
| enabled = Column(Boolean, default=True) | |
| priority = Column(Integer, default=0) # 优先级 | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class Proxy(Base): | |
| """代理列表表""" | |
| __tablename__ = 'proxies' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String(100), nullable=False) # 代理名称 | |
| type = Column(String(20), nullable=False, default='http') # http, socks5 | |
| host = Column(String(255), nullable=False) | |
| port = Column(Integer, nullable=False) | |
| username = Column(String(100)) | |
| password = Column(String(255)) | |
| enabled = Column(Boolean, default=True) | |
| is_default = Column(Boolean, default=False) # 是否为默认代理 | |
| priority = Column(Integer, default=0) # 优先级(保留字段) | |
| last_used = Column(DateTime) # 最后使用时间 | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| def to_dict(self, include_password: bool = False) -> Dict[str, Any]: | |
| """转换为字典""" | |
| result = { | |
| 'id': self.id, | |
| 'name': self.name, | |
| 'type': self.type, | |
| 'host': self.host, | |
| 'port': self.port, | |
| 'username': self.username, | |
| 'enabled': self.enabled, | |
| 'is_default': self.is_default or False, | |
| 'priority': self.priority, | |
| 'last_used': self.last_used.isoformat() if self.last_used else None, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None, | |
| } | |
| if include_password: | |
| result['password'] = self.password | |
| else: | |
| result['has_password'] = bool(self.password) | |
| return result | |
| def proxy_url(self) -> str: | |
| """获取完整的代理 URL""" | |
| if self.type == "http": | |
| scheme = "http" | |
| elif self.type == "socks5": | |
| scheme = "socks5" | |
| else: | |
| scheme = self.type | |
| auth = "" | |
| if self.username and self.password: | |
| auth = f"{self.username}:{self.password}@" | |
| return f"{scheme}://{auth}{self.host}:{self.port}" |