martynka commited on
Commit
b08ee08
·
verified ·
1 Parent(s): 5bf8328

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +51 -29
Dockerfile CHANGED
@@ -12,12 +12,13 @@ RUN apk update && apk add --no-cache \
12
 
13
  # Setup directories within /app
14
  RUN mkdir -p /app/admin/{templates,static} \
 
15
  && chmod -R 777 /app/uploads/temp \
16
  && chmod -R 777 /app/client/public/images \
17
  && chmod -R 777 /app/api/logs
18
 
19
  # ===== Integrated Admin Panel =====
20
- COPY <<EOF /app/admin/app.py
21
  from flask import Flask, request, jsonify, render_template
22
  import os
23
  import sqlite3
@@ -49,6 +50,10 @@ def login():
49
  return jsonify({"status": "success"})
50
  return jsonify({"error": "Invalid secret"}), 403
51
 
 
 
 
 
52
  @app.route('/sudo/add_user', methods=['POST'])
53
  def add_user():
54
  if request.headers.get('X-Sudo-Secret') != os.getenv("SUDO_SECRET"):
@@ -78,7 +83,7 @@ def list_users():
78
  EOF
79
 
80
  # Admin templates
81
- COPY <<EOF /app/admin/templates/login.html
82
  <!DOCTYPE html>
83
  <html>
84
  <head>
@@ -93,26 +98,12 @@ COPY <<EOF /app/admin/templates/login.html
93
  <button type="submit">Login</button>
94
  </form>
95
  </div>
96
- <script>
97
- document.getElementById('loginForm').addEventListener('submit', async (e) => {
98
- e.preventDefault();
99
- const response = await fetch('/sudo/login', {
100
- method: 'POST',
101
- headers: {'Content-Type': 'application/json'},
102
- body: JSON.stringify({sudo_secret: e.target.sudo_secret.value})
103
- });
104
- if (response.ok) {
105
- window.location.href = '/sudo/dashboard';
106
- } else {
107
- alert('Invalid secret!');
108
- }
109
- });
110
- </script>
111
  </body>
112
  </html>
113
  EOF
114
 
115
- COPY <<EOF /app/admin/templates/dashboard.html
116
  <!DOCTYPE html>
117
  <html>
118
  <head>
@@ -141,7 +132,7 @@ COPY <<EOF /app/admin/templates/dashboard.html
141
  EOF
142
 
143
  # Static files
