| import os |
| import shutil |
| import subprocess |
| import uuid |
| import gradio as gr |
| from fastapi.staticfiles import StaticFiles |
| from fastapi import FastAPI |
|
|
| |
| 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. |
| """ |
| |
| 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) |
| |
| |
| 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 |
|
|
| |
| 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: |
| |
| 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 |
| |
| |
| for root, dirs, files in os.walk(deploy_dir): |
| if "package.json" in files: |
| root_path = root |
| is_node_project = True |
| break |
|
|
| |
| 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) |
|
|
| |
| 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) |
|
|
| |
| |
| 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)") |
| |
| 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] |
| ) |
|
|
| |
| app = FastAPI() |
|
|
| |
| app.mount("/preview", StaticFiles(directory=PREVIEW_PATH), name="preview") |
|
|
| |
| 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) |