Add CLI source code and update .gitignore

This commit is contained in:
2026-04-07 09:38:12 +08:00
parent 6ef2dddf3f
commit 9e17f1bf29
18 changed files with 2663 additions and 1 deletions

View File

@@ -0,0 +1,115 @@
"""
User operations for CodiMD CLI.
"""
import json
import re
from typing import Dict, Any, Optional
from .client import CodiMDClient
class UserManager:
"""Manager for user operations."""
def __init__(self, client: CodiMDClient):
self.client = client
def login(self, email: str, password: str) -> Dict[str, Any]:
"""Login with email and password.
Returns the session cookies and user info.
"""
# First, get the login page to obtain CSRF token if needed
login_page = self.client.get("/login")
# Try to extract CSRF token from the page
csrf_token = None
csrf_match = re.search(r'name="csrf".*?value="([^"]+)"', login_page.text)
if csrf_match:
csrf_token = csrf_match.group(1)
# Prepare login data
login_data = {
"email": email,
"password": password,
}
if csrf_token:
login_data["_csrf"] = csrf_token
# Perform login
response = self.client.post(
"/login",
data=login_data,
allow_redirects=True
)
# Check if login was successful by trying to access /me
me_response = self.client.get("/me")
if me_response.status_code == 200:
user_data = me_response.json()
return {
"success": True,
"user": user_data,
"cookies": self.client.get_cookies()
}
# Login failed
if response.status_code == 302:
# Redirect might indicate failure or success
location = response.headers.get("Location", "")
if "login" in location.lower():
raise PermissionError("Login failed: Invalid credentials")
return {
"success": True,
"cookies": self.client.get_cookies()
}
raise PermissionError(f"Login failed: {response.status_code}")
def get_me(self) -> Dict[str, Any]:
"""Get current user information."""
response = self.client.get("/me")
if response.status_code == 403:
raise PermissionError("Not authenticated")
if response.status_code != 200:
raise Exception(f"Failed to get user info: {response.status_code}")
return response.json()
def export_my_data(self) -> Dict[str, Any]:
"""Export all user data."""
response = self.client.post(
"/me/export",
data={},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
if response.status_code == 403:
raise PermissionError("Not authenticated")
if response.status_code != 200:
raise Exception(f"Failed to export data: {response.status_code}")
return response.json()
def delete_user(self, delete_token: Optional[str] = None) -> bool:
"""Delete current user account."""
url = f"/me/delete/{delete_token}" if delete_token else "/me/delete"
response = self.client.get(url)
if response.status_code == 403:
raise PermissionError("Not authenticated or invalid token")
if response.status_code != 200:
raise Exception(f"Failed to delete user: {response.status_code}")
return True
def get_avatar(self, username: str) -> bytes:
"""Get user avatar SVG."""
response = self.client.get(f"/user/{username}/avatar.svg")
if response.status_code != 200:
raise Exception(f"Failed to get avatar: {response.status_code}")
return response.content