from __future__ import annotations import argparse import getpass import sys from .db import connect, init_db, utcnow from .services.auth import password_hash def reset_password(username: str, password: str) -> bool: """Note: Reset the selected user password hash without changing role or permissions.""" username = (username or "").strip() if not username: raise ValueError("Username is required") if password is None or password == "": raise ValueError("Password cannot be empty") init_db() now = utcnow() hashed = password_hash(password) with connect() as conn: row = conn.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone() if not row: return False conn.execute( "UPDATE users SET password_hash=?, updated_at=? WHERE username=?", (hashed, now, username), ) return True def _password_from_args(args: argparse.Namespace) -> str: """Note: Allow the password to be passed as an argument or entered securely in interactive mode.""" if args.password is not None: return args.password first = getpass.getpass("New password: ") second = getpass.getpass("Repeat password: ") if first != second: raise ValueError("Passwords do not match") return first def build_parser() -> argparse.ArgumentParser: """Note: Define simple administrative commands launched with python -m pytorrent.cli.""" parser = argparse.ArgumentParser(description="pyTorrent CLI") sub = parser.add_subparsers(dest="command", required=True) reset = sub.add_parser("reset-password", help="Reset password for an existing user") reset.add_argument("username", help="User login") reset.add_argument("password", nargs="?", help="New password; omit to type it interactively") reset.set_defaults(func=_cmd_reset_password) return parser def _cmd_reset_password(args: argparse.Namespace) -> int: """Note: Run the password reset and return a readable terminal status.""" password = _password_from_args(args) if reset_password(args.username, password): print(f"Password reset for user: {args.username}") return 0 print(f"User not found: {args.username}", file=sys.stderr) return 1 def main(argv: list[str] | None = None) -> int: """Note: Main CLI entrypoint with error handling and without starting the web app.""" parser = build_parser() args = parser.parse_args(argv) try: return int(args.func(args) or 0) except Exception as exc: print(f"Error: {exc}", file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())