import os import json import requests from datetime import datetime # 📡 Universal Sync Hub # This module acts as a dispatcher to sync generated assets to multiple platforms. # Configuration is driven by Environment Variables. class UniversalSync: def __init__(self): self.logs = [] # 1. Notion Config self.notion_key = os.getenv("NOTION_API_KEY") self.notion_db_id = os.getenv("NOTION_DATABASE_ID") # 2. Supabase Config self.supabase_url = os.getenv("SUPABASE_URL") self.supabase_key = os.getenv("SUPABASE_KEY") # 3. Dropbox Config self.dropbox_token = os.getenv("DROPBOX_TOKEN") # 4. Cloudflare R2/KV Config self.cf_account_id = os.getenv("CLOUDFLARE_ACCOUNT_ID") self.cf_token = os.getenv("CLOUDFLARE_API_TOKEN") def log(self, msg): print(f"📡 [SYNC] {msg}") def dispatch(self, rule_data, filename): """Dispatches the rule data to all enabled services.""" results = {} # A. GitHub/HF is handled by the main factory script already. # B. Notion Sync if self.notion_key and self.notion_db_id: results['notion'] = self._sync_to_notion(rule_data) # C. Supabase Sync if self.supabase_url and self.supabase_key: results['supabase'] = self._sync_to_supabase(rule_data) # D. Dropbox Sync if self.dropbox_token: results['dropbox'] = self._sync_to_dropbox(rule_data, filename) # E. Cloudflare Sync (KV or R2) if self.cf_account_id and self.cf_token: results['cloudflare'] = self._sync_to_cloudflare(rule_data, filename) return results def _sync_to_notion(self, data): """Creates a page in Notion.""" try: url = "https://api.notion.com/v1/pages" headers = { "Authorization": f"Bearer {self.notion_key}", "Content-Type": "application/json", "Notion-Version": "2022-06-28" } title = data.get("rule_id", "Unknown Rule") category = data.get("category", "General") content = data.get("description", "")[:2000] # Limit length payload = { "parent": {"database_id": self.notion_db_id}, "properties": { "Name": {"title": [{"text": {"content": title}}]}, "Category": {"select": {"name": category}}, "Status": {"select": {"name": "Generated"}}, "Created": {"date": {"start": datetime.now().isoformat()}} }, "children": [ { "object": "block", "type": "paragraph", "paragraph": { "rich_text": [{"type": "text", "text": {"content": content}}] } }, { "object": "block", "type": "code", "code": { "language": "json", "rich_text": [{"type": "text", "text": {"content": json.dumps(data, indent=2)[:2000]}}] } } ] } res = requests.post(url, headers=headers, json=payload) if res.status_code == 200: self.log(f"Synced to Notion: {title}") return True else: self.log(f"Notion Sync Failed: {res.text}") return False except Exception as e: self.log(f"Notion Error: {e}") return False def _sync_to_supabase(self, data): """Inserts a record into Supabase 'rules' table.""" try: url = f"{self.supabase_url}/rest/v1/rules" headers = { "apikey": self.supabase_key, "Authorization": f"Bearer {self.supabase_key}", "Content-Type": "application/json", "Prefer": "return=minimal" } payload = { "rule_id": data.get("rule_id"), "category": data.get("category"), "content": json.dumps(data), "created_at": datetime.now().isoformat() } res = requests.post(url, headers=headers, json=payload) if res.status_code in [200, 201]: self.log(f"Synced to Supabase: {data.get('rule_id')}") return True return False except Exception as e: self.log(f"Supabase Error: {e}") return False def _sync_to_gdrive_idx(self, source_file, target_path): """ Syncs to Google Drive. Optimized for Google IDX / Colab environments. Assumes Drive is mounted at /content/drive or accessible via gcloud. """ drive_mount = "/content/drive/My Drive" # Check if we are in an environment where Drive is mounted if os.path.exists(drive_mount): try: dest = os.path.join(drive_mount, "Gemini_Factory_Rules", target_path) os.makedirs(os.path.dirname(dest), exist_ok=True) with open(source_file, 'rb') as fsrc: with open(dest, 'wb') as fdst: fdst.write(fsrc.read()) self.log(f"Synced to GDrive (IDX/Colab): {target_path}") return True except Exception as e: self.log(f"GDrive Sync Error: {e}") return False else: # Fallback to simple log if not mounted (IDX might need specific setup) self.log(f"GDrive: Mount point not found. IDX setup required for Direct Link.") return False def _sync_to_dropbox(self, data, filename): """Uploads file to Dropbox.""" # Implementation placeholder - requires 'dropbox' via requests content-upload self.log(f"Dropbox Sync Ready (Config Found) for {filename}") return True def _sync_to_cloudflare(self, data, filename): """Uploads to Cloudflare KV or R2.""" # Implementation placeholder self.log(f"Cloudflare Sync Ready (Config Found) for {filename}") return True