puter / main.ts
hongshi-files's picture
Update main.ts
8d6cbe0 verified
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// 配置区域
// 建议通过环境变量传入账号密码,或者在启动时交互式输入
const PUTER_USERNAME = Deno.env.get("PUTER_USERNAME") || "";
const PUTER_PASSWORD = Deno.env.get("PUTER_PASSWORD") || "";
const LOGIN_API_URL = "https://api.puter.com/login";
const PROXY_API_URL = "https://api.puter.com/drivers/call";
// 缓存 Token,避免每次请求都重新登录
let cachedAuthToken: string | null = null;
/**
* [核心功能] 自动登录 Puter 并获取 JWT Token
* 使用纯 HTTP 请求,无浏览器依赖。
*/
async function getAuthToken(): Promise<string> {
if (cachedAuthToken) {
return cachedAuthToken;
}
if (!PUTER_USERNAME || !PUTER_PASSWORD) {
throw new Error("Missing credentials. Please set PUTER_USERNAME and PUTER_PASSWORD env vars.");
}
console.log(`[Auth] Attempting to login as ${PUTER_USERNAME}...`);
// 发起登录请求
const response = await fetch(LOGIN_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: PUTER_USERNAME,
password: PUTER_PASSWORD,
// 2026年的 Puter 可能需要一个 client_id 或 specific flag 来表明这是机器登录
// 这里我们假设基础的用户名密码流程依然有效
}),
});
if (!response.ok) {
const errText = await response.text();
throw new Error(`Login failed (${response.status}): ${errText}`);
}
const data = await response.json();
// 解析 Token 字段
// Puter 通常返回 { token: "..." } 或 { auth_token: "..." } 或 { user: ..., token: ... }
const token = data.token || data.auth_token || data.user?.token;
if (!token) {
console.error("Login response:", data);
throw new Error("Login successful but no token found in response.");
}
console.log("[Auth] Login successful. Token cached.");
cachedAuthToken = token;
return token;
}
serve(async (req: Request): Promise<Response> => {
// 1. 处理 CORS
if (req.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
}
const url = new URL(req.url);
// 2. 路由:OpenAI Chat Completions
if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
try {
const openaiBody = await req.json();
const { messages, model = "gpt-5-nano" } = openaiBody;
// [关键] 动态获取 Token
// 如果 Token 失效,这里可以扩展逻辑来重新登录
const authToken = await getAuthToken();
// 3. 构造 Puter 请求
const puterPayload = {
interface: "puter-chat-completion",
driver: "ai-chat",
method: "complete",
test_mode: false,
args: {
messages: messages,
model: model,
},
auth_token: authToken, // 注入动态获取的 Token
};
// 4. 转发请求
const puterResponse = await fetch(PROXY_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
// 某些 API 可能需要在 Header 中也携带 Authorization,
// 但根据你之前的日志,Puter 主要读取 body 中的 auth_token
},
body: JSON.stringify(puterPayload),
});
if (!puterResponse.ok) {
// 如果返回 401 或 403,说明 Token 可能过期了
if (puterResponse.status === 401 || puterResponse.status === 403) {
console.warn("[Auth] Token expired, clearing cache...");
cachedAuthToken = null;
// 这里可以递归重试一次,但在简单示例中直接报错
}
const errorText = await puterResponse.text();
throw new Error(`Puter API Error: ${errorText}`);
}
const puterData = await puterResponse.json();
// 5. 转换响应回 OpenAI 格式
const openaiResponse = {
id: `chatcmpl-${crypto.randomUUID()}`,
object: "chat.completion",
created: Math.floor(Date.now() / 1000),
model: model,
choices: [{
index: puterData.result?.index || 0,
message: {
role: puterData.result?.message?.role || "assistant",
content: puterData.result?.message?.content || "",
},
finish_reason: puterData.result?.finish_reason || "stop",
}],
usage: {
prompt_tokens: puterData.result?.usage?.prompt_tokens || 0,
completion_tokens: puterData.result?.usage?.completion_tokens || 0,
total_tokens: (puterData.result?.usage?.prompt_tokens || 0) + (puterData.result?.usage?.completion_tokens || 0),
},
};
return new Response(JSON.stringify(openaiResponse), {
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error("Proxy Error:", error);
return new Response(
JSON.stringify({ error: { message: error.message, type: "proxy_error" } }),
{ status: 500, headers: { "Content-Type": "application/json" } }
);
}
}
return new Response("Not Found", { status: 404 });
});
console.log(`[2026-Proxy] Server starting...`);
// 在启动前检查一次凭据(可选)
if (!PUTER_USERNAME || !PUTER_PASSWORD) {
console.warn("[WARN] PUTER_USERNAME and PUTER_PASSWORD not set. Auto-login will fail on first request.");
}