【我也来优雅优雅】快速搭建 GPT-4 个人分享站点

【既然选择优雅就优雅到底】GPT4私人拼车神器【UI版】 基础上继续优雅,只改动了样式。

感谢各位站里大佬的接力!!!

显示效果

  • 浅色桌面

  • 深色移动

简单上手

第一步,注册

注册 Cloudflare 账号并绑定域名,注册 OpenAI 并成为尊贵的Plus会员。

第二步,创建KV

在 Cloudflare 侧边栏 Workers 和 Pages - KV- 创建命名空间,输入名称后点击添加。


第三步,添加变量

KV添加成功后等待下方列表更新,更新后点击查看,在左边的输入框输入密钥,右边输入值,一共需要添加四次,除了at 可以只填变量不填值,其他都是必填的。


密钥
SITE_PASSWORD 自定义站点密码
YOUR_DOMAIN new.oaifree.com
at access_token
rt refresh_token

第四步,创建worker

在 Cloudflare 侧边栏 Workers 和 Pages - 概述 - 创建应用程序 - 创建worker,修改名称后点击部署。


第五步,编辑代码

worker 创建成功后,在跳转页面点击编辑代码,复制下面的代码覆盖原值,然后右上角点击部署,等待部署完成后点击左上角的←退出。

worker.js
addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event.request));
  });
  function parseJwt(token) {
    const base64Url = token.split(".")[1]; // 获取载荷部分
    const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); // 将 Base64Url 转为 Base64
    const jsonPayload = decodeURIComponent(
        atob(base64)
            .split("")
            .map(function (c) {
                return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
            })
            .join("")
    );
  
    return JSON.parse(jsonPayload); // 返回载荷解析后的 JSON 对象
  }
  
  async function getOAuthLink(shareToken, proxiedDomain) {
    const url = `https://${proxiedDomain}/api/auth/oauth_token`;
    const response = await fetch(url, {
        method: "POST",
        headers: {
            Origin: `https://${proxiedDomain}`,
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            share_token: shareToken,
        }),
    });
    const data = await response.json();
    return data.login_url;
  }
  
  function isTokenExpired(token) {
    const payload = parseJwt(token);
    const currentTime = Math.floor(Date.now() / 1000); // 获取当前时间戳(秒)
    return payload.exp < currentTime; // 检查 token 是否过期
  }
  
  async function handleRequest(request) {
    const requestURL = new URL(request.url);
  
    // ------------ 反代到 new.oaifree.com
    const path = requestURL.pathname;
    if (path !== "/auth/login_auth0" && path !== "/auth/login") {
        requestURL.host = "new.oaifree.com";
        return fetch(new Request(requestURL, request));
    }
  
    // ------------ 进行拿share_token去登录
    // @ts-ignore
    const token = await oai_global_variables.get("at"); // 这里填入你的 JWT
    if (token == "" || isTokenExpired(token)) {
        // 如果 Token 过期,执行获取新 Token 的逻辑
        const url = "https://token.oaifree.com/api/auth/refresh";
        // @ts-ignore
        const refreshToken = await oai_global_variables.get("rt"); // 实际情况下你可能会从某处获取这个值
  
        // 发送 POST 请求
        const response = await fetch(url, {
            method: "POST",
            headers: {
                "Content-Type":
                    "application/x-www-form-urlencoded;charset=UTF-8",
            },
            body: `refresh_token=${refreshToken}`,
        });
  
        // 检查响应状态
        if (response.ok) {
            const data = await response.json();
            const token = data.access_token;
  
            // @ts-ignore
            await oai_global_variables.put("at", token);
        } else {
            return new Response("Error fetching access token", {
                status: response.status,
            });
        }
    }
  
    // 如果 Token 未过期,继续执行原来的逻辑
    if (request.method === "POST") {
        const formData = await request.formData();
  
        // @ts-ignore
        const SITE_PASSWORD =
            (await oai_global_variables.get("SITE_PASSWORD")) || "";
        const site_password = formData.get("site_password") || "";
        if (site_password !== SITE_PASSWORD) {
            const htmlContent = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Password Incorrect</title>
    </head>
    <body>
      <script>
        alert("访问密码错误");
        // 可以添加其他逻辑或重定向
        window.history.back();
      </script>
    </body>
    </html>
  `;
  
            return new Response(htmlContent, {
                status: 401,
                headers: { "Content-Type": "text/html" },
            });
        }
  
        // @ts-ignore
        const access_token = await oai_global_variables.get("at");
        const unique_name = formData.get("unique_name");
        const site_limit = "";
        const expires_in = "0";
        const gpt35_limit = "-1";
        const gpt4_limit = "-1";
        const show_conversations = "false";
        const temporary_chat = 'true';
        const reset_limit = "false";
  
        const url = "https://chat.oaifree.com/token/register";
        const body = new URLSearchParams({
            unique_name,
            access_token, // 使用来自表单或KV变量的 access_token
            site_limit,
            expires_in,
            gpt35_limit,
            gpt4_limit,
            show_conversations,
            temporary_chat,
            reset_limit,
        }).toString();
  
        const apiResponse = await fetch(url, {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: body,
        });
  
        const respJson = await apiResponse.json();
        const tokenKey =
            "token_key" in respJson ? respJson.token_key : "未找到 Share_token";
  
        // @ts-ignore
        const YOUR_DOMAIN =
            (await oai_global_variables.get("YOUR_DOMAIN")) || requestURL.host;
  
        return Response.redirect(
            await getOAuthLink(tokenKey, YOUR_DOMAIN),
            302
        );
    } else {
        const formHtml = `
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <title>欢迎使用oaifree</title>
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
            <link rel="icon" type="image/png" href="https://img2.imgtp.com/2024/04/21/GM6434la.png">
            <style>
                body {
                    background-image: url('https://img2.imgtp.com/2024/04/09/8te5uv4t.png');
                    background-size: cover;
                    background-position: center;
                    background-attachment: fixed;
                }
                input::placeholder {
                    color: transparent;
                }
                input:not(:placeholder-shown) + label,
                input:focus + label {
                    top: -20px;
                    padding: 0 5px;
                    left: 12px;
                    z-index: 10;
                    background: rgba(255, 255, 255, 0);
                }
            </style>
        </head>
        <body class="flex justify-center items-center min-h-screen m-0 bg-black bg-opacity-60">
            <div class="bg-white bg-opacity-80 rounded-lg shadow-xl p-8 w-auto">
                <h1 class="text-center text-gray-700 text-2xl font-semibold mb-6">欢迎使用</h1>
                <form method="POST" class="space-y-6">
                    <div class="relative mb-4">
                        <input type="text" id="unique_name" name="unique_name" placeholder=" " required class="w-full h-12 pl-5 pr-3 rounded-lg border border-gray-300 focus:border-green-500 focus:ring-1 focus:ring-green-500 bg-transparent text-black text-lg">
                        <label for="unique_name" class="absolute left-5 top-1 text-green-500 text-sm transition-all duration-300">用户名</label>
                    </div>
                    <div class="relative mb-4">
                        <input type="password" id="site_password" name="site_password" placeholder=" " class="w-full h-12 pl-5 pr-3 rounded-lg border border-gray-300 focus:border-green-500 focus:ring-1 focus:ring-green-500 bg-transparent text-black text-lg">
                        <label for="site_password" class="absolute left-5 top-1 text-green-500 text-sm transition-all duration-300">口令</label>
                    </div>
                    <button type="submit" class="w-full h-12 bg-green-500 hover:bg-green-600 text-white font-bold rounded-lg transition-colors duration-300">开始使用</button>
                    <img src="https://img2.imgtp.com/2024/04/21/KcdFKRSL.png" alt="LINUX DO" id="site-logo" class="mx-auto mt-4 w-30 h-10">
                </form>
            </div>
        </body>
        </html>      
    `;
  
        return new Response(formHtml, {
            headers: {
                "Content-Type": "text/html; charset=utf-8",
            },
        });
    }
  }

第六步,绑定KV

退出来后,点击设置 - 变量 - KV 命名空间绑定 - 添加绑定,变量名称填写:oai_global_variables,KV命名空间选择你方才创建的。


现在,您的个人GPT-4分享站点就已经部署好,点击变量上面的触发器标签,找到路由下的worker地址即可直接点击访问了。

另外,您还可以在 触发器 - 添加二级域名 绑定自己的二级域名,快和小伙伴一起分享吧。

38 个赞

优雅

2 个赞

优雅太优雅了

2 个赞

优雅啊

2 个赞

太优雅了,不过,尊贵的plus会员在哪里领取 :face_with_peeking_eye:

3 个赞

厉害了

2 个赞

佬好,试试,可以把oai的原版页面搬过来。

2 个赞

很优雅

3 个赞

chat.openai.com

:crazy_face:

2 个赞

优雅太优雅了,灵魂都以经高杰了

2 个赞

mark一下

2 个赞

唉,又是worker

1 个赞

大佬又优雅了

1 个赞

非常优雅

1 个赞

人工智能wiki

1 个赞

wiki人工智能

1 个赞

优雅

非PLUS能玩不 :rofl:

优雅太优雅了

From #develop:ai to #share