Spaces:
Running
Running
deploy dc86eb14
Browse files- app/main.py +29 -3
- app/static/index.html +1 -1
app/main.py
CHANGED
|
@@ -165,8 +165,18 @@ def r2_read_all(coll):
|
|
| 165 |
|
| 166 |
|
| 167 |
# ------------------------------------------------------------------------------------------------ access list
|
| 168 |
-
_acl = {"data": None, "at": 0.0}
|
| 169 |
_acl_lock = threading.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
|
| 171 |
|
| 172 |
def access_list():
|
|
@@ -176,8 +186,10 @@ def access_list():
|
|
| 176 |
if _acl["data"] is not None and time.time() - _acl["at"] < ACCESS_TTL:
|
| 177 |
return _acl["data"]
|
| 178 |
try:
|
| 179 |
-
_acl["data"], _acl["at"] = r2_get(ACCESS_KEY) or {}, time.time()
|
| 180 |
-
except Exception: # noqa: BLE001
|
|
|
|
|
|
|
| 181 |
if _acl["data"] is None or time.time() - _acl["at"] > ACCESS_STALE_MAX:
|
| 182 |
raise
|
| 183 |
return _acl["data"]
|
|
@@ -254,6 +266,20 @@ def index():
|
|
| 254 |
return FileResponse(STATIC / "index.html", media_type="text/html; charset=utf-8")
|
| 255 |
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
@app.get("/app.js")
|
| 258 |
def app_js():
|
| 259 |
return FileResponse(STATIC / "app.js", media_type="text/javascript; charset=utf-8")
|
|
|
|
| 165 |
|
| 166 |
|
| 167 |
# ------------------------------------------------------------------------------------------------ access list
|
| 168 |
+
_acl = {"data": None, "at": 0.0, "error": None}
|
| 169 |
_acl_lock = threading.Lock()
|
| 170 |
+
R2_ENV = ("R2_ACCOUNT_ID", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY")
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def _error_code(e):
|
| 174 |
+
"""A short, value-free reason for a failed store read: the S3 error code or the exception class."""
|
| 175 |
+
if isinstance(e, botocore.exceptions.ClientError):
|
| 176 |
+
return e.response.get("Error", {}).get("Code") or "ClientError"
|
| 177 |
+
if isinstance(e, RuntimeError) and str(e).endswith("is not set"):
|
| 178 |
+
return str(e) # names the missing variable, never a value
|
| 179 |
+
return type(e).__name__
|
| 180 |
|
| 181 |
|
| 182 |
def access_list():
|
|
|
|
| 186 |
if _acl["data"] is not None and time.time() - _acl["at"] < ACCESS_TTL:
|
| 187 |
return _acl["data"]
|
| 188 |
try:
|
| 189 |
+
_acl["data"], _acl["at"], _acl["error"] = r2_get(ACCESS_KEY) or {}, time.time(), None
|
| 190 |
+
except Exception as e: # noqa: BLE001
|
| 191 |
+
_acl["error"] = _error_code(e)
|
| 192 |
+
print(f"access list read failed: {_acl['error']}", flush=True)
|
| 193 |
if _acl["data"] is None or time.time() - _acl["at"] > ACCESS_STALE_MAX:
|
| 194 |
raise
|
| 195 |
return _acl["data"]
|
|
|
|
| 266 |
return FileResponse(STATIC / "index.html", media_type="text/html; charset=utf-8")
|
| 267 |
|
| 268 |
|
| 269 |
+
@app.get("/status")
|
| 270 |
+
def status():
|
| 271 |
+
"""Unauthenticated health check: which store settings are present (true/false, never values) and whether the
|
| 272 |
+
access list can be read (an error code at most). Shows no data."""
|
| 273 |
+
try:
|
| 274 |
+
access_list()
|
| 275 |
+
state = "ok"
|
| 276 |
+
except Exception: # noqa: BLE001
|
| 277 |
+
state = _acl["error"] or "error"
|
| 278 |
+
return JSONResponse({"store": STORE if "STORE" in globals() else "r2",
|
| 279 |
+
"r2_settings_present": {n: bool(os.environ.get(n)) for n in R2_ENV},
|
| 280 |
+
"access_list": state}, headers={"Cache-Control": "no-store"})
|
| 281 |
+
|
| 282 |
+
|
| 283 |
@app.get("/app.js")
|
| 284 |
def app_js():
|
| 285 |
return FileResponse(STATIC / "app.js", media_type="text/javascript; charset=utf-8")
|
app/static/index.html
CHANGED
|
@@ -148,7 +148,7 @@ section { display: flex; flex-direction: column; gap: 12px; }
|
|
| 148 |
<h2>Add or update a project</h2>
|
| 149 |
<p>Each project is one JSON record under <code>projects/<slug></code>, with access guides under <code>docs/<id></code>. A session updates its own record with <code>python dataops/experiments_db.py</code>, and this page picks it up within a minute. Full instructions, fields and examples are in <code>DATA_EXPERIMENTS_DASHBOARD.md</code> at the root of the data-processing-tts repo.</p>
|
| 150 |
<p>Trained on a dataset? Press <b>Mark SFT done</b> on its card and add a note (the run name or result). Anyone signed in can mark or undo it; everyone sees who marked it and when.</p>
|
| 151 |
-
<span class="build">build
|
| 152 |
</div>
|
| 153 |
</main>
|
| 154 |
|
|
|
|
| 148 |
<h2>Add or update a project</h2>
|
| 149 |
<p>Each project is one JSON record under <code>projects/<slug></code>, with access guides under <code>docs/<id></code>. A session updates its own record with <code>python dataops/experiments_db.py</code>, and this page picks it up within a minute. Full instructions, fields and examples are in <code>DATA_EXPERIMENTS_DASHBOARD.md</code> at the root of the data-processing-tts repo.</p>
|
| 150 |
<p>Trained on a dataset? Press <b>Mark SFT done</b> on its card and add a note (the run name or result). Anyone signed in can mark or undo it; everyone sees who marked it and when.</p>
|
| 151 |
+
<span class="build">build dc86eb14</span>
|
| 152 |
</div>
|
| 153 |
</main>
|
| 154 |
|