1234ty commited on
Commit
a53ba79
·
verified ·
1 Parent(s): fc3660d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +323 -146
app.py CHANGED
@@ -1,154 +1,331 @@
1
- import gradio as gr
2
- from llama_cpp import Llama
3
- from huggingface_hub import hf_hub_download
4
- import os
5
-
6
- # --- การตั้งค่าโมเดล GGUF (ดึงตรงจาก Server-to-Server ของ Hugging Face) ---
7
- REPO_ID = "Qwen/Qwen2.5-1.5B-Instruct-GGUF"
8
- FILENAME = "qwen2.5-1.5b-instruct-q4_k_m.gguf"
9
-
10
- print(f"📦 กำลังดึงโมเดล {FILENAME} จากคลัง {REPO_ID}...")
11
- model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
12
-
13
- # โหลดโมเดลด้วย Llama-cpp (จำกัด Context และเธรดเพื่อความลื่นไหลบน CPU ฟรี)
14
- llm = Llama(
15
- model_path=model_path,
16
- n_ctx=2048, # ขนาดบริบทในการประมวลผลสูงสุด
17
- n_threads=4, # จำนวน Core CPU สำหรับการประมวลผลคู่ขนาน
 
18
  )
19
 
20
- def respond(message, history):
21
- # กำหนดโครงสร้าง Prompt (ChatML Format) เพื่อให้โมเดลตอบได้ฉลาดและเป็นธรรมชาติ
22
- prompt = f"<|im_start|>system\nYou are a helpful, polite and intelligent AI assistant. Respond in a friendly tone in Thai or English based on the user's input.<|im_end|>\n"
23
- for user_msg, ai_msg in history:
24
- prompt += f"<|im_start|>user\n{user_msg}<|im_end|>\n<|im_start|>assistant\n{ai_msg}<|im_end|>\n"
25
- prompt += f"<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # รันประมวลผล AI แบบ Streaming (ตวหนัสืค่อยๆ ไหลออก)
28
- output = llm(
29
- prompt,
30
- max_tokens=768,
31
- stop=["<|im_end|>"],
32
- stream=True
33
- )
 
 
 
 
 
 
 
34
 
