forked from FOSS/Steam-Panel
v3.0.4
This commit is contained in:
+36
-13
@@ -215,7 +215,9 @@ async def import_accounts(file: UploadFile = File(...)):
|
||||
Supported formats (: or | separator):
|
||||
login|password|{mafile_json}
|
||||
login|password|email|email_password|{mafile_json}
|
||||
login|password|email|email_password|{mafile_json}|notes
|
||||
login|password|email|email_password
|
||||
login|password|email|email_password|notes (no mafile, with notes)
|
||||
"""
|
||||
content = (await file.read()).decode("utf-8", errors="ignore")
|
||||
result = BulkImportResult()
|
||||
@@ -226,11 +228,27 @@ async def import_accounts(file: UploadFile = File(...)):
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Split off embedded mafile JSON (everything from first '{' onward)
|
||||
notes: str | None = None
|
||||
|
||||
# Split off embedded mafile JSON — find matching braces
|
||||
json_start = line.find("{")
|
||||
if json_start != -1:
|
||||
mafile_str = line[json_start:]
|
||||
depth = 0
|
||||
json_end = json_start
|
||||
for i in range(json_start, len(line)):
|
||||
if line[i] == "{":
|
||||
depth += 1
|
||||
elif line[i] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
json_end = i
|
||||
break
|
||||
|
||||
mafile_str = line[json_start : json_end + 1]
|
||||
prefix = line[:json_start].rstrip("|:")
|
||||
suffix = line[json_end + 1 :].lstrip("|:")
|
||||
if suffix:
|
||||
notes = suffix
|
||||
else:
|
||||
mafile_str = None
|
||||
prefix = line
|
||||
@@ -262,12 +280,15 @@ async def import_accounts(file: UploadFile = File(...)):
|
||||
email = mafile_data.get("mail") or mafile_data.get("email") or None
|
||||
email_password = mafile_data.get("mail_password") or mafile_data.get("email_password") or None
|
||||
else:
|
||||
# No mafile — must be exactly 4 fields
|
||||
if len(parts) != 4:
|
||||
result.errors.append(f"Invalid line (need 4 fields): {line[:50]}")
|
||||
# No mafile — 4 fields, or 5 fields with notes
|
||||
if len(parts) == 5:
|
||||
login, password, email, email_password, notes = parts
|
||||
elif len(parts) == 4:
|
||||
login, password, email, email_password = parts
|
||||
else:
|
||||
result.errors.append(f"Invalid line (need 4-5 fields): {line[:50]}")
|
||||
result.skipped += 1
|
||||
continue
|
||||
login, password, email, email_password = parts
|
||||
mafile_data = None
|
||||
|
||||
try:
|
||||
@@ -281,9 +302,9 @@ async def import_accounts(file: UploadFile = File(...)):
|
||||
continue
|
||||
|
||||
await db.execute(
|
||||
"""INSERT INTO accounts (login, password, email, email_password)
|
||||
VALUES (?, ?, ?, ?)""",
|
||||
(login, password, email or None, email_password or None),
|
||||
"""INSERT INTO accounts (login, password, email, email_password, notes)
|
||||
VALUES (?, ?, ?, ?, ?)""",
|
||||
(login, password, email or None, email_password or None, notes or None),
|
||||
)
|
||||
result.imported += 1
|
||||
except Exception as exc:
|
||||
@@ -298,7 +319,8 @@ async def import_accounts(file: UploadFile = File(...)):
|
||||
steam_id = (mafile_data.get("Session") or {}).get("SteamID") or None
|
||||
|
||||
settings.mafiles_dir.mkdir(parents=True, exist_ok=True)
|
||||
dest = settings.mafiles_dir / f"{account_name}.mafile"
|
||||
safe_name = Path(account_name).name
|
||||
dest = settings.mafiles_dir / f"{safe_name}.mafile"
|
||||
dest.write_text(json.dumps(mafile_data, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
await db.execute(
|
||||
@@ -325,12 +347,13 @@ async def upload_mafile(account_id: int, file: UploadFile = File(...)):
|
||||
content = (await file.read()).decode("utf-8")
|
||||
try:
|
||||
mafile = MafileData.model_validate_json(content)
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail="Invalid mafile format")
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid mafile format: {exc}")
|
||||
|
||||
settings.mafiles_dir.mkdir(parents=True, exist_ok=True)
|
||||
steam_id = mafile.Session.SteamID or row["steam_id"] or account_id
|
||||
mafile_path = settings.mafiles_dir / f"{steam_id}.mafile"
|
||||
safe_name = Path(str(steam_id)).name
|
||||
mafile_path = settings.mafiles_dir / f"{safe_name}.mafile"
|
||||
mafile_path.write_text(content, encoding="utf-8")
|
||||
|
||||
await db.execute(
|
||||
|
||||
@@ -84,3 +84,54 @@ async def generate_2fa_by_account(request: Generate2FAByAccountRequest):
|
||||
if not row or not row["shared_secret"]:
|
||||
raise HTTPException(status_code=404, detail="Account not found or has no shared_secret")
|
||||
return {"code": gen_code(row["shared_secret"])}
|
||||
|
||||
|
||||
class ConfirmationRespondRequest(BaseModel):
|
||||
ids: list[str]
|
||||
nonces: list[str]
|
||||
accept: bool
|
||||
|
||||
|
||||
@router.get("/confirmations/{account_id}")
|
||||
async def get_confirmations(account_id: int):
|
||||
"""Fetch pending Steam confirmations for an account."""
|
||||
from app.services.steam_confirmations import fetch_confirmations
|
||||
|
||||
db = await get_db()
|
||||
cursor = await db.execute("SELECT * FROM accounts WHERE id = ?", (account_id,))
|
||||
account = await cursor.fetchone()
|
||||
if not account:
|
||||
raise HTTPException(status_code=404, detail="Account not found")
|
||||
|
||||
try:
|
||||
confirmations = await fetch_confirmations(dict(account))
|
||||
return {"success": True, "confirmations": confirmations}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"[confirmations] Error fetching for account {account_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/confirmations/{account_id}/respond")
|
||||
async def respond_confirmations(account_id: int, request: ConfirmationRespondRequest):
|
||||
"""Accept or deny confirmations for an account."""
|
||||
from app.services.steam_confirmations import respond_to_confirmation
|
||||
|
||||
if len(request.ids) != len(request.nonces):
|
||||
raise HTTPException(status_code=400, detail="ids and nonces must have same length")
|
||||
|
||||
db = await get_db()
|
||||
cursor = await db.execute("SELECT * FROM accounts WHERE id = ?", (account_id,))
|
||||
account = await cursor.fetchone()
|
||||
if not account:
|
||||
raise HTTPException(status_code=404, detail="Account not found")
|
||||
|
||||
try:
|
||||
success = await respond_to_confirmation(dict(account), request.ids, request.nonces, request.accept)
|
||||
return {"success": success}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"[confirmations] Error responding for account {account_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -53,19 +53,23 @@ async def upload_mafiles(files: list[UploadFile] = File(...)):
|
||||
try:
|
||||
mafile = MafileData.model_validate_json(content)
|
||||
steam_id = mafile.Session.SteamID or mafile.account_name or file.filename
|
||||
dest = settings.mafiles_dir / f"{steam_id}.mafile"
|
||||
safe_name = Path(str(steam_id)).name
|
||||
dest = settings.mafiles_dir / f"{safe_name}.mafile"
|
||||
dest.write_text(content, encoding="utf-8")
|
||||
uploaded += 1
|
||||
|
||||
if mafile.account_name:
|
||||
mafile_steam_id = str(mafile.Session.SteamID) if mafile.Session.SteamID else None
|
||||
await db.execute(
|
||||
"""UPDATE accounts
|
||||
SET mafile_path = ?, shared_secret = ?, identity_secret = ?
|
||||
SET mafile_path = ?, shared_secret = ?, identity_secret = ?,
|
||||
steam_id = COALESCE(steam_id, ?)
|
||||
WHERE login = ?""",
|
||||
(
|
||||
str(dest),
|
||||
mafile.shared_secret or "",
|
||||
mafile.identity_secret or "",
|
||||
mafile_steam_id,
|
||||
mafile.account_name,
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user