import express from "express"; import path from "path"; import { fileURLToPath } from "url"; import dotenv from "dotenv"; import cookieParser from "cookie-parser"; import fs from "fs"; // 使用 ESM 语法导入 fs 模块 import { createRepo, uploadFiles, whoAmI, spaceInfo, fileExists, } from "@huggingface/hub"; import { InferenceClient } from "@huggingface/inference"; import bodyParser from "body-parser"; import checkUser from "./middlewares/checkUser.js"; import { PROVIDERS, DEFAULT_SYSTEM_PROMPT, convertToOpenAIFormat, convertToHFFormat } from "./utils/providers.js"; import { COLORS } from "./utils/colors.js"; // Load environment variables from .env file dotenv.config(); // 设置环境变量如果不存在 process.env.APP_PORT = 7860; // 打印环境变量(排除敏感信息) console.log('===== 环境变量 ====='); console.log('NODE_ENV:', process.env.NODE_ENV); console.log('APP_PORT:', process.env.APP_PORT); console.log('DEFAULT_MODEL_ID:', process.env.DEFAULT_MODEL_ID); console.log('REDIRECT_URI:', process.env.REDIRECT_URI); console.log('OPENAI_COMPATIBLE_ENDPOINT:', process.env.OPENAI_COMPATIBLE_ENDPOINT ? '已设置' : '未设置'); console.log('OPENAI_COMPATIBLE_MODEL_ID:', process.env.OPENAI_COMPATIBLE_MODEL_ID); console.log('==================='); const app = express(); const ipAddresses = new Map(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Hugging Face Spaces 使用 7860 端口 const PORT = 7860; const REDIRECT_URI = process.env.REDIRECT_URI || `http://localhost:${PORT}/auth/login`; const MODEL_ID = process.env.DEFAULT_MODEL_ID || "deepseek-ai/DeepSeek-V3-0324"; const MAX_REQUESTS_PER_IP = 4; app.use((req, res, next) => { console.log(`收到请求: ${req.method} ${req.url}`); next(); }); app.use(cookieParser()); app.use(express.static(path.join(__dirname, "dist"), { index: false, setHeaders: (res, filePath) => { console.log(`提供静态文件: ${filePath}`); // 移除可能导致问题的安全头部 res.removeHeader('Feature-Policy'); res.removeHeader('Permissions-Policy'); // 设置适当的内容安全策略 res.setHeader('Content-Security-Policy', "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';"); } })); app.use(bodyParser.json()); const getPTag = (repoId) => { return `
`; }; app.get("/api/login", (_req, res) => { res.redirect( 302, `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` ); }); app.get("/auth/login", async (req, res) => { const { code } = req.query; if (!code) { return res.redirect(302, "/"); } const Authorization = `Basic ${Buffer.from( `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}` ).toString("base64")}`; const request_auth = await fetch("https://huggingface.co/oauth/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization, }, body: new URLSearchParams({ grant_type: "authorization_code", code: code, redirect_uri: REDIRECT_URI, }), }); const response = await request_auth.json(); if (!response.access_token) { return res.redirect(302, "/"); } res.cookie("hf_token", response.access_token, { httpOnly: false, secure: true, sameSite: "none", maxAge: 30 * 24 * 60 * 60 * 1000, }); return res.redirect(302, "/"); }); app.get("/auth/logout", (req, res) => { res.clearCookie("hf_token", { httpOnly: false, secure: true, sameSite: "none", }); return res.redirect(302, "/"); }); app.get("/api/@me", checkUser, async (req, res) => { const { hf_token } = req.cookies; try { const request_user = await fetch("https://huggingface.co/oauth/userinfo", { headers: { Authorization: `Bearer ${hf_token}`, }, }); const user = await request_user.json(); res.send(user); } catch (err) { res.clearCookie("hf_token", { httpOnly: false, secure: true, sameSite: "none", }); res.status(401).send({ ok: false, message: err.message, }); } }); app.post("/api/deploy", checkUser, async (req, res) => { const { html, title, path } = req.body; if (!html || !title) { return res.status(400).send({ ok: false, message: "Missing required fields", }); } const { hf_token } = req.cookies; try { const repo = { type: "space", name: path ?? "", }; let readme; let newHtml = html; if (!path || path === "") { const { name: username } = await whoAmI({ accessToken: hf_token }); const newTitle = title .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .split("-") .filter(Boolean) .join("-") .slice(0, 96); const repoId = `${username}/${newTitle}`; repo.name = repoId; await createRepo({ repo, accessToken: hf_token, }); const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)]; const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)]; readme = `--- title: ${newTitle} emoji: 🐳 colorFrom: ${colorFrom} colorTo: ${colorTo} sdk: static pinned: false tags: - deepsite --- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference`; } newHtml = html.replace(/<\/body>/, `${getPTag(repo.name)}