Spaces:
Running
Running
Update Dockerfile
Browse files- Dockerfile +80 -82
Dockerfile
CHANGED
@@ -1,116 +1,114 @@
|
|
1 |
-
# Use official LibreChat base image
|
2 |
FROM ghcr.io/danny-avila/librechat-dev:latest
|
3 |
|
4 |
-
# Install
|
5 |
USER root
|
6 |
RUN apk update && apk add --no-cache \
|
|
|
7 |
python3 \
|
8 |
py3-pip \
|
9 |
-
|
10 |
-
&&
|
11 |
-
|
12 |
-
#
|
13 |
-
RUN mkdir -p /app/
|
14 |
-
&&
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
# ===== ADMIN PANEL (Adapted from LibreChat community) =====
|
21 |
-
COPY <<"EOF" /app/admin/app.py
|
22 |
-
from flask import Flask, request, jsonify, render_template
|
23 |
from pymongo import MongoClient
|
24 |
from werkzeug.security import generate_password_hash
|
25 |
import os
|
26 |
-
from
|
|
|
27 |
|
28 |
-
app = Flask(__name__
|
29 |
app.secret_key = os.getenv("FLASK_SECRET")
|
30 |
|
31 |
-
#
|
32 |
-
client = MongoClient(os.getenv("MONGO_URI", "mongodb://localhost:27017"))
|
33 |
db = client.get_default_database()
|
34 |
|
35 |
-
|
36 |
-
def
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
return
|
43 |
-
return
|
44 |
-
|
45 |
-
|
|
|
|
|
46 |
def list_users():
|
47 |
-
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
50 |
return jsonify(users)
|
51 |
|
52 |
-
@app.route('/
|
|
|
53 |
def add_user():
|
54 |
-
if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"):
|
55 |
-
return jsonify({"error": "Unauthorized"}), 403
|
56 |
user_data = {
|
57 |
"username": request.json["username"],
|
58 |
"password": generate_password_hash(request.json["password"]),
|
59 |
"email": request.json.get("email", ""),
|
|
|
60 |
"role": "user"
|
61 |
}
|
62 |
db.users.insert_one(user_data)
|
63 |
-
return jsonify({"status": "User added"})
|
64 |
-
|
65 |
-
@app.route('/
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
70 |
return jsonify({"status": "User deleted"})
|
71 |
|
72 |
if __name__ == "__main__":
|
73 |
-
|
74 |
EOF
|
75 |
|
76 |
-
# =====
|
77 |
-
COPY <<"EOF" /
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
<body>
|
89 |
-
<form id="loginForm">
|
90 |
-
<h2>Admin Login</h2>
|
91 |
-
<input type="password" placeholder="Admin Secret" required>
|
92 |
-
<button type="submit">Login</button>
|
93 |
-
</form>
|
94 |
-
<script>
|
95 |
-
document.getElementById('loginForm').onsubmit = async (e) => {
|
96 |
-
e.preventDefault();
|
97 |
-
const response = await fetch('/admin/login', {
|
98 |
-
method: 'POST',
|
99 |
-
headers: { 'Content-Type': 'application/json' },
|
100 |
-
body: JSON.stringify({ admin_secret: e.target[0].value })
|
101 |
-
});
|
102 |
-
if (response.ok) {
|
103 |
-
localStorage.setItem('admin_token', await response.text());
|
104 |
-
window.location.href = '/admin/dashboard';
|
105 |
-
} else {
|
106 |
-
alert('Invalid secret!');
|
107 |
-
}
|
108 |
-
};
|
109 |
-
</script>
|
110 |
-
</body>
|
111 |
-
</html>
|
112 |
-
EOF
|
113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
|
116 |
# Startup script
|
|
|
|
|
1 |
FROM ghcr.io/danny-avila/librechat-dev:latest
|
2 |
|
3 |
+
# Install dependencies
|
4 |
USER root
|
5 |
RUN apk update && apk add --no-cache \
|
6 |
+
caddy \
|
7 |
python3 \
|
8 |
py3-pip \
|
9 |
+
py3-pipx \
|
10 |
+
&& pipx install flask pymongo
|
11 |
+
|
12 |
+
# Create admin structure
|
13 |
+
RUN mkdir -p /app/sudo/{templates,static} \
|
14 |
+
&& chown -R 1000:1000 /app
|
15 |
+
|
16 |
+
# ===== Admin Backend =====
|
17 |
+
COPY <<"EOF" /app/sudo/app.py
|
18 |
+
from flask import Flask, request, jsonify
|
|
|
|
|
|
|
|
|
19 |
from pymongo import MongoClient
|
20 |
from werkzeug.security import generate_password_hash
|
21 |
import os
|
22 |
+
from datetime import datetime
|
23 |
+
from functools import wraps
|
24 |
|
25 |
+
app = Flask(__name__)
|
26 |
app.secret_key = os.getenv("FLASK_SECRET")
|
27 |
|
28 |
+
# MongoDB connection
|
29 |
+
client = MongoClient(os.getenv("MONGO_URI", "mongodb://localhost:27017/librechat"))
|
30 |
db = client.get_default_database()
|
31 |
|
32 |
+
# Authentication decorator
|
33 |
+
def sudo_required(f):
|
34 |
+
@wraps(f)
|
35 |
+
def wrapper(*args, **kwargs):
|
36 |
+
auth_token = request.headers.get('X-Sudo-Token')
|
37 |
+
if auth_token != os.getenv("SUDO_SECRET"):
|
38 |
+
return jsonify({"error": "Unauthorized"}), 403
|
39 |
+
return f(*args, **kwargs)
|
40 |
+
return wrapper
|
41 |
+
|
42 |
+
# Sudo API endpoints
|
43 |
+
@app.route('/sudo/users', methods=['GET'])
|
44 |
+
@sudo_required
|
45 |
def list_users():
|
46 |
+
users = list(db.users.find({}, {
|
47 |
+
"_id": 0,
|
48 |
+
"username": 1,
|
49 |
+
"email": 1,
|
50 |
+
"createdAt": 1,
|
51 |
+
"lastLogin": 1
|
52 |
+
}))
|
53 |
return jsonify(users)
|
54 |
|
55 |
+
@app.route('/sudo/users', methods=['POST'])
|
56 |
+
@sudo_required
|
57 |
def add_user():
|
|
|
|
|
58 |
user_data = {
|
59 |
"username": request.json["username"],
|
60 |
"password": generate_password_hash(request.json["password"]),
|
61 |
"email": request.json.get("email", ""),
|
62 |
+
"createdAt": datetime.utcnow(),
|
63 |
"role": "user"
|
64 |
}
|
65 |
db.users.insert_one(user_data)
|
66 |
+
return jsonify({"status": "User added"}), 201
|
67 |
+
|
68 |
+
@app.route('/sudo/users/<username>', methods=['DELETE'])
|
69 |
+
@sudo_required
|
70 |
+
def delete_user(username):
|
71 |
+
result = db.users.delete_one({"username": username})
|
72 |
+
if result.deleted_count == 0:
|
73 |
+
return jsonify({"error": "User not found"}), 404
|
74 |
return jsonify({"status": "User deleted"})
|
75 |
|
76 |
if __name__ == "__main__":
|
77 |
+
app.run(host='0.0.0.0', port=5000)
|
78 |
EOF
|
79 |
|
80 |
+
# ===== Caddy Configuration =====
|
81 |
+
COPY <<"EOF" /etc/caddy/Caddyfile
|
82 |
+
{
|
83 |
+
admin off
|
84 |
+
auto_https off
|
85 |
+
}
|
86 |
+
|
87 |
+
:7860 {
|
88 |
+
# LibreChat API (protected)
|
89 |
+
handle_path /api/* {
|
90 |
+
reverse_proxy localhost:3080
|
91 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
+
# Sudo API endpoints
|
94 |
+
handle_path /sudo/* {
|
95 |
+
reverse_proxy localhost:5000
|
96 |
+
}
|
97 |
+
|
98 |
+
# Sudo panel static files
|
99 |
+
handle_path /sudo-panel/* {
|
100 |
+
root * /app/sudo
|
101 |
+
file_server
|
102 |
+
}
|
103 |
+
|
104 |
+
# Main LibreChat frontend
|
105 |
+
handle {
|
106 |
+
root * /app/client/dist
|
107 |
+
try_files {path} /index.html
|
108 |
+
file_server
|
109 |
+
}
|
110 |
+
}
|
111 |
+
EOF
|
112 |
|
113 |
|
114 |
# Startup script
|