martynka commited on
Commit
9a077eb
·
verified ·
1 Parent(s): 1046ebd

Update Dockerfile

Browse files
Files changed (1) hide show
  1. 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 Python dependencies
5
  USER root
6
  RUN apk update && apk add --no-cache \
 
7
  python3 \
8
  py3-pip \
9
- sqlite \
10
- && pip3 install flask werkzeug waitress requests pymongo waitress --break-system-packages
11
-
12
- # Setup directory structure
13
- RUN mkdir -p /app/{admin/{templates,static},data,uploads,client/public/images,api/logs} \
14
- && mkdir -p /app/uploads/temp \
15
- && chown -R 1000:1000 /app \
16
- && chmod -R 777 /app/uploads \
17
- && chmod -R 777 /app/client/public/images \
18
- && chmod -R 777 /app/api/logs
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 waitress import serve
 
27
 
28
- app = Flask(__name__, template_folder='/app/admin/templates')
29
  app.secret_key = os.getenv("FLASK_SECRET")
30
 
31
- # Connect to LibreChat's MongoDB
32
- client = MongoClient(os.getenv("MONGO_URI", "mongodb://localhost:27017"))
33
  db = client.get_default_database()
34
 
35
- @app.route('/admin')
36
- def admin_home():
37
- return render_template('login.html')
38
-
39
- @app.route('/admin/login', methods=['POST'])
40
- def login():
41
- if request.json.get('admin_secret') == os.getenv("SUDO_SECRET"):
42
- return jsonify({"status": "success", "token": os.getenv("SUDO_SECRET")})
43
- return jsonify({"error": "Invalid secret"}), 403
44
-
45
- @app.route('/admin/users', methods=['GET'])
 
 
46
  def list_users():
47
- if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"):
48
- return jsonify({"error": "Unauthorized"}), 403
49
- users = list(db.users.find({}, {"_id": 0, "username": 1, "email": 1}))
 
 
 
 
50
  return jsonify(users)
51
 
52
- @app.route('/admin/users', methods=['POST'])
 
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('/admin/users', methods=['DELETE'])
66
- def delete_user():
67
- if request.headers.get('X-Admin-Token') != os.getenv("SUDO_SECRET"):
68
- return jsonify({"error": "Unauthorized"}), 403
69
- db.users.delete_one({"username": request.json["username"]})
 
70
  return jsonify({"status": "User deleted"})
71
 
72
  if __name__ == "__main__":
73
- serve(app, host='0.0.0.0', port=5000)
74
  EOF
75
 
76
- # ===== ADMIN TEMPLATES =====
77
- COPY <<"EOF" /app/admin/templates/login.html
78
- <!DOCTYPE html>
79
- <html>
80
- <head>
81
- <title>Admin Login</title>
82
- <style>
83
- body { font-family: Arial; padding: 20px; }
84
- form { max-width: 400px; margin: 0 auto; }
85
- input, button { width: 100%; padding: 8px; margin: 5px 0; }
86
- </style>
87
- </head>
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