#!/usr/bin/env python3 """ Admin CLI for managing D&D SRD API keys. Usage: python scripts/manage_keys.py list python scripts/manage_keys.py create [--rate-limit N] python scripts/manage_keys.py revoke """ import argparse import json import os import secrets import sys from pathlib import Path API_KEYS_FILE = Path(__file__).resolve().parent.parent / "data" / "api_keys.json" def load_keys() -> dict: if API_KEYS_FILE.exists(): with open(API_KEYS_FILE) as f: return json.load(f) return {"keys": {}} def save_keys(keys: dict): API_KEYS_FILE.parent.mkdir(parents=True, exist_ok=True) with open(API_KEYS_FILE, "w") as f: json.dump(keys, f, indent=2) def cmd_list(args): keys = load_keys() if not keys["keys"]: print("No API keys configured.") return print(f"{'Name':<20} {'Key':<40} {'Created':<20}") print("-" * 80) for key, info in keys["keys"].items(): print(f"{info.get('name', 'unknown'):<20} {key:<40} {info.get('created', ''):<20}") def cmd_create(args): keys = load_keys() new_key = "dnd_" + secrets.token_hex(16) keys["keys"][new_key] = { "name": args.name, "created": os.popen("date -Iseconds").read().strip(), "rate_limit": args.rate_limit or 60, } save_keys(keys) print(f"✅ Created API key for '{args.name}':") print(f" Key: {new_key}") print(f" Rate limit: {args.rate_limit or 60} req/min") def cmd_revoke(args): keys = load_keys() if args.key in keys["keys"]: info = keys["keys"].pop(args.key) save_keys(keys) print(f"✅ Revoked key for '{info['name']}' ({args.key[:20]}...)") else: print(f"❌ Key not found: {args.key[:20]}...") def main(): parser = argparse.ArgumentParser(description="D&D SRD API Key Manager") sub = parser.add_subparsers(dest="command") p_list = sub.add_parser("list", help="List all API keys") p_create = sub.add_parser("create", help="Create a new API key") p_create.add_argument("name", help="Name for the key (e.g., 'jez-personal')") p_create.add_argument("--rate-limit", type=int, default=60, help="Requests per minute limit") p_revoke = sub.add_parser("revoke", help="Revoke an API key") p_revoke.add_argument("key", help="Full API key to revoke") args = parser.parse_args() if args.command == "list": cmd_list(args) elif args.command == "create": cmd_create(args) elif args.command == "revoke": cmd_revoke(args) else: parser.print_help() if __name__ == "__main__": main()