jens-l commited on
Commit
66a32ca
·
1 Parent(s): 2497011

Add Docker configuration and deployment files

Browse files
Files changed (4) hide show
  1. Dockerfile +22 -5
  2. app.py +41 -50
  3. nginx.conf +63 -0
  4. start.sh +29 -0
Dockerfile CHANGED
@@ -1,14 +1,31 @@
1
  FROM python:3.12-slim
2
 
 
 
 
 
 
3
  RUN useradd -m -u 1000 user
4
- USER user
5
- ENV PATH="/home/user/.local/bin:$PATH"
6
 
 
7
  WORKDIR /app
8
-
9
- COPY --chown=user ./requirements.txt requirements.txt
10
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
 
 
 
 
 
 
 
 
 
 
12
  COPY --chown=user . /app
 
 
 
 
 
13
 
14
- CMD ["gradio", "app.py"]
 
1
  FROM python:3.12-slim
2
 
3
+ # Install nginx
4
+ RUN apt-get update && apt-get install -y nginx && \
5
+ apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Create user
8
  RUN useradd -m -u 1000 user
 
 
9
 
10
+ # Install Python packages globally so they're accessible to all users
11
  WORKDIR /app
12
+ COPY ./requirements.txt requirements.txt
 
13
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
 
15
+ # Copy nginx configuration as main config
16
+ COPY nginx.conf /etc/nginx/nginx.conf
17
+
18
+ # Create all nginx directories and set permissions for user
19
+ RUN mkdir -p /var/run/nginx /var/log/nginx /var/lib/nginx/body /var/lib/nginx/fastcgi \
20
+ /var/lib/nginx/proxy /var/lib/nginx/scgi /var/lib/nginx/uwsgi && \
21
+ chown -R user:user /var/run/nginx /var/log/nginx /var/lib/nginx
22
+
23
+ # Copy application files and make script executable
24
  COPY --chown=user . /app
25
+ RUN chmod +x /app/start.sh
26
+
27
+ # Switch to user for execution
28
+ USER user
29
+ ENV PATH="/home/user/.local/bin:$PATH"
30
 
31
+ CMD ["/app/start.sh"]
app.py CHANGED
@@ -11,15 +11,13 @@ from src.utils import load_file
11
  from ui_helpers import stream_to_gradio
12
 
13
  preview_process = None
14
- PREVIEW_PORT = 7861 # Different port from main app
15
 
16
 
17
  def get_preview_url():
18
  """Get the appropriate preview URL based on environment."""
19
- # For simplified HF Spaces deployment, preview is handled differently
20
- if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
21
- return "about:blank"
22
- return f"http://127.0.0.1:{PREVIEW_PORT}"
23
 
24
 
25
  PREVIEW_URL = get_preview_url()
@@ -58,11 +56,6 @@ def save_file(path, new_text):
58
 
59
  def stop_preview_app():
60
  """Stop the preview app subprocess if it's running."""
61
- # In HF Spaces, this logic is simplified
62
- if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
63
- print("ℹ️ Preview subprocess management is disabled in Hugging Face Spaces.")
64
- return
65
-
66
  global preview_process
67
  if preview_process and preview_process.poll() is None:
68
  print(f"🛑 Stopping preview app process (PID: {preview_process.pid})...")
@@ -81,11 +74,6 @@ def stop_preview_app():
81
 
82
  def start_preview_app():
83
  """Start the preview app in a subprocess if it's not already running."""
84
- # In HF Spaces, this logic is simplified
85
- if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
86
- print("✅ Preview functionality simplified for HF Spaces.")
87
- return True, "Preview is integrated and available."
88
-
89
  global preview_process
90
  # Stop any existing process before starting a new one
91
  stop_preview_app()
@@ -125,35 +113,32 @@ def start_preview_app():
125
 
126
  def create_iframe_preview():
127
  """Create an iframe that loads the sandbox app."""
128
- # In HF Spaces, we show a simplified message
129
- if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
130
- return (
131
- '<div style="padding: 20px; text-align: center; '
132
- "background-color: #f8f9fa; border: 1px solid #e9ecef; "
133
- 'border-radius: 8px;">'
134
- "<h3>📱 Preview</h3>"
135
- "<p>Code preview is available after the AI generates an application.</p>"
136
- "</div>"
137
- )
138
-
139
- # For local execution, attempt to start the preview and show an iframe
140
  success, message = start_preview_app()
 
141
  if success:
142
- return f'<iframe src="{PREVIEW_URL}" width="100%" height="500px"></iframe>'
 
 
 
 
143
  else:
144
- return f'<div style="color: red; padding: 20px;">{message}</div>'
 
 
145
 
146
 
147
  def is_preview_running():
148
  """Check if the preview app is running and accessible."""
149
- # For simplified deployment, always return True
150
- return True
151
 
152
 
153
  def ensure_preview_running():
