martynka commited on
Commit
4b64cf1
·
verified ·
1 Parent(s): 633350e

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +80 -211
Dockerfile CHANGED
@@ -7,7 +7,7 @@ RUN apk update && apk add --no-cache \
7
  python3 \
8
  py3-pip \
9
  sqlite \
10
- && pip3 install flask werkzeug waitress requests --break-system-packages
11
 
12
  # Setup directory structure
13
  RUN mkdir -p /app/{admin/{templates,static},data,uploads,client/public/images,api/logs} \
@@ -17,240 +17,109 @@ RUN mkdir -p /app/{admin/{templates,static},data,uploads,client/public/images,ap
17
  && chmod -R 777 /app/client/public/images \
18
  && chmod -R 777 /app/api/logs
19
 
20
- # ===== Admin Templates =====
21
- COPY <<"EOF" /app/admin/templates/login.html
22
- <!DOCTYPE html>
23
- <html>
24
- <head>
25
- <title>Admin Login</title>
26
- <link rel="stylesheet" href="/static/admin.css">
27
- </head>
28
- <body>
29
- <div class="container">
30
- <h2>Admin Portal</h2>
31
- <form id="loginForm">
32
- <input type="password" name="sudo_secret" placeholder="Admin Secret" required>
33
- <button type="submit">Login</button>
34
- </form>
35
- </div>
36
- <script src="/static/admin.js"></script>
37
- </body>
38
- </html>
39
- EOF
40
-
41
- COPY <<"EOF" /app/admin/templates/dashboard.html
42
- <!DOCTYPE html>
43
- <html>
44
- <head>
45
- <title>User Management</title>
46
- <link rel="stylesheet" href="/static/admin.css">
47
- </head>
48
- <body>
49
- <div class="container">
50
- <h1>User Management</h1>
51
- <div class="card">
52
- <h2>Add User</h2>
53
- <form id="addUserForm">
54
- <input type="text" name="username" placeholder="Username" required>
55
- <input type="password" name="password" placeholder="Password" required>
56
- <button type="submit">Add User</button>
57
- </form>
58
- </div>
59
- <div class="card">
60
- <h2>Current Users</h2>
61
- <ul id="userList"></ul>
62
- </div>
63
- </div>
64
- <script src="/static/admin.js"></script>
65
- </body>
66
- </html>
67
- EOF
68
-
69
- # ===== Admin Static Files =====
70
- COPY <<"EOF" /app/admin/static/admin.css
71
- body {
72
- font-family: Arial, sans-serif;
73
- margin: 0;
74
- padding: 20px;
75
- background-color: #f5f5f5;
76
- }
77
- .container {
78
- max-width: 800px;
79
- margin: 0 auto;
80
- }
81
- .card {
82
- background: white;
83
- padding: 20px;
84
- margin: 20px 0;
85
- border-radius: 8px;
86
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
87
- }
88
- input, button {
89
- width: 100%;
90
- padding: 10px;
91
- margin: 8px 0;
92
- box-sizing: border-box;
93
- }
94
- button {
95
- background-color: #4CAF50;
96
- color: white;
97
- border: none;
98
- cursor: pointer;
99
- }
100
- EOF
101
-
102
- COPY <<"EOF" /app/admin/static/admin.js
103
- async function loadUsers() {
104
- const response = await fetch('/sudo/list_users', {
105
- headers: {'X-Sudo-Secret': localStorage.getItem('sudo_token')}
106
- });
107
- if (response.ok) {
108
- const users = await response.json();
109
- document.getElementById('userList').innerHTML = users.map(user =>
110
- \`<li>\${user.username} <button onclick="deleteUser('\${user.username}')">Delete</button></li>\`
111
- ).join('');
112
- }
113
- }
114
-
115
- async function deleteUser(username) {
116
- if (confirm(\`Delete \${username}?\`)) {
117
- await fetch('/sudo/remove_user', {
118
- method: 'POST',
119
- headers: {
120
- 'Content-Type': 'application/json',
121
- 'X-Sudo-Secret': localStorage.getItem('sudo_token')
122
- },
123
- body: JSON.stringify({ username: username })
124
- });
125
- loadUsers();
126
- }
127
- }
128
-
129
- document.getElementById('addUserForm').addEventListener('submit', async (e) => {
130
- e.preventDefault();
131
- const formData = new FormData(e.target);
132
- const response = await fetch('/sudo/add_user', {
133
- method: 'POST',
134
- headers: {
135
- 'Content-Type': 'application/json',
136
- 'X-Sudo-Secret': localStorage.getItem('sudo_token')
137
- },
138
- body: JSON.stringify({
139
- username: formData.get('username'),
140
- password: formData.get('password')
141
- })
142
- });
143
- if (response.ok) {
144
- e.target.reset();
145
- await loadUsers();
146
- }
147
- });
148
-
149
- document.addEventListener('DOMContentLoaded', () => {
150
- const token = localStorage.getItem('sudo_token');
151
- if (!token && !window.location.pathname.includes('login')) {
152
- window.location.href = '/sudo';
153
- } else if (token) {
154
- loadUsers();
155
- }
156
- });
157
- EOF
158
-
159
- # 3. Create the Python server
160
- COPY <<"EOF" /app/server.py
161
  from flask import Flask, request, jsonify, render_template
162
- from waitress import serve
163
- import sqlite3
164
- import subprocess
165
- import threading
166
- import os
167
  from werkzeug.security import generate_password_hash
 
 
168
 
169
  app = Flask(__name__, template_folder='/app/admin/templates')
170
- app.secret_key = os.getenv("FLASK_SECRET", "secret-key-123")
171
 
172
- # Database setup
173
- def get_db():
174
- conn = sqlite3.connect('/app/data/users.db')
175
- conn.execute('''
176
- CREATE TABLE IF NOT EXISTS users (
177
- username TEXT PRIMARY KEY,
178
- password TEXT,
179
- role TEXT DEFAULT 'user'
180
- )
181
- ''')
182
- return conn
183
 
184
- # Admin routes
185
- @app.route('/sudo')
186
- def admin_login():
187
  return render_template('login.html')
188
 
189
- @app.route('/sudo/login', methods=['POST'])
190
  def login():
191
- if request.json.get('sudo_secret') == os.getenv("SUDO_SECRET"):
192
- return jsonify({
193
- "status": "success",
194
- "token": os.getenv("SUDO_SECRET")
195
- })
196
- return jsonify({"error": "Invalid password"}), 403
197
 
198
- @app.route('/sudo/dashboard')
199
- def dashboard():
200
- return render_template('dashboard.html')
201
-
202
- @app.route('/sudo/add_user', methods=['POST'])
203
- def add_user():
204
- if request.headers.get('X-Sudo-Secret') != os.getenv("SUDO_SECRET"):
205
  return jsonify({"error": "Unauthorized"}), 403
206
-
207
- db = get_db()
208
- try:
209
- db.execute(
210
- "INSERT INTO users (username, password) VALUES (?, ?)",
211
- [request.json["username"], generate_password_hash(request.json["password"])]
212
- )
213
- db.commit()
214
- return jsonify({"status": "User added"})
215
- except sqlite3.IntegrityError:
216
- return jsonify({"error": "User exists"}), 400
217
 
218
- @app.route('/sudo/list_users', methods=['GET'])
219
- def list_users():
220
- if request.headers.get('X-Sudo-Secret') != os.getenv("SUDO_SECRET"):
221
  return jsonify({"error": "Unauthorized"}), 403
222
-
223
- db = get_db()
224
- users = db.execute("SELECT username FROM users").fetchall()
225
- return jsonify([{"username": u[0]} for u in users])
 
 
 
 
226
 
227
- @app.route('/sudo/remove_user', methods=['POST'])
228
- def remove_user():
229
- if request.headers.get('X-Sudo-Secret') != os.getenv("SUDO_SECRET"):
230
  return jsonify({"error": "Unauthorized"}), 403
231
-
232
- db = get_db()
233
- db.execute("DELETE FROM users WHERE username = ?", [request.json["username"]])
234
- db.commit()
235
  return jsonify({"status": "User deleted"})
236
 
237
- # Start LibreChat in background
238
- def start_librechat():
239
- subprocess.run(["npm", "run", "backend"], cwd="/app")
240
-
241
- threading.Thread(target=start_librechat, daemon=True).start()
242
-
243
- # Start server
244
  if __name__ == "__main__":
245
- print("Admin panel: http://localhost:7860/sudo")
246
- serve(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  EOF
248
 
 
 
249
  # Startup script
250
  COPY <<"EOF" /app/start.sh
251
  #!/bin/sh
252
  # Start the combined server
253
- python3 /app/server.py
 
 
254
  EOF
255
  RUN chmod +x /app/start.sh
256
 
 
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} \
 
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
117
  COPY <<"EOF" /app/start.sh
118
  #!/bin/sh
119
  # Start the combined server
120
+ cd /app && npm run start:backend &
121
+ python3 /app/admin/app.py &
122
+ wait
123
  EOF
124
  RUN chmod +x /app/start.sh
125