Factor Studios commited on
Commit
61c8231
ยท
verified ยท
1 Parent(s): 398a244

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +285 -97
app.py CHANGED
@@ -1,97 +1,285 @@
1
- from fastapi import FastAPI
2
- from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import HTMLResponse
4
- import shutil
5
- import uvicorn
6
-
7
- app = FastAPI(title="Disk Space Monitor", description="A simple API to monitor disk space")
8
-
9
- # Add CORS middleware
10
- app.add_middleware(
11
- CORSMiddleware,
12
- allow_origins=["*"],
13
- allow_credentials=True,
14
- allow_methods=["*"],
15
- allow_headers=["*"],
16
- )
17
-
18
- def get_disk_space():
19
- """Get disk space information"""
20
- total, used, free = shutil.disk_usage("/")
21
- return {
22
- "total_gb": round(total / (1024**3), 2),
23
- "used_gb": round(used / (1024**3), 2),
24
- "free_gb": round(free / (1024**3), 2),
25
- "used_percentage": round((used / total) * 100, 2)
26
- }
27
-
28
- @app.get("/")
29
- async def root():
30
- """Root endpoint with HTML interface"""
31
- disk_info = get_disk_space()
32
- html_content = f"""
33
- <!DOCTYPE html>
34
- <html>
35
- <head>
36
- <title>Disk Space Monitor</title>
37
- <style>
38
- body {{ font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }}
39
- .container {{ max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
40
- h1 {{ color: #333; text-align: center; }}
41
- .disk-info {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0; }}
42
- .metric {{ display: flex; justify-content: space-between; margin: 10px 0; padding: 10px; background: white; border-radius: 5px; }}
43
- .metric-label {{ font-weight: bold; color: #555; }}
44
- .metric-value {{ color: #007bff; font-weight: bold; }}
45
- .progress-bar {{ width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; }}
46
- .progress-fill {{ height: 100%; background: linear-gradient(90deg, #28a745, #ffc107, #dc3545); transition: width 0.3s ease; }}
47
- .refresh-btn {{ background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 10px 0; }}
48
- .refresh-btn:hover {{ background: #0056b3; }}
49
- </style>
50
- </head>
51
- <body>
52
- <div class="container">
53
- <h1>๐Ÿ–ฅ๏ธ Disk Space Monitor</h1>
54
- <div class="disk-info">
55
- <div class="metric">
56
- <span class="metric-label">Total Space:</span>
57
- <span class="metric-value">{disk_info['total_gb']} GB</span>
58
- </div>
59
- <div class="metric">
60
- <span class="metric-label">Used Space:</span>
61
- <span class="metric-value">{disk_info['used_gb']} GB</span>
62
- </div>
63
- <div class="metric">
64
- <span class="metric-label">Free Space:</span>
65
- <span class="metric-value">{disk_info['free_gb']} GB</span>
66
- </div>
67
- <div class="metric">
68
- <span class="metric-label">Usage:</span>
69
- <span class="metric-value">{disk_info['used_percentage']}%</span>
70
- </div>
71
- <div class="progress-bar">
72
- <div class="progress-fill" style="width: {disk_info['used_percentage']}%"></div>
73
- </div>
74
- <button class="refresh-btn" onclick="location.reload()">๐Ÿ”„ Refresh</button>
75
- </div>
76
- <p style="text-align: center; color: #666; margin-top: 30px;">
77
- API Endpoints: <a href="/api/disk-space">/api/disk-space</a> | <a href="/docs">/docs</a>
78
- </p>
79
- </div>
80
- </body>
81
- </html>
82
- """
83
- return HTMLResponse(content=html_content)
84
-
85
- @app.get("/api/disk-space")
86
- async def get_disk_space_api():
87
- """API endpoint to get disk space information in JSON format"""
88
- return get_disk_space()
89
-
90
- @app.get("/health")
91
- async def health_check():
92
- """Health check endpoint"""
93
- return {"status": "healthy", "service": "disk-space-monitor"}
94
-
95
- if __name__ == "__main__":
96
- uvicorn.run(app, host="0.0.0.0", port=8000)
97
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import HTMLResponse, JSONResponse
4
+ import shutil
5
+ import uvicorn
6
+ import subprocess
7
+ import os
8
+ import json
9
+ from typing import List, Dict
10
+ import logging
11
+
12
+ app = FastAPI(title="Disk Space Monitor", description="A simple API to monitor and manage disk space")
13
+
14
+ # Add CORS middleware
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"],
18
+ allow_credentials=True,
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+ # Set up logging
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ def get_disk_space():
28
+ """Get disk space information"""
29
+ total, used, free = shutil.disk_usage("/")
30
+ return {
31
+ "total_gb": round(total / (1024**3), 2),
32
+ "used_gb": round(used / (1024**3), 2),
33
+ "free_gb": round(free / (1024**3), 2),
34
+ "used_percentage": round((used / total) * 100, 2)
35
+ }
36
+
37
+ def get_available_disks() -> List[Dict]:
38
+ """Get list of available disks"""
39
+ try:
40
+ result = subprocess.run(['lsblk', '-J', '-o', 'NAME,SIZE,TYPE,MOUNTPOINT'],
41
+ capture_output=True, text=True, check=True)
42
+ disks_data = json.loads(result.stdout)
43
+
44
+ disks = []
45
+ for device in disks_data['blockdevices']:
46
+ if device['type'] == 'disk':
47
+ disks.append({
48
+ 'name': device['name'],
49
+ 'size': device['size'],
50
+ 'type': device['type'],
51
+ 'mountpoint': device.get('mountpoint', '')
52
+ })
53
+ return disks
54
+ except Exception as e:
55
+ logger.error(f"Error getting disks: {e}")
56
+ return []
57
+
58
+ def format_disk(disk_name: str, filesystem: str = "ext4") -> Dict:
59
+ """
60
+ Format a disk with specified filesystem
61
+ WARNING: This will erase all data on the disk!
62
+ """
63
+ try:
64
+ # Validate disk exists
65
+ disks = get_available_disks()
66
+ disk_exists = any(disk['name'] == disk_name for disk in disks)
67
+
68
+ if not disk_exists:
69
+ raise HTTPException(status_code=404, detail=f"Disk {disk_name} not found")
70
+
71
+ # Check if disk is mounted
72
+ for disk in disks:
73
+ if disk['name'] == disk_name and disk.get('mountpoint'):
74
+ raise HTTPException(status_code=400, detail=f"Disk {disk_name} is mounted. Unmount first.")
75
+
76
+ # Format the disk (WARNING: DESTRUCTIVE OPERATION)
77
+ device_path = f"/dev/{disk_name}"
78
+
79
+ # Unmount if somehow still mounted
80
+ subprocess.run(['umount', device_path], capture_output=True)
81
+
82
+ # Create partition table (GPT)
83
+ subprocess.run(['parted', '-s', device_path, 'mklabel', 'gpt'], check=True)
84
+
85
+ # Create single partition
86
+ subprocess.run(['parted', '-s', device_path, 'mkpart', 'primary', filesystem, '0%', '100%'], check=True)
87
+
88
+ # Format the partition
89
+ partition_path = f"{device_path}1"
90
+ if filesystem == "ext4":
91
+ subprocess.run(['mkfs.ext4', '-F', partition_path], check=True)
92
+ elif filesystem == "xfs":
93
+ subprocess.run(['mkfs.xfs', '-f', partition_path], check=True)
94
+ elif filesystem == "ntfs":
95
+ subprocess.run(['mkfs.ntfs', '-F', partition_path], check=True)
96
+ else:
97
+ raise HTTPException(status_code=400, detail=f"Unsupported filesystem: {filesystem}")
98
+
99
+ return {
100
+ "status": "success",
101
+ "message": f"Disk {disk_name} formatted with {filesystem} filesystem",
102
+ "disk": disk_name,
103
+ "filesystem": filesystem
104
+ }
105
+
106
+ except subprocess.CalledProcessError as e:
107
+ logger.error(f"Formatting error: {e}")
108
+ raise HTTPException(status_code=500, detail=f"Formatting failed: {str(e)}")
109
+ except Exception as e:
110
+ logger.error(f"Unexpected error: {e}")
111
+ raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
112
+
113
+ @app.get("/")
114
+ async def root():
115
+ """Root endpoint with HTML interface"""
116
+ disk_info = get_disk_space()
117
+ available_disks = get_available_disks()
118
+
119
+ disks_html = ""
120
+ for disk in available_disks:
121
+ disks_html += f"""
122
+ <div class="disk-item">
123
+ <span class="disk-name">{disk['name']}</span>
124
+ <span class="disk-size">{disk['size']}</span>
125
+ <span class="disk-mount">{disk.get('mountpoint', 'Not mounted')}</span>
126
+ </div>
127
+ """
128
+
129
+ html_content = f"""
130
+ <!DOCTYPE html>
131
+ <html>
132
+ <head>
133
+ <title>Disk Space Monitor</title>
134
+ <style>
135
+ body {{ font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }}
136
+ .container {{ max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
137
+ h1 {{ color: #333; text-align: center; }}
138
+ .section {{ margin: 30px 0; }}
139
+ .disk-info, .disks-list, .format-section {{
140
+ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;
141
+ }}
142
+ .metric {{ display: flex; justify-content: space-between; margin: 10px 0; padding: 10px; background: white; border-radius: 5px; }}
143
+ .metric-label {{ font-weight: bold; color: #555; }}
144
+ .metric-value {{ color: #007bff; font-weight: bold; }}
145
+ .progress-bar {{ width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; }}
146
+ .progress-fill {{ height: 100%; background: linear-gradient(90deg, #28a745, #ffc107, #dc3545); }}
147
+ .refresh-btn {{ background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 10px 0; }}
148
+ .refresh-btn:hover {{ background: #0056b3; }}
149
+ .disk-item {{ display: flex; justify-content: space-between; padding: 8px; background: white; margin: 5px 0; border-radius: 5px; }}
150
+ .warning {{ color: #dc3545; font-weight: bold; background: #ffeaea; padding: 15px; border-radius: 5px; margin: 10px 0; }}
151
+ .format-form {{ margin: 15px 0; }}
152
+ .format-form select, .format-form input {{ padding: 8px; margin: 5px; border: 1px solid #ddd; border-radius: 4px; }}
153
+ .format-btn {{ background: #dc3545; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; }}
154
+ .format-btn:hover {{ background: #c82333; }}
155
+ </style>
156
+ </head>
157
+ <body>
158
+ <div class="container">
159
+ <h1>๐Ÿ–ฅ๏ธ Disk Space Monitor & Manager</h1>
160
+
161
+ <div class="section">
162
+ <h2>๐Ÿ“Š Current Disk Usage</h2>
163
+ <div class="disk-info">
164
+ <div class="metric">
165
+ <span class="metric-label">Total Space:</span>
166
+ <span class="metric-value">{disk_info['total_gb']} GB</span>
167
+ </div>
168
+ <div class="metric">
169
+ <span class="metric-label">Used Space:</span>
170
+ <span class="metric-value">{disk_info['used_gb']} GB</span>
171
+ </div>
172
+ <div class="metric">
173
+ <span class="metric-label">Free Space:</span>
174
+ <span class="metric-value">{disk_info['free_gb']} GB</span>
175
+ </div>
176
+ <div class="metric">
177
+ <span class="metric-label">Usage:</span>
178
+ <span class="metric-value">{disk_info['used_percentage']}%</span>
179
+ </div>
180
+ <div class="progress-bar">
181
+ <div class="progress-fill" style="width: {disk_info['used_percentage']}%"></div>
182
+ </div>
183
+ <button class="refresh-btn" onclick="location.reload()">๐Ÿ”„ Refresh</button>
184
+ </div>
185
+ </div>
186
+
187
+ <div class="section">
188
+ <h2>๐Ÿ’พ Available Disks</h2>
189
+ <div class="disks-list">
190
+ {disks_html if disks_html else "<p>No disks found</p>"}
191
+ </div>
192
+ </div>
193
+
194
+ <div class="section">
195
+ <h2>โš ๏ธ Format Disk (DANGER ZONE)</h2>
196
+ <div class="warning">
197
+ โš ๏ธ WARNING: Formatting will erase ALL data on the disk! This operation cannot be undone!
198
+ </div>
199
+ <div class="format-section">
200
+ <form class="format-form" onsubmit="formatDisk(event)">
201
+ <select id="diskSelect" required>
202
+ <option value="">Select a disk</option>
203
+ {"".join([f'<option value="{disk["name"]}">{disk["name"]} ({disk["size"]})</option>' for disk in available_disks])}
204
+ </select>
205
+ <select id="fsSelect" required>
206
+ <option value="ext4">ext4</option>
207
+ <option value="xfs">XFS</option>
208
+ <option value="ntfs">NTFS</option>
209
+ </select>
210
+ <button type="submit" class="format-btn">๐Ÿšซ Format Disk</button>
211
+ </form>
212
+ </div>
213
+ </div>
214
+
215
+ <p style="text-align: center; color: #666; margin-top: 30px;">
216
+ API Endpoints: <a href="/api/disk-space">/api/disk-space</a> |
217
+ <a href="/api/disks">/api/disks</a> |
218
+ <a href="/docs">/docs</a>
219
+ </p>
220
+ </div>
221
+
222
+ <script>
223
+ async function formatDisk(event) {{
224
+ event.preventDefault();
225
+ const disk = document.getElementById('diskSelect').value;
226
+ const filesystem = document.getElementById('fsSelect').value;
227
+
228
+ if (!disk) {{
229
+ alert('Please select a disk');
230
+ return;
231
+ }}
232
+
233
+ if (!confirm(`โš ๏ธ WARNING: This will ERASE ALL DATA on disk ${{disk}}! Are you absolutely sure?`)) {{
234
+ return;
235
+ }}
236
+
237
+ try {{
238
+ const response = await fetch('/api/format-disk', {{
239
+ method: 'POST',
240
+ headers: {{ 'Content-Type': 'application/json' }},
241
+ body: JSON.stringify({{ disk: disk, filesystem: filesystem }})
242
+ }});
243
+
244
+ const result = await response.json();
245
+ if (response.ok) {{
246
+ alert('โœ… ' + result.message);
247
+ location.reload();
248
+ }} else {{
249
+ alert('โŒ ' + result.detail);
250
+ }}
251
+ }} catch (error) {{
252
+ alert('โŒ Error: ' + error.message);
253
+ }}
254
+ }}
255
+ </script>
256
+ </body>
257
+ </html>
258
+ """
259
+ return HTMLResponse(content=html_content)
260
+
261
+ @app.get("/api/disk-space")
262
+ async def get_disk_space_api():
263
+ """API endpoint to get disk space information in JSON format"""
264
+ return get_disk_space()
265
+
266
+ @app.get("/api/disks")
267
+ async def get_disks_api():
268
+ """API endpoint to get list of available disks"""
269
+ return {"disks": get_available_disks()}
270
+
271
+ @app.post("/api/format-disk")
272
+ async def format_disk_api(disk: str, filesystem: str = "ext4"):
273
+ """
274
+ API endpoint to format a disk
275
+ WARNING: This will erase all data on the disk!
276
+ """
277
+ return format_disk(disk, filesystem)
278
+
279
+ @app.get("/health")
280
+ async def health_check():
281
+ """Health check endpoint"""
282
+ return {"status": "healthy", "service": "disk-space-monitor"}
283
+
284
+ if __name__ == "__main__":
285
+ uvicorn.run(app, host="0.0.0.0", port=8000)