fix: 添加auth功能
This commit is contained in:
315
python_core/cli/commands/auth.py
Normal file
315
python_core/cli/commands/auth.py
Normal file
@@ -0,0 +1,315 @@
|
||||
"""
|
||||
用户认证CLI命令
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.panel import Panel
|
||||
import json
|
||||
import getpass
|
||||
|
||||
from python_core.api.auth_api import auth_api
|
||||
from python_core.services.user_storage import user_storage
|
||||
from python_core.utils.jwt_auth import jwt_auth
|
||||
|
||||
console = Console()
|
||||
auth_app = typer.Typer(name="auth", help="用户认证管理命令")
|
||||
|
||||
|
||||
@auth_app.command("register")
|
||||
def register_user(
|
||||
username: str = typer.Argument(..., help="用户名"),
|
||||
email: str = typer.Argument(..., help="邮箱地址"),
|
||||
display_name: Optional[str] = typer.Option(None, "--display-name", "-d", help="显示名称"),
|
||||
password: Optional[str] = typer.Option(None, "--password", "-p", help="密码(不提供则交互式输入)"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""注册新用户"""
|
||||
|
||||
try:
|
||||
console.print(f"🆕 [bold blue]用户注册[/bold blue]")
|
||||
console.print(f"用户名: {username}")
|
||||
console.print(f"邮箱: {email}")
|
||||
if display_name:
|
||||
console.print(f"显示名称: {display_name}")
|
||||
|
||||
# 获取密码
|
||||
if not password:
|
||||
password = getpass.getpass("请输入密码: ")
|
||||
if not password:
|
||||
console.print("[red]❌ 密码不能为空[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# 执行注册
|
||||
result = auth_api.register({
|
||||
"username": username,
|
||||
"email": email,
|
||||
"password": password,
|
||||
"display_name": display_name
|
||||
})
|
||||
|
||||
if result["success"]:
|
||||
console.print(f"\n✅ [bold green]注册成功![/bold green]")
|
||||
console.print(f"用户ID: {result['data']['user']['id']}")
|
||||
console.print(f"创建时间: {result['data']['user']['created_at']}")
|
||||
|
||||
if verbose:
|
||||
# 显示详细信息
|
||||
user_info = f"""
|
||||
🆔 用户ID: {result['data']['user']['id']}
|
||||
👤 用户名: {result['data']['user']['username']}
|
||||
📧 邮箱: {result['data']['user']['email']}
|
||||
🏷️ 显示名称: {result['data']['user']['display_name']}
|
||||
📅 创建时间: {result['data']['user']['created_at']}
|
||||
🔑 Token: {result['data']['token'][:50]}...
|
||||
⏰ 过期时间: {result['data']['expires_at']}
|
||||
"""
|
||||
|
||||
panel = Panel(user_info.strip(), title="用户详情", border_style="green")
|
||||
console.print(panel)
|
||||
else:
|
||||
console.print(f"[red]❌ 注册失败: {result['message']}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 注册失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@auth_app.command("login")
|
||||
def login_user(
|
||||
username_or_email: str = typer.Argument(..., help="用户名或邮箱"),
|
||||
password: Optional[str] = typer.Option(None, "--password", "-p", help="密码(不提供则交互式输入)"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""用户登录"""
|
||||
|
||||
try:
|
||||
console.print(f"🔐 [bold blue]用户登录[/bold blue]")
|
||||
console.print(f"用户名/邮箱: {username_or_email}")
|
||||
|
||||
# 获取密码
|
||||
if not password:
|
||||
password = getpass.getpass("请输入密码: ")
|
||||
if not password:
|
||||
console.print("[red]❌ 密码不能为空[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# 执行登录
|
||||
result = auth_api.login({
|
||||
"username_or_email": username_or_email,
|
||||
"password": password
|
||||
})
|
||||
|
||||
if result["success"]:
|
||||
console.print(f"\n✅ [bold green]登录成功![/bold green]")
|
||||
console.print(f"欢迎回来,{result['data']['user']['display_name']}!")
|
||||
console.print(f"最后登录: {result['data']['user']['last_login']}")
|
||||
|
||||
if verbose:
|
||||
# 显示详细信息
|
||||
user_info = f"""
|
||||
🆔 用户ID: {result['data']['user']['id']}
|
||||
👤 用户名: {result['data']['user']['username']}
|
||||
📧 邮箱: {result['data']['user']['email']}
|
||||
🏷️ 显示名称: {result['data']['user']['display_name']}
|
||||
🕒 最后登录: {result['data']['user']['last_login']}
|
||||
🔑 Token: {result['data']['token'][:50]}...
|
||||
⏰ 过期时间: {result['data']['expires_at']}
|
||||
"""
|
||||
|
||||
panel = Panel(user_info.strip(), title="登录信息", border_style="green")
|
||||
console.print(panel)
|
||||
else:
|
||||
console.print(f"[red]❌ 登录失败: {result['message']}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 登录失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@auth_app.command("verify")
|
||||
def verify_token(
|
||||
token: str = typer.Argument(..., help="JWT token"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""验证JWT token"""
|
||||
|
||||
try:
|
||||
console.print(f"🔍 [bold blue]验证Token[/bold blue]")
|
||||
|
||||
# 验证token
|
||||
result = auth_api.verify_token({
|
||||
"token": token
|
||||
})
|
||||
|
||||
if result["success"]:
|
||||
console.print(f"\n✅ [bold green]Token有效![/bold green]")
|
||||
user = result['data']['user']
|
||||
console.print(f"用户: {user['display_name']} ({user['username']})")
|
||||
console.print(f"邮箱: {user['email']}")
|
||||
|
||||
if verbose:
|
||||
# 显示token详细信息
|
||||
token_info = jwt_auth.get_token_info(token)
|
||||
|
||||
info_text = f"""
|
||||
👤 用户信息:
|
||||
ID: {user['user_id']}
|
||||
用户名: {user['username']}
|
||||
邮箱: {user['email']}
|
||||
显示名称: {user['display_name']}
|
||||
|
||||
🔑 Token信息:
|
||||
有效性: {'有效' if token_info['valid'] else '无效'}
|
||||
签发时间: {token_info.get('issued_at', 'Unknown')}
|
||||
过期时间: {token_info.get('expires_at', 'Unknown')}
|
||||
剩余时间: {token_info.get('time_remaining', 'Unknown')}
|
||||
"""
|
||||
|
||||
panel = Panel(info_text.strip(), title="Token详情", border_style="green")
|
||||
console.print(panel)
|
||||
else:
|
||||
console.print(f"[red]❌ Token无效: {result['message']}[/red]")
|
||||
|
||||
if verbose:
|
||||
# 显示token信息(即使无效)
|
||||
token_info = jwt_auth.get_token_info(token)
|
||||
if token_info.get('error'):
|
||||
console.print(f"错误: {token_info['error']}")
|
||||
else:
|
||||
console.print(f"Token状态: {'过期' if token_info.get('is_expired') else '无效'}")
|
||||
|
||||
raise typer.Exit(1)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ Token验证失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@auth_app.command("list")
|
||||
def list_users(
|
||||
include_inactive: bool = typer.Option(False, "--include-inactive", "-i", help="包含非活跃用户"),
|
||||
limit: int = typer.Option(20, "--limit", "-l", help="显示数量限制"),
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="详细输出")
|
||||
):
|
||||
"""列出所有用户"""
|
||||
|
||||
try:
|
||||
console.print(f"👥 [bold blue]用户列表[/bold blue]")
|
||||
|
||||
# 获取用户列表
|
||||
users = user_storage.get_all_users(include_inactive)
|
||||
|
||||
if not users:
|
||||
console.print("📭 没有用户")
|
||||
return
|
||||
|
||||
# 限制显示数量
|
||||
total_count = len(users)
|
||||
if len(users) > limit:
|
||||
users = users[:limit]
|
||||
console.print(f"📊 显示前 {limit} 个用户(共 {total_count} 个)")
|
||||
else:
|
||||
console.print(f"📊 共 {total_count} 个用户")
|
||||
|
||||
# 创建表格
|
||||
table = Table(title="用户列表")
|
||||
table.add_column("ID", style="cyan", width=8)
|
||||
table.add_column("用户名", style="green")
|
||||
table.add_column("邮箱", style="yellow")
|
||||
table.add_column("显示名称", style="magenta")
|
||||
table.add_column("状态", style="blue")
|
||||
table.add_column("创建时间", style="dim")
|
||||
|
||||
if verbose:
|
||||
table.add_column("最后登录", style="dim")
|
||||
|
||||
for user in users:
|
||||
# 状态
|
||||
status = "✅ 活跃" if user.is_active else "❌ 禁用"
|
||||
|
||||
# 格式化创建时间
|
||||
created_at = user.created_at
|
||||
if 'T' in created_at:
|
||||
created_at = created_at.split('T')[0] + ' ' + created_at.split('T')[1][:8]
|
||||
|
||||
row = [
|
||||
user.id[:8],
|
||||
user.username,
|
||||
user.email,
|
||||
user.display_name,
|
||||
status,
|
||||
created_at
|
||||
]
|
||||
|
||||
if verbose:
|
||||
last_login = user.last_login or "从未登录"
|
||||
if last_login != "从未登录" and 'T' in last_login:
|
||||
last_login = last_login.split('T')[0] + ' ' + last_login.split('T')[1][:8]
|
||||
row.append(last_login)
|
||||
|
||||
table.add_row(*row)
|
||||
|
||||
console.print(table)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 获取用户列表失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@auth_app.command("stats")
|
||||
def show_stats():
|
||||
"""显示用户统计信息"""
|
||||
|
||||
try:
|
||||
console.print(f"📊 [bold blue]用户统计信息[/bold blue]")
|
||||
|
||||
# 获取统计信息
|
||||
stats = user_storage.get_user_count()
|
||||
|
||||
# 创建统计面板
|
||||
stats_content = f"""
|
||||
📈 [bold green]用户统计[/bold green]
|
||||
总用户数: {stats['total']}
|
||||
活跃用户: {stats['active']}
|
||||
禁用用户: {stats['inactive']}
|
||||
"""
|
||||
|
||||
stats_panel = Panel(stats_content.strip(), title="用户统计", border_style="green")
|
||||
console.print(stats_panel)
|
||||
|
||||
# 显示最近注册的用户
|
||||
recent_users = user_storage.get_all_users()[:5]
|
||||
|
||||
if recent_users:
|
||||
console.print(f"\n🕒 [bold green]最近注册的用户[/bold green]")
|
||||
|
||||
recent_table = Table()
|
||||
recent_table.add_column("用户名", style="green")
|
||||
recent_table.add_column("邮箱", style="yellow")
|
||||
recent_table.add_column("注册时间", style="dim")
|
||||
|
||||
for user in recent_users:
|
||||
created_at = user.created_at
|
||||
if 'T' in created_at:
|
||||
created_at = created_at.split('T')[0] + ' ' + created_at.split('T')[1][:8]
|
||||
|
||||
recent_table.add_row(
|
||||
user.username,
|
||||
user.email,
|
||||
created_at
|
||||
)
|
||||
|
||||
console.print(recent_table)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]❌ 获取统计信息失败: {str(e)}[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
auth_app()
|
||||
Reference in New Issue
Block a user