CoolFace
Apppublic

eddyencode/deepsitev3

sourceHugging Facemitupdated 1y agoView on Hugging Face
1likes
server.js400 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});50app.get("/auth/login", async (req, res) => {51  const { code } = req.query;52 53  if (!code) {54    return res.redirect(302, "/");55  }56  const Authorization = `Basic ${Buffer.from(57    `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}`58  ).toString("base64")}`;59 60  const request_auth = await fetch("https://huggingface.co/oauth/token", {61    method: "POST",62    headers: {63      "Content-Type": "application/x-www-form-urlencoded",64      Authorization,65    },66    body: new URLSearchParams({67      grant_type: "authorization_code",68      code: code,69      redirect_uri: REDIRECT_URI,70    }),71  });72 73  const response = await request_auth.json();74 75  if (!response.access_token) {76    return res.redirect(302, "/");77  }78 79  res.cookie("hf_token", response.access_token, {80    httpOnly: false,81    secure: true,82    sameSite: "none",83    maxAge: 30 * 24 * 60 * 60 * 1000,84  });85 86  return res.redirect(302, "/");87});88app.get("/auth/logout", (req, res) => {89  res.clearCookie("hf_token", {90    httpOnly: false,91    secure: true,92    sameSite: "none",93  });94  return res.redirect(302, "/");95});96 97app.get("/api/@me", checkUser, async (req, res) => {98  const { hf_token } = req.cookies;99  try {100    const request_user = await fetch("https://huggingface.co/oauth/userinfo", {101      headers: {102        Authorization: `Bearer ${hf_token}`,103      },104    });105 106    const user = await request_user.json();107    res.send(user);108  } catch (err) {109    res.clearCookie("hf_token", {110      httpOnly: false,111      secure: true,112      sameSite: "none",113    });114    res.status(401).send({115      ok: false,116      message: err.message,117    });118  }119});120 121app.post("/api/deploy", checkUser, async (req, res) => {122  const { html, title, path } = req.body;123  if (!html || !title) {124    return res.status(400).send({125      ok: false,126      message: "Missing required fields",127    });128  }129 130  const { hf_token } = req.cookies;131  try {132    const repo = {133      type: "space",134      name: path ?? "",135    };136 137    let readme;138    let newHtml = html;139 140    if (!path || path === "") {141      const { name: username } = await whoAmI({ accessToken: hf_token });142      const newTitle = title143        .toLowerCase()144        .replace(/[^a-z0-9]+/g, "-")145        .split("-")146        .filter(Boolean)147        .join("-")148        .slice(0, 96);149 150      const repoId = `${username}/${newTitle}`;151      repo.name = repoId;152 153      await createRepo({154        repo,155        accessToken: hf_token,156      });157      const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)];158      const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)];159      readme = `---160title: ${newTitle}161emoji: 🐳162colorFrom: ${colorFrom}163colorTo: ${colorTo}164sdk: static165pinned: false166tags:167  - deepsite168---169 170Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference`;171    }172 173    newHtml = html.replace(/<\/body>/, `${getPTag(repo.name)}</body>`);174    const file = new Blob([newHtml], { type: "text/html" });175    file.name = "index.html"; // Add name property to the Blob176 177    const files = [file];178    if (readme) {179      const readmeFile = new Blob([readme], { type: "text/markdown" });180      readmeFile.name = "README.md"; // Add name property to the Blob181      files.push(readmeFile);182    }183    await uploadFiles({184      repo,185      files,186      accessToken: hf_token,187    });188    return res.status(200).send({ ok: true, path: repo.name });189  } catch (err) {190    return res.status(500).send({191      ok: false,192      message: err.message,193    });194  }195});196 197app.post("/api/ask-ai", async (req, res) => {198  const { prompt, html, previousPrompt, provider } = req.body;199  if (!prompt) {200    return res.status(400).send({201      ok: false,202      message: "Missing required fields",203    });204  }205 206  const { hf_token } = req.cookies;207  let token = hf_token;208  const ip =209    req.headers["x-forwarded-for"]?.split(",")[0].trim() ||210    req.headers["x-real-ip"] ||211    req.socket.remoteAddress ||212    req.ip ||213    "0.0.0.0";214 215  if (!hf_token) {216    ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);217    if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {218      return res.status(429).send({219        ok: false,220        openLogin: true,221        message: "Log In to continue using the service",222      });223    }224 225    token = process.env.DEFAULT_HF_TOKEN;226  }227 228  // Set up response headers for streaming229  res.setHeader("Content-Type", "text/plain");230  res.setHeader("Cache-Control", "no-cache");231  res.setHeader("Connection", "keep-alive");232 233  const client = new InferenceClient(token);234  let completeResponse = "";235 236  let TOKENS_USED = prompt?.length;237  if (previousPrompt) TOKENS_USED += previousPrompt.length;238  if (html) TOKENS_USED += html.length;239 240  const DEFAULT_PROVIDER = PROVIDERS.novita;241  // const selectedProvider =242  //   provider === "auto"243  //     ? TOKENS_USED < PROVIDERS.sambanova.max_tokens244  //       ? PROVIDERS.sambanova245  //       : DEFAULT_PROVIDER246  //     : PROVIDERS[provider] ?? DEFAULT_PROVIDER;247  const selectedProvider =248    provider === "auto"249      ? DEFAULT_PROVIDER250      : PROVIDERS[provider] ?? DEFAULT_PROVIDER;251 252  if (provider !== "auto" && TOKENS_USED >= selectedProvider.max_tokens) {253    return res.status(400).send({254      ok: false,255      openSelectProvider: true,256      message: `Context is too long. ${selectedProvider.name} allow ${selectedProvider.max_tokens} max tokens.`,257    });258  }259 260  try {261    const chatCompletion = client.chatCompletionStream({262      model: MODEL_ID,263      provider: selectedProvider.id,264      messages: [265        {266          role: "system",267          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`,268        },269        ...(previousPrompt270          ? [271              {272                role: "user",273                content: previousPrompt,274              },275            ]276          : []),277        ...(html278          ? [279              {280                role: "assistant",281                content: `The current code is: ${html}.`,282              },283            ]284          : []),285        {286          role: "user",287          content: prompt,288        },289      ],290      ...(selectedProvider.id !== "sambanova"291        ? {292            max_tokens: selectedProvider.max_tokens,293          }294        : {}),295    });296 297    while (true) {298      const { done, value } = await chatCompletion.next();299      if (done) {300        break;301      }302      const chunk = value.choices[0]?.delta?.content;303      if (chunk) {304        if (provider !== "sambanova") {305          res.write(chunk);306          completeResponse += chunk;307 308          if (completeResponse.includes("</html>")) {309            break;310          }311        } else {312          let newChunk = chunk;313          if (chunk.includes("</html>")) {314            // Replace everything after the last </html> tag with an empty string315            newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");316          }317          completeResponse += newChunk;318          res.write(newChunk);319          if (newChunk.includes("</html>")) {320            break;321          }322        }323      }324    }325    // End the response stream326    res.end();327  } catch (error) {328    if (error.message.includes("exceeded your monthly included credits")) {329      return res.status(402).send({330        ok: false,331        openProModal: true,332        message: error.message,333      });334    }335    if (!res.headersSent) {336      res.status(500).send({337        ok: false,338        message:339          error.message || "An error occurred while processing your request.",340      });341    } else {342      // Otherwise end the stream343      res.end();344    }345  }346});347 348app.get("/api/remix/:username/:repo", async (req, res) => {349  const { username, repo } = req.params;350  const { hf_token } = req.cookies;351 352  const token = hf_token || process.env.DEFAULT_HF_TOKEN;353 354  const repoId = `${username}/${repo}`;355 356  const url = `https://huggingface.co/spaces/${repoId}/raw/main/index.html`;357  try {358    const space = await spaceInfo({359      name: repoId,360      accessToken: token,361    });362 363    if (!space || space.sdk !== "static" || space.private) {364      return res.status(404).send({365        ok: false,366        message: "Space not found",367      });368    }369 370    const response = await fetch(url);371    if (!response.ok) {372      return res.status(404).send({373        ok: false,374        message: "Space not found",375      });376    }377    let html = await response.text();378    // remove the last p tag including this url https://enzostvs-deepsite.hf.space379    html = html.replace(getPTag(repoId), "");380 381    res.status(200).send({382      ok: true,383      html,384    });385  } catch (error) {386    return res.status(500).send({387      ok: false,388      message: error.message,389    });390  }391});392 393app.get("*", (_req, res) => {394  res.sendFile(path.join(__dirname, "dist", "index.html"));395});396 397app.listen(PORT, () => {398  console.log(`Server is running on port ${PORT}`);399});400