hp-studios-ai-content-agent/backend/app/cli/reset_password.py
DJP 1d9fa62b0d Add reset_password CLI for admin password recovery
Avoids shell-quoting hell. Used as:

  docker compose ... exec -T api \
    python -m app.cli.reset_password admin@example.com NEW_PASSWORD

  docker compose ... exec -T api \
    python -m app.cli.reset_password --list

Lists existing users on lookup miss so it's obvious which email to
target. Refuses passwords shorter than 4 chars to catch typos.
2026-05-06 09:17:41 -04:00

78 lines
2.3 KiB
Python

"""Reset a user's password from the command line.
Usage (from the repo root, on the server, after a git pull):
sudo docker compose -p hp-studios-ai-content-agent \\
-f docker-compose.yml -f docker-compose.prod.yml \\
exec -T api python -m app.cli.reset_password admin@example.com NEW_PASSWORD
To list users without resetting anything:
sudo docker compose -p hp-studios-ai-content-agent \\
-f docker-compose.yml -f docker-compose.prod.yml \\
exec -T api python -m app.cli.reset_password --list
"""
from __future__ import annotations
import sys
from app.core.security import hash_password
from app.db.models import User
from app.db.session import SessionLocal
def list_users() -> int:
db = SessionLocal()
try:
rows = db.query(User).order_by(User.created_at).all()
if not rows:
print("No users in the database.")
return 0
print(f"{'EMAIL':40s} {'ROLE':6s} {'NAME'}")
print("-" * 80)
for u in rows:
print(f"{u.email:40s} {u.role:6s} {u.name}")
return 0
finally:
db.close()
def reset(email: str, new_password: str) -> int:
db = SessionLocal()
try:
u = db.query(User).filter(User.email == email).first()
if not u:
print(f"No user found with email: {email}")
print()
print("Existing users:")
for row in db.query(User).all():
print(f" {row.email:40s} {row.role:6s} {row.name}")
return 1
u.password_hash = hash_password(new_password)
db.commit()
print(f"OK — password reset for {u.email} (role={u.role})")
return 0
finally:
db.close()
def main() -> int:
args = sys.argv[1:]
if not args or args[0] in ("-h", "--help"):
print(__doc__)
return 0
if args[0] in ("--list", "-l", "list"):
return list_users()
if len(args) != 2:
print("Usage: python -m app.cli.reset_password EMAIL NEW_PASSWORD")
print(" python -m app.cli.reset_password --list")
return 2
email, new_password = args[0], args[1]
if len(new_password) < 4:
print("Refusing to set a password shorter than 4 characters.")
return 2
return reset(email, new_password)
if __name__ == "__main__":
raise SystemExit(main())