Dán link Ngrok (hoặc kiểm tra Firebase) rồi bấm nút Connect.
import React, { useState, useEffect } from 'react'; import { Image as ImageIcon, Loader2, Sparkles, Terminal, AlertCircle, Settings, Layers, Cpu, Maximize, Zap, Wifi, WifiOff, ChevronDown, ChevronUp, Play, RefreshCw, Link as LinkIcon } from 'lucide-react'; // 👇 LINK DATABASE CỦA BẠN (Thay link Firebase của bạn vào đây) const FIREBASE_DB_URL = "https://uploadlink-28426-default-rtdb.firebaseio.com/api_url.json"; // Link dự phòng (Local hoặc mặc định) const DEFAULT_API_URL = "pretty-dory-noble.ngrok-free.app"; const SAMPLERS = [ "Euler a", "Euler", "LMS", "Heun", "DPM2", "DPM2 a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SDE", "DPM++ 2M Karras", "DPM++ SDE Karras", "DDIM", "UniPC" ]; const App = () => { // --- STATE --- const [apiUrl, setApiUrl] = useState(DEFAULT_API_URL); const [prompt, setPrompt] = useState('A futuristic city with neon lights, cyberpunk style'); const [negPrompt, setNegPrompt] = useState('blurry, bad quality, watermark, text, ugly, distorted, nsfw'); const [checkpoints, setCheckpoints] = useState(["Loading..."]); const [loras, setLoras] = useState(["None"]); const [vaes, setVaes] = useState(["Default"]); const [upscalers, setUpscalers] = useState(["None"]); const [selectedCheckpoint, setSelectedCheckpoint] = useState(""); const [selectedLora, setSelectedLora] = useState("None"); const [selectedVae, setSelectedVae] = useState("Default"); const [selectedUpscaler, setSelectedUpscaler] = useState("None"); // Settings const [width, setWidth] = useState(1024); const [height, setHeight] = useState(1024); const [steps, setSteps] = useState(30); const [cfgScale, setCfgScale] = useState(7.0); const [seed, setSeed] = useState(-1); const [sampler, setSampler] = useState("DPM++ 2M Karras"); const [upscaleStrength, setUpscaleStrength] = useState(0.35); const [upscaleFactor, setUpscaleFactor] = useState(1.0); const [showAdvanced, setShowAdvanced] = useState(false); const [generatedImage, setGeneratedImage] = useState(null); const [loading, setLoading] = useState(false); const [fetchingInfo, setFetchingInfo] = useState(false); const [error, setError] = useState(''); const [logs, setLogs] = useState([]); const [serverStatus, setServerStatus] = useState('unknown'); // unknown, connected, disconnected // --- FUNCTIONS --- const addLog = (message) => { const timestamp = new Date().toLocaleTimeString(); setLogs(prev => [`[${timestamp}] ${message}`, ...prev].slice(0, 50)); }; // Hàm kết nối server (Logic Firebase + Manual) const connectToServer = async (manualUrl = null) => { setFetchingInfo(true); setError(''); let targetUrl = manualUrl || apiUrl; try { // 1. Nếu không nhập tay, thử lấy từ Firebase trước if (!manualUrl && FIREBASE_DB_URL.includes("firebaseio.com")) { addLog("☁️ Đang lấy link từ Firebase..."); try { const fireRes = await fetch(FIREBASE_DB_URL); if (fireRes.ok) { const fireUrl = await fireRes.json(); if (fireUrl && fireUrl.startsWith("http")) { targetUrl = fireUrl; addLog(`🔗 Tìm thấy server: ${fireUrl}`); } } } catch (e) { console.warn("Lỗi Firebase:", e); addLog("⚠️ Không lấy được link từ Firebase, dùng link mặc định."); } } // Clean URL const cleanUrl = targetUrl.replace(/\/$/, ""); setApiUrl(cleanUrl); // Cập nhật UI // FIX: Xử lý nhẹ nhàng nếu là link mặc định thay vì báo lỗi đỏ if (cleanUrl.includes("thay-link")) { setServerStatus('disconnected'); addLog("⚠️ Chưa có link Server. Vui lòng nhập link Ngrok vào ô bên dưới."); setFetchingInfo(false); return; // Dừng tại đây, không ném lỗi } addLog(`Kiểm tra kết nối: ${cleanUrl}...`); // 2. Gọi API /info để lấy danh sách model const res = await fetch(`${cleanUrl}/info`, { headers: { "ngrok-skip-browser-warning": "69420" } }); if (!res.ok) { const text = await res.text(); throw new Error(`Lỗi Server (${res.status}): ${text.slice(0, 50)}`); } const data = await res.json(); if (data.models?.length > 0) { setCheckpoints(data.models); setSelectedCheckpoint(data.models[0]); } if (data.loras?.length > 0) setLoras(data.loras); if (data.vaes?.length > 0) setVaes(data.vaes); if (data.upscalers?.length > 0) setUpscalers(data.upscalers); setServerStatus('connected'); addLog(`✅ Kết nối thành công!`); } catch (err) { console.error(err); setServerStatus('disconnected'); setError(err.message); addLog(`❌ Kết nối thất bại: ${err.message}`); } finally { setFetchingInfo(false); } }; // Auto connect khi mới vào useEffect(() => { connectToServer(); }, []); const handleGenerate = async () => { if (serverStatus !== 'connected') { setError('Vui lòng kết nối Server trước!'); return; } setLoading(true); setError(''); addLog(`🎨 Đang vẽ...`); try { let upscalerValue = "None"; if (selectedUpscaler !== "None") upscalerValue = selectedUpscaler; else if (upscaleFactor > 1) upscalerValue = upscaleFactor.toString(); const payload = { prompt, negative_prompt: negPrompt, width, height, // Gửi kích thước ảnh steps, cfg_scale: cfgScale, seed, sampler_name: sampler, checkpoint: selectedCheckpoint, lora: selectedLora, vae: selectedVae, upscaler: upscalerValue, upscale_strength: upscaleStrength }; const response = await fetch(`${apiUrl}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json', "ngrok-skip-browser-warning": "69420", }, body: JSON.stringify(payload), }); if (!response.ok) throw new Error(`Lỗi Server: ${response.status}`); const data = await response.json(); if (data.image) { setGeneratedImage(data.image); addLog('✨ Đã vẽ xong!'); } } catch (err) { setError(err.message); addLog(`Lỗi: ${err.message}`); } finally { setLoading(false); } }; return (
Dán link Ngrok (hoặc kiểm tra Firebase) rồi bấm nút Connect.