使用 express 写一个简单的 ChatGPT Token 维护页
本文最后更新于 205 天前,如有失效请评论区留言。

使用 https://new.oaifree.com 的网站时需要用到 access token,默认是 10天有效期,之后需要重新刷新 token,写一个简单的 token 维护页,用来管理手里的几十个账号。

首先需要到 https://token.oaifree.com/auth 这个页面获取账号的 token 信息,纯手动操作了

拿到所有的 token 信息后,保存到 token.json 文件里

[
  {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIm...",
    "expires_in": 864000,
    "refresh_token": "NKdQ1Y_ggASUHagYY9qkLJa1jVt2Hnj-VrYf40BfdTs",
    "startTime": 1717074950
  },
  {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV.....",
    "expires_in": 864000,
    "refresh_token": "ckH8_5P1bEYti0343f36r0Mm9EyrQjZEOC8sYWYhdJx",
    "token_type": "Bearer",
    "startTime": 1717074847
  },
]

页面功能

页面会展示 access token 和 refresh token ,点击 refresh token 按钮,就能刷新 access token,会将新的数据写入到 token.json 文件里

代码结构
/project
|-- public
->|-- index.html
->|-- token.json
|-- server.js
|-- package.json

因为要用 js 读写文件,所以使用 nodejs 来做,用 express 起一个服务端

const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();
const PORT = 5000;

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());

app.get('/tokens', (req, res) => {
    fs.readFile(path.join(__dirname, 'public', 'token.json'), 'utf8', (err, data) => {
        if (err) {
            return res.status(500).json({ error: 'Failed to read token file' });
        }
        res.json(JSON.parse(data));
    });
});

app.post('/update-tokens', (req, res) => {
    const tokens = req.body;
    fs.writeFile(path.join(__dirname, 'public', 'token.json'), JSON.stringify(tokens, null, 2), 'utf8', (err) => {
        if (err) {
            return res.status(500).json({ error: 'Failed to write token file' });
        }
        res.json({ status: 'Access token refresh success' });
    });
});

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ChatGPT Token</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        .token-container {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
        .token-title {
            font-weight: bold;
        }
        .token-item {
            margin-bottom: 5px;
            word-wrap: break-word;
        }
        .token-item .access-token {
            display: block;
            white-space: pre-wrap;
            word-break: break-all;
        }
        .copy-button {
            margin-left: 10px;
            padding: 3px 5px;
            font-size: 12px;
            cursor: pointer;
        }
        button {
            padding: 5px 10px;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <h1 style="text-align: center;">ChatGPT Token</h1>
    <p style="text-align: center;">总数:<span id="total"></span></p>
    <div id="token-list"></div>
    <script>
        const apiUrl = 'https://token.oaifree.com/api/auth/refresh';

        async function fetchTokens() {
            try {
                const response = await fetch('/tokens');
                const tokens = await response.json();
                const tokenTotal = document.getElementById('total');
                tokenTotal.innerText = tokens.length;
                initializeTokens(tokens);
            } catch (error) {
                console.error('Error fetching tokens:', error);
            }
        }

        function initializeTokens(tokens) {            
            displayTokens(tokens);
        }

        function displayTokens(tokens) {
            const tokenList = document.getElementById('token-list');
            tokenList.innerHTML = '';
            tokens.forEach((token, index) => {
                const tokenContainer = document.createElement('div');
                tokenContainer.className = 'token-container';

                const tokenTitle = document.createElement('div');
                tokenTitle.className = 'token-title';
                tokenTitle.innerText = `Token ${index + 1}`;
                tokenContainer.appendChild(tokenTitle);

                const accessToken = document.createElement('div');
                accessToken.className = 'token-item';
                const accessTokenContent = document.createElement('span');
                accessTokenContent.className = 'access-token';
                accessTokenContent.innerText = `Access Token: ${token.access_token.substr(0,255)}......`;
                accessToken.appendChild(accessTokenContent);

                const copyButton = document.createElement('button');
                copyButton.className = 'copy-button';
                copyButton.innerText = 'Copy';
                copyButton.onclick = () => copyToClipboard(token.access_token);
                accessToken.appendChild(copyButton);
                tokenContainer.appendChild(accessToken);

                // const refreshToken = document.createElement('div');
                // refreshToken.className = 'token-item';
                // refreshToken.innerText = `Refresh Token: ${token.refresh_token}`;
                // tokenContainer.appendChild(refreshToken);

                const expiresIn = document.createElement('div');
                expiresIn.className = 'token-item';
                expiresIn.innerText = `Expiry date: ${token.expires_in/60/60/24} day`;
                tokenContainer.appendChild(expiresIn);

                const timeLeft = document.createElement('div');
                timeLeft.className = 'token-item';
                timeLeft.innerText = `Expires at: ${formatDateTime(token.startTime*1000 + token.expires_in*1000)}`;
                tokenContainer.appendChild(timeLeft);

                const refreshButton = document.createElement('button');
                refreshButton.innerText = 'Refresh Token';
                refreshButton.onclick = () => refreshAccessToken(tokens, index);
                tokenContainer.appendChild(refreshButton);

                tokenList.appendChild(tokenContainer);
            });
        }

        function formatDateTime(timestamp) {
            // 将时间戳和毫秒数相加
            var date = new Date(timestamp);

            // 获取年月日时分秒毫秒,并格式化为两位数字
            var year = date.getFullYear();
            var month = ('0' + (date.getMonth() + 1)).slice(-2); // 月份是从0开始的
            var day = ('0' + date.getDate()).slice(-2);
            var hours = ('0' + date.getHours()).slice(-2);
            var minutes = ('0' + date.getMinutes()).slice(-2);
            var seconds = ('0' + date.getSeconds()).slice(-2);
            // var milliseconds = ('00' + date.getMilliseconds()).slice(-3); // 保证毫秒为3位数

            // 拼接字符串返回
            return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
        }

        function copyToClipboard(text) {
            const textarea = document.createElement('textarea');
            textarea.value = text;
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
            alert('Access Token copied to clipboard');
        }

        async function refreshAccessToken(tokens, index) {
            const token = tokens[index];
            const url = apiUrl;
            const refreshToken = token.refresh_token;

            try {
                const response = await fetch(url, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    body: new URLSearchParams({
                        'refresh_token': refreshToken
                    })
                });

                if (response.ok) {
                    const data = await response.json();
                    tokens[index] = {
                        ...token,
                        access_token: data.access_token,
                        expires_in: data.expires_in,
                        startTime: Math.floor(Date.now() / 1000)
                    };
                    displayTokens(tokens);
                    await updateTokenFile(tokens);
                } else {
                    console.error('Error refreshing token:', response.statusText);
                    alert('Failed to refresh token');
                }
            } catch (error) {
                console.error('Error refreshing token:', error);
                alert('Error refreshing token');
            }
        }

        async function updateTokenFile(tokens) {
            try {
                await fetch('update-tokens', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(tokens)
                });
            } catch (error) {
                console.error('Error updating token file:', error);
            }
        }

        fetchTokens();
    </script>
</body>
</html>

运行

运行项目 npm start ,注意这里的端口定义,之前使用 6000 端口,浏览器提示 unsafe port ,启动是成功了,但是打不开网页,换一个常见的端口即可

版权声明:除特殊说明,博客文章均为Gavin原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