Spaces:
Running
Running
Update Dockerfile
Browse files- Dockerfile +33 -10
Dockerfile
CHANGED
@@ -156,7 +156,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
156 |
});
|
157 |
EOF
|
158 |
|
159 |
-
# ===== Combined Server =====
|
160 |
COPY <<"EOF" /app/combined_server.py
|
161 |
from flask import Flask, request, jsonify, render_template, make_response
|
162 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
@@ -167,6 +167,8 @@ from pathlib import Path
|
|
167 |
import subprocess
|
168 |
import requests
|
169 |
from waitress import serve
|
|
|
|
|
170 |
|
171 |
app = Flask(__name__,
|
172 |
template_folder='/app/admin/templates',
|
@@ -184,7 +186,7 @@ def get_db():
|
|
184 |
(username TEXT PRIMARY KEY, password TEXT, role TEXT)''')
|
185 |
return db
|
186 |
|
187 |
-
# Admin
|
188 |
@app.route('/sudo')
|
189 |
def admin_home():
|
190 |
return render_template('login.html')
|
@@ -192,7 +194,7 @@ def admin_home():
|
|
192 |
@app.route('/sudo/login', methods=['POST'])
|
193 |
def login():
|
194 |
if request.json.get('sudo_secret') == os.getenv("SUDO_SECRET"):
|
195 |
-
return jsonify({"status": "success"})
|
196 |
return jsonify({"error": "Invalid secret"}), 403
|
197 |
|
198 |
@app.route('/sudo/dashboard')
|
@@ -236,34 +238,55 @@ def remove_user():
|
|
236 |
db.commit()
|
237 |
return jsonify({"status": "User removed"})
|
238 |
|
239 |
-
# LibreChat
|
|
|
|
|
|
|
|
|
|
|
|
|
240 |
@app.route('/', defaults={'path': ''})
|
241 |
-
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
242 |
def proxy(path):
|
|
|
243 |
if path.startswith('sudo/') or path == 'sudo':
|
244 |
return make_response("Not Found", 404)
|
245 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
resp = requests.request(
|
247 |
method=request.method,
|
248 |
url=f"http://localhost:3080/{path}",
|
249 |
-
headers={key: value for (key, value) in request.headers if key != '
|
250 |
data=request.get_data(),
|
251 |
cookies=request.cookies,
|
252 |
-
allow_redirects=False
|
|
|
253 |
)
|
254 |
|
|
|
255 |
response = make_response(resp.content, resp.status_code)
|
256 |
for key, value in resp.headers.items():
|
257 |
if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding']:
|
258 |
response.headers[key] = value
|
259 |
return response
|
260 |
|
|
|
|
|
|
|
261 |
if __name__ == "__main__":
|
262 |
-
# Start LibreChat
|
263 |
-
|
264 |
|
265 |
# Start combined server
|
266 |
-
serve(app, host='0.0.0.0', port=7860, threads=4)
|
267 |
EOF
|
268 |
|
269 |
# Startup script
|
|
|
156 |
});
|
157 |
EOF
|
158 |
|
159 |
+
# ===== Fixed Combined Server =====
|
160 |
COPY <<"EOF" /app/combined_server.py
|
161 |
from flask import Flask, request, jsonify, render_template, make_response
|
162 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
|
167 |
import subprocess
|
168 |
import requests
|
169 |
from waitress import serve
|
170 |
+
import threading
|
171 |
+
import time
|
172 |
|
173 |
app = Flask(__name__,
|
174 |
template_folder='/app/admin/templates',
|
|
|
186 |
(username TEXT PRIMARY KEY, password TEXT, role TEXT)''')
|
187 |
return db
|
188 |
|
189 |
+
# ===== Admin Routes =====
|
190 |
@app.route('/sudo')
|
191 |
def admin_home():
|
192 |
return render_template('login.html')
|
|
|
194 |
@app.route('/sudo/login', methods=['POST'])
|
195 |
def login():
|
196 |
if request.json.get('sudo_secret') == os.getenv("SUDO_SECRET"):
|
197 |
+
return jsonify({"status": "success", "token": os.getenv("SUDO_SECRET")})
|
198 |
return jsonify({"error": "Invalid secret"}), 403
|
199 |
|
200 |
@app.route('/sudo/dashboard')
|
|
|
238 |
db.commit()
|
239 |
return jsonify({"status": "User removed"})
|
240 |
|
241 |
+
# ===== LibreChat Proxy =====
|
242 |
+
def is_librechat_ready():
|
243 |
+
try:
|
244 |
+
return requests.get('http://localhost:3080', timeout=1).status_code < 500
|
245 |
+
except:
|
246 |
+
return False
|
247 |
+
|
248 |
@app.route('/', defaults={'path': ''})
|
249 |
+
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
|
250 |
def proxy(path):
|
251 |
+
# Don't proxy admin routes
|
252 |
if path.startswith('sudo/') or path == 'sudo':
|
253 |
return make_response("Not Found", 404)
|
254 |
|
255 |
+
# Wait for LibreChat to be ready
|
256 |
+
for _ in range(30): # 30 second timeout
|
257 |
+
if is_librechat_ready():
|
258 |
+
break
|
259 |
+
time.sleep(1)
|
260 |
+
else:
|
261 |
+
return make_response("LibreChat backend not ready", 503)
|
262 |
+
|
263 |
+
# Forward request to LibreChat
|
264 |
resp = requests.request(
|
265 |
method=request.method,
|
266 |
url=f"http://localhost:3080/{path}",
|
267 |
+
headers={key: value for (key, value) in request.headers if key.lower() != 'host'},
|
268 |
data=request.get_data(),
|
269 |
cookies=request.cookies,
|
270 |
+
allow_redirects=False,
|
271 |
+
timeout=30
|
272 |
)
|
273 |
|
274 |
+
# Create response
|
275 |
response = make_response(resp.content, resp.status_code)
|
276 |
for key, value in resp.headers.items():
|
277 |
if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding']:
|
278 |
response.headers[key] = value
|
279 |
return response
|
280 |
|
281 |
+
def start_librechat():
|
282 |
+
subprocess.run(["npm", "run", "backend"], cwd="/app")
|
283 |
+
|
284 |
if __name__ == "__main__":
|
285 |
+
# Start LibreChat in background
|
286 |
+
threading.Thread(target=start_librechat, daemon=True).start()
|
287 |
|
288 |
# Start combined server
|
289 |
+
serve(app, host='0.0.0.0', port=7860, threads=4, ident="LibreChat Proxy")
|
290 |
EOF
|
291 |
|
292 |
# Startup script
|