Padgenpro1 / app.py
Ezmary's picture
Update app.py
31a369a verified
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
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/use-credit', methods=['POST'])
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)