Opera8 commited on
Commit
c939581
·
verified ·
1 Parent(s): 4ced5fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -65
app.py CHANGED
@@ -3,6 +3,7 @@ import json
3
  import random
4
  import asyncio
5
  import re
 
6
  from datetime import datetime
7
  from concurrent.futures import ThreadPoolExecutor
8
  from fastapi import FastAPI, HTTPException, Request
@@ -15,40 +16,31 @@ load_dotenv()
15
 
16
  app = FastAPI()
17
 
18
- # --- حافظه موقت برای محدودیت روزانه ---
19
- # ساختار: { "identifier_string": { "date": "YYYY-MM-DD", "count": int } }
20
- DAILY_LIMITS = {}
21
  MAX_FREE_DAILY_REQUESTS = 5
 
22
 
23
- # --- بخش اصلاح شده برای بارگذاری تمیز کلیدها ---
24
  all_keys_str = os.getenv("ALL_GEMINI_API_KEYS", "")
25
-
26
- # استفاده از Regex برای جدا کردن کلیدها بر اساس کاما، خط جدید یا فاصله
27
  raw_keys = re.split(r'[,\s\n]+', all_keys_str)
28
 
29
  GEMINI_KEYS = []
30
  for k in raw_keys:
31
- # حذف کوتیشن‌های احتمالی (' یا ") و کاراکترهای کنترلی مثل \r
32
  clean_key = k.strip().replace('"', '').replace("'", "").replace("\r", "").replace("\n", "")
33
-
34
- # فیلتر کردن کلیدهای خراب (کلیدهای جمینای معمولاً طولانی هستند)
35
  if len(clean_key) > 30:
36
  GEMINI_KEYS.append(clean_key)
37
 
38
  if GEMINI_KEYS:
39
- print(f"تعداد {len(GEMINI_KEYS)} کلید سالم و تمیز بارگذاری شد.")
40
- # نمایش ۵ کاراکتر اول یکی از کلیدها جهت اطمینان در لاگ
41
- print(f"Sample loaded key start: {GEMINI_KEYS[0][:5]}...")
42
  else:
43
- print("هشدار جدی: هیچ کلید معتبری یافت نشد. لطفاً متغیر ALL_GEMINI_API_KEYS را بررسی کنید.")
44
 
45
- # تنظیم ThreadPool برای درخواست‌های موازی
46
  executor = ThreadPoolExecutor(max_workers=10)
47
 
48
  class IdeaRequest(BaseModel):
49
  idea: str
50
- fingerprint: str # شناسه مرورگر کاربر
51
- is_premium: bool = False # وضعیت اشتراک
52
 
53
  @app.get("/", response_class=HTMLResponse)
54
  async def read_root():
@@ -59,80 +51,100 @@ async def read_root():
59
  return "<h1>Error: index.html not found</h1>"
60
 
61
  def sync_generate_content(prompt):
62
- """
63
- این تابع همگام است و تلاش می‌کند با کلیدهای مختلف محتوا تولید کند.
64
- """
65
  max_retries = 1000
66
-
67
- # اگر کلیدی موجود نیست، فوراً خارج شو
68
  if not GEMINI_KEYS:
69
- print("No keys available to use.")
70
  return None
71
 
72
  for _ in range(max_retries):
73
  try:
74
  current_key = random.choice(GEMINI_KEYS)
75
-
76
  genai.configure(api_key=current_key)
77
- model = genai.GenerativeModel('gemini-2.5-flash') # استفاده از مدل جدیدتر و سریعتر
78
-
79
  response = model.generate_content(prompt)
80
-
81
  clean_json = response.text.replace("```json", "").replace("```", "").strip()
82
- data = json.loads(clean_json)
83
-
84
- return data
85
-
86
- except Exception as e:
87
- # خطا را لاگ نمی‌کنیم تا لاگ‌ها شلوغ نشود، فقط ریترای می‌کنیم
88
  continue
89
-
90
  return None
91
 
92
- def check_and_update_limit(identifier: str):
93
  """
94
- بررسی محدودیت روزانه برای یک شناسه ی‌پی یا فینگرپرینت).
95
- اگر محدودیت رد شده باشد False برمی‌گرداند.
96
  """
