201 lines
7.4 KiB
Python
Executable File
201 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Simple API Key Manager - Consolidate keys without encryption
|
|
Quick solution to organize scattered API keys
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
from datetime import datetime
|
|
import argparse
|
|
from collections import defaultdict
|
|
|
|
class SimpleKeyManager:
|
|
"""Simple key management without encryption"""
|
|
|
|
def __init__(self):
|
|
self.workspace_root = Path(__file__).parent.parent.parent.parent
|
|
self.keys_file = self.workspace_root / "config" / "consolidated_keys.json"
|
|
self.keys_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
def scan_all_projects(self) -> Dict[str, Dict[str, str]]:
|
|
"""Scan all projects for .env files"""
|
|
projects = {
|
|
"root": self.workspace_root,
|
|
"trax": self.workspace_root / "apps" / "trax",
|
|
"youtube-summarizer": self.workspace_root / "apps" / "youtube-summarizer",
|
|
"pdf-translator": self.workspace_root / "pdf-translator",
|
|
"directus-mcp": self.workspace_root / "tools" / "directus-mcp-server",
|
|
}
|
|
|
|
all_keys = defaultdict(dict)
|
|
|
|
for project_name, project_path in projects.items():
|
|
env_file = project_path / ".env"
|
|
if env_file.exists():
|
|
with open(env_file, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and not line.startswith('#'):
|
|
if '=' in line:
|
|
key, value = line.split('=', 1)
|
|
key = key.strip()
|
|
value = value.strip().strip('"').strip("'")
|
|
all_keys[key][project_name] = value
|
|
|
|
return dict(all_keys)
|
|
|
|
def consolidate(self):
|
|
"""Consolidate all keys and save to JSON"""
|
|
keys = self.scan_all_projects()
|
|
|
|
# Organize by category
|
|
categorized = {
|
|
"ai": {},
|
|
"services": {},
|
|
"database": {},
|
|
"settings": {},
|
|
"custom": {}
|
|
}
|
|
|
|
ai_prefixes = ["ANTHROPIC", "DEEPSEEK", "OPENAI", "PERPLEXITY", "OPENROUTER",
|
|
"GOOGLE_API", "XAI", "MISTRAL", "QWEN", "DASHSCOPE", "MODEL_STUDIO"]
|
|
service_prefixes = ["SLACK", "GITHUB", "GITEA", "YOUTUBE", "DIRECTUS", "MICROSOFT"]
|
|
db_prefixes = ["DATABASE", "REDIS", "POSTGRES"]
|
|
|
|
for key_name, values in keys.items():
|
|
# Pick the most common value or the one from root
|
|
if "root" in values:
|
|
final_value = values["root"]
|
|
else:
|
|
# Use the most frequent value
|
|
final_value = max(values.values(), key=lambda x: list(values.values()).count(x))
|
|
|
|
# Categorize
|
|
if any(key_name.startswith(prefix) for prefix in ai_prefixes):
|
|
categorized["ai"][key_name] = final_value
|
|
elif any(key_name.startswith(prefix) for prefix in service_prefixes):
|
|
categorized["services"][key_name] = final_value
|
|
elif any(key_name.startswith(prefix) for prefix in db_prefixes):
|
|
categorized["database"][key_name] = final_value
|
|
elif any(keyword in key_name for keyword in ["JWT", "SECRET", "TOKEN", "KEY"]):
|
|
categorized["settings"][key_name] = final_value
|
|
else:
|
|
categorized["custom"][key_name] = final_value
|
|
|
|
# Save to JSON
|
|
output = {
|
|
"consolidated_at": datetime.now().isoformat(),
|
|
"total_keys": sum(len(cat) for cat in categorized.values()),
|
|
"keys": categorized
|
|
}
|
|
|
|
with open(self.keys_file, 'w') as f:
|
|
json.dump(output, f, indent=2)
|
|
|
|
return output
|
|
|
|
def export_to_env(self, output_file: Path, filter_category: Optional[str] = None):
|
|
"""Export consolidated keys to .env format"""
|
|
if not self.keys_file.exists():
|
|
print("❌ No consolidated keys found. Run consolidate first.")
|
|
return
|
|
|
|
with open(self.keys_file, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
with open(output_file, 'w') as f:
|
|
f.write(f"# Consolidated API Keys\n")
|
|
f.write(f"# Generated: {datetime.now().isoformat()}\n\n")
|
|
|
|
for category, keys in data["keys"].items():
|
|
if filter_category and category != filter_category:
|
|
continue
|
|
|
|
f.write(f"# {category.upper()} KEYS\n")
|
|
for key_name, value in sorted(keys.items()):
|
|
f.write(f"{key_name}={value}\n")
|
|
f.write("\n")
|
|
|
|
print(f"✅ Exported to {output_file}")
|
|
|
|
def report(self):
|
|
"""Generate a summary report"""
|
|
keys = self.scan_all_projects()
|
|
|
|
print("\n" + "=" * 60)
|
|
print("API KEY CONSOLIDATION REPORT")
|
|
print("=" * 60)
|
|
|
|
# Count keys by project
|
|
project_counts = defaultdict(int)
|
|
for key_name, values in keys.items():
|
|
for project in values.keys():
|
|
project_counts[project] += 1
|
|
|
|
print("\nKeys per project:")
|
|
for project, count in sorted(project_counts.items()):
|
|
print(f" • {project}: {count} keys")
|
|
|
|
# Find conflicts
|
|
conflicts = []
|
|
for key_name, values in keys.items():
|
|
unique_values = set(values.values())
|
|
if len(unique_values) > 1:
|
|
conflicts.append(key_name)
|
|
|
|
if conflicts:
|
|
print(f"\n⚠️ {len(conflicts)} keys with conflicting values:")
|
|
for key in conflicts[:10]: # Show first 10
|
|
print(f" • {key}")
|
|
|
|
print(f"\nTotal unique keys: {len(keys)}")
|
|
print("=" * 60)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Simple API Key Manager")
|
|
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
|
|
|
# Consolidate command
|
|
cons_parser = subparsers.add_parser('consolidate', help='Consolidate all keys')
|
|
|
|
# Export command
|
|
export_parser = subparsers.add_parser('export', help='Export to .env')
|
|
export_parser.add_argument('file', help='Output file path')
|
|
export_parser.add_argument('--category', help='Filter by category')
|
|
|
|
# Report command
|
|
report_parser = subparsers.add_parser('report', help='Show summary report')
|
|
|
|
args = parser.parse_args()
|
|
|
|
manager = SimpleKeyManager()
|
|
|
|
if args.command == 'consolidate':
|
|
result = manager.consolidate()
|
|
print(f"✅ Consolidated {result['total_keys']} keys to {manager.keys_file}")
|
|
print("\nKeys by category:")
|
|
for cat, keys in result["keys"].items():
|
|
print(f" • {cat}: {len(keys)} keys")
|
|
|
|
elif args.command == 'export':
|
|
manager.export_to_env(Path(args.file), args.category)
|
|
|
|
elif args.command == 'report':
|
|
manager.report()
|
|
|
|
else:
|
|
# Default: consolidate and report
|
|
manager.report()
|
|
print("\nConsolidating keys...")
|
|
result = manager.consolidate()
|
|
print(f"\n✅ Saved to: {manager.keys_file}")
|
|
print("\nNext steps:")
|
|
print(f"1. Review: cat {manager.keys_file}")
|
|
print(f"2. Export: python3 {__file__} export .env")
|
|
print(f"3. Copy to projects as needed")
|
|
|
|
if __name__ == "__main__":
|
|
main() |