35
- token_stream = ""
36
- for token in output:
37
- content = token["choices"][0]["text"]
38
- token_stream += content
39
- yield token_stream
40
-
41
- # --- ดีไซน์หน้า็บระดับพรีเมียม (Ultra-Modern Glassmorphism & Neon Violet) ---
42
- # ตกแต่งด้วย CSS เพื่อลบความแข็งกระด้างของ Gradio และเพิ่มเอฟเฟกต์ถนอมสายตาโทนอวกาศ
43
- custom_css = """
44
- /* ตั้งค่าพื้นหลัง ไล่เฉดสีมืดลึกเพื่อการถนอมสายตาขั้นสุด */
45
- body, .gradio-container {
46
- background: linear-gradient(135deg, #070913 0%, #0c1020 50%, #151130 100%) !important;
47
- font-family: 'Inter', 'Chakra Petch', system-ui, -apple-system, sans-serif !important;
48
- color: #f1f5f9 !important;
49
- }
50
-
51
- /* ครอบกล่องแชทด้วยเอฟเฟกต์กระจกฝ้ากึ่งโปร่งใส */
52
- .glass-panel {
53
- background: rgba(13, 18, 36, 0.6) !important;
54
- backdrop-filter: blur(16px) !important;
55
- -webkit-backdrop-filter: blur(16px) !important;
56
- border: 1px solid rgba(255, 255, 255, 0.08) !important;
57
- border-radius: 24px !important;
58
- padding: 32px !important;
59
- box-shadow: 0 25px 60px rgba(0, 0, 0, 0.55) !important;
60
- margin-bottom: 20px !important;
61
- }
62
-
63
- /* ปรับแต่งหัวข้อด้านบนสุดให้มีความเป็นนีออนโกลว์ */
64
- .neon-title {
65
- text-align: center;
66
- padding: 15px 0 25px 0;
67
- }
68
- .neon-title h1 {
69
- font-size: 2.8rem !important;
70
- font-weight: 900 !important;
71
- background: linear-gradient(135deg, #cbd5e1 0%, #a78bfa 50%, #ec4899 100%);
72
- -webkit-background-clip: text !important;
73
- -webkit-text-fill-color: transparent !important;
74
- letter-spacing: -0.05em !important;
75
- margin-bottom: 8px !important;
76
- text-shadow: 0 0 35px rgba(139, 92, 246, 0.25) !important;
77
- }
78
- .neon-title p {
79
- color: #94a3b8 !important;
80
- font-size: 1.1rem !important;
81
- font-weight: 300 !important;
82
- }
83
-
84
- /* ตกแต่งกล่องข้อความสนทนาให้อ่อนนุ่ม สบายตา */
85
- .chatbot-frame {
86
- border: 1px solid rgba(255, 255, 255, 0.05) !important;
87
- background: rgba(8, 10, 24, 0.45) !important;
88
- border-radius: 18px !important;
89
- }
90
- .message.user {
91
- background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%) !important;
92
- color: #ffffff !important;
93
- border-radius: 20px 20px 4px 20px !important;
94
- box-shadow: 0 6px 20px rgba(109, 40, 217, 0.35) !important;
95
- border: none !important;
96
- }
97
- .message.bot {
98
- background: rgba(30, 41, 59, 0.65) !important;
99
- color: #e2e8f0 !important;
100
- border-radius: 20px 20px 20px 4px !important;
101
- border: 1px solid rgba(255, 255, 255, 0.08) !important;
102
- backdrop-filter: blur(8px) !important;
103
- }
104
-
105
- /* ปรับแต่งช่องป้อนข้อมูล (Input) */
106
- input[type="text"], textarea {
107
- background: rgba(11, 15, 30, 0.85) !important;
108
- border: 1px solid rgba(139, 92, 246, 0.3) !important;
109
- color: #ffffff !important;
110
- border-radius: 14px !important;
111
- padding: 14px !important;
112
- transition: all 0.3s ease !important;
113
- }
114
- input[type="text"]:focus, textarea:focus {
115
- border-color: #a78bfa !important;
116
- box-shadow: 0 0 15px rgba(139, 92, 246, 0.45) !important;
117
- }
118
-
119
- /* ซ่อนเครดิตส่วนเกินเพื่อความพรีเมียม */
120
- footer { display: none !important; }
121
- """
122
-
123
- # โครงสร้างหน้าเว็บ UI ด้วย Gradio Blocks
124
- with gr.Blocks(theme=gr.themes.Default(primary_hue="violet", neutral_hue="slate"), css=custom_css) as demo:
125
- with gr.Div(elem_classes="glass-panel"):
126
- # Header ของหน้าจอแชท
127
- gr.HTML("""
128
- <div class="neon-title">
129
- <h1>AETHER CHAT ENGINE v2</h1>
130
- <p>ระบบถาม-ตอบปัญญาประมวลผลผ่านโมเดล GGUF บนสเปกคลาวด์เสรี</p>
131
  </div>
132
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
- # ตัวควบคุมหน้าจอแชทแบบ Built-in ของ Gradio ที่เราจับแต่งหน้าตาผ่าน CSS แล้ว
135
- chat = gr.ChatInterface(
136
- fn=respond,
137
- examples=[
138
- "โมเดล AI ในฟอร์แมต GGUF มีความพิเศษอย่างไร?",
139
- "ช่วยแนะนำการเขียนโปรแกรมด้วยภาษา Python เบื้องต้น",
140
- "แต่งกลอนประทีปยามค่ำคืนในสไตล์อวกาศให้หน่อย"
141
- ],
142
- container=True,
143
- elem_classes="chatbot-frame"
144
- )
145
 
146
- # Footer แสดงสถานะด้านล่าง
147
- gr.HTML("""
148
- <div style="text-align: center; margin-top: 30px; font-size: 0.85rem; color: #64748b; letter-spacing: 0.08em;">
149
- ⚙️ ACTIVE ENGINE: <span style="color: #a78bfa; font-weight: 600;">QWEN 2.5 1.5B INSTRUCT (GGUF)</span> • HOSTED FREE ON HF SPACES
150
- </div>
151
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
- if __name__ == "__main__":
154
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import base64
3
+ import hashlib
4
+ import re
5
+ from fastapi import FastAPI, Request, HTTPException
6
+ from fastapi.responses import HTMLResponse
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from sqlitedict import SqliteDict
9
+
10
+ app = FastAPI()
11
+
12
+ # --- เปิด CORS ให้ทำงานร่วมกับหน้าเว็บหลักได้อย่างเสถียร ---
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"],
16
+ allow_credentials=True,
17
+ allow_methods=["*"],
18
+ allow_headers=["*"],
19
  )