144
- COPY <<EOF /app/admin/static/admin.css
145
  body {
146
  font-family: Arial, sans-serif;
147
  margin: 0;
@@ -173,19 +164,34 @@ button {
173
  }
174
  EOF
175
 
176
- COPY <<EOF /app/admin/static/admin.js
 
177
  async function loadUsers() {
178
  const response = await fetch('/sudo/list_users', {
179
  headers: {'X-Sudo-Secret': localStorage.getItem('sudo_token')}
180
  });
181
  if (response.ok) {
182
  const users = await response.json();
183
- document.getElementById('userList').innerHTML = users.map(user =>
184
- `<li>${user.username}</li>`
185
  ).join('');
186
  }
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  document.getElementById('addUserForm').addEventListener('submit', async (e) => {
190
  e.preventDefault();
191
  const formData = new FormData(e.target);
@@ -206,17 +212,26 @@ document.getElementById('addUserForm').addEventListener('submit', async (e) => {
206
  }
207
  });
208
 
209
- // Initialize
210
  document.addEventListener('DOMContentLoaded', () => {
211
- if (!localStorage.getItem('sudo_token') && !window.location.pathname.includes('/login')) {
212
  window.location.href = '/sudo';
213
  }
214
  loadUsers();
215
  });
216
  EOF
217
 
 
 
 
 
 
 
 
 
 
 
218
  # NGINX Configuration
219
- COPY <<EOF /etc/nginx/nginx.conf
220
  events { worker_connections 1024; }
221
  http {
222
  server {
@@ -244,7 +259,7 @@ http {
244
  EOF
245
 
246
  # Startup script
247
- COPY <<EOF /start.sh
248
  #!/bin/sh
249
  # Start LibreChat
250
  cd /app/api && npm run backend &
@@ -263,9 +278,16 @@ RUN chmod +x /start.sh
263
  # Environment variables
264
  ENV HOST=0.0.0.0 \
265
  PORT=3080 \
266
- MONGO_URI="mongodb://localhost:27017/librechat" \
267
- SUDO_SECRET="your-admin-secret" \
268
- FLASK_SECRET="your-flask-secret" \
 
 
 
 
 
 
 
269
  NODE_ENV="production"
270
 
271
  EXPOSE 7860
 
12
 
13
  # Setup directories within /app
14
  RUN mkdir -p /app/admin/{templates,static} \
15
+ && mkdir -p /app/data \
16
  && chmod -R 777 /app/uploads/temp \
17
  && chmod -R 777 /app/client/public/images \
18
  && chmod -R 777 /app/api/logs
19
 
20
  # ===== Integrated Admin Panel =====
21
+ COPY <<"EOF" /app/admin/app.py
22
  from flask import Flask, request, jsonify, render_template
23
  import os
24
  import sqlite3
 
50
  return jsonify({"status": "success"})
51
  return jsonify({"error": "Invalid secret"}), 403
52
 
53
+ @app.route('/sudo/dashboard')
54
+ def dashboard():
55
+ return render_template('dashboard.html')
56
+
57
  @app.route('/sudo/add_user', methods=['POST'])
58
  def add_user():
59
  if request.headers.get('X-Sudo-Secret') != os.getenv("SUDO_SECRET"):
 
83
  EOF
84
 
85
  # Admin templates
86
+ COPY <<"EOF" /app/admin/templates/login.html
87
  <!DOCTYPE html>
88
  <html>
89
  <head>
 
98
  <button type="submit">Login</button>
99
  </form>
100
  </div>
101
+ <script src="/static/admin.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  </body>
103
  </html>
104
  EOF
105
 
106
+ COPY <<"EOF" /app/admin/templates/dashboard.html
107
  <!DOCTYPE html>
108
  <html>
109
  <head>
 
132
  EOF
133
 
134
  # Static files
135
+ COPY <<"EOF" /app/admin/static/admin.css
136
  body {
137
  font-family: Arial, sans-serif;
138
  margin: 0;
 
164
  }
165
  EOF
166
 
167
+ # Properly escaped JavaScript
168
+ COPY <<"EOF" /app/admin/static/admin.js
169
  async function loadUsers() {
170
  const response = await fetch('/sudo/list_users', {
171
  headers: {'X-Sudo-Secret': localStorage.getItem('sudo_token')}
172
  });
173
  if (response.ok) {
174
  const users = await response.json();
175
+ document.getElementById('userList').innerHTML = users.map(user =>
176
+ \`<li>\${user.username} <button onclick="deleteUser('\${user.username}')">Delete</button></li>\`
177
  ).join('');
178
  }
179
  }
180
 
181
+ async function deleteUser(username) {
182
+ if (confirm(\`Delete \${username}?\`)) {
183
+ await fetch('/sudo/remove_user', {
184
+ method: 'POST',
185
+ headers: {
186
+ 'Content-Type': 'application/json',
187
+ 'X-Sudo-Secret': localStorage.getItem('sudo_token')
188
+ },
189
+ body: JSON.stringify({ username: username })
190
+ });
191
+ loadUsers();
192
+ }
193
+ }
194
+
195
  document.getElementById('addUserForm').addEventListener('submit', async (e) => {
196
  e.preventDefault();
197
  const formData = new FormData(e.target);
 
212
  }
213
  });
214
 
 
215
  document.addEventListener('DOMContentLoaded', () => {
216
+ if (!localStorage.getItem('sudo_token') && !window.location.pathname.includes('login')) {
217
  window.location.href = '/sudo';
218
  }
219
  loadUsers();
220
  });
221
  EOF
222
 
223
+ # Add delete endpoint to app.py
224
+ RUN echo '@app.route("/sudo/remove_user", methods=["POST"])' >> /app/admin/app.py
225
+ RUN echo 'def remove_user():' >> /app/admin/app.py
226
+ RUN echo ' if request.headers.get("X-Sudo-Secret") != os.getenv("SUDO_SECRET"):' >> /app/admin/app.py
227
+ RUN echo ' return jsonify({"error": "Unauthorized"}), 403' >> /app/admin/app.py
228
+ RUN echo ' db = get_db()' >> /app/admin/app.py
229
+ RUN echo ' db.execute("DELETE FROM users WHERE username=?", [request.json["username"]])' >> /app/admin/app.py
230
+ RUN echo ' db.commit()' >> /app/admin/app.py
231
+ RUN echo ' return jsonify({"status": "User removed"})' >> /app/admin/app.py
232
+
233
  # NGINX Configuration
234
+ COPY <<"EOF" /etc/nginx/nginx.conf
235
  events { worker_connections 1024; }
236
  http {
237
  server {
 
259
  EOF
260
 
261
  # Startup script
262
+ COPY <<"EOF" /start.sh
263
  #!/bin/sh
264
  # Start LibreChat
265
  cd /app/api && npm run backend &
 
278
  # Environment variables
279
  ENV HOST=0.0.0.0 \
280
  PORT=3080 \
281
+ SESSION_EXPIRY=900000 \
282
+ REFRESH_TOKEN_EXPIRY=604800000 \
283
+ SEARCH=true \
284
+ MEILI_NO_ANALYTICS=true \
285
+ MEILI_HOST=https://martynka-meilisearch.hf.space \
286
+ CONFIG_PATH=/app/librechat.yaml \
287
+ CUSTOM_FOOTER=EasierIT \
288
+ MONGO_URI="$MONGO_URI" \
289
+ SUDO_SECRET="$SUDO_SECRET" \
290
+ FLASK_SECRET="$FLASK_SECRET"
291
  NODE_ENV="production"
292
 
293
  EXPOSE 7860