Spaces:
Runtime error
Runtime error
Download scripts/create_admin.py from rahim237cm/jurix-api: direct link, hf CLI and curl.
- Browser
- Download file 6.03 kB
-
https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/scripts/create_admin.py
- Command line
-
hf download hf://spaces/rahim237cm/jurix-api/scripts/create_admin.py
-
curl -L -o create_admin.py https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/scripts/create_admin.py
6.03 kB
| """ | |
| Crée ou met à jour le compte administrateur initial. | |
| Aucun utilisateur n'était créé nulle part dans le projet : le modèle `User` et la | |
| table `users` existaient depuis la migration 0b21fb6a3651 sans qu'aucun code ne | |
| les alimente. Une fois l'authentification branchée, sans ce script il devient | |
| impossible de se connecter à sa propre instance. | |
| Idempotent : relancé, il ne duplique rien et ne modifie que ce qui doit l'être. | |
| Usage: | |
| python scripts/create_admin.py | |
| python scripts/create_admin.py --email admin@jurix.cm --username admin | |
| python scripts/create_admin.py --reset-password | |
| python scripts/create_admin.py --role superadmin | |
| python scripts/create_admin.py --promouvoir # compte ordinaire deja inscrit | |
| Les valeurs par défaut viennent de .env : ADMIN_EMAIL, ADMIN_USERNAME, | |
| ADMIN_PASSWORD. Si aucun mot de passe n'est fourni ni par option ni par | |
| l'environnement, il est demandé de façon interactive — jamais de valeur par | |
| défaut en dur. | |
| """ | |
| import argparse | |
| import asyncio | |
| import getpass | |
| import os | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from dotenv import load_dotenv | |
| from pydantic import ValidationError | |
| from sqlalchemy import select | |
| load_dotenv() | |
| from app.core.auth import hash_password | |
| from app.core.database import AsyncSessionLocal | |
| from app.models.user import User | |
| from app.schemas.user import UserCreate | |
| def parse_args() -> argparse.Namespace: | |
| p = argparse.ArgumentParser(description="Crée ou met à jour un administrateur JuriX") | |
| p.add_argument("--email", default=os.getenv("ADMIN_EMAIL", "admin@jurix.cm")) | |
| p.add_argument("--username", default=os.getenv("ADMIN_USERNAME", "admin")) | |
| p.add_argument("--password", default=os.getenv("ADMIN_PASSWORD")) | |
| p.add_argument( | |
| "--role", | |
| default=os.getenv("ADMIN_ROLE", "superadmin"), | |
| choices=["admin", "superadmin"], | |
| ) | |
| p.add_argument( | |
| "--reset-password", | |
| action="store_true", | |
| help="Réinitialise le mot de passe d'un compte existant", | |
| ) | |
| p.add_argument( | |
| "--promouvoir", | |
| action="store_true", | |
| help="Promeut un compte ORDINAIRE existant ; son mot de passe est alors remplacé", | |
| ) | |
| return p.parse_args() | |
| async def main() -> int: | |
| args = parse_args() | |
| password = args.password | |
| if not password: | |
| password = getpass.getpass(f"Mot de passe pour {args.email} : ") | |
| if not password: | |
| print("❌ Aucun mot de passe fourni.", file=sys.stderr) | |
| return 1 | |
| # On valide via UserCreate pour que le script et l'API appliquent | |
| # exactement la même politique de mot de passe. | |
| try: | |
| UserCreate( | |
| email=args.email, | |
| username=args.username, | |
| password=password, | |
| role=args.role, | |
| ) | |
| except ValidationError as e: | |
| # Jamais str(e) : pydantic y recopie la valeur refusee, donc le mot de | |
| # passe, en clair dans le terminal et dans la sortie du deploiement. | |
| for erreur in e.errors(include_input=False, include_url=False): | |
| champ = ".".join(str(partie) for partie in erreur["loc"]) | |
| print(f"❌ Données invalides, {champ} : {erreur['msg']}", file=sys.stderr) | |
| return 1 | |
| try: | |
| async with AsyncSessionLocal() as db: | |
| existing = ( | |
| await db.execute(select(User).where(User.email == args.email.lower())) | |
| ).scalar_one_or_none() | |
| if existing is None: | |
| db.add( | |
| User( | |
| email=args.email.lower(), | |
| username=args.username.lower(), | |
| hashed_password=hash_password(password), | |
| role=args.role, | |
| is_active=True, | |
| is_verified=True, | |
| ) | |
| ) | |
| await db.commit() | |
| print(f"✅ Compte créé : {args.email} (rôle {args.role})") | |
| return 0 | |
| # Un compte ordinaire deja inscrit a cette adresse n'est jamais promu | |
| # en silence. Le site est public avant la creation de | |
| # l'administrateur (demarrer.sh, etape 6) : quiconque s'inscrivait | |
| # sous ADMIN_EMAIL devenait superadmin, avec SON mot de passe. | |
| ordinaire = existing.role not in ("admin", "superadmin") | |
| if ordinaire and not args.promouvoir: | |
| print( | |
| f"❌ {args.email} est déjà un compte ordinaire (rôle {existing.role}) : " | |
| "il n'est pas promu sans confirmation. S'il est bien à vous, relancez avec " | |
| "--promouvoir (son mot de passe sera remplacé) ; sinon, choisissez un autre " | |
| "ADMIN_EMAIL.", | |
| file=sys.stderr, | |
| ) | |
| return 1 | |
| changed = [] | |
| if existing.role != args.role: | |
| existing.role = args.role | |
| changed.append("rôle") | |
| if not existing.is_active: | |
| existing.is_active = True | |
| changed.append("réactivation") | |
| if args.reset_password or ordinaire: | |
| existing.hashed_password = hash_password(password) | |
| changed.append("mot de passe") | |
| if changed: | |
| await db.commit() | |
| print(f"✅ Compte mis à jour : {args.email} ({', '.join(changed)})") | |
| else: | |
| print( | |
| f"ℹ️ Compte déjà conforme : {args.email} (rôle {existing.role}). " | |
| "Utilisez --reset-password pour changer le mot de passe." | |
| ) | |
| return 0 | |
| except Exception as e: | |
| print(f"❌ Base de données inaccessible : {e}", file=sys.stderr) | |
| print(" Vérifiez DATABASE_URL dans .env et que les migrations sont appliquées.", | |
| file=sys.stderr) | |
| return 1 | |
| if __name__ == "__main__": | |
| sys.exit(asyncio.run(main())) | |