Bl4ckSpaces commited on
Commit
c96411c
·
verified ·
1 Parent(s): 2d70905

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -59
app.py CHANGED
@@ -2,39 +2,56 @@ import os
2
  import requests
3
  import random
4
  import base64
 
5
  from datetime import datetime
6
  from fastapi import FastAPI, HTTPException
7
- from fastapi.middleware.cors import CORSMiddleware # <--- PENTING: Import ini
8
  from pydantic import BaseModel
9
  from typing import Optional
10
  from gradio_client import Client
 
11
 
12
  app = FastAPI()
13
 
14
  # ==========================================
15
- # 0. SETUP CORS (AGAR PERCHANCE BISA AKSES)
16
  # ==========================================
17
  app.add_middleware(
18
  CORSMiddleware,
19
- allow_origins=["*"], # Mengizinkan akses dari semua domain (termasuk Perchance)
20
  allow_credentials=True,
21
- allow_methods=["*"], # Mengizinkan semua method (GET, POST, dll)
22
- allow_headers=["*"], # Mengizinkan semua header
23
  )
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # ==========================================
26
- # 1. KONFIGURASI DAN TOKEN (ANTI-DETEKSI)
27
  # ==========================================
28
-
29
- # Database Config (JSONBin)
30
  JSONBIN_ID = "6962ebfe43b1c97be927b07d"
31
  JSONBIN_KEY = "$2a$10$khoKkhYEAUCG.O0xHDHiY.Ei88KlSV1l3olI2pxc86mUSsTaNpJx6"
32
  JSONBIN_URL = f"https://api.jsonbin.io/v3/b/{JSONBIN_ID}"
33
-
34
- # Target Engine (Flux.2 Dev Space)
35
  ENGINE_URL = "https://black-forest-labs-flux-2-dev.hf.space"
36
 
37
- # TOKEN OBFUSCATION (Agar tidak terdeteksi scanner HF)
38
  TOKEN_PREFIX = "hf_"
