eddyencode/deepsitev3
1
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 