97
  today = datetime.now().strftime("%Y-%m-%d")
98
 
99
- if identifier not in DAILY_LIMITS:
100
- DAILY_LIMITS[identifier] = {"date": today, "count": 1}
101
- return True
102
-
103
- record = DAILY_LIMITS[identifier]
104
-
105
- # اگر تاریخ رکورد قدیمی است، ریست کن
106
- if record["date"] != today:
107
- DAILY_LIMITS[identifier] = {"date": today, "count": 1}
108
- return True
109
-
110
- # بررسی تعداد
111
- if record["count"] >= MAX_FREE_DAILY_REQUESTS:
112
- return False
113
-
114
- # افزایش شمارنده
115
- DAILY_LIMITS[identifier]["count"] += 1
116
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  @app.post("/api/refine")
119
  async def refine_text(request: IdeaRequest, req: Request):
120
  if not GEMINI_KEYS:
121
  raise HTTPException(status_code=500, detail="کلید API تنظیم نشده است.")
122
 
123
- # --- بررسی محدودیت روزانه ---
124
  if not request.is_premium:
125
  client_ip = req.client.host
126
- client_fingerprint = request.fingerprint
127
-
128
- # بررسی آی‌پی
129
- if not check_and_update_limit(client_ip):
130
- return JSONResponse(content={"error": "LIMIT_REACHED"}, status_code=429)
131
 
132
- # بررسی فینگرپرینت (اگر متفاوت از آی‌پی باشد)
133
- if client_fingerprint and client_fingerprint != client_ip:
134
- if not check_and_update_limit(client_fingerprint):
135
- return JSONResponse(content={"error": "LIMIT_REACHED"}, status_code=429)
136
  # ---------------------------
137
 
138
  prompt = f"""
@@ -175,4 +187,4 @@ async def refine_text(request: IdeaRequest, req: Request):
175
  if result:
176
  return JSONResponse(content=result)
177
  else:
178
- return JSONResponse(content={"error": "Server busy (All keys limit reached or invalid). Please try again later."}, status_code=503)
 
3
  import random
4
  import asyncio
5
  import re
6
+ import fcntl # برای قفل کردن فایل و هماهنگی بین Worker ها
7
  from datetime import datetime
8
  from concurrent.futures import ThreadPoolExecutor
9
  from fastapi import FastAPI, HTTPException, Request
 
16
 
17
  app = FastAPI()
18
 
19
+ # --- تنظیمات محدودیت ---
 
 
20
  MAX_FREE_DAILY_REQUESTS = 5
21
+ USAGE_DB_FILE = "usage_data.json" # فایل ذخیره سوابق
22
 
23
+ # --- بخش بارگذاری کلیدها ---
24
  all_keys_str = os.getenv("ALL_GEMINI_API_KEYS", "")
 
 
25
  raw_keys = re.split(r'[,\s\n]+', all_keys_str)
26
 
27
  GEMINI_KEYS = []
28
  for k in raw_keys:
 
29
  clean_key = k.strip().replace('"', '').replace("'", "").replace("\r", "").replace("\n", "")
 
 
30
  if len(clean_key) > 30:
31
  GEMINI_KEYS.append(clean_key)
32
 
33
  if GEMINI_KEYS:
34
+ print(f"Loaded {len(GEMINI_KEYS)} keys.")
 
 
35
  else:
36
+ print("Warning: No valid keys found.")
37
 
 
38
  executor = ThreadPoolExecutor(max_workers=10)
39
 
40
  class IdeaRequest(BaseModel):
41
  idea: str
42
+ fingerprint: str
43
+ is_premium: bool = False
44
 
45
  @app.get("/", response_class=HTMLResponse)
46
  async def read_root():
 
51
  return "<h1>Error: index.html not found</h1>"
52
 
53
  def sync_generate_content(prompt):
 
 
 
54
  max_retries = 1000
 
 
55
  if not GEMINI_KEYS:
 
56
  return None
57
 
58
  for _ in range(max_retries):
59
  try:
60
  current_key = random.choice(GEMINI_KEYS)
 
61
  genai.configure(api_key=current_key)
62
+ model = genai.GenerativeModel('gemini-2.5-flash')
 
63
  response = model.generate_content(prompt)
 
64
  clean_json = response.text.replace("```json", "").replace("```", "").strip()
65
+ return json.loads(clean_json)
66
+ except Exception:
 
 
 
 
67
  continue
 
68
  return None
69
 
70
+ def check_and_increment_usage(ip: str, fingerprint: str):
71
  """
