rnilkyway commited on
Commit
86155c5
Β·
1 Parent(s): 40afc1d

refactor: remove Turnstile + disable local file writes (HF ephemeral runtime)

Browse files

- Remove Cloudflare Turnstile verification from /v1/keys/me endpoint
- Make dynamic providers storage ephemeral (no disk writes)
- HF Space runtime is ephemeral, local writes are lost on restart
- API keys already use GitHub Gist for persistence

.dockerignore ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HuggingFace Space Docker build - keep image lean
2
+
3
+ # Git / opencode meta
4
+ .git/
5
+ .gitignore
6
+ .dockerignore
7
+ .opencode/
8
+ thoughts/
9
+ *.md
10
+ !README.md
11
+
12
+ # Python artifacts
13
+ __pycache__/
14
+ *.pyc
15
+ *.pyo
16
+ *.pyd
17
+ *.egg-info/
18
+ .Python
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+ htmlcov/
23
+ .coverage
24
+
25
+ # Virtual envs
26
+ venv/
27
+ .venv/
28
+ env/
29
+ .env
30
+ .env.*
31
+
32
+ # Local-only runtime data (HF Space provides its own persistence)
33
+ app/data/
34
+ app/__pycache__/
35
+
36
+ # IDE / editor
37
+ .vscode/
38
+ .idea/
39
+ *.swp
40
+ *.swo
41
+ *~
42
+
43
+ # Tests & dev utilities
44
+ test_gist.py
45
+ encrypt_config.py
.opencode/config.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "agent": {
4
+ "commander": {
5
+ "model": "qwenim/qwen3.7-max"
6
+ },
7
+ "brainstormer": {
8
+ "model": "qwenim/qwen3.7-max"
9
+ },
10
+ "planner": {
11
+ "model": "qwenim/qwen3.7-max"
12
+ },
13
+ "executor": {
14
+ "model": "qwenim/qwen3.7-max"
15
+ },
16
+ "implementer": {
17
+ "model": "qwenim/qwen3.7-max"
18
+ },
19
+ "reviewer": {
20
+ "model": "qwenim/qwen3.7-max"
21
+ },
22
+ "codebase-locator": {
23
+ "model": "qwenim/qwen3.7-max"
24
+ },
25
+ "codebase-analyzer": {
26
+ "model": "qwenim/qwen3.7-max"
27
+ },
28
+ "pattern-finder": {
29
+ "model": "qwenim/qwen3.7-max"
30
+ },
31
+ "ledger-creator": {
32
+ "model": "qwenim/qwen3.7-max"
33
+ },
34
+ "artifact-searcher": {
35
+ "model": "qwenim/qwen3.7-max"
36
+ },
37
+ "mm-orchestrator": {
38
+ "model": "qwenim/qwen3.7-max"
39
+ }
40
+ }
41
+ }
app/config.py CHANGED
@@ -216,9 +216,6 @@ UPSTREAM_API_KEYS = {
216
 
217
  # ── GitHub Gist Storage ─────────────────────────────────────────────────────
218
 
219
- TURNSTILE_SITE_KEY = _decrypt("enc:v1:Q1ElsRjDMKIbb-7l:Q2ewoEx10wQFL-BXxTp8Xapb815hL5Iy1_H0Yz9zhQX6DTX2dTgiAA==")
220
- TURNSTILE_SECRET_KEY = _decrypt("enc:v1:5AVf8i4aUlDhzWIW:_yLBFEmdzSl3FRbaNI1t0FiKA7gXWNPlsBYLCBwMh6yvKCv3wGW_Uf6ArVs9wpzUqfvt")
221
-
222
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
223
  HF_DATASET = "rnilkyway/apiarium-logs"
224
 
 
216
 
217
  # ── GitHub Gist Storage ─────────────────────────────────────────────────────
218
 
 
 
 
219
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
220
  HF_DATASET = "rnilkyway/apiarium-logs"
221
 
app/index.py CHANGED
@@ -19,7 +19,6 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
19
  from config import (
20
  DEFAULT_UPSTREAM_URL, UPSTREAMS, UPSTREAM_API_KEYS,
21
  GITHUB_TOKEN, GIST_ID,
22
- TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY,
23
  HF_TOKEN, HF_DATASET,
24
  MASTER_KEY,
25
  AI_GATEWAY_URL, UPSTREAM_14448_URL,
@@ -46,7 +45,6 @@ MAX_CONTINUATIONS = 0
46
 
47
  RPM_LIMIT = 4
48
  KEYS_FILE = os.path.join(os.path.dirname(__file__), "data", "api_keys.json")
49
- PROVIDERS_FILE = os.path.join(os.path.dirname(__file__), "data", "providers.json")
50
 
51
  _rate_limits: dict = defaultdict(list)
52
 
@@ -1142,27 +1140,20 @@ class ProviderImportRequest(BaseModel):
1142
 
1143
 
1144
  def _load_providers():
1145
- """Load dynamic providers from disk and inject into UPSTREAMS."""
1146
  global _dynamic_providers
1147
- try:
1148
- if os.path.exists(PROVIDERS_FILE):
1149
- with open(PROVIDERS_FILE, "r") as f:
1150
- _dynamic_providers = json.load(f) or {}
1151
- for pid, prov in _dynamic_providers.items():
1152
- _register_provider_in_upstreams(prov)
1153
- logger.info("Loaded %d dynamic providers", len(_dynamic_providers))
1154
- except Exception as e:
1155
- logger.warning("Failed to load providers: %s", e)
1156
- _dynamic_providers = {}
1157
 
1158
 
1159
  def _save_providers():
1160
- """Persist dynamic providers to disk."""
1161
- try:
1162
- os.makedirs(os.path.dirname(PROVIDERS_FILE), exist_ok=True)
1163
- with open(PROVIDERS_FILE, "w") as f:
1164
- json.dump(_dynamic_providers, f, indent=2)
1165
- except Exception as e:
 
 
1166
  logger.warning("Failed to save providers: %s", e)
1167
 
1168
 
@@ -1436,25 +1427,7 @@ async def get_my_key(request: Request, cf_token: str = None):
1436
  detail={"error": {"message": "VPN/Proxy detected. Please turn off VPN and try again.", "type": "verification_error", "code": "vpn_blocked", "provider": vpn_result["provider"]}}
1437
  )
1438
 
1439
- # ── Step 2: Turnstile verification (optional when TURNSTILE_SECRET_KEY not configured) ──
1440
- if TURNSTILE_SECRET_KEY:
1441
- if not cf_token:
1442
- raise HTTPException(status_code=403, detail={"error": {"message": "Verification required", "type": "verification_error", "code": "missing_cf_token"}})
1443
- try:
1444
- verify_resp = await make_client().post(
1445
- "https://challenges.cloudflare.com/turnstile/v0/siteverify",
1446
- data={"response": cf_token, "secret": TURNSTILE_SECRET_KEY},
1447
- )
1448
- verify_data = verify_resp.json()
1449
- if not verify_data.get("success"):
1450
- raise HTTPException(status_code=403, detail={"error": {"message": "Verification failed", "type": "verification_error", "code": "invalid_cf_token"}})
1451
- except HTTPException:
1452
- raise
1453
- except Exception as e:
1454
- logger.warning("Turnstile verification error: %s", e)
1455
- raise HTTPException(status_code=500, detail={"error": {"message": "Verification service error", "type": "server_error", "code": "cf_verification_error"}})
1456
- else:
1457
- logger.info("Turnstile disabled (no TURNSTILE_SECRET_KEY), allowing key generation for IP %s", client_ip)
1458
 
1459
  # ── Step 3: Auto-generate key for verified user ──
1460
  new_key = generate_api_key()
 
19
  from config import (
20
  DEFAULT_UPSTREAM_URL, UPSTREAMS, UPSTREAM_API_KEYS,
21
  GITHUB_TOKEN, GIST_ID,
 
22
  HF_TOKEN, HF_DATASET,
23
  MASTER_KEY,
24
  AI_GATEWAY_URL, UPSTREAM_14448_URL,
 
45
 
46
  RPM_LIMIT = 4
47
  KEYS_FILE = os.path.join(os.path.dirname(__file__), "data", "api_keys.json")
 
48
 
49
  _rate_limits: dict = defaultdict(list)
50
 
 
1140
 
1141
 
1142
  def _load_providers():
1143
+ """Dynamic providers are ephemeral in HF Space runtime - no persistence."""
1144
  global _dynamic_providers
1145
+ _dynamic_providers = {}
 
 
 
 
 
 
 
 
 
1146
 
1147
 
1148
  def _save_providers():
1149
+ """No-op: HF Space runtime is ephemeral, local writes are lost on restart."""
1150
+ pass
1151
+ # Original code that wrote to PROVIDERS_FILE:
1152
+ # try:
1153
+ # os.makedirs(os.path.dirname(PROVIDERS_FILE), exist_ok=True)
1154
+ # with open(PROVIDERS_FILE, "w") as f:
1155
+ # json.dump(_dynamic_providers, f, indent=2)
1156
+ # except Exception as e:
1157
  logger.warning("Failed to save providers: %s", e)
1158
 
1159
 
 
1427
  detail={"error": {"message": "VPN/Proxy detected. Please turn off VPN and try again.", "type": "verification_error", "code": "vpn_blocked", "provider": vpn_result["provider"]}}
1428
  )
1429
 
1430
+ # Turnstile removed - no longer needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1431
 
1432
  # ── Step 3: Auto-generate key for verified user ──
1433
  new_key = generate_api_key()
app/templates/landing.html CHANGED
@@ -135,10 +135,7 @@
135
  </div>
136
  </div>
137
 
138
- <!-- Cloudflare Turnstile -->
139
- <div id="turnstile-container" class="hidden w-full flex justify-center py-4">
140
- <div class="cf-turnstile" data-sitekey="0x4AAAAAADTUsX1T7VmpXhDi" data-callback="onTurnstileSuccess"></div>
141
- </div>
142
 
143
  <!-- Key Display Area -->
144
  <div class="relative group cursor-pointer" onclick="copyKey()">
@@ -274,12 +271,11 @@
274
  Copied to clipboard
275
  </div>
276
 
277
- <!-- Cloudflare Turnstile -->
278
- <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
279
 
280
  <script>
281
  const myKey = { key: null, ip: null };
282
- const turnstileContainer = document.getElementById('turnstile-container');
283
  const keyBadge = document.getElementById('keyBadge');
284
  const ykStatus = document.getElementById('ykStatus');
285
  const ykHint = document.getElementById('ykHint');
@@ -315,7 +311,7 @@
315
  ykIp.textContent = data.ip;
316
  ykKey.textContent = data.key;
317
 
318
- turnstileContainer.classList.add('hidden');
319
 
320
  keyBadge.textContent = data.status === 'new' ? 'NEW' : 'AUTHORIZED';
321
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-emerald-500/30 bg-emerald-500/10 text-emerald-400";
@@ -328,7 +324,7 @@
328
  if (err.code === 'vpn_blocked') {
329
  ykIp.textContent = 'RESTRICTED';
330
  ykKey.textContent = 'β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’';
331
- turnstileContainer.classList.add('hidden');
332
 
333
  keyBadge.textContent = 'BLOCKED';
334
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-red-500/30 bg-red-500/10 text-red-400";
@@ -338,7 +334,7 @@
338
  } else {
339
  ykIp.textContent = 'Pending verification...';
340
  ykKey.textContent = 'β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’';
341
- turnstileContainer.classList.remove('hidden');
342
 
343
  keyBadge.textContent = 'CHALLENGE';
344
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-blue-500/30 bg-blue-500/10 text-blue-400";
 
135
  </div>
136
  </div>
137
 
138
+
 
 
 
139
 
140
  <!-- Key Display Area -->
141
  <div class="relative group cursor-pointer" onclick="copyKey()">
 
271
  Copied to clipboard
272
  </div>
273
 
274
+
 
275
 
276
  <script>
277
  const myKey = { key: null, ip: null };
278
+ // Turnstile removed
279
  const keyBadge = document.getElementById('keyBadge');
280
  const ykStatus = document.getElementById('ykStatus');
281
  const ykHint = document.getElementById('ykHint');
 
311
  ykIp.textContent = data.ip;
312
  ykKey.textContent = data.key;
313
 
314
+ // turnstileContainer.classList.add('hidden');
315
 
316
  keyBadge.textContent = data.status === 'new' ? 'NEW' : 'AUTHORIZED';
317
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-emerald-500/30 bg-emerald-500/10 text-emerald-400";
 
324
  if (err.code === 'vpn_blocked') {
325
  ykIp.textContent = 'RESTRICTED';
326
  ykKey.textContent = 'β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’';
327
+ // turnstileContainer.classList.add('hidden');
328
 
329
  keyBadge.textContent = 'BLOCKED';
330
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-red-500/30 bg-red-500/10 text-red-400";
 
334
  } else {
335
  ykIp.textContent = 'Pending verification...';
336
  ykKey.textContent = 'β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’';
337
+ // turnstileContainer.classList.remove('hidden');
338
 
339
  keyBadge.textContent = 'CHALLENGE';
340
  keyBadge.className = "px-2 py-1 text-[10px] font-mono uppercase tracking-wider rounded border border-blue-500/30 bg-blue-500/10 text-blue-400";