a2ae74d582
- Backend: auth refresh reuse, schedule/retry/sync/agent/files fixes - Frontend: push WS, files ETag browse, vite build assets - Docs: audit/changelog, production deploy gate, bug scan registry B7-77 - Deploy: deploy-production.sh, prune assets, gate logs
76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Create initial admin on empty local DB (127.0.0.1 only).
|
|
|
|
Usage:
|
|
NEXUS_TEST_ADMIN_PASSWORD='your-pass' python3 scripts/seed_local_admin.py
|
|
# or set NEXUS_TEST_ADMIN_PASSWORD in .env first
|
|
|
|
Refuses non-local hosts and skips if any admin already exists.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import bcrypt
|
|
from sqlalchemy import text
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from server.config import settings
|
|
from server.infrastructure.database.session import init_db, AsyncSessionLocal
|
|
|
|
|
|
def _local_db_only() -> None:
|
|
url = settings.DATABASE_URL
|
|
if "127.0.0.1" not in url and "localhost" not in url:
|
|
raise SystemExit(f"Refusing seed on non-local DATABASE_URL: {url!r}")
|
|
|
|
|
|
def _password() -> str:
|
|
pwd = os.environ.get("NEXUS_TEST_ADMIN_PASSWORD", "").strip()
|
|
if not pwd:
|
|
raise SystemExit(
|
|
"Set NEXUS_TEST_ADMIN_PASSWORD in .env or environment (min 6 chars)."
|
|
)
|
|
if len(pwd) < 6:
|
|
raise SystemExit("Password must be at least 6 characters.")
|
|
return pwd
|
|
|
|
|
|
async def main() -> int:
|
|
_local_db_only()
|
|
username = os.environ.get("NEXUS_TEST_ADMIN_USER", "admin").strip() or "admin"
|
|
password = _password()
|
|
|
|
await init_db()
|
|
async with AsyncSessionLocal() as session:
|
|
count = (await session.execute(text("SELECT COUNT(*) FROM admins"))).scalar() or 0
|
|
if count > 0:
|
|
print(f"SKIP: {count} admin(s) already exist")
|
|
return 0
|
|
|
|
password_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode(
|
|
"utf-8"
|
|
)
|
|
await session.execute(
|
|
text(
|
|
"INSERT INTO admins (username, password_hash, email, is_active, totp_enabled) "
|
|
"VALUES (:u, :h, NULL, 1, 0)"
|
|
),
|
|
{"u": username, "h": password_hash},
|
|
)
|
|
await session.commit()
|
|
|
|
print(f"OK: created admin '{username}' on local database")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(asyncio.run(main()))
|