72
+ این تابع با استفاده از قفل فایل، دسترسی همزمان چندین Worker را مدیریت می‌کند
73
+ تا محدودیت دقیقاً اعمال شود.
74
  """
75
  today = datetime.now().strftime("%Y-%m-%d")
76
 
77
+ # اطمینان از وجود فایل
78
+ if not os.path.exists(USAGE_DB_FILE):
79
+ with open(USAGE_DB_FILE, 'w') as f:
80
+ json.dump({}, f)
81
+
82
+ # باز کردن فایل با قابلیت خواندن �� نوشتن
83
+ with open(USAGE_DB_FILE, 'r+') as f:
84
+ try:
85
+ # اعمال قفل انحصاری (فقط یک Worker در لحظه می‌تواند فایل را بخواند/بنویسد)
86
+ fcntl.flock(f, fcntl.LOCK_EX)
87
+
88
+ try:
89
+ data = json.load(f)
90
+ except json.JSONDecodeError:
91
+ data = {}
92
+
93
+ # آماده‌سازی کلیدها
94
+ ip_key = f"ip:{ip}"
95
+ fp_key = f"fp:{fingerprint}"
96
+
97
+ # خواندن وضعیت فعلی IP
98
+ ip_record = data.get(ip_key, {"date": today, "count": 0})
99
+ if ip_record["date"] != today: # ریست کردن روز جدید
100
+ ip_record = {"date": today, "count": 0}
101
+
102
+ # خواندن وضعیت فعلی Fingerprint
103
+ fp_record = data.get(fp_key, {"date": today, "count": 0})
104
+ if fp_record["date"] != today: # ریست کردن روز جدید
105
+ fp_record = {"date": today, "count": 0}
106
+
107
+ # بررسی محدودیت (اگر هر کدام از سقف رد شده باشند، مسدود می‌شود)
108
+ if ip_record["count"] >= MAX_FREE_DAILY_REQUESTS:
109
+ return False
110
+ if fingerprint and fp_record["count"] >= MAX_FREE_DAILY_REQUESTS:
111
+ return False
112
+
113
+ # افزایش شمارنده برای هر دو (برای جلوگیری از دور زدن)
114
+ ip_record["count"] += 1
115
+ data[ip_key] = ip_record
116
+
117
+ if fingerprint:
118
+ fp_record["count"] += 1
119
+ data[fp_key] = fp_record
120
+
121
+ # بازنویسی فایل
122
+ f.seek(0)
123
+ f.truncate()
124
+ json.dump(data, f)
125
+
126
+ return True
127
+
128
+ finally:
129
+ # آزاد کردن قفل
130
+ fcntl.flock(f, fcntl.LOCK_UN)
131
 
132
  @app.post("/api/refine")
133
  async def refine_text(request: IdeaRequest, req: Request):
134
  if not GEMINI_KEYS:
135
  raise HTTPException(status_code=500, detail="کلید API تنظیم نشده است.")
136
 
137
+ # --- بررسی دقیق محدودیت روزانه ---
138
  if not request.is_premium:
139
  client_ip = req.client.host
140
+ client_fp = request.fingerprint
141
+
142
+ # فراخوانی تابع امن با قفل فایل
143
+ allowed = check_and_increment_usage(client_ip, client_fp)
 
144
 
145
+ if not allowed:
146
+ # کد 429 به معنی Too Many Requests
147
+ return JSONResponse(content={"error": "LIMIT_REACHED"}, status_code=429)
 
148
  # ---------------------------
149
 
150
  prompt = f"""
 
187
  if result:
188
  return JSONResponse(content=result)
189
  else:
190
+ return JSONResponse(content={"error": "Server busy. Please try again later."}, status_code=503)