Spaces:
Paused
Paused
| # Use official LibreChat base image | |
| FROM ghcr.io/danny-avila/librechat-dev:latest | |
| # Install Python dependencies | |
| USER root | |
| RUN apk update && apk add --no-cache \ | |
| python3 \ | |
| py3-pip \ | |
| sqlite \ | |
| && pip3 install flask werkzeug waitress requests pymongo waitress --break-system-packages | |
| # Setup directory structure | |
| RUN mkdir -p /app/{admin/{templates,static},data,uploads,client/public/images,api/logs} \ | |
| && mkdir -p /app/uploads/temp \ | |
| && chown -R 1000:1000 /app \ | |
| && chmod -R 777 /app/uploads \ | |
| && chmod -R 777 /app/client/public/images \ | |
| && chmod -R 777 /app/api/logs | |
| # ===== ADMIN PANEL (Adapted from LibreChat community) ===== | |
| COPY <<"EOF" /app/admin/app.py | |
| from flask import Flask, request, jsonify, render_template | |
| from pymongo import MongoClient | |
| from werkzeug.security import generate_password_hash | |
| import os | |
| from waitress import serve | |
| app = Flask(__name__, template_folder='/app/admin/templates') | |
| app.secret_key = os.getenv("FLASK_SECRET") | |
| # Connect to LibreChat's MongoDB | |
| client = MongoClient(os.getenv("MONGO_URI", "mongodb://localhost:27017")) | |
| db = client.get_default_database() | |
| @app.route('/admin') | |
| def admin_home(): | |
| return render_template('login.html') | |
| @app.route('/admin/login', methods=['POST']) | |
| def login(): | |
| if request.json.get('admin_secret') == os.getenv("SUDO_SECRET"): | |
| return jsonify({"status": "success", "token": os.getenv("SUDO_SECRET")}) | |
| return jsonify({"error": "Invalid secret"}), 403 | |
| @app.route('/admin/users', methods=['GET']) | |
| def list_users(): | |
| if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"): | |
| return jsonify({"error": "Unauthorized"}), 403 | |
| users = list(db.users.find({}, {"_id": 0, "username": 1, "email": 1})) | |
| return jsonify(users) | |
| @app.route('/admin/users', methods=['POST']) | |
| def add_user(): | |
| if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"): | |
| return jsonify({"error": "Unauthorized"}), 403 | |
| user_data = { | |
| "username": request.json["username"], | |
| "password": generate_password_hash(request.json["password"]), | |
| "email": request.json.get("email", ""), | |
| "role": "user" | |
| } | |
| db.users.insert_one(user_data) | |
| return jsonify({"status": "User added"}) | |
| @app.route('/admin/users', methods=['DELETE']) | |
| def delete_user(): | |
| if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"): | |
| return jsonify({"error": "Unauthorized"}), 403 | |
| db.users.delete_one({"username": request.json["username"]}) | |
| return jsonify({"status": "User deleted"}) | |
| if __name__ == "__main__": | |
| serve(app, host='0.0.0.0', port=5000) | |
| EOF | |
| # ===== ADMIN TEMPLATES ===== | |
| COPY <<"EOF" /app/admin/templates/login.html | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Admin Login</title> | |
| <style> | |
| body { font-family: Arial; padding: 20px; } | |
| form { max-width: 400px; margin: 0 auto; } | |
| input, button { width: 100%; padding: 8px; margin: 5px 0; } | |
| </style> | |
| </head> | |
| <body> | |
| <form id="loginForm"> | |
| <h2>Admin Login</h2> | |
| <input type="password" placeholder="Admin Secret" required> | |
| <button type="submit">Login</button> | |
| </form> | |
| <script> | |
| document.getElementById('loginForm').onsubmit = async (e) => { | |
| e.preventDefault(); | |
| const response = await fetch('/admin/login', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ admin_secret: e.target[0].value }) | |
| }); | |
| if (response.ok) { | |
| localStorage.setItem('admin_token', await response.text()); | |
| window.location.href = '/admin/dashboard'; | |
| } else { | |
| alert('Invalid secret!'); | |
| } | |
| }; | |
| </script> | |
| </body> | |
| </html> | |
| EOF | |
| # Startup script | |
| COPY <<"EOF" /app/start.sh | |
| #!/bin/sh | |
| # Start the combined server | |
| cd /app && npm run backend & | |
| python3 /app/admin/app.py & | |
| wait | |
| EOF | |
| RUN chmod +x /app/start.sh | |
| # Environment variables | |
| ENV HOST=0.0.0.0 \ | |
| PORT=3080 \ | |
| SESSION_EXPIRY=900000 \ | |
| REFRESH_TOKEN_EXPIRY=604800000 \ | |
| SEARCH=true \ | |
| MEILI_NO_ANALYTICS=true \ | |
| MEILI_HOST=https://martynka-meilisearch.hf.space \ | |
| CONFIG_PATH=/app/librechat.yaml \ | |
| CUSTOM_FOOTER=EasierIT \ | |
| MONGO_URI="$MONGO_URI" \ | |
| SUDO_SECRET="$SUDO_SECRET" \ | |
| FLASK_SECRET="$FLASK_SECRET" \ | |
| NODE_ENV=production | |
| EXPOSE 7860 | |
| CMD ["/app/start.sh"] |