CoolFace
Apppublic

aborboa/A6Life_HTMLer

sourceHugging Facemitupdated 1y agoView on Hugging Face
1likes
server.js432 linesDownload Raw Back to root
1import express from "express";2import path from "path";3import { fileURLToPath } from "url";4import dotenv from "dotenv";5import cookieParser from "cookie-parser";6import {7  createRepo,8  uploadFiles,9  whoAmI,10  spaceInfo,11  fileExists,12} from "@huggingface/hub";13import { InferenceClient } from "@huggingface/inference";14import bodyParser from "body-parser";15 16import checkUser from "./middlewares/checkUser.js";17import { PROVIDERS } from "./utils/providers.js";18import { COLORS } from "./utils/colors.js";19 20// Load environment variables from .env file21dotenv.config();22 23const app = express();24 25const ipAddresses = new Map();26 27const __filename = fileURLToPath(import.meta.url);28const __dirname = path.dirname(__filename);29 30const PORT = process.env.APP_PORT || 3000;31const REDIRECT_URI =32  process.env.REDIRECT_URI || `http://localhost:${PORT}/auth/login`;33const MODEL_ID = "deepseek-ai/DeepSeek-V3-0324";34const MAX_REQUESTS_PER_IP = 2;35 36app.use(cookieParser());37app.use(bodyParser.json());38app.use(express.static(path.join(__dirname, "dist")));39 40const getPTag = (repoId) => {41  return `<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=${repoId}" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p>`;42};43 44app.get("/api/login", (_req, res) => {45  // res.redirect(46  //   302,47  //   `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`48  // );49  // redirect in new tab50  const redirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;51  res.status(200).send({52    ok: true,53    redirectUrl,54  });55});56 57app.get( "/test", (req, res) =>{58  res.send( "Hola" );59});60 61app.get("/auth/login", async (req, res) => {62  const { code } = req.query;63 64  if (!code) {65    return res.redirect(302, "/");66  }67  const Authorization = `Basic ${Buffer.from(68    `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}`69  ).toString("base64")}`;70 71  const request_auth = await fetch("https://huggingface.co/oauth/token", {72    method: "POST",73    headers: {74      "Content-Type": "application/x-www-form-urlencoded",75      Authorization,76    },77    body: new URLSearchParams({78      grant_type: "authorization_code",79      code: code,80      redirect_uri: REDIRECT_URI,81    }),82  });83 84  const response = await request_auth.json();85 86  if (!response.access_token) {87    return res.redirect(302, "/");88  }89 90  res.cookie("hf_token", response.access_token, {91    httpOnly: false,92    secure: true,93    sameSite: "none",94    maxAge: 30 * 24 * 60 * 60 * 1000,95  });96 97  return res.redirect(302, "/");98});99app.get("/auth/logout", (req, res) => {100  res.clearCookie("hf_token", {101    httpOnly: false,102    secure: true,103    sameSite: "none",104  });105  return res.redirect(302, "/");106});107 108app.get("/api/@me", checkUser, async (req, res) => {109  const { hf_token } = req.cookies;110  try {111    const request_user = await fetch("https://huggingface.co/oauth/userinfo", {112      headers: {113        Authorization: `Bearer ${hf_token}`,114      },115    });116 117    const user = await request_user.json();118    res.send(user);119  } catch (err) {120    res.clearCookie("hf_token", {121      httpOnly: false,122      secure: true,123      sameSite: "none",124    });125    res.status(401).send({126      ok: false,127      message: err.message,128    });129  }130});131 132app.post("/api/deploy", checkUser, async (req, res) => {133  const { html, title, path } = req.body;134  if (!html || (!path && !title)) {135    return res.status(400).send({136      ok: false,137      message: "Missing required fields",138    });139  }140 141  const { hf_token } = req.cookies;142  try {143    const repo = {144      type: "space",145      name: path ?? "",146    };147 148    let readme;149    let newHtml = html;150 151    if (!path || path === "") {152      const { name: username } = await whoAmI({ accessToken: hf_token });153      const newTitle = title154        .toLowerCase()155        .replace(/[^a-z0-9]+/g, "-")156        .split("-")157        .filter(Boolean)158        .join("-")159        .slice(0, 96);160 161      const repoId = `${username}/${newTitle}`;162      repo.name = repoId;163 164      await createRepo({165        repo,166        accessToken: hf_token,167      });168      const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)];169      const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)];170      readme = `---171title: ${newTitle}172emoji: 🐳173colorFrom: ${colorFrom}174colorTo: ${colorTo}175sdk: static176pinned: false177tags:178  - deepsite179---180 181Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference`;182    }183 184    newHtml = html.replace(/<\/body>/, `${getPTag(repo.name)}</body>`);185    const file = new Blob([newHtml], { type: "text/html" });186    file.name = "index.html"; // Add name property to the Blob187 188    const files = [file];189    if (readme) {190      const readmeFile = new Blob([readme], { type: "text/markdown" });191      readmeFile.name = "README.md"; // Add name property to the Blob192      files.push(readmeFile);193    }194    await uploadFiles({195      repo,196      files,197      accessToken: hf_token,198    });199    return res.status(200).send({ ok: true, path: repo.name });200  } catch (err) {201    return res.status(500).send({202      ok: false,203      message: err.message,204    });205  }206});207 208app.post("/api/ask-ai", async (req, res) => {209  const { prompt, html, previousPrompt, provider } = req.body;210  if (!prompt) {211    return res.status(400).send({212      ok: false,213      message: "Missing required fields",214    });215  }216 217  const { hf_token } = req.cookies;218  let token = hf_token;219  const ip =220    req.headers["x-forwarded-for"]?.split(",")[0].trim() ||221    req.headers["x-real-ip"] ||222    req.socket.remoteAddress ||223    req.ip ||224    "0.0.0.0";225 226  if (!hf_token) {227    ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);228    if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {229      return res.status(429).send({230        ok: false,231        openLogin: true,232        message: "Log In to continue using the service",233      });234    }235 236    token = process.env.DEFAULT_HF_TOKEN;237  }238 239  // Set up response headers for streaming240  res.setHeader("Content-Type", "text/plain");241  res.setHeader("Cache-Control", "no-cache");242  res.setHeader("Connection", "keep-alive");243 244  const client = new InferenceClient(token);245  let completeResponse = "";246 247  let TOKENS_USED = prompt?.length;248  if (previousPrompt) TOKENS_USED += previousPrompt.length;249  if (html) TOKENS_USED += html.length;250 251  const DEFAULT_PROVIDER = PROVIDERS.novita;252  // const selectedProvider =253  //   provider === "auto"254  //     ? TOKENS_USED < PROVIDERS.sambanova.max_tokens255  //       ? PROVIDERS.sambanova256  //       : DEFAULT_PROVIDER257  //     : PROVIDERS[provider] ?? DEFAULT_PROVIDER;258  const selectedProvider =259    provider === "auto"260      ? DEFAULT_PROVIDER261      : PROVIDERS[provider] ?? DEFAULT_PROVIDER;262 263  if (provider !== "auto" && TOKENS_USED >= selectedProvider.max_tokens) {264    return res.status(400).send({265      ok: false,266      openSelectProvider: true,267      message: `Context is too long. ${selectedProvider.name} allow ${selectedProvider.max_tokens} max tokens.`,268    });269  }270 271  try {272    const chatCompletion = client.chatCompletionStream({273      model: MODEL_ID,274      provider: selectedProvider.id,275      messages: [276        {277          role: "system",278          content: `ONLY USE HTML, CSS AND JAVASCRIPT. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.tailwindcss.com"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`,279        },280        ...(previousPrompt281          ? [282              {283                role: "user",284                content: previousPrompt,285              },286            ]287          : []),288        ...(html289          ? [290              {291                role: "assistant",292                content: `The current code is: ${html}.`,293              },294            ]295          : []),296        {297          role: "user",298          content: prompt,299        },300      ],301      ...(selectedProvider.id !== "sambanova"302        ? {303            max_tokens: selectedProvider.max_tokens,304          }305        : {}),306    });307 308    while (true) {309      const { done, value } = await chatCompletion.next();310      if (done) {311        break;312      }313      const chunk = value.choices[0]?.delta?.content;314      if (chunk) {315        if (provider !== "sambanova") {316          res.write(chunk);317          completeResponse += chunk;318 319          if (completeResponse.includes("</html>")) {320            break;321          }322        } else {323          let newChunk = chunk;324          if (chunk.includes("</html>")) {325            // Replace everything after the last </html> tag with an empty string326            newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");327          }328          completeResponse += newChunk;329          res.write(newChunk);330          console.log( newChunk );331          if (newChunk.includes("</html>")) {332            break;333          }334        }335      }336    }337    // End the response stream338    res.end();339  } catch (error) {340    if (error.message.includes("exceeded your monthly included credits")) {341      return res.status(402).send({342        ok: false,343        openProModal: true,344        message: error.message,345      });346    }347    if (!res.headersSent) {348      res.status(500).send({349        ok: false,350        message:351          error.message || "An error occurred while processing your request.",352      });353    } else {354      // Otherwise end the stream355      res.end();356    }357  }358});359 360app.get("/api/remix/:username/:repo", async (req, res) => {361  const { username, repo } = req.params;362  const { hf_token } = req.cookies;363 364  const token = hf_token || process.env.DEFAULT_HF_TOKEN;365 366  const repoId = `${username}/${repo}`;367 368  const url = `https://huggingface.co/spaces/${repoId}/raw/main/index.html`;369  try {370    const space = await spaceInfo({371      name: repoId,372      accessToken: token,373      additionalFields: ["author"],374    });375 376    if (!space || space.sdk !== "static" || space.private) {377      return res.status(404).send({378        ok: false,379        message: "Space not found",380      });381    }382 383    const response = await fetch(url);384    if (!response.ok) {385      return res.status(404).send({386        ok: false,387        message: "Space not found",388      });389    }390    let html = await response.text();391    // remove the last p tag including this url https://enzostvs-deepsite.hf.space392    html = html.replace(getPTag(repoId), "");393 394    let user = null;395 396    if (token) {397      const request_user = await fetch(398        "https://huggingface.co/oauth/userinfo",399        {400          headers: {401            Authorization: `Bearer ${hf_token}`,402          },403        }404      )405        .then((res) => res.json())406        .catch(() => null);407 408      user = request_user;409    }410 411    res.status(200).send({412      ok: true,413      html,414      isOwner: space.author === user?.preferred_username,415      path: repoId,416    });417  } catch (error) {418    return res.status(500).send({419      ok: false,420      message: error.message,421    });422  }423});424 425app.get("*", (_req, res) => {426  res.sendFile(path.join(__dirname, "dist", "index.html"));427});428 429app.listen(PORT, () => {430  console.log(`Server is running on port ${PORT}`);431});432