Spaces:
Running
Running
Hermes Agent commited on
Commit ·
21859b2
1
Parent(s): ee68a9d
feat(oauth): add OAuth paste-credentials, keys capture, and direct SQLite vault sync
Browse files- gateway/credentials_sync.py +49 -3
- gateway/omniroute.py +18 -5
- health_doctor.py +5 -0
gateway/credentials_sync.py
CHANGED
|
@@ -122,8 +122,54 @@ def sync_secret_to_huggingface(secret_name: str, secret_val: str):
|
|
| 122 |
|
| 123 |
def handle_captured_credential(provider_hint: str, api_key: str):
|
| 124 |
"""High-level dispatch to save locally and attempt HF cloud sync."""
|
| 125 |
-
if not api_key
|
| 126 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
env_name = normalize_provider_name(provider_hint)
|
| 128 |
-
save_to_local_vault(env_name,
|
| 129 |
-
sync_secret_to_huggingface(env_name,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
def handle_captured_credential(provider_hint: str, api_key: str):
|
| 124 |
"""High-level dispatch to save locally and attempt HF cloud sync."""
|
| 125 |
+
if not api_key:
|
| 126 |
return
|
| 127 |
+
api_key_str = str(api_key).strip()
|
| 128 |
+
if len(api_key_str) < 5:
|
| 129 |
+
return
|
| 130 |
+
|
| 131 |
+
# Check if this is a structured JSON string (like Antigravity OAuth object)
|
| 132 |
+
if api_key_str.startswith("{") and api_key_str.endswith("}"):
|
| 133 |
+
try:
|
| 134 |
+
parsed = json.loads(api_key_str)
|
| 135 |
+
if isinstance(parsed, dict):
|
| 136 |
+
# Extract refresh token or access token if available
|
| 137 |
+
rt = parsed.get("refresh_token") or parsed.get("token") or parsed.get("access_token")
|
| 138 |
+
if rt and isinstance(rt, str):
|
| 139 |
+
env_name = normalize_provider_name(provider_hint)
|
| 140 |
+
save_to_local_vault(env_name, rt)
|
| 141 |
+
sync_secret_to_huggingface(env_name, rt)
|
| 142 |
+
except Exception:
|
| 143 |
+
pass
|
| 144 |
+
|
| 145 |
env_name = normalize_provider_name(provider_hint)
|
| 146 |
+
save_to_local_vault(env_name, api_key_str)
|
| 147 |
+
sync_secret_to_huggingface(env_name, api_key_str)
|
| 148 |
+
|
| 149 |
+
def sync_sqlite_credentials_to_vault(db_path="/root/.omniroute/storage.sqlite"):
|
| 150 |
+
"""Scans OmniRoute SQLite database for any newly configured providers and syncs them."""
|
| 151 |
+
import sqlite3
|
| 152 |
+
if not os.path.exists(db_path):
|
| 153 |
+
return
|
| 154 |
+
try:
|
| 155 |
+
conn = sqlite3.connect(db_path)
|
| 156 |
+
cursor = conn.cursor()
|
| 157 |
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
| 158 |
+
tables = [t[0] for t in cursor.fetchall()]
|
| 159 |
+
|
| 160 |
+
# Check connections table
|
| 161 |
+
for tbl in ["connections", "provider_connections", "keys"]:
|
| 162 |
+
if tbl in tables:
|
| 163 |
+
cursor.execute(f"SELECT * FROM {tbl}")
|
| 164 |
+
rows = cursor.fetchall()
|
| 165 |
+
col_names = [d[0].lower() for d in cursor.description]
|
| 166 |
+
for r in rows:
|
| 167 |
+
row_dict = dict(zip(col_names, r))
|
| 168 |
+
provider = str(row_dict.get("provider_id") or row_dict.get("provider") or row_dict.get("name") or "")
|
| 169 |
+
key_val = str(row_dict.get("api_key") or row_dict.get("key") or row_dict.get("token") or row_dict.get("credentials") or "")
|
| 170 |
+
if provider and key_val and len(key_val) > 5 and not key_val.startswith("enc:"):
|
| 171 |
+
handle_captured_credential(provider, key_val)
|
| 172 |
+
conn.close()
|
| 173 |
+
except Exception as e:
|
| 174 |
+
logger.debug(f"[VAULT] SQLite scan skipped: {e}")
|
| 175 |
+
|
gateway/omniroute.py
CHANGED
|
@@ -146,7 +146,7 @@ async def omniroute_dashboard(request: Request, path: str = ""):
|
|
| 146 |
|
| 147 |
from gateway.credentials_sync import handle_captured_credential
|
| 148 |
|
| 149 |
-
async def try_capture_credentials(request: Request):
|
| 150 |
"""Inspects write payloads to automatically capture and sync API keys."""
|
| 151 |
if request.method in ("POST", "PUT", "PATCH"):
|
| 152 |
try:
|
|
@@ -154,8 +154,13 @@ async def try_capture_credentials(request: Request):
|
|
| 154 |
if body:
|
| 155 |
data = json.loads(body.decode("utf-8", "ignore"))
|
| 156 |
if isinstance(data, dict):
|
| 157 |
-
api_key =
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
if api_key and isinstance(api_key, str) and len(api_key.strip()) > 5:
|
| 160 |
handle_captured_credential(str(provider_hint), api_key.strip())
|
| 161 |
except Exception:
|
|
@@ -165,15 +170,22 @@ async def try_capture_credentials(request: Request):
|
|
| 165 |
@router.api_route("/api/connections/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 166 |
@router.api_route("/api/connection/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 167 |
async def omniroute_connections(request: Request, path: str = ""):
|
| 168 |
-
await try_capture_credentials(request)
|
| 169 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections"
|
| 170 |
return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
@router.api_route("/api/providers", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 173 |
@router.api_route("/api/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 174 |
@router.api_route("/api/v1/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 175 |
async def omniroute_providers(request: Request, path: str = ""):
|
| 176 |
-
await try_capture_credentials(request)
|
| 177 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers"
|
| 178 |
try:
|
| 179 |
res = await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
|
@@ -217,6 +229,7 @@ async def omniroute_auth_login(request: Request, path: str = "login"):
|
|
| 217 |
@router.api_route("/api/oauth", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 218 |
@router.api_route("/api/oauth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 219 |
async def omniroute_oauth(request: Request, path: str = ""):
|
|
|
|
| 220 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth"
|
| 221 |
return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
| 222 |
|
|
|
|
| 146 |
|
| 147 |
from gateway.credentials_sync import handle_captured_credential
|
| 148 |
|
| 149 |
+
async def try_capture_credentials(request: Request, path_hint: str = ""):
|
| 150 |
"""Inspects write payloads to automatically capture and sync API keys."""
|
| 151 |
if request.method in ("POST", "PUT", "PATCH"):
|
| 152 |
try:
|
|
|
|
| 154 |
if body:
|
| 155 |
data = json.loads(body.decode("utf-8", "ignore"))
|
| 156 |
if isinstance(data, dict):
|
| 157 |
+
api_key = (
|
| 158 |
+
data.get("apiKey") or data.get("api_key") or
|
| 159 |
+
data.get("key") or data.get("token") or
|
| 160 |
+
data.get("credentials") or data.get("refreshToken") or
|
| 161 |
+
data.get("code") or data.get("raw")
|
| 162 |
+
)
|
| 163 |
+
provider_hint = data.get("provider") or data.get("providerId") or data.get("name") or data.get("id") or path_hint or ""
|
| 164 |
if api_key and isinstance(api_key, str) and len(api_key.strip()) > 5:
|
| 165 |
handle_captured_credential(str(provider_hint), api_key.strip())
|
| 166 |
except Exception:
|
|
|
|
| 170 |
@router.api_route("/api/connections/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 171 |
@router.api_route("/api/connection/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 172 |
async def omniroute_connections(request: Request, path: str = ""):
|
| 173 |
+
await try_capture_credentials(request, path_hint=path)
|
| 174 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections"
|
| 175 |
return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
| 176 |
|
| 177 |
+
@router.api_route("/api/keys", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 178 |
+
@router.api_route("/api/keys/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 179 |
+
async def omniroute_keys(request: Request, path: str = ""):
|
| 180 |
+
await try_capture_credentials(request, path_hint=path)
|
| 181 |
+
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/keys/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/keys"
|
| 182 |
+
return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
| 183 |
+
|
| 184 |
@router.api_route("/api/providers", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 185 |
@router.api_route("/api/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 186 |
@router.api_route("/api/v1/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 187 |
async def omniroute_providers(request: Request, path: str = ""):
|
| 188 |
+
await try_capture_credentials(request, path_hint=path)
|
| 189 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers"
|
| 190 |
try:
|
| 191 |
res = await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
|
|
|
| 229 |
@router.api_route("/api/oauth", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 230 |
@router.api_route("/api/oauth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
|
| 231 |
async def omniroute_oauth(request: Request, path: str = ""):
|
| 232 |
+
await try_capture_credentials(request, path_hint=path)
|
| 233 |
target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth"
|
| 234 |
return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
|
| 235 |
|
health_doctor.py
CHANGED
|
@@ -101,6 +101,11 @@ def run_health_check_cycle():
|
|
| 101 |
|
| 102 |
check_disk_space()
|
| 103 |
purge_old_backups()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
|
| 106 |
def main():
|
|
|
|
| 101 |
|
| 102 |
check_disk_space()
|
| 103 |
purge_old_backups()
|
| 104 |
+
try:
|
| 105 |
+
from gateway.credentials_sync import sync_sqlite_credentials_to_vault
|
| 106 |
+
sync_sqlite_credentials_to_vault("/root/.omniroute/storage.sqlite")
|
| 107 |
+
except Exception:
|
| 108 |
+
pass
|
| 109 |
|
| 110 |
|
| 111 |
def main():
|