raw9 commited on
Commit
69a52d2
·
verified ·
1 Parent(s): fc25ee8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -40
app.py CHANGED
@@ -1,48 +1,128 @@
 
 
1
  import os
2
- import sys
3
- import subprocess
4
- import logging
5
- import signal
6
 
7
- # Minimal and clean logging setup
8
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
9
 
10
- def start_terminal():
11
- user_pass = os.environ.get("TERMINAL_AUTH")
12
-
13
- if not user_pass:
14
- logging.warning("⚠️ TERMINAL_AUTH secret is not set in Space Settings!")
15
- logging.warning("Using fallback credentials. Please set 'TERMINAL_AUTH' for security.")
16
- user_pass = "admin:1234"
17
- else:
18
- logging.info("🔒 TERMINAL_AUTH secret loaded successfully.")
19
-
20
- logging.info("🚀 Starting Ultra-Fast Professional Workspace on Port 7860...")
21
-
22
- # Using absolute path for ttyd to prevent any PATH-related bugs
23
- command = ["/usr/local/bin/ttyd", "-p", "7860", "-c", user_pass, "tmux", "new-session", "-A", "-s", "workspace", "zsh"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
 
25
  try:
26
- # Popen bebohar kora holo jate graceful shutdown kaaj kore
27
- process = subprocess.Popen(command)
28
-
29
- # Hugging Face-er jonno Graceful Shutdown setup
30
- def sigterm_handler(signum, frame):
31
- logging.info("🛑 Shutting down terminal server gracefully...")
32
- process.terminate()
33
- sys.exit(0)
 
 
 
 
 
 
34
 
35
- signal.signal(signal.SIGTERM, sigterm_handler)
36
- signal.signal(signal.SIGINT, sigterm_handler)
37
-
38
- process.wait()
39
-
40
- except FileNotFoundError:
41
- logging.error("❌ Error: 'ttyd' or 'tmux' command not found. Please check Dockerfile installations.")
42
- sys.exit(1)
43
  except Exception as e:
44
- logging.error(f"❌ Terminal crashed: {e}")
45
- sys.exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- if __name__ == "__main__":
48
- start_terminal()
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import time
3
  import os
4
+ from contextlib import asynccontextmanager
5
+ from fastapi import FastAPI, Request, HTTPException, status
6
+ from fastapi.responses import JSONResponse
 
7
 
8
+ # গ্লোবাল ভেরিয়েবল সেটআপ
9
+ MAX_CONCURRENT_REQUESTS = 100 # আপনার "Unlimited" সিস্টেমকে কন্ট্রোল করার জন্য লিমিট
10
+ request_semaphore = None
11
 
12
+ @asynccontextmanager
13
+ async def lifespan(app: FastAPI):
14
+ """
15
+ অ্যাপ্লিকেশন স্টার্টআপ এবং শাটডাউন ইভেন্ট হ্যান্ডলার।
16
+ এখানে আমরা সেমাফোর ইনিশিয়ালাইজ করছি যাতে একসাথে অতিরিক্ত রিকোয়েস্ট না আসে।
17
+ """
18
+ global request_semaphore
19
+ request_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
20
+ print("🚀 FastAPI Server Starting up... Protection Mechanisms Enabled.")
21
+ yield
22
+ print("🛑 FastAPI Server Shutting down gracefully...")
23
+
24
+ # FastAPI ইনস্ট্যান্স তৈরি
25
+ app = FastAPI(
26
+ title="High-Performance Protected API",
27
+ description="Designed for High-Load Shared Environments",
28
+ version="1.0.0",
29
+ lifespan=lifespan
30
+ )
31
+
32
+ @app.middleware("http")
33
+ async def limit_concurrency_and_timeout(request: Request, call_next):
34
+ """
35
+ এই মিডলওয়্যারটি দুটি কাজ করে:
36
+ ১. কনকারেন্সি লিমিট চেক করে (Semaphore)
37
+ ২. রিকোয়েস্ট প্রসেস হতে কত সময় লাগছে তা মাপে
38
+ """
39
+ start_time = time.time()
40
 
41
+ # সেমাফোর দিয়ে কনকারেন্সি কন্ট্রোল
42
  try:
43
+ async with request_semaphore:
44
+ # যদি সার্ভার স্লো থাকে, তবে একটি টাইমআউট মেকানিজম (যেমন ৫ সেকেন্ড)
45
+ # এখানে asyncio.wait_for ব্যবহার করা যেতে পারে, তবে সাধারণ ব্যবহারের জন্য call_next যথেষ্ট
46
+ response = await call_next(request)
47
+
48
+ process_time = time.time() - start_time
49
+ # হেডারে রেসপন্স টাইম যুক্ত করে দেওয়া হলো
50
+ response.headers["X-Process-Time"] = str(round(process_time, 4))
51
+
52
+ # যদি রিকোয়েস্ট খুব বেশি সময় নেয় (যেমন ২ সেকেন্ডের বেশি), তাহলে ওয়ার্নিং দিতে পারি (লগিংয়ের জন্য)
53
+ if process_time > 2.0:
54
+ print(f"⚠️ Warning: Slow request detected on {request.url.path} ({process_time:.2f}s)")
55
+
56
+ return response
57
 
 
 
 
 
 
 
 
 
58
  except Exception as e:
59
+ # কোনো কারণে এরর হলে ৫০০ স্ট্যাটাস রিটার্ন করবে
60
+ return JSONResponse(
61
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
62
+ content={"detail": "Internal Server Error or Server Overloaded", "error": str(e)}
63
+ )
64
+
65
+ def get_system_metrics():
66
+ """
67
+ সরাসরি লিনাক্স কার্নেল থেকে সিস্টেমের অবস্থা জানার ফাংশন।
68
+ এটি আপনার ১৩+ লোড এভারেজ ট্র্যাক করতে সাহায্য করবে।
69
+ """
70
+ metrics = {"load_average": None, "memory_mb": {}}
71
+ try:
72
+ # Load Average চেক (1 min, 5 min, 15 min)
73
+ load1, load5, load15 = os.getloadavg()
74
+ metrics["load_average"] = {"1_min": load1, "5_min": load5, "15_min": load15}
75
+
76
+ # Memory চেক (/proc/meminfo থেকে)
77
+ with open('/proc/meminfo', 'r') as f:
78
+ meminfo = {}
79
+ for line in f.readlines()[:5]: # প্রথম ৫ লাইন যথেষ্ট
80
+ parts = line.split(':')
81
+ if len(parts) == 2:
82
+ key = parts[0].strip()
83
+ # KB থেকে MB তে কনভার্ট
84
+ val = int(parts[1].strip().replace(' kB', '')) // 1024
85
+ meminfo[key] = val
86
+ metrics["memory_mb"] = meminfo
87
+ except Exception:
88
+ pass
89
+
90
+ return metrics
91
+
92
+ @app.get("/")
93
+ async def root():
94
+ """বেসিক হেলথ এবং ওয়েলকাম এন্ডপয়েন্ট"""
95
+ return {
96
+ "message": "Welcome to the Protected FastAPI Server",
97
+ "status": "Running smoothly despite the ghost load!"
98
+ }
99
+
100
+ @app.get("/system-health")
101
+ async def system_health():
102
+ """
103
+ আপনার কন্টেইনার এবং মেইন হোস্টের বর্তমান স্বাস্থ্য পরীক্ষা করার এন্ডপয়েন্ট।
104
+ """
105
+ metrics = get_system_metrics()
106
+
107
+ # যদি লোড ১৫ এর বেশি হয়, তবে সতর্কবার্তা দেবে
108
+ is_overloaded = False
109
+ if metrics["load_average"] and metrics["load_average"]["1_min"] > 15.0:
110
+ is_overloaded = True
111
+
112
+ return {
113
+ "status": "warning" if is_overloaded else "healthy",
114
+ "host_overloaded": is_overloaded,
115
+ "metrics": metrics,
116
+ "advice": "If host_overloaded is true, expect slower API response times due to shared context switching."
117
+ }
118
 
119
+ @app.get("/heavy-task")
120
+ async def simulate_heavy_task():
121
+ """
122
+ একটি নন-ব্লকিং টাস্কের উদাহরণ।
123
+ টাইম ডট স্লিপ (time.sleep) ব্যবহার না করে asyncio.sleep ব্যবহার করা হয়েছে,
124
+ যাতে হাই কন্টেক্সট সুইচিংয়ের সময় অন্যান্য রিকোয়েস্ট ব্লক না হয়।
125
+ """
126
+ # ৩ সেকেন্ডের একটি এসিনক্রোনাস ডিলে (ডাটাবেস কল বা এপিআই কলের বিকল্প)
127
+ await asyncio.sleep(3)
128
+ return {"message": "Heavy task completed successfully without blocking the CPU."}