deer-flow/backend/app/plugins/auth/ops/reset_admin.py
rayhpeng 0f82f8a3a2 feat(app): add plugin system with auth plugin and static assets
Add new application structure:
- app/main.py - application entry point
- app/plugins/ - plugin system with auth plugin:
  - api/ - REST API endpoints and schemas
  - authorization/ - auth policies, providers, hooks
  - domain/ - business logic (service, models, jwt, password)
  - injection/ - route injection and guards
  - ops/ - operational utilities
  - runtime/ - runtime configuration
  - security/ - middleware, CSRF, dependencies
  - storage/ - user repositories and models
- app/static/ - static assets (scalar.js for API docs)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-22 11:31:42 +08:00

75 lines
2.5 KiB
Python

"""CLI tool to reset an admin password."""
from __future__ import annotations
import argparse
import asyncio
import secrets
import sys
from sqlalchemy import select
from app.plugins.auth.domain.password import hash_password
from app.plugins.auth.ops.credential_file import write_initial_credentials
from app.plugins.auth.storage import DbUserRepository
from app.plugins.auth.storage.models import User as UserModel
async def _run(email: str | None) -> int:
from store.persistence import create_persistence
app_persistence = await create_persistence()
await app_persistence.setup()
try:
if email:
async with app_persistence.session_factory() as session:
repo = DbUserRepository(session)
user = await repo.get_user_by_email(email)
else:
async with app_persistence.session_factory() as session:
stmt = select(UserModel).where(UserModel.system_role == "admin").limit(1)
row = (await session.execute(stmt)).scalar_one_or_none()
if row is None:
user = None
else:
repo = DbUserRepository(session)
user = await repo.get_user_by_id(row.id)
if user is None:
print(f"Error: user '{email}' not found." if email else "Error: no admin user found.", file=sys.stderr)
return 1
new_password = secrets.token_urlsafe(16)
updated_user = user.model_copy(
update={
"password_hash": hash_password(new_password),
"token_version": user.token_version + 1,
"needs_setup": True,
}
)
async with app_persistence.session_factory() as session:
repo = DbUserRepository(session)
await repo.update_user(updated_user)
await session.commit()
cred_path = write_initial_credentials(user.email, new_password, label="reset")
print(f"Password reset for: {user.email}")
print(f"Credentials written to: {cred_path} (mode 0600)")
print("Next login will require setup (new email + password).")
return 0
finally:
await app_persistence.aclose()
def main() -> None:
parser = argparse.ArgumentParser(description="Reset admin password")
parser.add_argument("--email", help="Admin email (default: first admin found)")
args = parser.parse_args()
exit_code = asyncio.run(_run(args.email))
sys.exit(exit_code)
if __name__ == "__main__":
main()