20
 
21
+ # --- CONFIGURATION ---
22
+ TARGET_WEBSITE_URL = "https://rov3.vercel.app"
23
+ FIREWALL_DB = "firewall.sqlite"
24
+ MAX_REQUESTS_PER_MINUTE = 20
25
+ BLOCK_TIME_SECONDS = 3600
26
+ SECRET_KEY = "TANZAi_SUPER_SECRET_CORE_PRO"
27
+
28
+ # บัญชีดำสำหรับบอท, โปรแกรมแสกน และแฮกเกอร์ทูล
29
+ BOT_BLACKLIST = [
30
+ "sqlmap", "nmap", "dirbuster", "nikto", "hound", "scan", "headless",
31
+ "python-requests", "go-http-client", "java/", "curl/", "wget", "zmeu",
32
+ "masscan", "acunetix", "netsparker", "openvas", "censys", "shodan",
33
+ "headlesschrome", "puppeteer", "selenium", "playwright", "scrapy",
34
+ "postmanruntime", "insomnia", "cyberscan", "gobuster", "wuzz"
35
+ ]
36
+
37
+ # สแกนรูปแบบคำสั่งแฮกเกอร์ XSS, SQLi, RCE, Path Traversal, LFI/RFI
38
+ EXPLOIT_PAYLOADS = [
39
+ r"select\s+.*\s+from", r"insert\s+into", r"union\s+select", r"drop\s+table",
40
+ r"<\s*script", r"javascript:", r"onerror\s*=", r"onload\s*=", r"\'\s*or\s*.*=.*",
41
+ r"\.\.\/", r"etc\/passwd", r"boot\.ini", r"\{\s*\{\s*.*\}\s*\}", r"\$\{.*\}",
42
+ r"exec\s*\(\s*", r"eval\s*\(\s*", r"union\s+all\s+select"
43
+ ]
44
+
45
+ def get_client_ip(request: Request):
46
+ for header in ["cf-connecting-ip", "x-real-ip", "x-forwarded-for"]:
47
+ value = request.headers.get(header)
48
+ if value:
49
+ return value.split(',')[0].strip()
50
+ return request.client.host
51
+
52
+ def generate_token(ip):
53
+ # เปลี่ยนแปลงค่า Token ทุกๆ 10 นาที ผูกกับเวลาปัจจุบันของระบบ
54
+ time_window = int(time.time() / 600)
55
+ return hashlib.md5(f"{ip}{SECRET_KEY}{time_window}".encode()).hexdigest()
56
+
57
+ def is_blocked(ip):
58
+ with SqliteDict(FIREWALL_DB, autocommit=True) as db:
59
+ if ip in db:
60
+ blocked_until = db[ip].get("blocked_until", 0)
61
+ if blocked_until > time.time(): return True
62
+ else: del db[ip]
63
+ return False
64
+
65
+ def check_rate_limit_and_block(ip):
66
+ with SqliteDict(FIREWALL_DB, autocommit=True) as db:
67
+ data = db.get(ip, {"count": 0, "first_request": time.time()})
68
+ data["count"] += 1
69
+ db[ip] = data
70
+ if time.time() - data["first_request"] > 60:
71
+ del db[ip]
72
+ return
73
+ if data["count"] > MAX_REQUESTS_PER_MINUTE:
74
+ data["blocked_until"] = time.time() + BLOCK_TIME_SECONDS
75
+ db[ip] = data
76
+
77
+ def force_block_ip(ip):
78
+ with SqliteDict(FIREWALL_DB, autocommit=True) as db:
79
+ db[ip] = {
80
+ "count": 999,
81
+ "first_request": time.time(),
82
+ "blocked_until": time.time() + BLOCK_TIME_SECONDS
83
+ }
84
+
85
+ def cyber_security_waf(request: Request):
86
+ user_agent = request.headers.get("user-agent", "").lower()
87
 