154
  """Ensure the preview app is running, start it if needed."""
155
- # For simplified deployment, nothing needed
156
- pass
157
 
158
 
159
  def get_default_model_for_provider(provider: str) -> str:
@@ -355,11 +340,12 @@ class GradioUI:
355
  with gr.Row(elem_classes="main-container"):
356
  # Left side - Chat Interface
357
  with gr.Column(scale=1, elem_classes="chat-container"):
 
 
 
 
358
  chatbot = gr.Chatbot(
359
- avatar_images=(
360
- None,
361
- "http://em-content.zobj.net/source/apple/419/growing-heart_1f497.png",
362
- ),
363
  type="messages",
364
  resizable=True,
365
  height="70vh",
@@ -376,10 +362,12 @@ class GradioUI:
376
  # Right side - Preview/Code/Settings Toggle
377
  with gr.Column(scale=4, elem_classes="preview-container"):
378
  with gr.Tab("Preview"):
 
 
 
 
379
  preview_html = gr.HTML(
380
- value='<div style="padding: 20px;">'
381
- "Preview will load here."
382
- "</div>",
383
  elem_id="preview-container",
384
  )
385
 
@@ -594,16 +582,11 @@ class GradioUI:
594
  [text_input, submit_btn],
595
  )
596
 
597
- def on_app_load():
598
- return create_iframe_preview()
599
-
600
- demo.load(fn=on_app_load, outputs=[preview_html])
601
 
602
  # Clean up on app close
603
- def cleanup():
604
- stop_preview_app()
605
-
606
- demo.unload(cleanup)
607
 
608
  return demo
609
 
@@ -642,7 +625,15 @@ if __name__ == "__main__":
642
 
643
  agent = KISSAgent()
644
 
645
- # For HF Spaces, always use port 7860 directly (simplified approach)
646
- port = 7860
 
 
 
 
 
 
 
 
647
 
648
  GradioUI(agent).launch(share=False, server_name="0.0.0.0", server_port=port)
 
11
  from ui_helpers import stream_to_gradio
12
 
13
  preview_process = None
14
+ PREVIEW_PORT = 7861 # Internal port for preview apps
15
 
16
 
17
  def get_preview_url():
18
  """Get the appropriate preview URL based on environment."""
19
+ # In Docker/HF Spaces with nginx proxy, use the proxy path
20
+ return "/preview/"
 
 
21
 
22
 
23
  PREVIEW_URL = get_preview_url()
 
56
 
57
  def stop_preview_app():
58
  """Stop the preview app subprocess if it's running."""
 
 
 
 
 
59
  global preview_process
60
  if preview_process and preview_process.poll() is None:
61
  print(f"🛑 Stopping preview app process (PID: {preview_process.pid})...")
 
74
 
75
  def start_preview_app():
76
  """Start the preview app in a subprocess if it's not already running."""
 
 
 
 
 
77
  global preview_process
78
  # Stop any existing process before starting a new one
79
  stop_preview_app()
 
113
 
114
  def create_iframe_preview():
115
  """Create an iframe that loads the sandbox app."""
116
+ print("🔍 create_iframe_preview() called")
117
+ # Try to start the preview app and show an iframe
 
 
 
 
 
 
 
 
 
 
118
  success, message = start_preview_app()
119
+ print(f"🔍 start_preview_app() result: success={success}, message={message}")
120
  if success:
121
+ iframe_html = (
122
+ f'<iframe src="{PREVIEW_URL}" ' 'width="100%" height="500px"></iframe>'
123
+ )
124
+ print(f"🔍 Creating iframe: {iframe_html}")
125
+ return iframe_html
126
  else:
127
+ error_html = f'<div style="color: red; padding: 20px;">{message}</div>'
128
+ print(f"🔍 Error in preview: {error_html}")
129
+ return error_html
130
 
131
 
132
  def is_preview_running():
133
  """Check if the preview app is running and accessible."""
134
+ global preview_process
135
+ return preview_process is not None and preview_process.poll() is None
136
 
137
 
138
  def ensure_preview_running():
139
  """Ensure the preview app is running, start it if needed."""
140
+ if not is_preview_running():
141
+ start_preview_app()
142
 
143
 
144
  def get_default_model_for_provider(provider: str) -> str:
 
340
  with gr.Row(elem_classes="main-container"):
341
  # Left side - Chat Interface
342
  with gr.Column(scale=1, elem_classes="chat-container"):
