Spaces:
Sleeping
Sleeping
Commit
·
95dc3cb
1
Parent(s):
a660b0d
new UI
Browse files- app.py +33 -1
- templates/dashboard.html +77 -12
app.py
CHANGED
|
@@ -127,7 +127,16 @@ def fetch_user_spaces(username, token=None):
|
|
| 127 |
try:
|
| 128 |
resp = requests.get(url, headers=headers, timeout=10)
|
| 129 |
if resp.status_code == 200:
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
except Exception:
|
| 132 |
pass
|
| 133 |
|
|
@@ -135,6 +144,29 @@ def fetch_user_spaces(username, token=None):
|
|
| 135 |
return spaces
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def fetch_space_discussions(space_id, token=None):
|
| 139 |
cache_key = f"discussions:{space_id}"
|
| 140 |
cached = cache_get(cache_key)
|
|
|
|
| 127 |
try:
|
| 128 |
resp = requests.get(url, headers=headers, timeout=10)
|
| 129 |
if resp.status_code == 200:
|
| 130 |
+
space_list = resp.json()
|
| 131 |
+
# Fetch individual space details to get runtime info
|
| 132 |
+
for space in space_list:
|
| 133 |
+
space_id = space.get("id", "")
|
| 134 |
+
if space_id:
|
| 135 |
+
detail = fetch_space_detail(space_id, token)
|
| 136 |
+
if detail:
|
| 137 |
+
spaces.append(detail)
|
| 138 |
+
else:
|
| 139 |
+
spaces.append(space)
|
| 140 |
except Exception:
|
| 141 |
pass
|
| 142 |
|
|
|
|
| 144 |
return spaces
|
| 145 |
|
| 146 |
|
| 147 |
+
def fetch_space_detail(space_id, token=None):
|
| 148 |
+
cache_key = f"space_detail:{space_id}"
|
| 149 |
+
cached = cache_get(cache_key)
|
| 150 |
+
if cached is not None:
|
| 151 |
+
return cached
|
| 152 |
+
|
| 153 |
+
headers = {}
|
| 154 |
+
if token:
|
| 155 |
+
headers["Authorization"] = f"Bearer {token}"
|
| 156 |
+
|
| 157 |
+
try:
|
| 158 |
+
url = f"https://huggingface.co/api/spaces/{space_id}"
|
| 159 |
+
resp = requests.get(url, headers=headers, timeout=10)
|
| 160 |
+
if resp.status_code == 200:
|
| 161 |
+
detail = resp.json()
|
| 162 |
+
cache_set(cache_key, detail)
|
| 163 |
+
return detail
|
| 164 |
+
except Exception:
|
| 165 |
+
pass
|
| 166 |
+
|
| 167 |
+
return None
|
| 168 |
+
|
| 169 |
+
|
| 170 |
def fetch_space_discussions(space_id, token=None):
|
| 171 |
cache_key = f"discussions:{space_id}"
|
| 172 |
cached = cache_get(cache_key)
|
templates/dashboard.html
CHANGED
|
@@ -132,7 +132,7 @@
|
|
| 132 |
background: #2a2a0a;
|
| 133 |
color: #eab308;
|
| 134 |
}
|
| 135 |
-
.space-status.stopped, .space-status.paused {
|
| 136 |
background: #1a1a1a;
|
| 137 |
color: #666;
|
| 138 |
}
|
|
@@ -140,6 +140,35 @@
|
|
| 140 |
background: #2a0a0a;
|
| 141 |
color: #ef4444;
|
| 142 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
.main {
|
| 144 |
flex: 1;
|
| 145 |
margin-left: 280px;
|
|
@@ -354,17 +383,44 @@
|
|
| 354 |
{% if spaces %}
|
| 355 |
<div class="spaces-list">
|
| 356 |
{% for space in spaces %}
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
{
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
{% endfor %}
|
| 369 |
</div>
|
| 370 |
{% else %}
|
|
@@ -436,5 +492,14 @@
|
|
| 436 |
{% endif %}
|
| 437 |
</main>
|
| 438 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
</body>
|
| 440 |
</html>
|
|
|
|
| 132 |
background: #2a2a0a;
|
| 133 |
color: #eab308;
|
| 134 |
}
|
| 135 |
+
.space-status.stopped, .space-status.paused, .space-status.sleeping {
|
| 136 |
background: #1a1a1a;
|
| 137 |
color: #666;
|
| 138 |
}
|
|
|
|
| 140 |
background: #2a0a0a;
|
| 141 |
color: #ef4444;
|
| 142 |
}
|
| 143 |
+
.space-details {
|
| 144 |
+
display: none;
|
| 145 |
+
padding: 0.5rem 0.75rem;
|
| 146 |
+
margin-top: 2px;
|
| 147 |
+
background: #111;
|
| 148 |
+
border-radius: 4px;
|
| 149 |
+
font-size: 0.75rem;
|
| 150 |
+
}
|
| 151 |
+
.space-details.open {
|
| 152 |
+
display: block;
|
| 153 |
+
}
|
| 154 |
+
.space-detail-row {
|
| 155 |
+
display: flex;
|
| 156 |
+
justify-content: space-between;
|
| 157 |
+
padding: 0.25rem 0;
|
| 158 |
+
border-bottom: 1px solid #1a1a1a;
|
| 159 |
+
}
|
| 160 |
+
.space-detail-row:last-child {
|
| 161 |
+
border-bottom: none;
|
| 162 |
+
}
|
| 163 |
+
.space-detail-label {
|
| 164 |
+
color: #555;
|
| 165 |
+
}
|
| 166 |
+
.space-detail-value {
|
| 167 |
+
color: #999;
|
| 168 |
+
}
|
| 169 |
+
.space-toggle {
|
| 170 |
+
cursor: pointer;
|
| 171 |
+
}
|
| 172 |
.main {
|
| 173 |
flex: 1;
|
| 174 |
margin-left: 280px;
|
|
|
|
| 383 |
{% if spaces %}
|
| 384 |
<div class="spaces-list">
|
| 385 |
{% for space in spaces %}
|
| 386 |
+
{% set status = space.runtime.stage|default('STOPPED')|upper if space.runtime else 'STOPPED' %}
|
| 387 |
+
{% set hardware = space.runtime.hardware.current|default('unknown') if space.runtime and space.runtime.hardware else 'unknown' %}
|
| 388 |
+
{% set storage = space.runtime.storage.current|default('none') if space.runtime and space.runtime.storage else 'none' %}
|
| 389 |
+
<div class="space-entry">
|
| 390 |
+
<div class="space-item space-toggle" data-space="{{ loop.index }}">
|
| 391 |
+
<span class="space-name">{{ space.id.split('/')[-1] }}</span>
|
| 392 |
+
<span class="space-status {{ status|lower }}">
|
| 393 |
+
{% if status == 'RUNNING' %}running
|
| 394 |
+
{% elif status == 'BUILDING' %}building
|
| 395 |
+
{% elif status == 'RUNTIME_ERROR' or status == 'BUILD_ERROR' %}error
|
| 396 |
+
{% elif status == 'PAUSED' %}paused
|
| 397 |
+
{% elif status == 'SLEEPING' %}sleeping
|
| 398 |
+
{% else %}stopped{% endif %}
|
| 399 |
+
</span>
|
| 400 |
+
</div>
|
| 401 |
+
<div class="space-details" id="space-{{ loop.index }}">
|
| 402 |
+
<div class="space-detail-row">
|
| 403 |
+
<span class="space-detail-label">Hardware</span>
|
| 404 |
+
<span class="space-detail-value">{{ hardware }}</span>
|
| 405 |
+
</div>
|
| 406 |
+
<div class="space-detail-row">
|
| 407 |
+
<span class="space-detail-label">Storage</span>
|
| 408 |
+
<span class="space-detail-value">{{ storage }}</span>
|
| 409 |
+
</div>
|
| 410 |
+
<div class="space-detail-row">
|
| 411 |
+
<span class="space-detail-label">SDK</span>
|
| 412 |
+
<span class="space-detail-value">{{ space.sdk|default('unknown') }}</span>
|
| 413 |
+
</div>
|
| 414 |
+
<div class="space-detail-row">
|
| 415 |
+
<span class="space-detail-label">Likes</span>
|
| 416 |
+
<span class="space-detail-value">{{ space.likes|default(0) }}</span>
|
| 417 |
+
</div>
|
| 418 |
+
<a href="https://huggingface.co/spaces/{{ space.id }}" target="_blank" class="space-detail-row" style="color: #6366f1;">
|
| 419 |
+
<span>Open on HF</span>
|
| 420 |
+
<span>→</span>
|
| 421 |
+
</a>
|
| 422 |
+
</div>
|
| 423 |
+
</div>
|
| 424 |
{% endfor %}
|
| 425 |
</div>
|
| 426 |
{% else %}
|
|
|
|
| 492 |
{% endif %}
|
| 493 |
</main>
|
| 494 |
</div>
|
| 495 |
+
<script>
|
| 496 |
+
document.querySelectorAll('.space-toggle').forEach(el => {
|
| 497 |
+
el.addEventListener('click', () => {
|
| 498 |
+
const id = el.dataset.space;
|
| 499 |
+
const details = document.getElementById('space-' + id);
|
| 500 |
+
details.classList.toggle('open');
|
| 501 |
+
});
|
| 502 |
+
});
|
| 503 |
+
</script>
|
| 504 |
</body>
|
| 505 |
</html>
|