88
+ # 1. ตรวจสอบักษณะพื้นฐานละบัญชีดำขอ
89
+ if not user_agent or len(user_agent) < 12:
90
+ return False
91
+ for bot in BOT_BLACKLIST:
92
+ if bot in user_agent:
93
+ return False
94
+
95
+ # ตรวจสอบว่ามี Headers ที่เบราว์เซอร์ปกติควรมีหรือไม่
96
+ if not request.headers.get("accept") or not request.headers.get("accept-language"):
97
+ return False
98
+
99
+ # 2. Deep Input Inspection (สแกนการพยายามเจาะระบบผ่านทาง URL, Query, Path)
100
+ query_params = str(request.query_params).lower()
101
+ url_path = str(request.url.path).lower()
102
 
103
+ for pattern in EXPLOIT_PAYLOADS:
104
+ if re.search(pattern, query_params) or re.search(pattern, url_path):
105
+ return False
106
+
107
+ return True
108
+
109
+ # หน้า UI ด่านตรจความปลอดภัยระดับสูง
110
+ def generate_challenge_html(ip):
111
+ html = f"""
112
+ <!DOCTYPE html>
113
+ <html lang="th">
114
+ <head>
115
+ <meta charset="UTF-8">
116
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
117
+ <title>TANZAi | กำลังทำการตรวจสอบความปลอดภัย</title>
118
+ <style>
119
+ body {{ font-family: -apple-system, system-ui, sans-serif; background: white; color: black; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; }}
120
+ .container {{ max-width: 500px; width: 90%; }}
121
+ h1 {{ font-size: 40px; font-weight: 500; margin-bottom: 0px; color: #10b981; }}
122
+ h2 {{ font-size: 28px; font-weight: 500; margin-top: 10px; margin-bottom: 5px; }}
123
+ p {{ font-size: 16px; color: #555; line-height: 1.4; }}
124
+ #cf-widget-container {{ border: 1px solid #ddd; border-radius: 8px; background: #fafafa; padding: 15px; display: flex; align-items: center; justify-content: space-between; margin-top: 25px; }}
125
+ .cf-left {{ display: flex; align-items: center; }}
126
+ .cf-logo {{ width: 100px; filter: hue-rotate(140deg); }}
127
+ .cf-ip-info {{ font-size: 12px; color: #666; margin-top: 20px; }}
128
+ .captcha-box {{ display: flex; align-items: center; cursor: pointer; user-select: none; }}
129
+ .captcha-checkbox {{ width: 24px; height: 24px; border: 2px solid #ccc; border-radius: 4px; margin-right: 12px; display: flex; align-items: center; justify-content: center; background: #ffffff; }}
130
+ .captcha-checkbox.checked {{ background: #10b981; border-color: #10b981; }}
131
+ .captcha-checkbox.checked::after {{ content: '✔'; color: white; font-size: 14px; font-weight: bold; }}
132
+ .cf-spinner {{ border: 4px solid #eee; border-top: 4px solid #10b981; border-radius: 50%; width: 25px; height: 25px; animation: spin 1s linear infinite; margin-right: 15px; display: none; }}
133
+ @keyframes spin {{ 0% {{ transform: rotate(0deg); }} 100% {{ transform: rotate(360deg); }} }}
134
+ </style>
135
+ </head>
136
+ <body>
137
+ <div class="container">
138
+ <h1>TANZAi</h1>
139
+ <h2>กำลังทำการตรวจสอบความปลอดภัย</h2>
140
+ <p>กรุณาติ๊กเลือกช่องด้านล่างเพื่อยืนยันว่าคุณไม่ใช่สคริปต์อัตโนมัติ</p>
141
+ <div id="cf-widget-container">
142
+ <div class="cf-left">
143
+ <div class="cf-spinner" id="main-spinner"></div>
144
+ <div id="status-area">
145
+ <div class="captcha-box" id="click-target" onclick="triggerChallenge()">
146
+ <div class="captcha-checkbox" id="check-visual"></div>
147
+ <span style="font-size: 15px; color: #333; font-weight: 500;">ฉันไม่ใช่โปรแกรมอัตโนมัติ</span>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ <div class="cf-right">
152
+ <img src="https://upload.wikimedia.org/wikipedia/commons/4/4b/Cloudflare_Logo.svg" alt="Cloudflare" class="cf-logo">
153
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  </div>
155
+ <div class="cf-ip-info">IP ของคุณ: {ip}</div>
156
+ </div>
157
+
158
+ <script>
159
+ let isProcessing = false;
160
+ let humanVerified = false;
161
+
162
+ // ดักจับการขยับเมาส์หรือทัชหน้าจอจริง ป้องกันบอทส่งคำสั่งคลิกตรงๆ
163
+ window.addEventListener('mousemove', () => humanVerified = true);
164
+ window.addEventListener('touchstart', () => humanVerified = true);
165
+
166
+ async function triggerChallenge() {{
167
+ if(isProcessing) return;
168
+ isProcessing = true;
169
+ document.getElementById('check-visual').classList.add('checked');
170
+
171
+ setTimeout(async () => {{
172
+ document.getElementById('click-target').style.display = 'none';
173
+ document.getElementById('main-spinner').style.display = 'block';
174
+ const statusArea = document.getElementById('status-area');
175
+ statusArea.innerHTML = '<span style="font-size:14px;">กำลังตรวจสอบคุณลักษณะเบราว์เซอร์...</span>';
176
+
177
+ // ด่านตรวจจับ Client-Side Security แบบเข้มข้นสูงสุด
178
+ const isBot = navigator.webdriver ||
179
+ !navigator.userAgent ||
180
+ window.outerWidth === 0 ||
181
+ window.outerHeight === 0 ||
182
+ (window.chrome && !window.chrome.runtime) ||
183
+ !humanVerified;
184
+
185
+ if (isBot) {{
186
+ statusArea.innerHTML = '<span style="color:#ef4444;font-size:14px;">ตรวจพบข้อผิดพลาดการเชื่อมต่ออัตโนมัติ!</span>';
187
+ await fetch('/report_spam');
188
+ return;
189
+ }}
190
+
191
+ try {{
192
+ const res = await fetch('/get_token');
193
+ if(res.status != 200) {{ throw new Error("Server Error"); }}
194
+ const data = await res.json();
195
+ if(data.token) {{
196
+ statusArea.innerHTML = '<span style="color:#10b981;font-size:14px;">ตรวจสอบสำเร็จ! กำลังนำทาง...</span>';
197
+ setTimeout(() => {{
198
+ window.location.href = "{TARGET_WEBSITE_URL}?tz_token=" + data.token;
199
+ }}, 500);
200
+ }} else {{ location.reload(); }}
201
+ }} catch(e) {{
202
+ // แสดงผลเมื่อหลังบ้านค้าง/หลับ
203
+ document.body.innerHTML = '<div style="font-family:-apple-system,sans-serif;background:#0f172a;color:#f8fafc;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;text-align:center;"><div style="max-width:450px;width:90%;background:#1e1b4b;border:2px solid #ef4444;padding:40px;border-radius:16px;box-shadow:0 10px 25px rgba(239,68,68,0.2);"><div style="font-size:4rem;margin-bottom:20px;">⚠️</div><h1 style="color:#ef4444;font-size:1.8rem;margin:0 0 12px 0;">เกิดข้อผิดพลาด</h1><p style="color:#cbd5e1;font-size:1.1rem;margin:0 0 10px 0;">ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้</p><p style="color:#64748b;font-size:0.9rem;margin-bottom:25px;">เซิร์ฟเวอร์อาจจะกำลังหลับหรือมีปัญหาชั่วคราว</p><button onclick="window.location.reload()" style="background:#ef4444;color:white;border:none;padding:12px 24px;border-radius:6px;font-size:1rem;font-weight:bold;cursor:pointer;">ลองใหม่อีกครั้ง</button></div></div>';
204
+ }}
205
+ }}, 400);
206
+ }}
207
+ </script>
208
+ </body>
209
+ </html>
210
+ """
211
+ return HTMLResponse(content=html)
212
+
213
+ @app.get("/")
214
+ async def gatekeeper(request: Request):
215
+ ip = get_client_ip(request)
216
+ if not cyber_security_waf(request):
217
+ force_block_ip(ip)
218
+ raise HTTPException(status_code=400, detail="Security Violation Detected")
219
+ if is_blocked(ip):
220
+ return HTMLResponse(content='<div style="font-family:-apple-system,sans-serif;background:#0f172a;color:#f8fafc;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;text-align:center;"><div style="max-width:450px;width:90%;background:#1e1b4b;border:2px solid #ef4444;padding:40px;border-radius:16px;"><h1 style="color:#ef4444;">เกิดข้อผิดพลาด</h1><p>ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้ (IP Blocked)</p></div></div>', status_code=403)
221
+ check_rate_limit_and_block(ip)
222
+ return generate_challenge_html(ip)
223
+
224
+ @app.get("/verify")
225
+ async def verify(token: str, request: Request):
226
+ ip = get_client_ip(request)
227
+ if is_blocked(ip):
228
+ return {"status": "error", "valid": False, "banned": True}
229
+ is_valid = (token == generate_token(ip))
230
+ return {"status": "ok" if is_valid else "error", "valid": is_valid, "banned": False}
231
+
232
+ @app.get("/get_token")
233
+ async def get_token(request: Request):
234
+ ip = get_client_ip(request)
235
+ if is_blocked(ip):
236
+ raise HTTPException(status_code=403, detail="Blocked")
237
+ return {"token": generate_token(ip)}
238
+
239
+ @app.get("/report_spam")
240
+ async def report_spam(request: Request):
241
+ ip = get_client_ip(request)
242
+ force_block_ip(ip)
243
+ return {"status": "banned"}
244
+
245
+ # --- แทร็กสคริปต์ส่งไปฝังหน้าเว็บหลัก Vercel (ระบบความปลอดภัยขั้นสูง + ดักจับเซิร์ฟเวอร์หลับ + หน้าต่างยินยอมคุกกี้) ---
246
+ @app.get("/js")
247
+ async def get_js_script(request: Request):
248
+ ip = get_client_ip(request)
249
+ hf_url = f"{request.url.scheme}://{request.url.netloc}"
250
+
251
+ error_layout = """<div style='font-family:-apple-system,sans-serif;background:#0f172a;color:#f8fafc;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;text-align:center;position:fixed;top:0;left:0;width:100vw;z-index:999999;'><div style='max-width:450px;width:90%;background:#1e1b4b;border:2px solid #ef4444;padding:40px;border-radius:16px;box-shadow:0 10px 25px rgba(239,68,68,0.2);'><div style='font-size:4rem;margin-bottom:20px;'>⚠️</div><h1 style='color:#ef4444;font-size:1.8rem;margin:0 0 12px 0;'>เกิดข้อผิดพลาด</h1><p style='color:#cbd5e1;font-size:1.1rem;margin:0 0 10px 0;'>ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้</p><p style='color:#64748b;font-size:0.9rem;margin-bottom:25px;'>เซิร์ฟเวอร์อาจจะกำลังหลับหรือมีปัญหาชั่วคราว</p><button onclick='window.location.reload()' style='background:#ef4444;color:white;border:none;padding:12px 24px;border-radius:6px;font-size:1rem;font-weight:bold;cursor:pointer;'>ลองใหม่อีกครั้ง</button></div></div>"""
252
+
253
+ if is_blocked(ip):
254
+ js_code = f"""
255
+ (function() {{
256
+ localStorage.clear();
257
+ document.body.innerHTML="{error_layout}";
258
+ }})();
259
+ """
260
+ return HTMLResponse(content=js_code, media_type="application/javascript")
261
 
