Ezmary commited on
Commit
3dab6f8
·
verified ·
1 Parent(s): b819585

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -67
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py
2
  import os
3
  import json
4
  import time
@@ -9,21 +9,25 @@ import threading
9
  from huggingface_hub import HfApi, hf_hub_download
10
  from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError
11
  import requests
 
12
 
13
  # --- تنظیمات اصلی ---
14
  DATASET_REPO = "Ezmary/Karbaran-rayegan-tedad"
15
  DATASET_FILENAME = "image_usage_data.json"
16
  USAGE_LIMIT = 5
17
  HF_TOKEN = os.environ.get("HF_TOKEN")
 
18
 
19
  # --- راه‌اندازی Flask و لاگ‌ها ---
20
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
21
  app = Flask(__name__)
22
  CORS(app)
23
 
24
- # --- مدیریت داده‌های کاربران ---
25
  usage_data_cache = []
26
- cache_lock = threading.Lock()
 
 
27
  api = None
28
 
29
  if not HF_TOKEN:
@@ -38,73 +42,81 @@ def load_initial_data():
38
  if not api: return
39
  try:
40
  logging.info(f"Attempting to load data from '{DATASET_REPO}/{DATASET_FILENAME}'...")
41
- local_path = hf_hub_download(
42
- repo_id=DATASET_REPO,
43
- filename=DATASET_FILENAME,
44
- repo_type="dataset",
45
- token=HF_TOKEN,
46
- force_download=True
47
- )
48
- with open(local_path, 'r', encoding='utf-8') as f:
49
- content = f.read()
50
- data_from_hub = json.loads(content) if content else []
51
- logging.info(f"Loaded {len(data_from_hub)} records from {DATASET_FILENAME}.")
52
-
53
- # --- START: حذف خودکار رکوردهای قدیمی‌تر از ۶ ماه ---
54
- now = time.time()
55
- six_months_in_seconds = 6 * 30 * 24 * 60 * 60
56
- six_months_ago = now - six_months_in_seconds
57
-
58
- # فقط رکوردهایی را نگه می‌داریم که در ۶ ماه اخیر فعال بوده‌اند
59
- cleaned_data = [user for user in data_from_hub if user.get('last_seen', 0) > six_months_ago]
60
-
61
- pruned_count = len(data_from_hub) - len(cleaned_data)
62
- if pruned_count > 0:
63
- logging.info(f"Pruned {pruned_count} user records older than 6 months.")
64
- # اگر رکوردی حذف شد، فایل دیتاست باید آپدیت شود
65
- # این کار توسط ترد پس‌زمینه انجام خواهد شد
66
- threading.Thread(target=persist_data_to_hub, args=(list(cleaned_data),)).start()
67
-
68
- usage_data_cache = cleaned_data
69
- # --- END: حذف خودکار ---
70
 
 
 
 
 
71
  except (RepositoryNotFoundError, EntryNotFoundError):
72
  logging.warning(f"Dataset file '{DATASET_FILENAME}' not found. A new one will be created.")
73
  usage_data_cache = []
74
  except Exception as e:
75
- logging.error(f"Failed to load initial data: {e}")
 
76
 
77
- # *** تغییر: این تابع حالا می‌تواند داده‌ها را به عنوان آرگومان بگیرد ***
78
- def persist_data_to_hub(data_to_write=None):
79
- with cache_lock:
80
- if not api: return
 
81
 
82
- # اگر داده‌ای به تابع پاس داده نشده، از کش سراسری استفاده کن
83
- if data_to_write is None:
84
  data_to_write = list(usage_data_cache)
85
-
86
- logging.info(f"Persisting {len(data_to_write)} records to Hub...")
 
87
  try:
88
- temp_filepath = "temp_usage_data.json"
89
- with open(temp_filepath, 'w', encoding='utf-8') as f:
90
- json.dump(data_to_write, f, ensure_ascii=False, indent=2)
91
 
 
92
  api.upload_file(
93
  path_or_fileobj=temp_filepath,
94
  path_in_repo=DATASET_FILENAME,
95
  repo_id=DATASET_REPO,
96
  repo_type="dataset",
97
- commit_message=f"Update image editor usage data [automated]"
98
  )
99
- os.remove(temp_filepath)
100
- logging.info(f"Successfully persisted data to '{DATASET_FILENAME}' on Hub.")
101
  except Exception as e:
102
- logging.error(f"CRITICAL: Failed to persist data to Hub: {e}")
103
-
104
- def background_persister_fallback():
105
- # این ترد حالا به عنوان یک پشتیبان هر ۵ دقیقه یکبار اجرا می‌شود
 
 
 
 
106
  while True:
107
- time.sleep(300) # 5 minutes
108
  persist_data_to_hub()
109
 
110
  # --- روت‌های API ---
@@ -135,12 +147,11 @@ def check_credit():
135
  reset_timestamp = 0
136
 
137
  if user_record:
138
- # *** تغییر: آپدیت زمان آخرین فعالیت کاربر ***
139
- user_record['last_seen'] = now
140
-
141
  if user_record.get('week_start', 0) < (now - one_week_seconds):
142
  user_record['count'] = 0
143
  user_record['week_start'] = now
 
144
 
145
  credits_remaining = max(0, USAGE_LIMIT - user_record.get('count', 0))
146
  if credits_remaining == 0:
@@ -172,20 +183,15 @@ def use_credit():
172
  return jsonify({"status": "limit_reached", "credits_remaining": 0, "reset_timestamp": reset_timestamp}), 429
173
 
174
  user_record['count'] += 1
175
- user_record['last_seen'] = now # آپدیت زمان آخرین فعالیت
176
  else:
177
  user_record = {"id": user_id, "count": 1, "week_start": now, "last_seen": now}
178
  usage_data_cache.append(user_record)
179
 
180
  credits_remaining = USAGE_LIMIT - user_record['count']
181
 
182
- # --- START: ذخیره‌سازی فوری و غیرمسدود کننده ---
183
- # یک کپی از داده‌های فعلی برای ارسال به ترد ذخیره‌سازی ایجاد می‌کنیم
184
- current_cache_copy = list(usage_data_cache)
185
- # یک ترد جدید برای ذخیره داده‌ها در پس‌زمینه ایجاد و اجرا می‌کنیم
186
- save_thread = threading.Thread(target=persist_data_to_hub, args=(current_cache_copy,))
187
- save_thread.start()
188
- # --- END: ذخیره‌سازی فوری ---
189
 
190
  return jsonify({"status": "success", "credits_remaining": credits_remaining})
191
 
@@ -203,10 +209,14 @@ def proxy_image():
203
 
204
  # --- اجرای برنامه ---
205
  if __name__ != '__main__':
206
- load_initial_data()
207
- # ترد پشتیبان را اجرا می‌کنیم
208
- persister_thread_fallback = threading.Thread(target=background_persister_fallback, daemon=True)
209
- persister_thread_fallback.start()
 
 
 
 
210
 
211
  if __name__ == '__main__':
212
  port = int(os.environ.get('PORT', 7860))
 
1
+ # app.py (نسخه نهایی، پایدار و بهینه شده)
2
  import os
3
  import json
4
  import time
 
9
  from huggingface_hub import HfApi, hf_hub_download
10
  from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError
11
  import requests
12
+ import tempfile
13
 
14
  # --- تنظیمات اصلی ---
15
  DATASET_REPO = "Ezmary/Karbaran-rayegan-tedad"
16
  DATASET_FILENAME = "image_usage_data.json"
17
  USAGE_LIMIT = 5
18
  HF_TOKEN = os.environ.get("HF_TOKEN")
19
+ TEMP_DIR = "/app/tmp" # <<< تغییر کلیدی: استفاده از پوشه موقت امن
20
 
21
  # --- راه‌اندازی Flask و لاگ‌ها ---
22
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
23
  app = Flask(__name__)
24
  CORS(app)
25
 
26
+ # --- مدیریت داده‌های کاربران و قفل‌ها ---
27
  usage_data_cache = []
28
+ cache_lock = threading.Lock() # قفل برای دسترسی به حافظه کش
29
+ data_changed = threading.Event() # برای اطلاع از وجود تغییرات برای ذخیره
30
+ persistence_lock = threading.Lock() # <<< تغییر کلیدی: قفل جدید برای اتمی کردن عملیات ذخیره‌سازی >>>
31
  api = None
32
 
33
  if not HF_TOKEN:
 
42
  if not api: return
43
  try:
44
  logging.info(f"Attempting to load data from '{DATASET_REPO}/{DATASET_FILENAME}'...")
45
+ # <<< تغییر کلیدی: دانلود در پوشه موقت برای جلوگیری از مشکلات دسترسی >>>
46
+ with tempfile.TemporaryDirectory(dir=TEMP_DIR) as tmp_download_dir:
47
+ local_path = hf_hub_download(
48
+ repo_id=DATASET_REPO,
49
+ filename=DATASET_FILENAME,
50
+ repo_type="dataset",
51
+ token=HF_TOKEN,
52
+ force_download=True,
53
+ cache_dir=tmp_download_dir
54
+ )
55
+ with open(local_path, 'r', encoding='utf-8') as f:
56
+ content = f.read()
57
+ data_from_hub = json.loads(content) if content else []
58
+
59
+ logging.info(f"Loaded {len(data_from_hub)} records from {DATASET_FILENAME}.")
60
+
61
+ # --- حذف خودکار رکوردهای قدیمی‌تر از ۶ ماه ---
62
+ now = time.time()
63
+ six_months_ago = now - (6 * 30 * 24 * 60 * 60)
64
+ cleaned_data = [user for user in data_from_hub if user.get('last_seen', 0) > six_months_ago]
65
+ pruned_count = len(data_from_hub) - len(cleaned_data)
66
+
67
+ if pruned_count > 0:
68
+ logging.info(f"Pruned {pruned_count} user records older than 6 months.")
69
+ data_changed.set() # <<< تغییر کلیدی: فقط فلگ را ست می‌کنیم، ترد پس‌زمینه ذخیره می‌کند >>>
70
+
71
+ usage_data_cache = cleaned_data
 
 
72
 