343
+ avatar_url = (
344
+ "http://em-content.zobj.net/source/apple/419/"
345
+ "growing-heart_1f497.png"
346
+ )
347
  chatbot = gr.Chatbot(
348
+ avatar_images=(None, avatar_url),
 
 
 
349
  type="messages",
350
  resizable=True,
351
  height="70vh",
 
362
  # Right side - Preview/Code/Settings Toggle
363
  with gr.Column(scale=4, elem_classes="preview-container"):
364
  with gr.Tab("Preview"):
365
+ iframe_url = (
366
+ f'<iframe src="{PREVIEW_URL}" '
367
+ 'width="100%" height="500px"></iframe>'
368
+ )
369
  preview_html = gr.HTML(
370
+ value=iframe_url,
 
 
371
  elem_id="preview-container",
372
  )
373
 
 
582
  [text_input, submit_btn],
583
  )
584
 
585
+ # Load the preview iframe when the app starts
586
+ demo.load(fn=create_iframe_preview, outputs=[preview_html])
 
 
587
 
588
  # Clean up on app close
589
+ demo.unload(stop_preview_app)
 
 
 
590
 
591
  return demo
592
 
 
625
 
626
  agent = KISSAgent()
627
 
628
+ # Start the preview app automatically when the main app starts
629
+ print("🚀 Starting preview app automatically...")
630
+ success, message = start_preview_app()
631
+ if success:
632
+ print(f"✅ Preview app started: {message}")
633
+ else:
634
+ print(f"❌ Failed to start preview app: {message}")
635
+
636
+ # Main app runs on internal port 7862, nginx proxies from 7860
637
+ port = 7862
638
 
639
  GradioUI(agent).launch(share=False, server_name="0.0.0.0", server_port=port)
nginx.conf ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Basic nginx configuration for running as non-root user
2
+ worker_processes auto;
3
+ pid /var/run/nginx/nginx.pid;
4
+ error_log /var/log/nginx/error.log;
5
+
6
+ events {
7
+ worker_connections 768;
8
+ }
9
+
10
+ http {
11
+ include /etc/nginx/mime.types;
12
+ default_type application/octet-stream;
13
+
14
+ # Temp directories that user can write to
15
+ client_body_temp_path /var/lib/nginx/body;
16
+ proxy_temp_path /var/lib/nginx/proxy;
17
+ fastcgi_temp_path /var/lib/nginx/fastcgi;
18
+ uwsgi_temp_path /var/lib/nginx/uwsgi;
19
+ scgi_temp_path /var/lib/nginx/scgi;
20
+
21
+ access_log /var/log/nginx/access.log;
22
+
23
+ server {
24
+ listen 7860 default_server;
25
+ listen [::]:7860 default_server;
26
+
27
+ server_name _;
28
+
29
+ # Main Gradio app - serve on root path, proxy to internal port 7862
30
+ location / {
31
+ proxy_pass http://localhost:7862;
32
+ proxy_http_version 1.1;
33
+ proxy_set_header Upgrade $http_upgrade;
34
+ proxy_set_header Connection 'upgrade';
35
+ proxy_set_header Host $host;
36
+ proxy_set_header X-Real-IP $remote_addr;
37
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
38
+ proxy_set_header X-Forwarded-Host $host;
39
+ proxy_set_header X-Forwarded-Proto $scheme;
40
+ proxy_cache_bypass $http_upgrade;
41
+ proxy_read_timeout 86400;
42
+ proxy_redirect off;
43
+ }
44
+
45
+ # Preview apps - route to internal port 7861
46
+ location /preview/ {
47
+ # Remove /preview prefix and pass to the sandbox app
48
+ rewrite /preview/(.*) /$1 break;
49
+ proxy_pass http://localhost:7861;
50
+ proxy_http_version 1.1;
51
+ proxy_set_header Upgrade $http_upgrade;
52
+ proxy_set_header Connection 'upgrade';
53
+ proxy_set_header Host $host;
54
+ proxy_set_header X-Real-IP $remote_addr;
55
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
56
+ proxy_set_header X-Forwarded-Host $host;
57
+ proxy_set_header X-Forwarded-Proto $scheme;
58
+ proxy_cache_bypass $http_upgrade;
59
+ proxy_read_timeout 86400;
60
+ proxy_redirect off;
61
+ }
62
+ }
63
+ }
start.sh ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Start nginx in background as non-root user
4
+ nginx -g 'daemon off;' &
5
+ NGINX_PID=$!
6
+
7
+ # Wait a moment for nginx to start
8
+ sleep 2
9
+
10
+ # Start the main Gradio app on port 7862 (internal)
11
+ python app.py --server-port 7862 --server-name 0.0.0.0 &
12
+ APP_PID=$!
13
+
14
+ # Function to handle shutdown
15
+ cleanup() {
16
+ echo "Shutting down..."
17
+ kill $NGINX_PID $APP_PID 2>/dev/null
18
+ wait $NGINX_PID $APP_PID 2>/dev/null
19
+ exit 0
20
+ }
21
+
22
+ # Set up signal handlers
23
+ trap cleanup SIGTERM SIGINT
24
+
25
+ # Wait for any process to exit
26
+ wait -n
27
+
28
+ # If we get here, one process exited, so clean up
29
+ cleanup