const http = require("node:http"); const path = require("node:path"); const fs = require("node:fs/promises"); const { URL } = require("node:url"); const { SessionStore } = require("./src/session-store"); const { HfApiError, buildSpaceStreamUrl, createResource, deleteResource, getDownloadBlob, getPageData, getResourceUpdates, getSearchResults, getViewer, moveResource, performSpaceAction, removeFile, saveSpaceSecret, saveSpaceVariable, updateTextFile, updateResourceVisibility, } = require("./src/hf-api"); const { renderAppShell, renderLoginPage } = require("./src/templates"); const HOST = "0.0.0.0"; const PORT = Number(process.env.PORT || 7860); const SESSION_COOKIE_NAME = "hf_auth"; const STATIC_DIR = path.join(__dirname, "public"); const SESSION_STORE_DIR = process.env.SESSION_STORE_DIR || "/data"; const SESSION_TTL_HOURS = Number(process.env.SESSION_TTL_HOURS || 168); const REQUEST_BODY_LIMIT = 1024 * 1024; const SECURITY_HEADERS = { "Referrer-Policy": "same-origin", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Permissions-Policy": "camera=(), geolocation=(), microphone=()", "Content-Security-Policy": "default-src 'self'; img-src 'self' https: data:; style-src 'self'; script-src 'self'; connect-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'", }; if (!process.env.ENCRYPTION_KEY) { throw new Error("ENCRYPTION_KEY is required."); } if (!Number.isFinite(SESSION_TTL_HOURS) || SESSION_TTL_HOURS <= 0) { throw new Error("SESSION_TTL_HOURS must be a positive number."); } const sessionStore = new SessionStore({ storageDir: SESSION_STORE_DIR, encryptionKey: process.env.ENCRYPTION_KEY, sessionTtlMs: SESSION_TTL_HOURS * 60 * 60 * 1000, }); const staticCache = new Map(); const mimeTypes = { ".css": "text/css; charset=utf-8", ".js": "application/javascript; charset=utf-8", }; function parseCookies(cookieHeader = "") { return cookieHeader .split(";") .map((part) => part.trim()) .filter(Boolean) .reduce((cookies, pair) => { const separator = pair.indexOf("="); if (separator === -1) { return cookies; } const name = pair.slice(0, separator).trim(); const value = pair.slice(separator + 1).trim(); try { cookies[name] = decodeURIComponent(value); } catch (error) { cookies[name] = value; } return cookies; }, {}); } function buildCookie(name, value, options = {}) { const parts = [`${name}=${encodeURIComponent(value)}`]; parts.push(`Path=${options.path || "/"}`); if (typeof options.maxAge === "number") { parts.push(`Max-Age=${Math.max(0, Math.floor(options.maxAge))}`); } if (options.httpOnly !== false) { parts.push("HttpOnly"); } parts.push(`SameSite=${options.sameSite || "Lax"}`); if (options.secure) { parts.push("Secure"); } return parts.join("; "); } function shouldUseSecureCookies() { return process.env.NODE_ENV === "production" || process.env.COOKIE_SECURE === "true"; } function getExpectedOrigin(req) { const forwardedProto = String(req.headers["x-forwarded-proto"] || "") .split(",")[0] .trim() .toLowerCase(); const protocol = forwardedProto || (req.socket.encrypted ? "https" : "http"); const host = String(req.headers.host || "").trim(); return host ? `${protocol}://${host}` : ""; } function matchesTrustedOrigin(req) { const expectedOrigin = getExpectedOrigin(req); if (!expectedOrigin) { return false; } const origin = String(req.headers.origin || "").trim(); if (origin) { return origin === expectedOrigin; } const referer = String(req.headers.referer || "").trim(); if (referer) { try { return new URL(referer).origin === expectedOrigin; } catch (error) { return false; } } const fetchSite = String(req.headers["sec-fetch-site"] || "") .trim() .toLowerCase(); if (fetchSite) { return fetchSite === "same-origin" || fetchSite === "same-site" || fetchSite === "none"; } return false; } function redirect(res, location) { res.writeHead(302, { ...SECURITY_HEADERS, Location: location, }); res.end(); } function sendHtml(res, statusCode, html, extraHeaders = {}) { res.writeHead(statusCode, { ...SECURITY_HEADERS, "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", ...extraHeaders, }); res.end(html); } function sendJson(res, statusCode, payload, extraHeaders = {}) { res.writeHead(statusCode, { ...SECURITY_HEADERS, "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store", ...extraHeaders, }); res.end(JSON.stringify(payload)); } function sendNotFound(res) { sendJson(res, 404, { error: "Not found" }); } function clearSessionCookie(res) { res.setHeader( "Set-Cookie", buildCookie(SESSION_COOKIE_NAME, "", { maxAge: 0, secure: shouldUseSecureCookies(), }), ); } function setSessionCookie(res, value) { res.setHeader( "Set-Cookie", buildCookie(SESSION_COOKIE_NAME, value, { maxAge: Math.floor(sessionStore.sessionTtlMs / 1000), secure: shouldUseSecureCookies(), }), ); } async function readBody(req, limit = REQUEST_BODY_LIMIT) { return new Promise((resolve, reject) => { const chunks = []; let size = 0; req.on("data", (chunk) => { size += chunk.length; if (size > limit) { reject(new Error("Request body too large.")); req.destroy(); return; } chunks.push(chunk); }); req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); req.on("error", reject); }); } function parseFormBody(rawBody) { return Object.fromEntries(new URLSearchParams(rawBody)); } function parseJsonBody(rawBody) { try { return JSON.parse(rawBody || "{}"); } catch (error) { throw new HfApiError(400, "That request body was not valid JSON."); } } async function getStaticAsset(fileName) { if (staticCache.has(fileName)) { return staticCache.get(fileName); } const filePath = path.join(STATIC_DIR, fileName); const content = await fs.readFile(filePath); staticCache.set(fileName, content); return content; } async function sendStaticAsset(reqPath, res) { const fileName = reqPath.replace(/^\/+/, ""); const extension = path.extname(fileName); const contentType = mimeTypes[extension]; if (!contentType) { sendNotFound(res); return; } try { const asset = await getStaticAsset(fileName); res.writeHead(200, { ...SECURITY_HEADERS, "Content-Type": contentType, "Cache-Control": "public, max-age=300", }); res.end(asset); } catch (error) { sendNotFound(res); } } async function getRequestSession(req) { const cookies = parseCookies(req.headers.cookie); const cookieValue = cookies[SESSION_COOKIE_NAME]; if (!cookieValue) { return null; } return sessionStore.validateSession(cookieValue); } async function getSessionAccessToken(session) { if (!session) { return null; } return sessionStore.getAccessToken(session.id, session.secret); } function getSessionCsrfToken(session) { return sessionStore.getCsrfToken(session.id, session.secret); } function matchesCsrfToken(req, session) { const token = req.headers["x-csrf-token"]; if (!token) { return false; } return token === getSessionCsrfToken(session); } function sanitizeError(error) { if (error instanceof HfApiError) { return { statusCode: error.statusCode, message: error.publicMessage, }; } return { statusCode: 500, message: "Something went wrong while handling that request.", }; } function isAppRoute(pathname) { if (pathname.startsWith("/api/") || pathname.startsWith("/public/") || pathname.startsWith("/auth/")) { return false; } return true; } async function renderShell(req, res) { const session = await getRequestSession(req); if (!session) { sendHtml(res, 200, renderLoginPage({})); return; } sendHtml( res, 200, renderAppShell({ viewer: session.user, csrfToken: getSessionCsrfToken(session), }), ); } async function handleAuthFailure(res, session) { if (session) { await sessionStore.revokeSession(session.id); } clearSessionCookie(res); sendJson(res, 401, { error: "Authentication required." }); } async function handleLogin(req, res) { if (!matchesTrustedOrigin(req)) { sendHtml( res, 403, renderLoginPage({ loginError: "That sign-in attempt was blocked because the request origin did not match this workspace.", }), ); return; } const rawBody = await readBody(req); const form = parseFormBody(rawBody); const accessToken = (form.accessToken || "").trim(); if (!accessToken) { sendHtml( res, 400, renderLoginPage({ loginError: "Enter a Hugging Face access token.", }), ); return; } try { const viewer = await getViewer(accessToken); const currentSession = await getRequestSession(req); if (currentSession) { await sessionStore.revokeSession(currentSession.id); } const session = await sessionStore.createSession({ accessToken, user: viewer, }); setSessionCookie(res, session.cookieValue); redirect(res, "/"); } catch (error) { const safeError = sanitizeError(error); sendHtml( res, safeError.statusCode, renderLoginPage({ loginError: safeError.message, }), ); } } async function handleLogout(req, res) { const session = await getRequestSession(req); if (session && !matchesCsrfToken(req, session)) { sendJson(res, 403, { error: "That action was blocked because the security token did not match." }); return; } if (session) { await sessionStore.revokeSession(session.id); } clearSessionCookie(res); sendJson(res, 200, { ok: true }); } async function handlePageApi(req, res, url) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return; } try { const requestedPath = url.searchParams.get("path") || "/"; const payload = await getPageData(accessToken, requestedPath); if (payload.viewer.username !== session.user.username || payload.viewer.tokenRole !== session.user.tokenRole) { await sessionStore.updateUserSummary(session.id, payload.viewer); } sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); if (safeError.statusCode === 401) { await handleAuthFailure(res, session); return; } sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleSearchApi(req, res, url) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return; } try { const query = url.searchParams.get("q") || ""; const payload = await getSearchResults(accessToken, url.searchParams.get("path") || "/", query); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleFileDownload(req, res, url) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return; } try { const type = url.searchParams.get("type"); const repoId = url.searchParams.get("repoId"); const filePath = url.searchParams.get("path"); const branch = url.searchParams.get("branch") || ""; if (!type || !repoId || !filePath) { throw new HfApiError(400, "Missing file download parameters."); } const { blob, fileName, contentType } = await getDownloadBlob(accessToken, { type, repoId, path: filePath, branch, }); const arrayBuffer = await blob.arrayBuffer(); res.writeHead(200, { ...SECURITY_HEADERS, "Content-Type": contentType, "Cache-Control": "no-store", "Content-Disposition": `inline; filename="${fileName.replaceAll('"', "")}"`, }); res.end(Buffer.from(arrayBuffer)); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function requireMutationSession(req, res) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return null; } if (!matchesCsrfToken(req, session)) { sendJson(res, 403, { error: "That action was blocked because the security token did not match." }); return null; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return null; } return { session, accessToken }; } async function handleFileUpdate(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await updateTextFile(auth.accessToken, { type: body.type, repoId: body.repoId, path: body.path, branch: body.branch, parentCommit: body.parentCommit, content: body.content, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleFileDelete(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await removeFile(auth.accessToken, { type: body.type, repoId: body.repoId, path: body.path, branch: body.branch, parentCommit: body.parentCommit, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleResourceCreate(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await createResource(auth.accessToken, { type: body.type, namespace: body.namespace, name: body.name, visibility: body.visibility, sdk: body.sdk, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleResourceMove(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await moveResource(auth.accessToken, { type: body.type, fromId: body.fromId, toId: body.toId, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleResourceVisibility(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await updateResourceVisibility(auth.accessToken, { type: body.type, repoId: body.repoId, visibility: body.visibility, private: body.private, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleResourceDelete(req, res) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await deleteResource(auth.accessToken, { type: body.type, repoId: body.repoId, }); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleSpaceSecretSave(req, res, owner, name) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await saveSpaceSecret(auth.accessToken, `${owner}/${name}`, body); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleSpaceVariableSave(req, res, owner, name) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await saveSpaceVariable(auth.accessToken, `${owner}/${name}`, body); sendJson(res, 200, payload); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } async function handleSpaceAction(req, res, owner, name) { const auth = await requireMutationSession(req, res); if (!auth) { return; } try { const body = parseJsonBody(await readBody(req)); const payload = await performSpaceAction(auth.accessToken, `${owner}/${name}`, body.action); sendJson(res, 200, { ok: true, action: body.action, payload, }); } catch (error) { const safeError = sanitizeError(error); sendJson(res, safeError.statusCode, { error: safeError.message }); } } function sendSseHeaders(res) { res.writeHead(200, { ...SECURITY_HEADERS, "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-store", Connection: "keep-alive", }); } function writeSseEvent(res, eventName, data) { if (eventName) { res.write(`event: ${eventName}\n`); } res.write(`data: ${JSON.stringify(data)}\n\n`); } async function proxySpaceStream(req, res, owner, name, kind) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return; } const streamKind = kind === "events" || kind === "metrics" || kind === "build" || kind === "run" ? kind : "run"; const upstreamUrl = buildSpaceStreamUrl(`${owner}/${name}`, streamKind); const abortController = new AbortController(); req.on("close", () => abortController.abort()); try { const upstream = await fetch(upstreamUrl, { signal: abortController.signal, headers: { Accept: "text/event-stream", Authorization: `Bearer ${accessToken}`, }, }); if (!upstream.ok || !upstream.body) { const safeError = sanitizeError(new HfApiError(upstream.status, "Couldn't open that Space stream.")); sendSseHeaders(res); writeSseEvent(res, "error", { error: safeError.message }); res.end(); return; } sendSseHeaders(res); for await (const chunk of upstream.body) { res.write(Buffer.from(chunk)); } } catch (error) { if (!abortController.signal.aborted) { sendSseHeaders(res); writeSseEvent(res, "error", { error: "The live Space stream disconnected." }); } } finally { res.end(); } } function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function streamResourceUpdates(req, res, type, owner, name, url) { const session = await getRequestSession(req); if (!session) { sendJson(res, 401, { error: "Authentication required." }); return; } const accessToken = await getSessionAccessToken(session); if (!accessToken) { await handleAuthFailure(res, session); return; } sendSseHeaders(res); let live = true; let lastSignature = ""; req.on("close", () => { live = false; }); while (live) { try { const snapshot = await getResourceUpdates(accessToken, type, `${owner}/${name}`, { branch: url.searchParams.get("branch") || "", path: url.searchParams.get("path") || "", }); if (snapshot.signature !== lastSignature) { lastSignature = snapshot.signature; writeSseEvent(res, "update", snapshot); } else { res.write(": keepalive\n\n"); } } catch (error) { const safeError = sanitizeError(error); writeSseEvent(res, "error", { error: safeError.message }); } await delay(15000); } } async function handleRequest(req, res) { const url = new URL(req.url, `http://${req.headers.host || "localhost"}`); const pathname = url.pathname; if (req.method === "GET" && pathname === "/health") { sendJson(res, 200, { status: "ok" }); return; } if (req.method === "POST" && pathname === "/auth/login") { await handleLogin(req, res); return; } if (req.method === "POST" && pathname === "/auth/logout") { await handleLogout(req, res); return; } if (req.method === "GET" && pathname === "/api/page") { await handlePageApi(req, res, url); return; } if (req.method === "GET" && pathname === "/api/search") { await handleSearchApi(req, res, url); return; } if (req.method === "GET" && pathname === "/api/file/download") { await handleFileDownload(req, res, url); return; } if (req.method === "POST" && pathname === "/api/files/update") { await handleFileUpdate(req, res); return; } if (req.method === "POST" && pathname === "/api/files/delete") { await handleFileDelete(req, res); return; } if (req.method === "POST" && pathname === "/api/resources/create") { await handleResourceCreate(req, res); return; } if (req.method === "POST" && pathname === "/api/resources/move") { await handleResourceMove(req, res); return; } if (req.method === "POST" && pathname === "/api/resources/visibility") { await handleResourceVisibility(req, res); return; } if (req.method === "POST" && pathname === "/api/resources/delete") { await handleResourceDelete(req, res); return; } const spaceSecretMatch = req.method === "POST" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/secrets$/) : null; if (spaceSecretMatch) { await handleSpaceSecretSave(req, res, decodeURIComponent(spaceSecretMatch[1]), decodeURIComponent(spaceSecretMatch[2])); return; } const spaceVariableMatch = req.method === "POST" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/variables$/) : null; if (spaceVariableMatch) { await handleSpaceVariableSave(req, res, decodeURIComponent(spaceVariableMatch[1]), decodeURIComponent(spaceVariableMatch[2])); return; } const spaceActionMatch = req.method === "POST" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/action$/) : null; if (spaceActionMatch) { await handleSpaceAction(req, res, decodeURIComponent(spaceActionMatch[1]), decodeURIComponent(spaceActionMatch[2])); return; } const spaceLogsMatch = req.method === "GET" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/logs\/stream$/) : null; if (spaceLogsMatch) { const kind = url.searchParams.get("kind") === "build" ? "build" : "run"; await proxySpaceStream(req, res, decodeURIComponent(spaceLogsMatch[1]), decodeURIComponent(spaceLogsMatch[2]), kind); return; } const spaceEventsMatch = req.method === "GET" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/events\/stream$/) : null; if (spaceEventsMatch) { await proxySpaceStream(req, res, decodeURIComponent(spaceEventsMatch[1]), decodeURIComponent(spaceEventsMatch[2]), "events"); return; } const spaceMetricsMatch = req.method === "GET" ? pathname.match(/^\/api\/spaces\/([^/]+)\/([^/]+)\/metrics\/stream$/) : null; if (spaceMetricsMatch) { await proxySpaceStream(req, res, decodeURIComponent(spaceMetricsMatch[1]), decodeURIComponent(spaceMetricsMatch[2]), "metrics"); return; } const updatesMatch = req.method === "GET" ? pathname.match(/^\/api\/resources\/(space|model|dataset|bucket)\/([^/]+)\/([^/]+)\/updates\/stream$/) : null; if (updatesMatch) { await streamResourceUpdates( req, res, decodeURIComponent(updatesMatch[1]), decodeURIComponent(updatesMatch[2]), decodeURIComponent(updatesMatch[3]), url, ); return; } if (req.method === "GET" && pathname.startsWith("/public/")) { await sendStaticAsset(pathname.replace("/public/", ""), res); return; } if (req.method === "GET" && isAppRoute(pathname)) { await renderShell(req, res); return; } sendNotFound(res); } async function main() { const server = await startServer({ port: PORT, host: HOST }); console.log(`HF Home listening on http://${HOST}:${PORT}`); return server; } async function startServer({ port, host }) { await sessionStore.init(); const server = http.createServer(async (req, res) => { try { await handleRequest(req, res); } catch (error) { console.error(error); const safeError = sanitizeError(error); sendJson(res, safeError.statusCode || 500, { error: safeError.message }); } }); return new Promise((resolve, reject) => { server.once("error", reject); server.listen(port, host, () => { server.off("error", reject); resolve(server); }); }); } if (require.main === module) { main().catch((error) => { console.error(error); process.exitCode = 1; }); } module.exports = { startServer, };