262
+ js_code = f"""
263
+ (function() {{
264
+ const h='{hf_url}',u=new URL(location),t=u.searchParams.get('tz_token')||localStorage.tz;let c=[];
 
 
 
 
 
 
 
 
265
 
266
+ function showErrPage() {{
267
+ localStorage.clear();
268
+ document.body.innerHTML="{error_layout}";
269
+ }}
270
+
271
+ // ฟังก์ชันสร้างหน้าต่างยอมรับคุกกี้ (Cookie Consent Banner)
272
+ function initCookieBanner() {{
273
+ if (localStorage.cookieAccepted === 'true') return;
274
+ const b = document.createElement('div');
275
+ b.id = 'tz-cookie-banner';
276
+ b.style = 'position:fixed;bottom:20px;left:50%;transform:translateX(-50%);width:90%;max-width:400px;background:#1e293b;color:#f8fafc;padding:16px;border-radius:12px;box-shadow:0 10px 25px rgba(0,0,0,0.3);z-index:99999;font-family:-apple-system,sans-serif;display:flex;flex-direction:column;gap:12px;border:1px solid #334155;';
277
+ b.innerHTML = '<div style="font-size:14px;line-height:1.4;">🍪 เว็บไซต์นี้ใช้คุกกี้และระบบจัดเก็บข้อมูลในเบราว์เซอร์เพื่อความปลอดภัยและมอบประสบการณ์การใช้งานที่ดีที่สุดให้กับคุณ</div><button id="tz-accept-cookie" style="background:#10b981;color:white;border:none;padding:8px 16px;border-radius:6px;font-weight:bold;cursor:pointer;font-size:13px;transition:background 0.2s;">ยอมรับทั้งหมด</button>';
278
+ document.body.appendChild(b);
279
+ document.getElementById('tz-accept-cookie').onclick = function() {{
280
+ localStorage.cookieAccepted = 'true';
281
+ b.remove();
282
+ }};
283
+ }}
284
+
285
+ // 1. ตรวจสอบความปลอดภัยระดับลึกบนเบราว์เซอร์เป้าหมาย
286
+ const isSpamBot = navigator.webdriver ||
287
+ window.outerWidth === 0 ||
288
+ self !== top ||
289
+ (window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized);
290
+
291
+ if (isSpamBot) {{ showErrPage(); return; }}
292
+
293
+ // 2. ดักจับการสแปมคลิกถี่เกินความจริง (Anti-Click Spam)
294
+ window.addEventListener('click',()=>{{
295
+ c=c.filter(x=>Date.now()-x<1000);c.push(Date.now());
296
+ if(c.length>=5){'{'}
297
+ fetch(h+'/report_spam').then(()=>{{ showErrPage(); }}).catch(()=>{{ showErrPage(); }});
298
+ {'}'}
299
+ }});
300
+
301
+ if(t){{
302
+ if(t.length !== 32) {{ showErrPage(); return; }}
303
+ localStorage.tz=t;
304
+ if(u.searchParams.has('tz_token')){{u.searchParams.delete('tz_token');history.replaceState({{}},'',u);}}
305
+
306
+ const controller = new AbortController();
307
+ const timeoutId = setTimeout(() => {{ controller.abort(); showErrPage(); }}, 5000);
308
 
309
+ fetch(h+'/verify?token='+t, {{ signal: controller.signal }})
310
+ .then(r=>{{
311
+ clearTimeout(timeoutId);
312
+ if(r.status !== 200) {{ showErrPage(); return; }}
313
+ return r.json();
314
+ }})
315
+ .then(d=>{{
316
+ if(!d || !d.valid || d.banned){{ showErrPage(); }}
317
+ else {{ initCookieBanner(); }} // เมื่อ Token ผ่าน ให้รันระบบคุกกี้ขึ้นมา
318
+ }})
319
+ .catch(()=>{{ showErrPage(); }});
320
+ }}else{{
321
+ // ระบบตรวจสอบและปลุกเซิร์ฟเวอร์ (ดักจับ Error/หลับ)
322
+ const controller = new AbortController();
323
+ const timeoutId = setTimeout(() => {{ controller.abort(); showErrPage(); }}, 5000);
324
+
325
+ fetch(h, {{ method: 'HEAD', mode: 'no-cors', signal: controller.signal }})
326
+ .then(() => {{ window.location.href = h; }})
327
+ .catch(() => {{ showErrPage(); }});
328
+ }}
329
+ })();
330
+ """
331
+ return HTMLResponse(content=js_code, media_type="application/javascript")