Spaces:
Running
Running
Commit ·
7d9bf9a
1
Parent(s): ff50e8c
fix admin auth for private HF Space; add manage_results script
Browse filesThe HF Space is private, so its proxy requires a valid HF token as the
Authorization Bearer header, preventing our ADMIN_TOKEN from reaching
Express. adminAuth now also accepts the token via X-Admin-Token header.
manage_results.sh sends both headers (HF_TOKEN as Bearer, ADMIN_TOKEN
as X-Admin-Token) so downloads and deletes work through the proxy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- manage_results.sh +50 -0
- server.js +7 -2
- start.sh +32 -0
manage_results.sh
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 5 |
+
|
| 6 |
+
# Load .env
|
| 7 |
+
if [[ -f "$SCRIPT_DIR/.env" ]]; then
|
| 8 |
+
set -a; source "$SCRIPT_DIR/.env"; set +a
|
| 9 |
+
else
|
| 10 |
+
echo "Error: .env file not found"; exit 1
|
| 11 |
+
fi
|
| 12 |
+
|
| 13 |
+
HOST="${DIFFMT_HOST:-https://htw-ki-werkstatt-diffmt.hf.space}"
|
| 14 |
+
|
| 15 |
+
# The Space is private, so HF's proxy requires a valid HF_TOKEN as Bearer.
|
| 16 |
+
# Our ADMIN_TOKEN travels in X-Admin-Token to avoid the conflict.
|
| 17 |
+
auth_headers=(-H "Authorization: Bearer ${HF_TOKEN}" -H "X-Admin-Token: ${ADMIN_TOKEN}")
|
| 18 |
+
|
| 19 |
+
cmd="${1:-}"
|
| 20 |
+
|
| 21 |
+
if [[ "$cmd" == "download" ]]; then
|
| 22 |
+
OUT="$SCRIPT_DIR/results_$(date +%Y%m%d_%H%M%S).json"
|
| 23 |
+
echo "Downloading results from $HOST …"
|
| 24 |
+
http_code=$(curl -sf -w "%{http_code}" -o "$OUT" \
|
| 25 |
+
"${auth_headers[@]}" "$HOST/api/export")
|
| 26 |
+
if [[ "$http_code" == "200" ]]; then
|
| 27 |
+
echo "Saved to $OUT"
|
| 28 |
+
python3 -c "
|
| 29 |
+
import json, sys
|
| 30 |
+
d = json.load(open('$OUT'))
|
| 31 |
+
print(f\"Sessions: {len(d.get('sessions',[]))}\")
|
| 32 |
+
print(f\"Trials: {len(d.get('trials',[]))}\")
|
| 33 |
+
"
|
| 34 |
+
else
|
| 35 |
+
rm -f "$OUT"
|
| 36 |
+
echo "Error: server returned HTTP $http_code"
|
| 37 |
+
exit 1
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
elif [[ "$cmd" == "delete" ]]; then
|
| 41 |
+
read -rp "Delete ALL results on the server? Type YES to confirm: " confirm
|
| 42 |
+
if [[ "$confirm" != "YES" ]]; then echo "Aborted."; exit 0; fi
|
| 43 |
+
http_code=$(curl -sf -w "%{http_code}" -o /dev/null -X POST \
|
| 44 |
+
"${auth_headers[@]}" "$HOST/api/admin/clear")
|
| 45 |
+
echo "Server responded: HTTP $http_code"
|
| 46 |
+
|
| 47 |
+
else
|
| 48 |
+
echo "Usage: $0 <download|delete>"
|
| 49 |
+
exit 1
|
| 50 |
+
fi
|
server.js
CHANGED
|
@@ -13,8 +13,13 @@ function adminAuth(req, res, next) {
|
|
| 13 |
if (!ADMIN_TOKEN) {
|
| 14 |
return res.status(503).json({ error: 'Admin access disabled: ADMIN_TOKEN not set' });
|
| 15 |
}
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
return res.status(401).json({ error: 'Unauthorized' });
|
| 19 |
}
|
| 20 |
next();
|
|
|
|
| 13 |
if (!ADMIN_TOKEN) {
|
| 14 |
return res.status(503).json({ error: 'Admin access disabled: ADMIN_TOKEN not set' });
|
| 15 |
}
|
| 16 |
+
// Accept token via Authorization Bearer OR X-Admin-Token header.
|
| 17 |
+
// The X-Admin-Token header is needed when the HF Space is private:
|
| 18 |
+
// HF's proxy requires a valid HF token as the Bearer header, so the
|
| 19 |
+
// admin token must travel in a separate header.
|
| 20 |
+
const bearer = (req.headers.authorization || '').replace(/^Bearer\s+/i, '');
|
| 21 |
+
const custom = req.headers['x-admin-token'] || '';
|
| 22 |
+
if (bearer !== ADMIN_TOKEN && custom !== ADMIN_TOKEN) {
|
| 23 |
return res.status(401).json({ error: 'Unauthorized' });
|
| 24 |
}
|
| 25 |
next();
|
start.sh
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 5 |
+
cd "$SCRIPT_DIR"
|
| 6 |
+
|
| 7 |
+
# Load .env if present
|
| 8 |
+
if [[ -f .env ]]; then
|
| 9 |
+
set -a; source .env; set +a
|
| 10 |
+
fi
|
| 11 |
+
|
| 12 |
+
# Locate node — try nvm, then Homebrew, then PATH
|
| 13 |
+
if [[ -z "$(command -v node 2>/dev/null)" ]]; then
|
| 14 |
+
[[ -s "$HOME/.nvm/nvm.sh" ]] && source "$HOME/.nvm/nvm.sh"
|
| 15 |
+
for p in /opt/homebrew/bin /usr/local/bin; do
|
| 16 |
+
[[ -x "$p/node" ]] && export PATH="$p:$PATH" && break
|
| 17 |
+
done
|
| 18 |
+
fi
|
| 19 |
+
|
| 20 |
+
NODE="$(command -v node 2>/dev/null)" \
|
| 21 |
+
|| { echo "Error: node not found. Install Node.js and try again."; exit 1; }
|
| 22 |
+
|
| 23 |
+
# Use local data dir instead of HF bucket
|
| 24 |
+
export DATA_PATH="$SCRIPT_DIR/data/results.json"
|
| 25 |
+
|
| 26 |
+
PORT="${PORT:-3000}"
|
| 27 |
+
URL="http://localhost:$PORT"
|
| 28 |
+
|
| 29 |
+
echo "Starting DiffMT on $URL (node: $NODE)"
|
| 30 |
+
open "$URL" 2>/dev/null || true # open browser on macOS; silently skipped elsewhere
|
| 31 |
+
|
| 32 |
+
"$NODE" server.js
|