Spaces:
Running
Running
Update Dockerfile
Browse files- 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 |
-
|
|
|
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 |
-
|
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('
|
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 |
-
|
267 |
-
|
268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|