Spaces:
Paused
Paused
| import os | |
| import json | |
| import time | |
| import logging | |
| import threading | |
| import atexit | |
| from flask import Flask, request, jsonify, render_template | |
| from flask_cors import CORS | |
| from huggingface_hub import HfApi, hf_hub_download | |
| from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError | |
| # --- مسیر امن برای کش که همیشه قابل نوشتن است --- | |
| CACHE_DIRECTORY = "/tmp/huggingface_cache_ezmary" | |
| os.makedirs(CACHE_DIRECTORY, exist_ok=True) | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') | |
| app = Flask(__name__) | |
| CORS(app) | |
| # --- تنظیمات سیستم اعتبار --- | |
| DATASET_REPO = "Ezmary/Karbaran-rayegan-tedad" | |
| DATASET_FILENAME = "usage_data.json" | |
| USAGE_LIMIT = 5 | |
| HF_TOKEN = os.environ.get("HF_TOKEN") | |
| # --- شروع بخش جدید: تنظیمات پاکسازی خودکار --- | |
| CLEANUP_INTERVAL_SECONDS = 6 * 30 * 24 * 60 * 60 # تقریبا هر 6 ماه | |
| last_cleanup_time = time.time() | |
| # --- پایان بخش جدید --- | |
| # --- مدیریت وضعیت در حافظه --- | |
| usage_data_cache = [] | |
| cache_lock = threading.Lock() | |
| data_changed = threading.Event() | |
| api = None | |
| # --- راهاندازی HfApi با توکن به صورت مستقیم --- | |
| if not HF_TOKEN: | |
| logging.error("CRITICAL: Secret 'HF_TOKEN' not found. Cannot access the private dataset.") | |
| else: | |
| api = HfApi(token=HF_TOKEN) | |
| logging.info("HfApi initialized with token. Private repository access is enabled.") | |
| def load_initial_data(): | |
| global usage_data_cache | |
| with cache_lock: | |
| if not api: return | |
| try: | |
| logging.info(f"Attempting to load data from '{DATASET_REPO}'...") | |
| local_path = hf_hub_download( | |
| repo_id=DATASET_REPO, | |
| filename=DATASET_FILENAME, | |
| repo_type="dataset", | |
| token=HF_TOKEN, | |
| force_download=True, | |
| cache_dir=CACHE_DIRECTORY | |
| ) | |
| with open(local_path, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| if content: | |
| usage_data_cache = json.loads(content) | |
| logging.info(f"Loaded {len(usage_data_cache)} records into memory.") | |
| else: | |
| logging.info("Dataset file is empty.") | |
| except (RepositoryNotFoundError, EntryNotFoundError): | |
| logging.warning("Dataset file not found. A new file will be created.") | |
| except Exception as e: | |
| logging.error(f"Failed to load initial data: {e}") | |
| def persist_data_to_hub(): | |
| global last_cleanup_time, usage_data_cache | |
| with cache_lock: | |
| # --- شروع منطق پاکسازی خودکار --- | |
| now = time.time() | |
| if (now - last_cleanup_time) > CLEANUP_INTERVAL_SECONDS: | |
| logging.info("Scheduled cleanup starting...") | |
| six_months_ago = now - CLEANUP_INTERVAL_SECONDS | |
| original_count = len(usage_data_cache) | |
| # فقط رکوردهایی را نگه دار که در 6 ماه اخیر فعال بودهاند | |
| active_users = [ | |
| user for user in usage_data_cache | |
| if user.get('week_start', 0) > six_months_ago | |
| ] | |
| usage_data_cache = active_users | |
| removed_count = original_count - len(usage_data_cache) | |
| if removed_count > 0: | |
| logging.info(f"Cleanup complete. Removed {removed_count} inactive user records.") | |
| data_changed.set() # چون دادهها تغییر کرده، باید حتما ذخیره شود | |
| else: | |
| logging.info("Cleanup complete. No inactive records found to remove.") | |
| last_cleanup_time = now # زمانسنج پاکسازی را ریست کن | |
| # --- پایان منطق پاکسازی خودکار --- | |
| if not data_changed.is_set() or not api: return | |
| logging.info("Preparing to write changes to Hub...") | |
| try: | |
| data_to_write = list(usage_data_cache) | |
| temp_filepath = os.path.join(CACHE_DIRECTORY, "temp_usage_data.json") | |
| with open(temp_filepath, 'w', encoding='utf-8') as f: | |
| json.dump(data_to_write, f, indent=2, ensure_ascii=False) | |
| api.upload_file( | |
| path_or_fileobj=temp_filepath, | |
| path_in_repo=DATASET_FILENAME, | |
| repo_id=DATASET_REPO, | |
| repo_type="dataset", | |
| commit_message="Periodic usage data update" | |
| ) | |
| os.remove(temp_filepath) | |
| data_changed.clear() | |
| logging.info(f"Persisted {len(data_to_write)} records to Hub.") | |
| except Exception as e: | |
| logging.error(f"CRITICAL: Failed to persist data to Hub: {e}") | |
| def background_persister(): | |
| while True: | |
| time.sleep(10) | |
| persist_data_to_hub() | |
| def get_user_ip(): | |
| if request.headers.getlist("X-Forwarded-For"): | |
| return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip() | |
| return request.remote_addr | |
| def index(): | |
| return render_template('index.html') | |
| def use_credit(): | |
| data = request.get_json() | |
| fingerprint = data.get('fingerprint') | |
| if not fingerprint: | |
| return jsonify({"status": "error", "message": "Fingerprint is required."}), 400 | |
| with cache_lock: | |
| current_ip = get_user_ip() | |
| now = time.time() | |
| one_week_seconds = 7 * 24 * 60 * 60 | |
| one_week_ago = now - one_week_seconds | |
| found_by_fingerprint = next((user for user in usage_data_cache if user.get('fingerprint') == fingerprint), None) | |
| found_by_ip = next((user for user in usage_data_cache if current_ip in user.get('ips', [])), None) | |
| user_record = found_by_fingerprint or found_by_ip | |
| if user_record: | |
| if user_record['week_start'] < one_week_ago: | |
| user_record['count'] = 0 | |
| user_record['week_start'] = now | |
| if user_record['count'] >= USAGE_LIMIT: | |
| reset_timestamp = user_record['week_start'] + one_week_seconds | |
| return jsonify({ | |
| "status": "limit_reached", | |
| "credits_remaining": 0, | |
| "reset_timestamp": reset_timestamp | |
| }), 429 | |
| user_record['count'] += 1 | |
| if current_ip not in user_record['ips']: | |
| user_record['ips'].append(current_ip) | |
| credits_remaining = USAGE_LIMIT - user_record['count'] | |
| else: | |
| user_record = {"fingerprint": fingerprint, "ips": [current_ip], "count": 1, "week_start": now} | |
| usage_data_cache.append(user_record) | |
| credits_remaining = USAGE_LIMIT - 1 | |
| data_changed.set() | |
| return jsonify({"status": "success", "credits_remaining": credits_remaining}), 200 | |
| load_initial_data() | |
| persister_thread = threading.Thread(target=background_persister, daemon=True) | |
| persister_thread.start() | |
| atexit.register(persist_data_to_hub) | |
| if __name__ == '__main__': | |
| port = int(os.environ.get('PORT', 7860)) | |
| app.run(host='0.0.0.0', port=port) |