Test / app.py
Sjwjne's picture
Update app.py
9f37e34 verified
Raw
History Blame Contribute Delete
6.39 kB
import os
import shutil
import subprocess
import uuid
import gradio as gr
from fastapi.staticfiles import StaticFiles
from fastapi import FastAPI
# --- Static Folder Setup ---
PREVIEW_PATH = os.path.abspath("hosted_sites")
if not os.path.exists(PREVIEW_PATH):
os.makedirs(PREVIEW_PATH)
def find_index_html(start_dir):
"""
Project mein index.html dhoondhne ke liye helper function.
Build directories ko priority milti hai.
"""
# Priority folders first
for root, dirs, files in os.walk(start_dir):
if "index.html" in files:
parts = os.path.normpath(root).split(os.sep)
if any(p in parts for p in ['dist', 'build', 'out', 'public']):
return os.path.relpath(os.path.join(root, "index.html"), PREVIEW_PATH)
# Fallback to any index.html
for root, dirs, files in os.walk(start_dir):
if "index.html" in files:
return os.path.relpath(os.path.join(root, "index.html"), PREVIEW_PATH)
return None
def run_command(cmd, cwd):
"""Real-time build logs capture karne ke liye generator function"""
process = subprocess.Popen(
cmd,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
shell=True
)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
yield output.strip()
rc = process.poll()
if rc != 0:
raise subprocess.CalledProcessError(rc, cmd)
def deploy_project(zip_file):
if zip_file is None:
yield "❌ Please upload a ZIP file.", "", gr.update(visible=False), gr.update(visible=False)
return
# Unique deployment folder create karna
deploy_id = f"site-{str(uuid.uuid4())[:8]}"
deploy_dir = os.path.join(PREVIEW_PATH, deploy_id)
os.makedirs(deploy_dir)
log_accumulator = []
def log(msg):
log_accumulator.append(msg)
return "\n".join(log_accumulator)
try:
# Extract files
yield log("πŸ“¦ Extracting ZIP archive..."), "", gr.update(visible=False), gr.update(visible=False)
shutil.unpack_archive(zip_file.name, deploy_dir)
root_path = deploy_dir
is_node_project = False
# Check node project
for root, dirs, files in os.walk(deploy_dir):
if "package.json" in files:
root_path = root
is_node_project = True
break
# Build steps
if is_node_project:
yield log("πŸ“ Node.js project detected."), "", gr.update(visible=False), gr.update(visible=False)
yield log("⚑ Running 'npm install'..."), "", gr.update(visible=False), gr.update(visible=False)
for line in run_command("npm install", root_path):
yield log(f"[npm] {line}"), "", gr.update(visible=False), gr.update(visible=False)
yield log("πŸ—οΈ Running 'npm run build'..."), "", gr.update(visible=False), gr.update(visible=False)
for line in run_command("npm run build", root_path):
yield log(f"[build] {line}"), "", gr.update(visible=False), gr.update(visible=False)
else:
yield log("πŸ“„ Static HTML project detected. Skipping build steps."), "", gr.update(visible=False), gr.update(visible=False)
# Locate preview entry-point
relative_index = find_index_html(deploy_dir)
if relative_index:
preview_url = f"/preview/{relative_index.replace(os.sep, '/')}"
success_msg = f"πŸš€ **Deployment Successful!**\n\nYour site is live at: `/preview/{relative_index}`"
yield log("πŸŽ‰ Site deployed successfully!"), success_msg, gr.update(src=preview_url, visible=True), gr.update(value=f"Open App in New Tab 🌐", link=preview_url, visible=True)
else:
yield log("❌ Error: 'index.html' not found."), "❌ Error: index.html not found.", gr.update(visible=False), gr.update(visible=False)
except subprocess.CalledProcessError as e:
yield log(f"\n❌ Build Failed with exit code {e.returncode}"), "❌ Deployment Failed. Check console logs.", gr.update(visible=False), gr.update(visible=False)
except Exception as e:
yield log(f"\n❌ Unexpected Error: {str(e)}"), "❌ Deployment Failed.", gr.update(visible=False), gr.update(visible=False)
# --- UI Setup ---
# Removed 'css' parameter to resolve warning in Gradio 6.0
with gr.Blocks(title="Vercel Minimal Clone") as demo:
gr.HTML("""
<div style='text-align: center; padding: 20px; border-bottom: 1px solid #eaeaea;'>
<h1 style='margin: 0; font-family: monospace;'>β–² VERCEL CLONE</h1>
<p style='color: #666; margin: 5px 0 0 0;'>Instant Serverless Hosting & Preview for ZIP Deployments</p>
</div>
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### πŸ› οΈ New Deployment")
zip_input = gr.File(label="Upload Project ZIP", file_types=[".zip"])
deploy_btn = gr.Button("Deploy to Production", variant="primary")
gr.Markdown("### πŸ“‹ Build Logs (Console)")
# Fixed: Removed 'language="bash"' and used default rendering
console_output = gr.Code(label="Terminal", value="Terminal ready...", lines=12)
with gr.Column(scale=1):
gr.Markdown("### 🌐 Live Production URL")
status_text = gr.Markdown("No active deployment.")
external_link = gr.Button("Open App in New Tab 🌐", visible=False)
preview_iframe = gr.Iframe(
label="Live Frame",
src="",
width="100%",
height="550px",
visible=False
)
deploy_btn.click(
fn=deploy_project,
inputs=zip_input,
outputs=[console_output, status_text, preview_iframe, external_link]
)
# --- FastAPI Server ---
app = FastAPI()
# Serving static preview folders
app.mount("/preview", StaticFiles(directory=PREVIEW_PATH), name="preview")
# Mounting Gradio
demo_app = gr.mount_gradio_app(app, demo, path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(demo_app, host="0.0.0.0", port=7860)