本文最后更新于 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 ,启动是成功了,但是打不开网页,换一个常见的端口即可