feat: trace-level matching, health watcher/worker status, timezone config
This commit is contained in:
117
scripts/sync_users_from_sftpgo.py
Normal file
117
scripts/sync_users_from_sftpgo.py
Normal file
@@ -0,0 +1,117 @@
|
||||
#!/opt/homebrew/bin/python3.11
|
||||
"""
|
||||
Sync users from SFTPGo to Momentry users table.
|
||||
|
||||
Usage:
|
||||
python3 scripts/sync_users_from_sftpgo.py
|
||||
python3 scripts/sync_users_from_sftpgo.py --sftpgo-url http://localhost:8080
|
||||
python3 scripts/sync_users_from_sftpgo.py --db "dbname=momentry user=accusys"
|
||||
|
||||
Environment:
|
||||
SFTPGO_BASE_URL Default: http://localhost:8080
|
||||
DATABASE_URL Default: dbname=momentry user=accusys host=localhost
|
||||
|
||||
This script does NOT copy passwords. It creates user records with placeholder
|
||||
password hashes. The real password will be captured on the user's first
|
||||
login through Momentry (which verifies against SFTPGo and caches the hash).
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
import requests
|
||||
|
||||
|
||||
def get_sftpgo_users(sftpgo_url: str, admin_user: str, admin_pass: str) -> list[dict[str, Any]]:
|
||||
"""Get all users from SFTPGo."""
|
||||
# Get admin token (SFTPGo uses GET, not POST)
|
||||
token_url = f"{sftpgo_url}/api/v2/token"
|
||||
resp = requests.get(token_url, auth=(admin_user, admin_pass), timeout=10)
|
||||
resp.raise_for_status()
|
||||
token = resp.json().get("access_token")
|
||||
if not token:
|
||||
print("ERROR: Failed to get SFTPGo admin token", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# List users
|
||||
users_url = f"{sftpgo_url}/api/v2/users"
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
resp = requests.get(users_url, headers=headers, timeout=10)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Sync SFTPGo users to Momentry")
|
||||
parser.add_argument("--sftpgo-url", default=os.getenv("SFTPGO_BASE_URL", "http://localhost:8080"))
|
||||
parser.add_argument("--db", default=os.getenv("DATABASE_URL", "dbname=momentry user=accusys host=localhost"))
|
||||
parser.add_argument("--admin-user", default="admin")
|
||||
parser.add_argument("--admin-pass", default=os.getenv("SFTPGO_ADMIN_PASSWORD", "Test3200Test3200"))
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print what would be done without executing")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Fetch users from SFTPGo
|
||||
print(f"[SFTPGo] Connecting to {args.sftpgo_url}...")
|
||||
try:
|
||||
sftpgo_users = get_sftpgo_users(args.sftpgo_url, args.admin_user, args.admin_pass)
|
||||
except Exception as e:
|
||||
print(f"ERROR: Failed to fetch SFTPGo users: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[SFTPGo] Found {len(sftpgo_users)} users")
|
||||
|
||||
# Connect to Momentry DB and set schema
|
||||
conn = psycopg2.connect(args.db)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SET search_path TO dev")
|
||||
|
||||
synced = 0
|
||||
skipped = 0
|
||||
|
||||
for user in sftpgo_users:
|
||||
username = user.get("username")
|
||||
status = user.get("status", 0)
|
||||
|
||||
if not username or status != 1:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
role = "admin" if username == "admin" else "user"
|
||||
# Placeholder hash — will be updated on first login via SFTPGo fallback
|
||||
placeholder_hash = "$placeholder$synced_from_sftpgo"
|
||||
|
||||
if args.dry_run:
|
||||
print(f" Would insert: {username} (role={role})")
|
||||
synced += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
cur.execute(
|
||||
"INSERT INTO users (username, password_hash, role) VALUES (%s, %s, %s) "
|
||||
"ON CONFLICT (username) DO NOTHING",
|
||||
(username, placeholder_hash, role),
|
||||
)
|
||||
if cur.rowcount > 0:
|
||||
print(f" ✅ {username} (role={role})")
|
||||
synced += 1
|
||||
else:
|
||||
print(f" ⏭️ {username} already exists, skipped")
|
||||
skipped += 1
|
||||
except Exception as e:
|
||||
print(f" ❌ {username}: {e}", file=sys.stderr)
|
||||
skipped += 1
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
print(f"\nDone: {synced} synced, {skipped} skipped/errors")
|
||||
print("Note: Password hashes are placeholders. First login via Momentry will cache the real hash.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user