Dooratre commited on
Commit
256382c
·
verified ·
1 Parent(s): 619ede5

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +40 -0
  2. app.py +212 -0
  3. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Set environment variables
7
+ ENV PYTHONUNBUFFERED=1 \
8
+ PYTHONDONTWRITEBYTECODE=1 \
9
+ PORT=7860 \
10
+ TZ=UTC
11
+
12
+ # Install system dependencies
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ curl \
15
+ tzdata \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+ # Copy requirements and install Python dependencies
19
+ COPY requirements.txt .
20
+ RUN pip install --no-cache-dir --upgrade pip && \
21
+ pip install --no-cache-dir -r requirements.txt
22
+
23
+ # Copy application code
24
+ COPY app.py .
25
+
26
+ # Create a non-root user (required by Hugging Face Spaces)
27
+ RUN useradd -m -u 1000 user && \
28
+ chown -R user:user /app
29
+
30
+ USER user
31
+
32
+ # Expose Hugging Face port
33
+ EXPOSE 7860
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=60s --timeout=10s --start-period=10s --retries=3 \
37
+ CMD curl -f http://localhost:7860/health || exit 1
38
+
39
+ # Run the app
40
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, jsonify
2
+ import requests
3
+ import schedule
4
+ import time
5
+ from datetime import datetime
6
+ import logging
7
+ import threading
8
+ import os
9
+
10
+ # Configure logging
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format='%(asctime)s - %(levelname)s - %(message)s',
14
+ handlers=[logging.StreamHandler()]
15
+ )
16
+
17
+ app = Flask(__name__)
18
+
19
+ # Global state
20
+ scheduler_thread = None
21
+ scheduler_running = False
22
+ last_run_status = {
23
+ "last_run": None,
24
+ "last_status": "never",
25
+ "results": {}
26
+ }
27
+
28
+ # ==================== BACKUP FUNCTIONS ====================
29
+
30
+ def backup_main_db():
31
+ try:
32
+ url = "https://dooratre-db.hf.space/admin/backup"
33
+ headers = {"X-Admin-Secret": "Dbpassword2000$"}
34
+ response = requests.post(url, headers=headers, timeout=60)
35
+ logging.info(f"[Main DB] Status: {response.status_code}")
36
+ return {"status": response.status_code, "ok": response.ok}
37
+ except Exception as e:
38
+ logging.error(f"[Main DB] Error: {e}")
39
+ return {"status": "error", "ok": False, "error": str(e)}
40
+
41
+ def backup_tele_db():
42
+ try:
43
+ url = "https://dooratre-tele-db.hf.space/api/backup"
44
+ headers = {"X-API-Key": "Telegrampassword2000$"}
45
+ response = requests.post(url, headers=headers, timeout=60)
46
+ logging.info(f"[Tele DB] Status: {response.status_code}")
47
+ return {"status": response.status_code, "ok": response.ok}
48
+ except Exception as e:
49
+ logging.error(f"[Tele DB] Error: {e}")
50
+ return {"status": "error", "ok": False, "error": str(e)}
51
+
52
+ def backup_cards_db():
53
+ try:
54
+ url = "https://dooratre-cards.hf.space/admin/backup?secret=Cardspassword2000$"
55
+ response = requests.post(url, timeout=60)
56
+ logging.info(f"[Cards DB] Status: {response.status_code}")
57
+ return {"status": response.status_code, "ok": response.ok}
58
+ except Exception as e:
59
+ logging.error(f"[Cards DB] Error: {e}")
60
+ return {"status": "error", "ok": False, "error": str(e)}
61
+
62
+ def backup_main_app(server_num):
63
+ try:
64
+ url = f"https://serverclass{server_num}-app.hf.space/backup_db"
65
+ headers = {"Adminpassword2000$": ""}
66
+ response = requests.post(url, headers=headers, timeout=60)
67
+ logging.info(f"[Server {server_num}] Status: {response.status_code}")
68
+ return {"status": response.status_code, "ok": response.ok}
69
+ except Exception as e:
70
+ logging.error(f"[Server {server_num}] Error: {e}")
71
+ return {"status": "error", "ok": False, "error": str(e)}
72
+
73
+ def backup_all_servers():
74
+ results = {}
75
+ logging.info("Starting backup for all 55 servers...")
76
+ for i in range(1, 56):
77
+ results[f"server{i}"] = backup_main_app(i)
78
+ time.sleep(1)
79
+ logging.info("Finished backup for all 55 servers.")
80
+ return results
81
+
82
+ def run_all_backups():
83
+ global last_run_status
84
+ logging.info("=" * 60)
85
+ logging.info(f"Starting daily backup at {datetime.now()}")
86
+ logging.info("=" * 60)
87
+
88
+ results = {
89
+ "main_db": backup_main_db(),
90
+ "tele_db": backup_tele_db(),
91
+ "cards_db": backup_cards_db(),
92
+ "servers": backup_all_servers()
93
+ }
94
+
95
+ last_run_status["last_run"] = datetime.now().isoformat()
96
+ last_run_status["last_status"] = "completed"
97
+ last_run_status["results"] = results
98
+
99
+ logging.info("=" * 60)
100
+ logging.info(f"Daily backup completed at {datetime.now()}")
101
+ logging.info("=" * 60)
102
+ return results
103
+
104
+ # ==================== SCHEDULER ====================
105
+
106
+ def scheduler_loop():
107
+ global scheduler_running
108
+ logging.info("Scheduler loop started.")
109
+ while scheduler_running:
110
+ schedule.run_pending()
111
+ time.sleep(30)
112
+ logging.info("Scheduler loop stopped.")
113
+
114
+ # ==================== ROUTES ====================
115
+
116
+ @app.route("/")
117
+ def home():
118
+ return jsonify({
119
+ "service": "Backup Scheduler",
120
+ "version": "1.0",
121
+ "endpoints": ["/start", "/stop", "/health", "/run-now", "/status"],
122
+ "scheduler_running": scheduler_running,
123
+ "schedule": "Daily at 00:00 UTC"
124
+ })
125
+
126
+ @app.route("/start", methods=["GET", "POST"])
127
+ def start():
128
+ global scheduler_thread, scheduler_running
129
+
130
+ if scheduler_running:
131
+ return jsonify({
132
+ "status": "already_running",
133
+ "message": "Scheduler is already running."
134
+ }), 200
135
+
136
+ schedule.clear()
137
+ schedule.every().day.at("00:00").do(run_all_backups)
138
+
139
+ scheduler_running = True
140
+ scheduler_thread = threading.Thread(target=scheduler_loop, daemon=True)
141
+ scheduler_thread.start()
142
+
143
+ logging.info("Scheduler STARTED via API.")
144
+ return jsonify({
145
+ "status": "started",
146
+ "message": "Scheduler started. Will run daily at 00:00 UTC.",
147
+ "scheduled_time": "00:00"
148
+ }), 200
149
+
150
+ @app.route("/stop", methods=["GET", "POST"])
151
+ def stop():
152
+ global scheduler_running
153
+
154
+ if not scheduler_running:
155
+ return jsonify({
156
+ "status": "not_running",
157
+ "message": "Scheduler is not running."
158
+ }), 200
159
+
160
+ scheduler_running = False
161
+ schedule.clear()
162
+ logging.info("Scheduler STOPPED via API.")
163
+ return jsonify({
164
+ "status": "stopped",
165
+ "message": "Scheduler has been stopped."
166
+ }), 200
167
+
168
+ @app.route("/health", methods=["GET"])
169
+ def health():
170
+ next_run = None
171
+ jobs = schedule.get_jobs()
172
+ if jobs:
173
+ next_run = str(jobs[0].next_run)
174
+
175
+ return jsonify({
176
+ "status": "healthy",
177
+ "scheduler_running": scheduler_running,
178
+ "current_time": datetime.now().isoformat(),
179
+ "next_scheduled_run": next_run,
180
+ "last_run": last_run_status["last_run"],
181
+ "last_status": last_run_status["last_status"]
182
+ }), 200
183
+
184
+ @app.route("/run-now", methods=["GET", "POST"])
185
+ def run_now():
186
+ threading.Thread(target=run_all_backups, daemon=True).start()
187
+ return jsonify({
188
+ "status": "triggered",
189
+ "message": "Backup started in background. Check /health for status."
190
+ }), 200
191
+
192
+ @app.route("/status", methods=["GET"])
193
+ def status():
194
+ return jsonify({
195
+ "scheduler_running": scheduler_running,
196
+ "last_run": last_run_status["last_run"],
197
+ "last_status": last_run_status["last_status"],
198
+ "last_results": last_run_status["results"]
199
+ }), 200
200
+
201
+ # ==================== MAIN ====================
202
+
203
+ if __name__ == "__main__":
204
+ # Auto-start scheduler
205
+ schedule.every().day.at("00:00").do(run_all_backups)
206
+ scheduler_running = True
207
+ scheduler_thread = threading.Thread(target=scheduler_loop, daemon=True)
208
+ scheduler_thread.start()
209
+ logging.info("Scheduler auto-started on app launch.")
210
+
211
+ port = int(os.environ.get("PORT", 7860))
212
+ app.run(host="0.0.0.0", port=port, debug=False)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ flask==3.0.0
2
+ requests==2.31.0
3
+ schedule==1.2.1
4
+ gunicorn==21.2.0