Ezmary commited on
Commit
3424ff7
·
verified ·
1 Parent(s): 7ca5027

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +184 -0
app.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import json
4
+ import time
5
+ from flask import Flask, request, jsonify, render_template
6
+ from flask_cors import CORS
7
+ import logging
8
+ import threading
9
+ from huggingface_hub import HfApi, hf_hub_download
10
+ from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError
11
+
12
+ # --- تنظیمات اصلی ---
13
+ # !مهم: نام دیتاست خصوصی شما که از قبل وجود دارد
14
+ DATASET_REPO = "Ezmary/Karbaran-rayegan-tedad" # همان دیتاست قبلی
15
+ # *** تغییر کلیدی: یک نام فایل جدید برای این اسپیس ***
16
+ DATASET_FILENAME = "image_usage_data.json" # داده‌های این اسپیس جدا ذخیره می‌شوند
17
+ USAGE_LIMIT = 5 # هر کاربر رایگان می‌تواند ۵ تصویر در هفته ویرایش کند
18
+ HF_TOKEN = os.environ.get("HF_TOKEN")
19
+
20
+ # --- راه‌اندازی Flask و لاگ‌ها ---
21
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
+ app = Flask(__name__)
23
+ CORS(app)
24
+
25
+ # --- مدیریت داده‌های کاربران ---
26
+ usage_data_cache = []
27
+ cache_lock = threading.Lock()
28
+ data_changed = threading.Event()
29
+ api = None
30
+
31
+ if not HF_TOKEN:
32
+ logging.error("CRITICAL: Secret 'HF_TOKEN' not found. Cannot access the private dataset.")
33
+ else:
34
+ api = HfApi(token=HF_TOKEN)
35
+ logging.info("HfApi initialized successfully.")
36
+
37
+ def load_initial_data():
38
+ global usage_data_cache
39
+ with cache_lock:
40
+ if not api: return
41
+ try:
42
+ logging.info(f"Attempting to load data from '{DATASET_REPO}/{DATASET_FILENAME}'...")
43
+ local_path = hf_hub_download(
44
+ repo_id=DATASET_REPO,
45
+ filename=DATASET_FILENAME,
46
+ repo_type="dataset",
47
+ token=HF_TOKEN,
48
+ force_download=True
49
+ )
50
+ with open(local_path, 'r', encoding='utf-8') as f:
51
+ content = f.read()
52
+ usage_data_cache = json.loads(content) if content else []
53
+ logging.info(f"Loaded {len(usage_data_cache)} records from {DATASET_FILENAME}.")
54
+ except (RepositoryNotFoundError, EntryNotFoundError):
55
+ logging.warning(f"Dataset file '{DATASET_FILENAME}' not found. A new one will be created on first use.")
56
+ usage_data_cache = []
57
+ except Exception as e:
58
+ logging.error(f"Failed to load initial data: {e}")
59
+
60
+ def persist_data_to_hub():
61
+ with cache_lock:
62
+ if not data_changed.is_set() or not api:
63
+ return
64
+
65
+ logging.info("Change detected, preparing to write to Hub...")
66
+ try:
67
+ data_to_write = list(usage_data_cache)
68
+ temp_filepath = "temp_usage_data.json"
69
+ with open(temp_filepath, 'w', encoding='utf-8') as f:
70
+ json.dump(data_to_write, f, ensure_ascii=False, indent=2)
71
+
72
+ api.upload_file(
73
+ path_or_fileobj=temp_filepath,
74
+ path_in_repo=DATASET_FILENAME,
75
+ repo_id=DATASET_REPO,
76
+ repo_type="dataset",
77
+ commit_message=f"Update image editor usage data" # پیام کامیت مرتبط
78
+ )
79
+ os.remove(temp_filepath)
80
+ data_changed.clear()
81
+ logging.info(f"Successfully persisted {len(data_to_write)} records to '{DATASET_FILENAME}' on Hub.")
82
+ except Exception as e:
83
+ logging.error(f"CRITICAL: Failed to persist data to Hub: {e}")
84
+
85
+ def background_persister():
86
+ while True:
87
+ time.sleep(30)
88
+ persist_data_to_hub()
89
+
90
+ # --- روت‌های API ---
91
+ @app.route('/')
92
+ def index():
93
+ return render_template('index.html')
94
+
95
+ def get_user_identifier(data):
96
+ fingerprint = data.get('fingerprint')
97
+ if fingerprint:
98
+ return str(fingerprint)
99
+
100
+ if request.headers.getlist("X-Forwarded-For"):
101
+ return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
102
+ return request.remote_addr
103
+
104
+ @app.route('/api/check-credit', methods=['POST'])
105
+ def check_credit():
106
+ data = request.get_json()
107
+ if not data: return jsonify({"error": "Invalid request"}), 400
108
+
109
+ user_id = get_user_identifier(data)
110
+ if not user_id: return jsonify({"error": "User identifier is required."}), 400
111
+
112
+ with cache_lock:
113
+ now = time.time()
114
+ one_week_seconds = 7 * 24 * 60 * 60
115
+
116
+ user_record = next((user for user in usage_data_cache if user.get('id') == user_id), None)
117
+
118
+ credits_remaining = USAGE_LIMIT
119
+ limit_reached = False
120
+ reset_timestamp = 0
121
+
122
+ if user_record:
123
+ if user_record.get('week_start', 0) < (now - one_week_seconds):
124
+ user_record['count'] = 0
125
+ user_record['week_start'] = now
126
+ data_changed.set()
127
+
128
+ credits_remaining = max(0, USAGE_LIMIT - user_record.get('count', 0))
129
+ if credits_remaining == 0:
130
+ limit_reached = True
131
+ reset_timestamp = user_record.get('week_start', now) + one_week_seconds
132
+
133
+ return jsonify({
134
+ "credits_remaining": credits_remaining,
135
+ "limit_reached": limit_reached,
136
+ "reset_timestamp": reset_timestamp
137
+ })
138
+
139
+ @app.route('/api/use-credit', methods=['POST'])
140
+ def use_credit():
141
+ data = request.get_json()
142
+ if not data: return jsonify({"error": "Invalid request"}), 400
143
+
144
+ user_id = get_user_identifier(data)
145
+ if not user_id: return jsonify({"error": "User identifier is required."}), 400
146
+
147
+ with cache_lock:
148
+ now = time.time()
149
+ one_week_seconds = 7 * 24 * 60 * 60
150
+
151
+ user_record = next((user for user in usage_data_cache if user.get('id') == user_id), None)
152
+
153
+ if user_record:
154
+ if user_record.get('week_start', 0) < (now - one_week_seconds):
155
+ user_record['count'] = 0
156
+ user_record['week_start'] = now
157
+
158
+ if user_record.get('count', 0) >= USAGE_LIMIT:
159
+ reset_timestamp = user_record.get('week_start', now) + one_week_seconds
160
+ return jsonify({
161
+ "status": "limit_reached",
162
+ "credits_remaining": 0,
163
+ "reset_timestamp": reset_timestamp
164
+ }), 429
165
+
166
+ user_record['count'] += 1
167
+ else:
168
+ user_record = {"id": user_id, "count": 1, "week_start": now}
169
+ usage_data_cache.append(user_record)
170
+
171
+ credits_remaining = USAGE_LIMIT - user_record['count']
172
+ data_changed.set()
173
+
174
+ return jsonify({"status": "success", "credits_remaining": credits_remaining})
175
+
176
+ # --- اجرای برنامه ---
177
+ if __name__ != '__main__':
178
+ load_initial_data()
179
+ persister_thread = threading.Thread(target=background_persister, daemon=True)
180
+ persister_thread.start()
181
+
182
+ if __name__ == '__main__':
183
+ port = int(os.environ.get('PORT', 7860))
184
+ app.run(host='0.0.0.0', port=port)