anurag008w commited on
Commit
6113d21
Β·
1 Parent(s): 5ce4ce7

Implement private space redirect functionality

Browse files

Add support for private space redirection in HuggingClaw.

Files changed (1) hide show
  1. health-server.js +72 -1
health-server.js CHANGED
@@ -37,6 +37,21 @@ const DEVDATA_SEPARATE_DATASET = DEVDATA_DATASET_NAME !== BACKUP_DATASET_NAME;
37
  const DEVDATA_ENABLED = JUPYTER_ENABLED && HF_BACKUP_ENABLED && DEVDATA_SEPARATE_DATASET && !/^(off|false|0|no)$/i.test((process.env.DEVDATA || "on").trim());
38
  const APP_BASE = normalizeBase(process.env.APP_BASE, "/app");
39
  const SYNC_STATUS_FILE = "/tmp/sync-status.json";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
41
  "/tmp/huggingclaw-cloudflare-keepalive-status.json";
42
 
@@ -176,12 +191,24 @@ function renderDashboard(data) {
176
  <a class="hero-action env" data-space-link="env-builder" href="/env-builder">βš™οΈ Env Builder β†’</a>
177
  </div>
178
  <section class="overview">${tilesHtml}</section>
179
- <footer>Built by <a href="https://github.com/somratpro" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none">@somratpro</a>${JUPYTER_ENABLED ? " Β· Terminal by JupyterLab" : ""}<br><span>Public Spaces can be opened directly via <code>.hf.space</code>; private Spaces require the App tab session.</span></footer>
180
  </main>
181
  <script>
182
  document.querySelectorAll('.local-time').forEach(el=>{const d=new Date(el.getAttribute('data-iso'));if(!isNaN(d))el.textContent='At '+d.toLocaleTimeString()});
183
  const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
184
  const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
 
 
 
 
 
 
 
 
 
 
 
 
185
  // If inside the HF App iframe, force new-tab navigation so users can break out
186
  // to the standalone Space host. Also keep direct .hf.space behavior opening new tabs.
187
  const openInNewTab = inEmbeddedApp || isDirectHfSpaceHost;
@@ -198,6 +225,39 @@ function renderDashboard(data) {
198
  </body></html>`;
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  function renderEnvBuilder() {
202
  try {
203
  return fs.readFileSync(require("path").join(__dirname, "env-builder.html"), "utf8");
@@ -308,6 +368,17 @@ const server = http.createServer(async (req, res) => {
308
  return res.end(JSON.stringify({ model: LLM_MODEL, uptime: formatUptime(Date.now() - startTime), gatewayReady, jupyterReady, sync: getSyncStatus(), whatsapp: readGuardianStatus(), keepalive: getKeepaliveStatus() }));
309
  }
310
 
 
 
 
 
 
 
 
 
 
 
 
311
  if (pathname === "/env-builder" || pathname === "/env-builder/") {
312
  res.writeHead(200, { "Content-Type": "text/html" });
313
  return res.end(renderEnvBuilder());
 
37
  const DEVDATA_ENABLED = JUPYTER_ENABLED && HF_BACKUP_ENABLED && DEVDATA_SEPARATE_DATASET && !/^(off|false|0|no)$/i.test((process.env.DEVDATA || "on").trim());
38
  const APP_BASE = normalizeBase(process.env.APP_BASE, "/app");
39
  const SYNC_STATUS_FILE = "/tmp/sync-status.json";
40
+
41
+ // ── Private Space redirect support ──
42
+ // HF automatically sets SPACE_ID as "username/spacename" in every Space container.
43
+ const SPACE_ID = (process.env.SPACE_ID || "").trim();
44
+ function deriveHfSpaceUrl() {
45
+ if (SPACE_ID) return `https://huggingface.co/spaces/${SPACE_ID}`;
46
+ const host = (process.env.SPACE_HOST || "").replace(/\.hf\.space$/i, "");
47
+ const author = (process.env.SPACE_AUTHOR_NAME || "").trim().toLowerCase();
48
+ if (author && host.toLowerCase().startsWith(author + "-")) {
49
+ const spaceName = host.slice(author.length + 1);
50
+ return `https://huggingface.co/spaces/${process.env.SPACE_AUTHOR_NAME}/${spaceName}`;
51
+ }
52
+ return "";
53
+ }
54
+ const HF_SPACE_URL = deriveHfSpaceUrl();
55
  const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
56
  "/tmp/huggingclaw-cloudflare-keepalive-status.json";
57
 
 
191
  <a class="hero-action env" data-space-link="env-builder" href="/env-builder">βš™οΈ Env Builder β†’</a>
192
  </div>
193
  <section class="overview">${tilesHtml}</section>
194
+ <footer>Built by <a href="https://github.com/somratpro" target="_blank" rel="noopener noreferrer" style="color:inherit;text-decoration:none">@somratpro</a>${JUPYTER_ENABLED ? " Β· Terminal by JupyterLab" : ""}<br><span>Public Spaces open via <code>.hf.space</code> directly. Private Spaces require the <a href="${HF_SPACE_URL || "#"}" target="_blank" rel="noopener noreferrer" style="color:inherit">Hugging Face App tab</a> for the authenticated session${HF_SPACE_URL ? ` β€” or share <code>huggingface.co/spaces/${SPACE_ID}</code>` : ""}.</span></footer>
195
  </main>
196
  <script>
197
  document.querySelectorAll('.local-time').forEach(el=>{const d=new Date(el.getAttribute('data-iso'));if(!isNaN(d))el.textContent='At '+d.toLocaleTimeString()});
198
  const inEmbeddedApp = (() => { try { return window.top !== window.self; } catch { return true; } })();
199
  const isDirectHfSpaceHost = /\.hf\.space$/i.test(window.location.hostname);
200
+ const HF_SPACE_URL = ${JSON.stringify(HF_SPACE_URL)};
201
+ // ── Private Space Guard ──
202
+ // Direct .hf.space access outside the HF App iframe has no valid session cookie
203
+ // for private spaces β€” HF CDN returns 404 before the request reaches the container.
204
+ // Redirect users to huggingface.co/spaces/... which authenticates them properly.
205
+ if (isDirectHfSpaceHost && !inEmbeddedApp && HF_SPACE_URL) {
206
+ const notice = document.createElement('div');
207
+ notice.style.cssText = 'position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#08080f;color:#f6f4ff;font-family:sans-serif;flex-direction:column;gap:16px;z-index:9999';
208
+ notice.innerHTML = '<span style="font-size:1.1rem">πŸ”’ Private Space &mdash; Redirecting&hellip;</span><a href="' + HF_SPACE_URL + '" style="color:#a5b4fc;font-size:.85rem">Click here if not redirected</a>';
209
+ document.body.appendChild(notice);
210
+ setTimeout(() => { window.location.replace(HF_SPACE_URL); }, 300);
211
+ }
212
  // If inside the HF App iframe, force new-tab navigation so users can break out
213
  // to the standalone Space host. Also keep direct .hf.space behavior opening new tabs.
214
  const openInNewTab = inEmbeddedApp || isDirectHfSpaceHost;
 
225
  </body></html>`;
226
  }
227
 
228
+ function renderPrivateRedirect(targetUrl) {
229
+ const safeUrl = escapeHtml(targetUrl);
230
+ return `<!doctype html><html lang="en"><head>
231
+ <meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
232
+ <meta http-equiv="refresh" content="3;url=${safeUrl}"/>
233
+ <title>HuggingClaw β€” Private Space</title>
234
+ <style>
235
+ :root{color-scheme:dark}
236
+ body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;
237
+ font-family:Inter,ui-sans-serif,system-ui,-apple-system,sans-serif;
238
+ background:#08080f;color:#f6f4ff;text-align:center;padding:24px}
239
+ .card{border:1px solid #26243a;background:#12111b;border-radius:14px;padding:36px 32px;max-width:440px}
240
+ h1{margin:0 0 12px;font-size:1.5rem}
241
+ p{color:#b8b3d7;line-height:1.6;margin:0 0 24px}
242
+ .btn{display:inline-flex;align-items:center;justify-content:center;
243
+ background:#fff;color:#000;font-weight:850;font-size:.95rem;
244
+ border-radius:8px;padding:12px 28px;text-decoration:none;transition:opacity .15s}
245
+ .btn:hover{opacity:.85}
246
+ .sub{color:#7f7a9e;font-size:.78rem;margin-top:16px}
247
+ </style></head><body>
248
+ <div class="card">
249
+ <h1>πŸ”’ Private Space</h1>
250
+ <p>This HuggingFace Space is private. You need to be logged in to <strong>huggingface.co</strong> to access it.<br><br>Redirecting you now&hellip;</p>
251
+ <a class="btn" href="${safeUrl}">Open on Hugging Face β†’</a>
252
+ <div class="sub">Redirecting in 3 seconds&hellip;</div>
253
+ </div>
254
+ <script>
255
+ // Immediate redirect if JS available β€” don't wait for meta refresh
256
+ setTimeout(() => { window.location.replace(${JSON.stringify(targetUrl)}); }, 100);
257
+ </script>
258
+ </body></html>`;
259
+ }
260
+
261
  function renderEnvBuilder() {
262
  try {
263
  return fs.readFileSync(require("path").join(__dirname, "env-builder.html"), "utf8");
 
368
  return res.end(JSON.stringify({ model: LLM_MODEL, uptime: formatUptime(Date.now() - startTime), gatewayReady, jupyterReady, sync: getSyncStatus(), whatsapp: readGuardianStatus(), keepalive: getKeepaliveStatus() }));
369
  }
370
 
371
+ // Private space redirect β€” send users to the authenticated HF Spaces page.
372
+ // Works for both direct .hf.space links AND programmatic shares.
373
+ if (pathname === "/hf-redirect" || pathname === "/hf-redirect/") {
374
+ if (HF_SPACE_URL) {
375
+ res.writeHead(302, { Location: HF_SPACE_URL, "Cache-Control": "no-store" });
376
+ return res.end();
377
+ }
378
+ res.writeHead(404, { "Content-Type": "text/plain" });
379
+ return res.end("SPACE_ID not configured.");
380
+ }
381
+
382
  if (pathname === "/env-builder" || pathname === "/env-builder/") {
383
  res.writeHead(200, { "Content-Type": "text/html" });
384
  return res.end(renderEnvBuilder());