2023-07-29 01:48:21 +08:00

703 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenAIAPI 信息查询</title>
<meta name="description" content="批量快速查询OPENAI的余额支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效">
<style>
:root {
--color-primary: #1E3B7A;
--color-primary-dark: #1E3B7A;
--color-primary-alpha: #01847f;
--body-color: #dadada;
--body-bg: #000000;
--border-color: #f8f8f800;
}
body {
background: url('https://cdn-img.czl.net/2023/05/23/pjbczr.webp') no-repeat center center fixed;
/* 自定义背景图 */
background-size: cover;
width: 90%;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
padding-left: 2rem;
padding-right: 2rem;
color: var(--body-color);
/* background: var(--body-bg); */
font-family: system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
line-height: 1.5;
-webkit-tap-highlight-color: rgb(252, 247, 247);
text-rendering: optimizelegibility;
-webkit-font-smoothing: antialiased;
}
.query {
max-width: 50rem;
margin: auto;
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color .3s;
}
a:hover {
color: var(--color-primary);
text-decoration: underline;
}
h1 {
font-size: 2.5rem;
text-align: center;
}
main[x-cloak] {
opacity: 0;
}
main:not([x-cloak]) {
opacity: 1;
transition: opacity .3s;
}
textarea {
-webkit-appearance: none;
appearance: none;
display: block;
width: 100%;
padding: .5rem 1rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
box-sizing: border-box;
color: #33404d;
line-height: inherit;
font-size: 1rem;
transition: border .3s, box-shadow .3s;
}
textarea:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
input {
-webkit-appearance: none;
appearance: none;
display: block;
width: 100%;
padding: .5rem 1rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
box-sizing: border-box;
color: #33404d;
line-height: inherit;
font-size: 1rem;
transition: border .3s, box-shadow .3s;
}
input:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
details {
margin: 1rem 0 2rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
transition: background .3s;
}
details[open] {
background: #fff;
}
details summary {
padding: .5rem 1rem;
font-weight: 500;
user-select: none;
cursor: pointer;
opacity: .8;
outline: 0;
}
details div {
padding: 1rem;
border-top: 1px solid var(--border-color);
}
details small {
margin: 0;
font-size: .875rem;
line-height: 2;
}
button {
appearance: none;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: auto;
max-width: 50rem;
margin-bottom: 1rem;
padding: .5rem .75rem;
border: 1px solid var(--color-primary);
border-radius: .25rem;
background: var(--color-primary);
color: #fff;
font-size: 1rem;
font-weight: 500;
line-height: inherit;
cursor: pointer;
user-select: none;
transition: border .3s, background .3s, ;
}
button:hover {
border-color: var(--color-primary-dark);
background: var(--color-primary-dark);
}
button:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
button:disabled {
background: var(--color-primary);
border-color: var(--color-primary);
opacity: .6;
cursor: not-allowed;
}
button.loading::before {
content: '';
display: inline-block;
margin-right: .5rem;
border: 2px solid #fff;
border-top-color: transparent;
border-bottom-color: transparent;
border-radius: 50%;
width: .75rem;
height: .75rem;
animation: rotate .5s linear infinite;
}
footer {
padding: 1rem;
border-top: 1px solid var(--border-color);
text-align: center;
opacity: .5;
}
footer i {
font-style: normal;
color: #b32142;
}
.success,
.error {
margin-bottom: 1rem;
padding: .5rem 1rem;
border-radius: .25rem;
color: #fff;
text-align: center;
opacity: 1;
transition: opacity .3s;
}
.success {
/* border: 1px solid #12b886; */
background: #38d9a9;
}
.error {
/* border: 1px solid #b32142; */
background: #b32142;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
/* 设置API URL选择器和自定义URL输入框的样式 */
select#api-url-select,
input#custom-url-input {
width: 100%;
height: 2.4rem;
font-size: 1rem;
background-color: #212121;
margin-bottom: .5rem;
padding: .5rem .75rem;
color: #ffffff;
border: none;
padding: .5rem .75rem;
border-radius: .25rem;
margin-right: 10px;
margin-top: 2vhpx;
/* 添加了顶部边距 */
}
/* 隐藏自定义API链接输入框 */
input#custom-url-input {
display: none;
height: 50px;
/* 添加高度 */
}
/* 下面的代码定义了结果表格的样式 */
#result-table {
border-collapse: collapse;
width: 100%;
margin-top: 20px;
font-size: 10px;
border-radius: 5px;
border: none;
overflow: hidden;
}
/* 下面的代码定义了结果表格表头的样式 */
#result-table th {
background-color: var(--color-primary);
color: #ffffff;
font-weight: 400;
height: 20px;
padding: 10px 5px;
text-align: left;
/* border: 1px solid #dcdcdc; */
}
/* 下面的代码定义了结果表格数据单元格的样式 */
#result-table td {
height: 20px;
text-align: left;
padding: 10px 5px;
/* border: 1px solid #d3d3d3; */
}
/* 下面的代码定义了结果表格奇数行的背景颜色 */
#result-table tbody tr:nth-child(odd) {
background-color: #252422;
}
/* 下面的代码定义了结果表格偶数行的背景颜色 */
#result-table tbody tr:nth-child(even) {
background-color: #31302d;
}
/* 下面的代码定义了结果表格表头的宽度 */
#result-table .table-header {
width: 25%;
}
/* 下面的代码定义了结果表格数据单元格的宽度、字体加粗和颜色 */
#result-table .table-data {
width: 25%;
font-weight: bold;
color: #1c248b;
}
/* 下面的代码定义了结果表格数据单元格的样式 */
#result-table .status-error {
height: 20px;
text-align: left;
padding: 10px 5px;
/* border: 1px solid #dcdcdc; */
}
/* 下面的代码定义了一个类名为status-ok的样式用于设置成功状态的文本颜色 */
.status-ok {
color: #2d8d2d;
}
/* 下面的代码定义了一个类名为status-error的样式用于设置错误状态的文本颜色 */
.status-error {
color: #ed0808;
}
#api-key-input {
width: 100%;
height: 100px;
/* border: 1px solid #999; */
background-color: #fff;
}
.emoji {
font-size: 30px;
/* 调整为适当的大小 */
}
</style>
</head>
<body>
<header>
<h1 class="text-3xl font-semibold text-center mb-8 text-gradient">OpenAI API 信息查询</h1>
<p class=" text-center text-gradient" style="text-align: center;">7月22日更新后需使用sess码进行查询。使用key只能查询总额度、绑卡、GPT4和组织ID。
</p>
<a href="oapi.html" style="text-align: center;color:antiquewhite;text-decoration: none;font-weight: 700;">
<p>Oapi的余额查询</p>
</a>
</header>
<div class="query">
<div>
<h2 style="display: inline-block;">输入 API KEY</h2>
<p style="display: inline-block; font-size: smaller;">本站不保存 KEY 信息,查询后请自行保存</p>
</div>
<textarea id="api-key-input"
placeholder="请输入 API-KEY或sessID多个可直接粘贴"></textarea></p>
<!-- API链接选择框 -->
<div class="api-url-container"></div>
<!-- API链接选择 -->
<div>
<h2 style="display: inline-block;">选择查询线路</h2>
<p style="display: inline-block; font-size: smaller;">支持自定义线路,官网线路需要魔法</p>
</div>
<select id="api-url-select" style="width:49%;">
<option value="https://api.openai.com">【官网线路】api.openai.com</option>
<option value="custom">【已前置https】自定义 ...</option>
</select>
<!-- 自定义API链接输入框 -->
<input type="text" id="custom-url-input" placeholder="输入自定义API填域名即可无需https://" style="width:45%;" />
</div>
<button :class="{ loading }" :disabled="loading" onclick="sendRequest()">查询</button> </p>
</div>
<h2 id="result-head" style="visibility:hidden">查询结果</h2>
<table id="result-table" style="visibility:hidden">
<thead>
<tr>
<th style="width:35px">序号</th>
<th>API KEY</th>
<th style="width: 50px;">总额度</th>
<th style="width: 50px">已使用</th>
<th style="width: 50px">剩余量</th>
<th style="width: 100px">已用比例</th>
<th style="width: 90px">到期时间</th>
<th style="width: 50px">GPT-3.5</th>
<th style="width: 50px">GPT-4</th>
<th style="width: 50px">32K</th>
<th style="width: 50px">绑卡</th>
<th style="width: 220px">绑卡信息</th>
<th style="width: 100px">组织名称+邮箱+ID</th>
<th style="width: 50px;">是否有效</th>
</tr>
</thead>
<tbody></tbody>
</table>
<footer>
<a href="https://chat.czl.net" style="color: white;" target="_blank">CZLChat</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://oapi.czl.net" style="color: white;" target="_blank">CZLOapi</a>
</footer>
<script>
let queriedApiKeys = [];
let serialNumber = 1;
async function checkBilling(apiKey, apiUrl) {
const now = new Date();
let startDate = new Date(now - 90 * 24 * 60 * 60 * 1000);
const endDate = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const subDate = new Date(now);
subDate.setDate(1);
const headers = {
"Authorization": "Bearer " + apiKey,
"Content-Type": "application/json"
};
const gpt4Check = `${apiUrl}/v1/models`;
const urlSubscription = `${apiUrl}/v1/dashboard/billing/subscription`;
let urlUsage = `${apiUrl}/v1/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`;
const urlsetid = apiUrl + '/v1/organizations';
try {
let totalAmount, totalUsage, remaining, GPT35CheckResult, GPT4CheckResult, GPT432kCheckResult, setid, isSubscrible;
let SubscribleInformation = {};
let SubInformation;
let errors = {};
let response = await fetch(urlSubscription, { headers });
let currentDate = new Date();
const subscriptionData = await response.json();
console.log("1:" + subscriptionData);
const expiryDate = new Date(subscriptionData.access_until * 1000 + 8 * 60 * 60 * 1000);
const formattedDate = `${expiryDate.getFullYear()}-${(expiryDate.getMonth() + 1).toString().padStart(2, '0')}-${expiryDate.getDate().toString().padStart(2, '0')}`;
const modelsCheckResponse = await fetch(modelsCheck, { headers });
const modelsCheckData = await modelsCheckResponse.json();
let GPT35CheckSuccess = GPT35CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-3.5-turbo')) ? '✅' : '❌';
let GPT4CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4')) ? '✅' : '❌';
let GPT432kCheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4-32k')) ? '✅' : '❌';
let isSubscrible = subscriptionData.plan.id.includes('payg') ? '✅' : '❌';
if (totalAmount > 20) {
startDate = subDate;
urlUsage = `${apiUrl}/v1/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`;
response = await fetch(urlUsage, { headers });
const usageData = await response.json();
}
response = await fetch(urlUsage, { headers });
const usageData = await response.json();
totalUsage = usageData.total_usage / 100;
remaining = currentDate > expiryDate ? "❌过期" : (totalAmount - totalUsage).toFixed(3);
} catch (error) {
console.error(error);
errors['subscription'] = error.message;
}
//获取是否绑卡
try {
isSubscrible = subscriptionData.plan.id.includes('payg') ? '✅' : '❌';
} catch (error) {
console.error(error);
}
//获取绑卡信息
try {
// 从 subscriptionData 中获取 SubscribleInformation 的值...
SubscribleInformation.account_name = subscriptionData.account_name;
SubscribleInformation.po_number = subscriptionData.po_number;
SubscribleInformation.billing_email = subscriptionData.billing_email;
SubscribleInformation.tax_ids = subscriptionData.tax_ids;
let billingAddress = subscriptionData.billing_address; // 定义并赋值 billingAddress
let businessAddress = subscriptionData.business_address; // 定义并赋值 businessAddress
SubInformation = "Account Name: " + SubscribleInformation.account_name + "---";
SubInformation += "PO Number: " + SubscribleInformation.po_number + "---";
SubInformation += "Billing Email: " + SubscribleInformation.billing_email + "---";
SubInformation += "Tax IDs: " + SubscribleInformation.tax_ids + "---";
SubInformation += "Billing Address: " + billingAddress.line1 + ", " + billingAddress.city + ", " + billingAddress.state + ", " + billingAddress.country + ", " + billingAddress.postal_code + "---";
SubInformation += "Business Address: " + businessAddress.line1 + ", " + businessAddress.city + ", " + businessAddress.state + ", " + businessAddress.country + ", " + businessAddress.postal_code;
}
catch (error) {
console.error(error);
}
//组织信息
try {
response = await fetch(urlsetid, { headers });
const setiddata = await response.json();
setid = '';
const emailStartIndex = setiddata.data[0].description.lastIndexOf(' ') + 1;
const email = setiddata.data[0].description.substring(emailStartIndex);
if (typeof setiddata.data[1] !== 'undefined') {
setid = setiddata.data[0].title + '----' + email + '----' + setiddata.data[0].id + '----' + setiddata.data[1].id;
} else {
setid = setiddata.data[0].title + '----' + email + '----' + setiddata.data[0].id;
}
} catch (error) {
console.error(error);
errors['setid'] = error.message;
}
return [totalAmount, totalUsage, remaining, formattedDate, GPT35CheckResult, GPT4CheckResult, GPT432kCheckResult,isSubscrible, setid];
} catch (error) {
return ["Error", null, null, null, null, null, null, null];
}
}
function formatDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
//查询函数
function sendRequest() {
let apiKeyInput = document.getElementById("api-key-input");
let apiUrlSelect = document.getElementById("api-url-select");
let customUrlInput = document.getElementById("custom-url-input");
let table = document.getElementById("result-table");
let h2 = document.getElementById("result-head");
h2.style.visibility = "visible";
table.style.visibility = "visible";
if (apiKeyInput.value.trim() === "") {
alert("请填写API KEY");
return;
}
document.getElementById("result-table").getElementsByTagName('tbody')[0].innerHTML = "";
let apiUrl = "";
if (apiUrlSelect.value === "custom") {
if (customUrlInput.value.trim() === "") {
alert("请设置API链接");
return;
} else {
apiUrl = customUrlInput.value.trim();
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
apiUrl = "https://" + apiUrl;
}
}
} else {
apiUrl = apiUrlSelect.value;
}
let apiKeys = apiKeyInput.value.split(/[,\s\n]+/);
if (apiKeys.length === 0) {
alert("未匹配到 API-KEY请检查输入内容");
return;
}
alert("成功匹配到 API Key确认后开始查询" + apiKeys);
let tableBody = document.querySelector("#result-table tbody");
for (let i = 0; i < apiKeys.length; i++) {
let apiKey = apiKeys[i].trim();
if (queriedApiKeys.includes(apiKey)) {
console.log(`API KEY ${apiKey} 已查询过,跳过此次查询`);
continue;
}
queriedApiKeys.push(apiKey);
checkBilling(apiKey, apiUrl).then((data) => {
data = data.map(item => {
if (item === undefined) {
return 'Not Found.'
} else {
return item
}
}
)
let row = document.createElement("tr");
let serialNumberCell = document.createElement("td"); // 创建序列号单元格
serialNumberCell.textContent = serialNumber; // 设置序列号文本
row.appendChild(serialNumberCell); // 将序列号单元格添加到行中
let apiKeyCell = document.createElement("td");
apiKeyCell.textContent = apiKey.replace(/^(.{5}).*(.{4})$/, "$1***$2");
row.appendChild(apiKeyCell);
console.log('查看查询结果', data); // 添加 console.log 以查看 data 的值
if (data[0] === undefined) {
let errorMessageCell = document.createElement("td");
errorMessageCell.colSpan = "8";
errorMessageCell.classList.add("status-error");
errorMessageCell.textContent = "不正确或已失效的API-KEY";
row.appendChild(errorMessageCell);
} else {
let totalAmount = document.createElement("td");
totalAmount.textContent = data[0];
row.appendChild(totalAmount);
let totalUsedCell = document.createElement("td");
if (!isNaN(data[1])) {
totalUsedCell.textContent = data[1].toFixed(3);
} else {
totalUsedCell.textContent = '❌'
}
row.appendChild(totalUsedCell);
let totalAvailableCell = document.createElement("td");
totalAvailableCell.textContent = typeof data[2] === 'number' ? data[2] : data[2];
row.appendChild(totalAvailableCell);
let progressCell = document.createElement("td");
let progressContainer = document.createElement("div"); // 添加一个新的容器元素
progressContainer.style.width = "100%";
progressContainer.style.height = "20px";
progressContainer.style.backgroundColor = "#f3f3f3"; // 设置容器的背景色为灰白色
let progressBar = document.createElement("div");
progressBar.style.width = (data[1] / data[0] * 100).toFixed(2) + "%";
progressBar.style.height = "20px";
progressBar.style.backgroundColor = "#4CAF50";
progressBar.style.position = "relative"; // 设置进度条的 position 为 relative
progressBar.textContent = (data[1] / data[0] * 100).toFixed(2) + "%"; // 显示百分比
progressBar.style.textAlign = "right"; // 设置文本对齐方式为右对齐
progressBar.style.paddingRight = "5px"; // 设置右边距以确保文本不超出边界
progressBar.style.color = "black"; // 设置文本颜色为白色
progressContainer.appendChild(progressBar); // 将进度条添加到容器中
progressCell.appendChild(progressContainer); // 将容器添加到单元格中
row.appendChild(progressCell);
let expireTime = document.createElement("td");
expireTime.textContent = data[3];
row.appendChild(expireTime);
let GPT4CheckResult = document.createElement("td");
GPT4CheckResult.textContent = data[4];
row.appendChild(GPT4CheckResult);
let GPT432kCheckResult = document.createElement("td");
GPT432kCheckResult.textContent = data[5];
row.appendChild(GPT432kCheckResult);
let isSubscribe = document.createElement("td");
isSubscribe.textContent = data[6];
row.appendChild(isSubscribe);
let SubInformation = document.createElement("td");
SubInformation.textContent = data[8];
row.appendChild(SubInformation);
let setidCell = document.createElement("td");
setidCell.textContent = data[8];
row.appendChild(setidCell);
// 是否有效列
let isSubscriptionValid = document.createElement("td");
isSubscriptionValid.textContent = data[4] === '✅' ? '有效' : '无效'; // 使用 GPT35CheckResult 的值进行判断
row.appendChild(isSubscriptionValid);
}
console.log('查看行信息', row)
tableBody.appendChild(row);
if (i === apiKeys.length - 1) {
queriedApiKeys = [];
}
serialNumber++; // 增加序列号
h2.style.display = 'block';
table.style.display = 'table';
})
}
}
let apiUrlSelect = document.getElementById("api-url-select");
let customUrlInput = document.getElementById("custom-url-input");
apiUrlSelect.addEventListener("change", function () {
if (apiUrlSelect.value === "custom") {
customUrlInput.style.display = "inline-block";
customUrlInput.style.marginTop = "5px";
} else {
customUrlInput.style.display = "none";
}
});
</script>
</body>
</html>