Spaces:
Running
Running
FROM ghcr.io/danny-avila/librechat-dev:latest | |
# Install dependencies | |
USER root | |
RUN apk update && apk add --no-cache \ | |
caddy \ | |
python3 \ | |
py3-pip \ | |
&& pip install flask pymongo[srv] --break-system-packages | |
# Create admin structure | |
RUN mkdir -p /app/sudo/{templates,static} \ | |
&& chown -R 1000:1000 /app | |
# ===== Admin Backend ===== | |
COPY <<"EOF" /app/sudo/app.py | |
from flask import Flask, request, jsonify | |
from pymongo import MongoClient | |
from werkzeug.security import generate_password_hash | |
import os | |
from datetime import datetime | |
from functools import wraps | |
app = Flask(__name__) | |
app.secret_key = os.getenv("FLASK_SECRET") | |
# MongoDB connection | |
client = MongoClient(os.getenv("MONGO_URI")) | |
db = client[ | |
# Authentication decorator | |
def sudo_required(f): | |
@wraps(f) | |
def wrapper(*args, **kwargs): | |
auth_token = request.headers.get('X-Sudo-Token') | |
if auth_token != os.getenv("SUDO_SECRET"): | |
return jsonify({"error": "Unauthorized"}), 403 | |
return f(*args, **kwargs) | |
return wrapper | |
# Sudo API endpoints | |
@app.route('/sudo/users', methods=['GET']) | |
@sudo_required | |
def list_users(): | |
users = list(db.users.find({}, { | |
"_id": 0, | |
"username": 1, | |
"email": 1, | |
"createdAt": 1, | |
"lastLogin": 1 | |
})) | |
return jsonify(users) | |
@app.route('/sudo/users', methods=['POST']) | |
@sudo_required | |
def add_user(): | |
user_data = { | |
"username": request.json["username"], | |
"password": generate_password_hash(request.json["password"]), | |
"email": request.json.get("email", ""), | |
"createdAt": datetime.utcnow(), | |
"role": "user" | |
} | |
db.users.insert_one(user_data) | |
return jsonify({"status": "User added"}), 201 | |
@app.route('/sudo/users/<username>', methods=['DELETE']) | |
@sudo_required | |
def delete_user(username): | |
result = db.users.delete_one({"username": username}) | |
if result.deleted_count == 0: | |
return jsonify({"error": "User not found"}), 404 | |
return jsonify({"status": "User deleted"}) | |
if __name__ == "__main__": | |
app.run(host='0.0.0.0', port=5000) | |
EOF | |
# ===== Caddy Configuration ===== | |
COPY <<"EOF" /etc/caddy/Caddyfile | |
{ | |
admin off | |
auto_https off | |
} | |
:7860 { | |
# LibreChat API (protected) | |
handle_path /api/* { | |
reverse_proxy localhost:3080 | |
} | |
# Sudo API endpoints | |
handle_path /sudo/* { | |
reverse_proxy localhost:5000 | |
} | |
# Sudo panel static files | |
handle_path /sudo-panel/* { | |
root * /app/sudo | |
file_server | |
} | |
# Main LibreChat frontend | |
handle { | |
root * /app/client/dist | |
try_files {path} /index.html | |
file_server | |
} | |
} | |
EOF | |
# Startup script | |
COPY <<"EOF" /app/start.sh | |
#!/bin/sh | |
# Start the combined server | |
cd /app && npm run backend & | |
python3 /app/sudo/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"] |