73
+ except json.JSONDecodeError:
74
+ # <<< تغییر کلیدی: مدیریت فایل خراب برای جلوگیری از کرش و صفحه سفید >>>
75
+ logging.error(f"CRITICAL: Failed to decode JSON from '{DATASET_FILENAME}'. The file is likely corrupted. Starting fresh.")
76
+ usage_data_cache = []
77
  except (RepositoryNotFoundError, EntryNotFoundError):
78
  logging.warning(f"Dataset file '{DATASET_FILENAME}' not found. A new one will be created.")
79
  usage_data_cache = []
80
  except Exception as e:
81
+ logging.error(f"Failed to load initial data: {e}", exc_info=True)
82
+ usage_data_cache = []
83
 
84
+ def persist_data_to_hub():
85
+ # <<< تغییر کلیدی: این تابع اکنون کاملاً Thread-Safe و امن است >>>
86
+ with persistence_lock:
87
+ if not data_changed.is_set() or not api:
88
+ return
89
 
90
+ with cache_lock:
 
91
  data_to_write = list(usage_data_cache)
92
+ data_changed.clear()
93
+
94
+ temp_filepath = None
95
  try:
96
+ with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', dir=TEMP_DIR, delete=False, suffix='.json') as temp_f:
97
+ temp_filepath = temp_f.name
98
+ json.dump(data_to_write, temp_f, ensure_ascii=False, indent=2)
99
 
100
+ logging.info(f"Change detected, persisting {len(data_to_write)} records to Hub...")
101
  api.upload_file(
102
  path_or_fileobj=temp_filepath,
103
  path_in_repo=DATASET_FILENAME,
104
  repo_id=DATASET_REPO,
105
  repo_type="dataset",
106
+ commit_message="Update image editor usage data [automated]"
107
  )
108
+ logging.info(f"Successfully persisted data to Hub.")
 
109
  except Exception as e:
110
+ logging.error(f"CRITICAL: Failed to persist data to Hub: {e}", exc_info=True)
111
+ data_changed.set() # اگر آپلود ناموفق بود، فلگ را دوباره ست می‌کنیم
112
+ finally:
113
+ if temp_filepath and os.path.exists(temp_filepath):
114
+ os.remove(temp_filepath)
115
+
116
+ def background_persister():
117
+ # <<< تغییر کلیدی: این ترد اصلی برای ذخیره‌سازی است و هر ۳۰ ثانیه اجرا می‌شود >>>
118
  while True:
119
+ time.sleep(30)
120
  persist_data_to_hub()
121
 
122
  # --- روت‌های API ---
 
147
  reset_timestamp = 0
148
 
149
  if user_record:
150
+ user_record['last_seen'] = now # آپدیت زمان آخرین فعالیت
 
 
151
  if user_record.get('week_start', 0) < (now - one_week_seconds):
152
  user_record['count'] = 0
153
  user_record['week_start'] = now
154
+ data_changed.set() # اگر هفته ریست شد، باید ذخیره شود
155
 
156
  credits_remaining = max(0, USAGE_LIMIT - user_record.get('count', 0))
157
  if credits_remaining == 0:
 
183
  return jsonify({"status": "limit_reached", "credits_remaining": 0, "reset_timestamp": reset_timestamp}), 429
184
 
185
  user_record['count'] += 1
186
+ user_record['last_seen'] = now
187
  else:
188
  user_record = {"id": user_id, "count": 1, "week_start": now, "last_seen": now}
189
  usage_data_cache.append(user_record)
190
 
191
  credits_remaining = USAGE_LIMIT - user_record['count']
192
 
193
+ # <<< تغییر کلیدی: به جای ایجاد ترد جدید، فقط فلگ تغییرات را فعال می‌کنیم >>>
194
+ data_changed.set()
 
 
 
 
 
195
 
196
  return jsonify({"status": "success", "credits_remaining": credits_remaining})
197
 
 
209
 
210
  # --- اجرای برنامه ---
211
  if __name__ != '__main__':
212
+ # <<< تغییر کلیدی: افزایش پایداری در استارت‌آپ >>>
213
+ try:
214
+ load_initial_data()
215
+ persister_thread = threading.Thread(target=background_persister, daemon=True)
216
+ persister_thread.start()
217
+ logging.info("Application startup complete.")
218
+ except Exception as e:
219
+ logging.critical(f"A critical error occurred during application startup: {e}", exc_info=True)
220
 
221
  if __name__ == '__main__':
222
  port = int(os.environ.get('PORT', 7860))