39
  TOKEN_BODIES = [
40
  "PiRCDDtPcPFMLWkTkVaZmzoleHOunXnLIA", "BHvZXGICstaktSwycmwNmzHGrTNmKxnlRZ",
@@ -50,48 +67,49 @@ VALID_TOKENS = []
50
  # ==========================================
51
  # 2. FUNGSI BANTUAN (DATABASE & UTILITAS)
52
  # ==========================================
53
-
54
  def check_tokens():
55
- """Mengecek validitas token saat server startup"""
56
  global VALID_TOKENS
57
- print("--- MEMULAI PENGECEKAN TOKEN ---")
58
  valid_list = []
59
  test_url = "https://huggingface.co/api/whoami-v2"
 
60
 
61
  for token in RAW_HF_TOKENS:
62
  headers = {"Authorization": f"Bearer {token}"}
63
  try:
64
- r = requests.get(test_url, headers=headers, timeout=3)
65
  if r.status_code == 200:
66
  print(f"✅ Valid: ...{token[-6:]}")
67
  valid_list.append(token)
68
  else:
69
  print(f"❌ Invalid: ...{token[-6:]} (Status: {r.status_code})")
70
- except:
71
- print(f"⚠️ Error checking: ...{token[-6:]}")
72
 
73
  VALID_TOKENS = valid_list
74
- print(f"--- SELESAI. Total Token Siap Pakai: {len(VALID_TOKENS)} ---")
75
 
76
  check_tokens()
77
 
78
  def get_db():
79
  headers = {"X-Master-Key": JSONBIN_KEY}
 
80
  try:
81
- response = requests.get(JSONBIN_URL, headers=headers)
82
- if response.status_code == 200:
83
- data = response.json().get("record", {})
84
- if not isinstance(data, dict): return {}
85
- return data
86
- return {}
87
  except Exception as e:
88
  print(f"Error DB Read: {e}")
 
89
  return {}
90
 
91
  def save_db(data):
92
  headers = {"X-Master-Key": JSONBIN_KEY, "Content-Type": "application/json"}
 
93
  try:
94
- requests.put(JSONBIN_URL, json=data, headers=headers)
95
  except Exception as e:
96
  print(f"Error DB Save: {e}")
97
 
@@ -99,7 +117,7 @@ def get_random_token():
99
  if not VALID_TOKENS:
100
  check_tokens()
101
  if not VALID_TOKENS:
102
- raise HTTPException(status_code=500, detail="Semua Token HF Mati/Limit.")
103
  return random.choice(VALID_TOKENS)
104
 
105
  def image_to_base64(image_path):
@@ -113,7 +131,6 @@ def image_to_base64(image_path):
113
  # ==========================================
114
  # 3. MODEL DATA & ENDPOINTS
115
  # ==========================================
116
-
117
  class UserLogin(BaseModel):
118
  username: str
119
  password: str
@@ -130,68 +147,63 @@ class GenerateReq(BaseModel):
130
 
131
  @app.get("/")
132
  def home():
133
- return {"status": "Online", "service": "FLUX2-GEN Backend", "tokens": len(VALID_TOKENS)}
134
 
135
  @app.get("/ping")
136
  def ping():
137
- return {"status": "awake", "time": datetime.now().strftime("%H:%M:%S")}
138
 
139
  @app.post("/auth/signup")
140
  def signup(user: UserLogin):
141
  db = get_db()
142
  if user.username in db:
143
- raise HTTPException(status_code=400, detail="Username sudah dipakai.")
144
 
145
  today_str = datetime.now().strftime("%Y-%m-%d")
146
  db[user.username] = {
147
  "password": user.password,
148
  "credits": 10,
149
- "last_reset": today_str,
150
- "history": []
151
  }
152
  save_db(db)
153
- return {"message": "Akun berhasil dibuat! Silakan login."}
154
 
155
  @app.post("/auth/login")
156
  def login(user: UserLogin):
157
  db = get_db()
158
  if user.username not in db:
159
- raise HTTPException(status_code=404, detail="User tidak ditemukan.")
160
 
161
  stored_user = db[user.username]
162
  if stored_user["password"] != user.password:
163
- raise HTTPException(status_code=401, detail="Password salah.")
164
 
165
  today_str = datetime.now().strftime("%Y-%m-%d")
166
- msg = "Login sukses."
167
-
168
  if stored_user.get("last_reset") != today_str:
169
  stored_user["credits"] = 10
170
  stored_user["last_reset"] = today_str
171
  save_db(db)
172
- msg = "Login sukses. Kredit harian di-reset ke 10."
173
 
174
- return {
175
- "message": msg,
176
- "credits": stored_user["credits"],
177
- "history": stored_user.get("history", [])
178
- }
179
 
180
  @app.post("/generate")
181
  def generate_image(req: GenerateReq):
182
  db = get_db()
183
  if req.username not in db:
184
- raise HTTPException(status_code=404, detail="User tidak valid.")
185
-
186
  user_data = db[req.username]
187
 
188
  if user_data["credits"] <= 0:
189
- raise HTTPException(status_code=403, detail="Kredit harian habis! Kembali besok.")
190
 
191
  hf_token = get_random_token()
192
 
193
  try:
194
  print(f"🚀 Generating for {req.username}...")
 
 
195
  client = Client(ENGINE_URL, hf_token=hf_token)
196
 
197
  result = client.predict(
@@ -209,29 +221,25 @@ def generate_image(req: GenerateReq):
209
  img_b64 = image_to_base64(img_path)
210
 
211
  if not img_b64:
212
- raise Exception("Gagal memproses file output.")
213
 
 
214
  user_data["credits"] -= 1
215
-
216
- new_history = {
217
- "prompt": req.prompt,
218
- "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
219
- "status": "success"
220
- }
221
- user_data["history"] = [new_history] + user_data.get("history", [])[:19]
222
  save_db(db)
223
 
224
  return {
225
  "status": "success",
226
  "image_base64": img_b64,
227
- "remaining_credits": user_data["credits"],
228
- "warning": "NSFW content is not supported."
229
  }
230
 
231
  except Exception as e:
232
- print(f"❌ Error Generasi: {e}")
233
  error_msg = str(e)
 
234
  if "NSFW" in error_msg or "safety" in error_msg:
235
- return {"status": "error", "message": "Prompt terdeteksi NSFW atau melanggar aturan Safety Filter."}
236
- raise HTTPException(status_code=500, detail=f"Engine Error: {error_msg}")
 
 
237
 
 
2
  import requests
3
  import random
4
  import base64
5
+ import time
6
  from datetime import datetime
7
  from fastapi import FastAPI, HTTPException
8
+ from fastapi.middleware.cors import CORSMiddleware
9
  from pydantic import BaseModel
10
  from typing import Optional
11
  from gradio_client import Client
12
+ from requests.adapters import HTTPAdapter, Retry
13
 
14
  app = FastAPI()
15
 
16
  # ==========================================
17
+ # 0. SETUP CORS & ROBUST REQUESTS SESSION
18
  # ==========================================
19
  app.add_middleware(
20
  CORSMiddleware,
21
+ allow_origins=["*"],
22
  allow_credentials=True,
23
+ allow_methods=["*"],
24
+ allow_headers=["*"],
25
  )
26
 
27
+ # Fungsi untuk membuat session yang otomatis mencoba lagi jika gagal koneksi
28
+ def requests_retry_session(
29
+ retries=3,
30
+ backoff_factor=1,
31
+ status_forcelist=(500, 502, 503, 504),
32
+ session=None,
33
+ ):
34
+ session = session or requests.Session()
35
+ retry = Retry(
36
+ total=retries,
37
+ read=retries,
38
+ connect=retries,
39
+ backoff_factor=backoff_factor,
40
+ status_forcelist=status_forcelist,
41
+ )
42
+ adapter = HTTPAdapter(max_retries=retry)
43
+ session.mount("http://", adapter)
44
+ session.mount("https://", adapter)
45
+ return session
46
+
47
  # ==========================================
48
+ # 1. KONFIGURASI & TOKEN (ANTI-DETEKSI)
49
  # ==========================================
 
 
50
  JSONBIN_ID = "6962ebfe43b1c97be927b07d"
51
  JSONBIN_KEY = "$2a$10$khoKkhYEAUCG.O0xHDHiY.Ei88KlSV1l3olI2pxc86mUSsTaNpJx6"
52
  JSONBIN_URL = f"https://api.jsonbin.io/v3/b/{JSONBIN_ID}"
 
 
53
  ENGINE_URL = "https://black-forest-labs-flux-2-dev.hf.space"
54
 
 
55
  TOKEN_PREFIX = "hf_"
56
  TOKEN_BODIES = [
57
  "PiRCDDtPcPFMLWkTkVaZmzoleHOunXnLIA", "BHvZXGICstaktSwycmwNmzHGrTNmKxnlRZ",
 
67
  # ==========================================
68
  # 2. FUNGSI BANTUAN (DATABASE & UTILITAS)
69
  # ==========================================
 
70
  def check_tokens():
 
71
  global VALID_TOKENS
72
+ print("--- CHECKING TOKENS ---")
73
  valid_list = []
74
  test_url = "https://huggingface.co/api/whoami-v2"
75
+ http = requests_retry_session(retries=2, backoff_factor=0.5) # Retry session
76
 
77
  for token in RAW_HF_TOKENS:
78
  headers = {"Authorization": f"Bearer {token}"}
79
  try:
80
+ r = http.get(test_url, headers=headers, timeout=5)
81
  if r.status_code == 200:
82
  print(f"✅ Valid: ...{token[-6:]}")
83
  valid_list.append(token)
84
  else:
85
  print(f"❌ Invalid: ...{token[-6:]} (Status: {r.status_code})")
86
+ except Exception as e:
87
+ print(f"⚠️ Error checking ...{token[-6:]}: {e}")
88
 
89
  VALID_TOKENS = valid_list
90
+ print(f"--- DONE. Total Active Tokens: {len(VALID_TOKENS)} ---")
91
 
92
  check_tokens()
93
 
94
  def get_db():
95
  headers = {"X-Master-Key": JSONBIN_KEY}
96
+ http = requests_retry_session()
97
  try:
98
+ response = http.get(JSONBIN_URL, headers=headers, timeout=10)
99
+ response.raise_for_status() # Raise error untuk status 4xx/5xx
100
+ data = response.json().get("record", {})
101
+ if not isinstance(data, dict): return {}
102
+ return data
 
103
  except Exception as e:
104
  print(f"Error DB Read: {e}")
105
+ # Kembalikan dict kosong jika gagal total, agar server tidak crash
106
  return {}
107
 
108
  def save_db(data):
109
  headers = {"X-Master-Key": JSONBIN_KEY, "Content-Type": "application/json"}
110
+ http = requests_retry_session()
111
  try:
112
+ http.put(JSONBIN_URL, json=data, headers=headers, timeout=10)
113
  except Exception as e:
114
  print(f"Error DB Save: {e}")
115
 
 
117
  if not VALID_TOKENS:
118
  check_tokens()
119
  if not VALID_TOKENS:
120
+ raise HTTPException(status_code=503, detail="No valid HF tokens available. Service temporarily unavailable.")
121
  return random.choice(VALID_TOKENS)
122
 
123
  def image_to_base64(image_path):
 
131
  # ==========================================
132
  # 3. MODEL DATA & ENDPOINTS
133
  # ==========================================
 
134
  class UserLogin(BaseModel):
135
  username: str
136
  password: str
 
147
 
148
  @app.get("/")
149
  def home():
150
+ return {"status": "Online", "service": "FLUX2-GEN Backend (Intl)", "active_tokens": len(VALID_TOKENS)}
151
 
152
  @app.get("/ping")
153
  def ping():
154
+ return {"status": "awake", "time": datetime.now().strftime("%H:%M:%S UTC")}
155
 
156
  @app.post("/auth/signup")
157
  def signup(user: UserLogin):
158
  db = get_db()
159
  if user.username in db:
160
+ raise HTTPException(status_code=400, detail="Username already taken.")
161
 
162
  today_str = datetime.now().strftime("%Y-%m-%d")
163
  db[user.username] = {
164
  "password": user.password,
165
  "credits": 10,
166
+ "last_reset": today_str
 
167
  }
168
  save_db(db)
169
+ return {"message": "Account created successfully."}
170
 
171
  @app.post("/auth/login")
172
  def login(user: UserLogin):
173
  db = get_db()
174
  if user.username not in db:
175
+ raise HTTPException(status_code=404, detail="User not found.")
176
 
177
  stored_user = db[user.username]
178
  if stored_user["password"] != user.password:
179
+ raise HTTPException(status_code=401, detail="Incorrect password.")
180
 
181
  today_str = datetime.now().strftime("%Y-%m-%d")
182
+ msg = "Login successful."
 
183
  if stored_user.get("last_reset") != today_str:
184
  stored_user["credits"] = 10
185
  stored_user["last_reset"] = today_str
186
  save_db(db)
187
+ msg = "Login successful. Daily credits reset to 10."
188
 
189
+ return {"message": msg, "credits": stored_user["credits"]}
 
 
 
 
190
 
191
  @app.post("/generate")
192
  def generate_image(req: GenerateReq):
193
  db = get_db()
194
  if req.username not in db:
195
+ raise HTTPException(status_code=401, detail="Unauthorized user.")
 
196
  user_data = db[req.username]
197
 
198
  if user_data["credits"] <= 0:
199
+ raise HTTPException(status_code=403, detail="Daily credits exhausted. Please return tomorrow.")
200
 
201
  hf_token = get_random_token()
202
 
203
  try:
204
  print(f"🚀 Generating for {req.username}...")
205
+ # Inisialisasi Client. Gradio client menangani retry secara internal untuk beberapa kasus,
206
+ # tapi kita bungkus dalam try-except untuk menangkap error koneksi awal.
207
  client = Client(ENGINE_URL, hf_token=hf_token)
208
 
209
  result = client.predict(
 
221
  img_b64 = image_to_base64(img_path)
222
 
223
  if not img_b64:
224
+ raise Exception("Failed to process output image file.")
225
 
226
+ # Kurangi kredit hanya jika sukses
227
  user_data["credits"] -= 1
 
 
 
 
 
 
 
228
  save_db(db)
229
 
230
  return {
231
  "status": "success",
232
  "image_base64": img_b64,
233
+ "remaining_credits": user_data["credits"]
 
234
  }
235
 
236
  except Exception as e:
237
+ print(f"❌ Generation Error: {e}")
238
  error_msg = str(e)
239
+ # Cek error spesifik NSFW dari Flux
240
  if "NSFW" in error_msg or "safety" in error_msg:
241
+ return {"status": "error", "message": "NSFW content detected and blocked by the engine safety filter."}
242
+
243
+ # Error umum lainnya (koneksi, timeout, dll)
244
+ raise HTTPException(status_code=502, detail=f"Engine Error: Failed to communicate with generation engine. {error_msg}")
245