|
|
import os |
|
|
import json |
|
|
import requests |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UniversalSync: |
|
|
def __init__(self): |
|
|
self.logs = [] |
|
|
|
|
|
|
|
|
self.notion_key = os.getenv("NOTION_API_KEY") |
|
|
self.notion_db_id = os.getenv("NOTION_DATABASE_ID") |
|
|
|
|
|
|
|
|
self.supabase_url = os.getenv("SUPABASE_URL") |
|
|
self.supabase_key = os.getenv("SUPABASE_KEY") |
|
|
|
|
|
|
|
|
self.dropbox_token = os.getenv("DROPBOX_TOKEN") |
|
|
|
|
|
|
|
|
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 = {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.notion_key and self.notion_db_id: |
|
|
results['notion'] = self._sync_to_notion(rule_data) |
|
|
|
|
|
|
|
|
if self.supabase_url and self.supabase_key: |
|
|
results['supabase'] = self._sync_to_supabase(rule_data) |
|
|
|
|
|
|
|
|
if self.dropbox_token: |
|
|
results['dropbox'] = self._sync_to_dropbox(rule_data, filename) |
|
|
|
|
|
|
|
|
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] |
|
|
|
|
|
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" |
|
|
|
|
|
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: |
|
|
|
|
|
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.""" |
|
|
|
|
|
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.""" |
|
|
|
|
|
self.log(f"Cloudflare Sync Ready (Config Found) for {filename}") |
|
|
return True |
|
|
|
|
|
|