mirror of
https://github.com/woodchen-ink/openai-billing-query.git
synced 2025-07-18 14:01:59 +08:00
Merge pull request #11 from woodchen-ink:woodchen-ink/issue10
添加速率查询,添加多绑卡信息查询,css和js从html分离开
This commit is contained in:
commit
598ca998c0
39
README.md
39
README.md
@ -9,40 +9,9 @@ Telegram交流群:https://t.me/ai_cn2023
|
|||||||
|
|
||||||
## 最新示意图(2023.08.28)
|
## 最新示意图(2023.08.28)
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## 更新日志
|
|
||||||
### 8月26日更新
|
|
||||||
1. 有效检查改为发送请求,不再使用正则;
|
|
||||||
|
|
||||||
### 8月22日更新
|
|
||||||
|
|
||||||
1. 组织信息增加`userid`和`创建时间`;
|
|
||||||
|
|
||||||
### 7月29日更新(点个star吧)
|
|
||||||
|
|
||||||
1. 新增GPT-3.5查询、绑卡信息(人名和地址)、组织名称、邮箱、组织ID、是否有效;
|
|
||||||
2. 使用sk查询不再整体报错,而是展示可以查询到的内容;
|
|
||||||
|
|
||||||
### 7月22日更新后,如要查看完整信息,需登录一次账号,F12查看sess码,使用sess码进行查询
|
|
||||||
|
|
||||||
7月22日更新后,已删除key校验规则,需使用sess码进行查询。使用key只能查询部分信息。
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## sess查询示例(120刀4.0key)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## sk查询示例(120刀4.0key)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## sk查询示例(5刀未绑卡key)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## SESS ID获取方法
|
## SESS ID获取方法
|
||||||
@ -55,8 +24,6 @@ Telegram交流群:https://t.me/ai_cn2023
|
|||||||
``` html
|
``` html
|
||||||
<option value="反代网址">【自定义名称】自定义名称</option>
|
<option value="反代网址">【自定义名称】自定义名称</option>
|
||||||
```
|
```
|
||||||
## 反代代码示例
|
|
||||||

|
|
||||||
|
|
||||||
## 怎么部署
|
## 怎么部署
|
||||||
下载index.html直接打开就行,除了背景图片,没有任何外部资源。
|
下载index.html直接打开就行,除了背景图片,没有任何外部资源。
|
||||||
|
799
index.html
799
index.html
@ -6,365 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>OpenAIAPI 信息查询</title>
|
<title>OpenAIAPI 信息查询</title>
|
||||||
<meta name="description" content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效">
|
<meta name="description" content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效">
|
||||||
<style>
|
<link rel="stylesheet" href="static/css.css" type="text/css" />
|
||||||
:root {
|
|
||||||
--color-primary: #2EA7E0;
|
|
||||||
--color-primary-dark: #2482AD;
|
|
||||||
--color-primary-alpha: #75C2E6;
|
|
||||||
--body-color: #ffffff;
|
|
||||||
--body-bg: #000000;
|
|
||||||
--border-color: #f1ebeb00;
|
|
||||||
}
|
|
||||||
|
|
||||||
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-alpha);
|
|
||||||
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: 2px; */
|
|
||||||
/* border-top: 1px solid var(--border-color); */
|
|
||||||
text-align: center;
|
|
||||||
/* opacity: .5; */
|
|
||||||
/* position: fixed; */
|
|
||||||
/* bottom: 0; */
|
|
||||||
/* left: 0; */
|
|
||||||
margin-top: 20px;
|
|
||||||
/* width: 100%; */
|
|
||||||
/* background-color:#212121; */
|
|
||||||
line-height: unset !important;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer .p {
|
|
||||||
display: flex;
|
|
||||||
margin-block-start: 0 !important;
|
|
||||||
margin-block-end: 0 !important;
|
|
||||||
padding: 0;
|
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
/* 调整为适当的大小 */
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -415,8 +57,9 @@
|
|||||||
<th style="width: 50px">GPT-4</th>
|
<th style="width: 50px">GPT-4</th>
|
||||||
<th style="width: 50px">32K</th>
|
<th style="width: 50px">32K</th>
|
||||||
<th style="width: 50px">绑卡</th>
|
<th style="width: 50px">绑卡</th>
|
||||||
<th style="width: 200px">绑卡信息</th>
|
<th style="width: 230px">绑卡信息</th>
|
||||||
<th style="width: 200px">组织信息</th>
|
<th style="width: 230px">组织信息</th>
|
||||||
|
<th style="width: 150px">速率</th>
|
||||||
<th style="width: 50px;">是否有效</th>
|
<th style="width: 50px;">是否有效</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -432,439 +75,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="static/js.js"></script>
|
||||||
|
|
||||||
<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 modelsCheck = `${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';
|
|
||||||
const urlPaymentmethods = `${apiUrl}/v1/dashboard/billing/payment_methods`;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
|
||||||
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')}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
totalAmount = subscriptionData.system_hard_limit_usd;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
if (subscriptionData.plan.id.includes('payg')) {
|
|
||||||
switch (subscriptionData.billing_mechanism) {
|
|
||||||
case 'advance':
|
|
||||||
isSubscrible = '✅预付费';
|
|
||||||
break;
|
|
||||||
case 'arrears':
|
|
||||||
isSubscrible = '✅已欠费';
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
isSubscrible = '✅后付费';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
isSubscrible = '✅';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
isSubscrible = '❌';
|
|
||||||
}
|
|
||||||
} 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 = "名称: " + SubscribleInformation.account_name + "\n";
|
|
||||||
SubInformation += "PO号: " + SubscribleInformation.po_number + "\n";
|
|
||||||
SubInformation += "帐单邮箱: " + SubscribleInformation.billing_email + "\n";
|
|
||||||
SubInformation += "税号: " + SubscribleInformation.tax_ids + "\n";
|
|
||||||
//使用 JavaScript 的可选链式调用来确定是否为null,避免异常控制台报错
|
|
||||||
SubInformation += "账单地址: " + (billingAddress?.line1 ? billingAddress.line1 : '') + ", " + (billingAddress?.city ? billingAddress.city : '') + ", " + (billingAddress?.state ? billingAddress.state : '') + ", " + (billingAddress?.country ? billingAddress.country : '') + ", " + (billingAddress?.postal_code ? billingAddress.postal_code : '') + "\n";
|
|
||||||
SubInformation += "商业地址: " + (businessAddress?.line1 ? businessAddress.line1 : '') + ", " + (businessAddress?.city ? businessAddress.city : '') + ", " + (businessAddress?.state ? businessAddress.state : '') + ", " + (businessAddress?.country ? businessAddress.country : '') + ", " + (businessAddress?.postal_code ? businessAddress.postal_code : '\n');
|
|
||||||
|
|
||||||
// 获取付款方法信息
|
|
||||||
response = await fetch(urlPaymentmethods, { headers });
|
|
||||||
const paymentMethodsData = await response.json();
|
|
||||||
|
|
||||||
if (paymentMethodsData.data && paymentMethodsData.data.length > 0) {
|
|
||||||
const paymentMethod = paymentMethodsData.data[0];
|
|
||||||
if (paymentMethod.type === 'card' && paymentMethod.card) {
|
|
||||||
const cardInfo = paymentMethod.card;
|
|
||||||
SubInformation += `卡种: ${cardInfo.brand} \n`; // Card Brand
|
|
||||||
SubInformation += `后4位: ${cardInfo.last4} \n`; // Card Last4
|
|
||||||
SubInformation += `卡日期: ${cardInfo.exp_month}/${cardInfo.exp_year} \n`; // Card Expiry
|
|
||||||
SubInformation += `卡国家: ${cardInfo.country}`; // Card Country
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
const title = setiddata.data[0].title;
|
|
||||||
const id1 = setiddata.data[0].id;
|
|
||||||
const name = setiddata.data[0].name;
|
|
||||||
const description = setiddata.data[0].description;
|
|
||||||
const createdTimestamp = setiddata.data[0].created;
|
|
||||||
const createdDate = new Date(createdTimestamp * 1000);
|
|
||||||
|
|
||||||
// 格式化日期
|
|
||||||
const formattedDate = `${createdDate.getFullYear()}.${(createdDate.getMonth() + 1).toString().padStart(2, '0')}.${createdDate.getDate().toString().padStart(2, '0')}`;
|
|
||||||
|
|
||||||
const timeAndZone = createdDate.toString().match(/\d{2}:\d{2}:\d{2} \w+/)[0];
|
|
||||||
|
|
||||||
if (typeof setiddata.data[1] !== 'undefined') {
|
|
||||||
const id2 = setiddata.data[1].id;
|
|
||||||
setid = `${title}\n${email}\n${id1}\n${id2}\n${name}\n${description}\n${formattedDate} ${timeAndZone}`;
|
|
||||||
} else {
|
|
||||||
setid = `${title}\n${email}\n${id1}\n${name}\n${description}\n${formattedDate} ${timeAndZone}`;
|
|
||||||
}
|
|
||||||
if (typeof setiddata.data[1] !== 'undefined') {
|
|
||||||
const id2 = setiddata.data[1].id;
|
|
||||||
setid = `人名: ${title}\n邮箱: ${email}\n组织: ${id1} ${id2}\nUserID:${name}\n\n注册时间:${formattedDate} ${timeAndZone}`;
|
|
||||||
} else {
|
|
||||||
setid = `人名: ${title}\n邮箱: ${email}\n组织: ${id1} \nUserID:${name}\n注册时间:${formattedDate} ${timeAndZone}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
errors['setid'] = error.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化模型查询结果
|
|
||||||
GPT35CheckResult = '❌';
|
|
||||||
GPT4CheckResult = '❌';
|
|
||||||
GPT432kCheckResult = '❌';
|
|
||||||
//3.5模型查询
|
|
||||||
let GPT35CheckSuccess = false; // 初始化为 false
|
|
||||||
try {
|
|
||||||
const modelsCheckResponse = await fetch(modelsCheck, { headers });
|
|
||||||
const modelsCheckData = await modelsCheckResponse.json();
|
|
||||||
GPT35CheckSuccess = GPT35CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-3.5-turbo')) ? '✅' : '❌';
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
errors['modelsCheck'] = error.message;
|
|
||||||
}
|
|
||||||
//4模型查询
|
|
||||||
try {
|
|
||||||
const modelsCheckResponse = await fetch(modelsCheck, { headers });
|
|
||||||
const modelsCheckData = await modelsCheckResponse.json();
|
|
||||||
GPT4CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4')) ? '✅' : '❌';
|
|
||||||
GPT432kCheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4-32k')) ? '✅' : '❌';
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
errors['modelsCheck'] = error.message;
|
|
||||||
}
|
|
||||||
// 是否有效查询
|
|
||||||
async function checkCompletion(apiKey, apiUrl) {
|
|
||||||
const urlCompletion = `${apiUrl}/v1/chat/completions`;
|
|
||||||
const headers = {
|
|
||||||
"Authorization": "Bearer " + apiKey,
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
};
|
|
||||||
const postBody = JSON.stringify({
|
|
||||||
"model": "gpt-3.5-turbo",
|
|
||||||
"messages": [{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Hello"
|
|
||||||
}],
|
|
||||||
"max_tokens": 5
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = await fetch(urlCompletion, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: headers,
|
|
||||||
body: postBody
|
|
||||||
});
|
|
||||||
|
|
||||||
let data = await response.json();
|
|
||||||
// 判断请求是否成功
|
|
||||||
if (response.status === 200) {
|
|
||||||
return ['✅', data.usage.total_tokens]; // 返回状态和 total_tokens
|
|
||||||
} else {
|
|
||||||
return ['❌', null];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 调用 checkCompletion 函数并获取结果
|
|
||||||
let completionCheckResult = await checkCompletion(apiKey, apiUrl);
|
|
||||||
//返回值
|
|
||||||
return [totalAmount, totalUsage, remaining, formattedDate, GPT35CheckResult, GPT4CheckResult, GPT432kCheckResult, isSubscrible, SubInformation, setid, errors, GPT35CheckSuccess, completionCheckResult];
|
|
||||||
} catch (error) {
|
|
||||||
return ["Error", null, 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 button = document.querySelector("button");
|
|
||||||
button.textContent = "加载中...";
|
|
||||||
button.disabled = true;
|
|
||||||
button.classList.add("loading")
|
|
||||||
|
|
||||||
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 GPT35CheckResult = document.createElement("td");
|
|
||||||
GPT35CheckResult.textContent = data[4];
|
|
||||||
row.appendChild(GPT35CheckResult);
|
|
||||||
|
|
||||||
let GPT4CheckResult = document.createElement("td");
|
|
||||||
GPT4CheckResult.textContent = data[5];
|
|
||||||
row.appendChild(GPT4CheckResult);
|
|
||||||
|
|
||||||
let GPT432kCheckResult = document.createElement("td");
|
|
||||||
GPT432kCheckResult.textContent = data[6];
|
|
||||||
row.appendChild(GPT432kCheckResult);
|
|
||||||
|
|
||||||
let isSubscribe = document.createElement("td");
|
|
||||||
isSubscribe.style.whiteSpace = "pre"; // 添加这一行来保留换行
|
|
||||||
isSubscribe.textContent = data[7];
|
|
||||||
row.appendChild(isSubscribe);
|
|
||||||
|
|
||||||
let SubInformation = document.createElement("td");
|
|
||||||
let SubInformationContainer = document.createElement("div");
|
|
||||||
SubInformationContainer.style.whiteSpace = "pre-wrap";
|
|
||||||
SubInformationContainer.textContent = data[8];
|
|
||||||
SubInformation.appendChild(SubInformationContainer);
|
|
||||||
row.appendChild(SubInformation);
|
|
||||||
|
|
||||||
|
|
||||||
let setidCell = document.createElement("td");
|
|
||||||
let setidCellContainer = document.createElement("div");
|
|
||||||
setidCellContainer.style.whiteSpace = "pre-wrap";
|
|
||||||
setidCellContainer.textContent = data[9];
|
|
||||||
setidCell.appendChild(setidCellContainer);
|
|
||||||
// setidCell.style.whiteSpace = "pre"; // 添加这一行来保留换行
|
|
||||||
// setidCell.textContent = data[9];
|
|
||||||
row.appendChild(setidCell);
|
|
||||||
|
|
||||||
|
|
||||||
// 是否有效列
|
|
||||||
let completionCheckResultCell = document.createElement("td");
|
|
||||||
completionCheckResultCell.innerHTML = `<span style="font-size:24px">${data[12][0]}</span><br>消耗${data[12][1]} tokens`; // 使用 innerHTML 添加两行内容
|
|
||||||
row.appendChild(completionCheckResultCell);
|
|
||||||
// let isSubscriptionValid = document.createElement("td");
|
|
||||||
// isSubscriptionValid.textContent = data[4] === '✅' ? '✅' : '❌'; // 使用 GPT35CheckResult 的值进行判断
|
|
||||||
|
|
||||||
// row.appendChild(completionCheckResultCell);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
|
|
||||||
if (i === apiKeys.length - 1) {
|
|
||||||
queriedApiKeys = [];
|
|
||||||
}
|
|
||||||
serialNumber++; // 增加序列号
|
|
||||||
h2.style.display = 'block';
|
|
||||||
table.style.display = 'table';
|
|
||||||
|
|
||||||
button.textContent = "查询";
|
|
||||||
button.disabled = false;
|
|
||||||
button.classList.remove("loading")
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
</body>
|
||||||
|
|
||||||
|
357
static/css.css
Normal file
357
static/css.css
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
:root {
|
||||||
|
--color-primary: #2EA7E0;
|
||||||
|
--color-primary-dark: #2482AD;
|
||||||
|
--color-primary-alpha: #75C2E6;
|
||||||
|
--body-color: #ffffff;
|
||||||
|
--body-bg: #000000;
|
||||||
|
--border-color: #f1ebeb00;
|
||||||
|
}
|
||||||
|
|
||||||
|
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-alpha);
|
||||||
|
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: 2px; */
|
||||||
|
/* border-top: 1px solid var(--border-color); */
|
||||||
|
text-align: center;
|
||||||
|
/* opacity: .5; */
|
||||||
|
/* position: fixed; */
|
||||||
|
/* bottom: 0; */
|
||||||
|
/* left: 0; */
|
||||||
|
margin-top: 20px;
|
||||||
|
/* width: 100%; */
|
||||||
|
/* background-color:#212121; */
|
||||||
|
line-height: unset !important;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer .p {
|
||||||
|
display: flex;
|
||||||
|
margin-block-start: 0 !important;
|
||||||
|
margin-block-end: 0 !important;
|
||||||
|
padding: 0;
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
/* 调整为适当的大小 */
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
456
static/js.js
Normal file
456
static/js.js
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
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 modelsCheck = `${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';
|
||||||
|
const urlPaymentmethods = `${apiUrl}/v1/dashboard/billing/payment_methods`;
|
||||||
|
const urlRatelimits = `${apiUrl}/v1/dashboard/rate_limits`;
|
||||||
|
|
||||||
|
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();
|
||||||
|
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')}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
totalAmount = subscriptionData.system_hard_limit_usd;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
if (subscriptionData.plan.id.includes('payg')) {
|
||||||
|
switch (subscriptionData.billing_mechanism) {
|
||||||
|
case 'advance':
|
||||||
|
isSubscrible = '✅预付费';
|
||||||
|
break;
|
||||||
|
case 'arrears':
|
||||||
|
isSubscrible = '✅已欠费';
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
isSubscrible = '✅后付费';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
isSubscrible = '✅';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isSubscrible = '❌';
|
||||||
|
}
|
||||||
|
} 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 = "名称: " + SubscribleInformation.account_name + "\n";
|
||||||
|
SubInformation += "PO号: " + SubscribleInformation.po_number + "\n";
|
||||||
|
SubInformation += "帐单邮箱: " + SubscribleInformation.billing_email + "\n";
|
||||||
|
SubInformation += "税号: " + SubscribleInformation.tax_ids + "\n";
|
||||||
|
//使用 JavaScript 的可选链式调用来确定是否为null,避免异常控制台报错
|
||||||
|
SubInformation += "账单地址: " + (billingAddress?.line1 ? billingAddress.line1 : '') + ", " + (billingAddress?.city ? billingAddress.city : '') + ", " + (billingAddress?.state ? billingAddress.state : '') + ", " + (billingAddress?.country ? billingAddress.country : '') + ", " + (billingAddress?.postal_code ? billingAddress.postal_code : '') + "\n";
|
||||||
|
SubInformation += "商业地址: " + (businessAddress?.line1 ? businessAddress.line1 : '') + ", " + (businessAddress?.city ? businessAddress.city : '') + ", " + (businessAddress?.state ? businessAddress.state : '') + ", " + (businessAddress?.country ? businessAddress.country : '') + ", " + (businessAddress?.postal_code ? businessAddress.postal_code : '\n');
|
||||||
|
|
||||||
|
// 获取付款方法信息
|
||||||
|
response = await fetch(urlPaymentmethods, { headers });
|
||||||
|
const paymentMethodsData = await response.json();
|
||||||
|
|
||||||
|
if (paymentMethodsData.data && paymentMethodsData.data.length > 0) {
|
||||||
|
paymentMethodsData.data.forEach((paymentMethod, index) => {
|
||||||
|
if (paymentMethod.type === 'card' && paymentMethod.card) {
|
||||||
|
const cardInfo = paymentMethod.card;
|
||||||
|
SubInformation += `\n\n付款方式 ${index + 1}: \n`;
|
||||||
|
SubInformation += paymentMethod.is_default ? "默认卡:\n" : "非默认卡:\n"; // Add default label
|
||||||
|
SubInformation += `卡种: ${cardInfo.brand} \n`; // Card Brand
|
||||||
|
SubInformation += `后4位: ${cardInfo.last4} \n`; // Card Last4
|
||||||
|
SubInformation += `卡日期: ${cardInfo.exp_month}/${cardInfo.exp_year} \n`; // Card Expiry
|
||||||
|
SubInformation += `卡国家: ${cardInfo.country}`; // Card Country
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
const title = setiddata.data[0].title;
|
||||||
|
const id1 = setiddata.data[0].id;
|
||||||
|
const name = setiddata.data[0].name;
|
||||||
|
const description = setiddata.data[0].description;
|
||||||
|
const createdTimestamp = setiddata.data[0].created;
|
||||||
|
const createdDate = new Date(createdTimestamp * 1000);
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formattedDate = `${createdDate.getFullYear()}.${(createdDate.getMonth() + 1).toString().padStart(2, '0')}.${createdDate.getDate().toString().padStart(2, '0')}`;
|
||||||
|
|
||||||
|
const timeAndZone = createdDate.toString().match(/\d{2}:\d{2}:\d{2} \w+/)[0];
|
||||||
|
|
||||||
|
if (typeof setiddata.data[1] !== 'undefined') {
|
||||||
|
const id2 = setiddata.data[1].id;
|
||||||
|
setid = `${title}\n${email}\n${id1}\n${id2}\n${name}\n${description}\n${formattedDate} ${timeAndZone}`;
|
||||||
|
} else {
|
||||||
|
setid = `${title}\n${email}\n${id1}\n${name}\n${description}\n${formattedDate} ${timeAndZone}`;
|
||||||
|
}
|
||||||
|
if (typeof setiddata.data[1] !== 'undefined') {
|
||||||
|
const id2 = setiddata.data[1].id;
|
||||||
|
setid = `人名: ${title}\n邮箱: ${email}\n组织: ${id1} ${id2}\nUserID:${name}\n\n注册时间:${formattedDate} ${timeAndZone}`;
|
||||||
|
} else {
|
||||||
|
setid = `人名: ${title}\n邮箱: ${email}\n组织: ${id1} \nUserID:${name}\n注册时间:${formattedDate} ${timeAndZone}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
errors['setid'] = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取速率
|
||||||
|
let rateLimits;
|
||||||
|
try {
|
||||||
|
const response = await fetch(urlRatelimits, { headers });
|
||||||
|
const rateLimitsData = await response.json();
|
||||||
|
rateLimits = {
|
||||||
|
'gpt-3.5-turbo': rateLimitsData['gpt-3.5-turbo'],
|
||||||
|
'gpt-3.5-turbo-16k': rateLimitsData['gpt-3.5-turbo-16k'],
|
||||||
|
'gpt-4': rateLimitsData['gpt-4'],
|
||||||
|
'gpt-4-32k': rateLimitsData['gpt-4-32k']
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
errors['rateLimits'] = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 初始化模型查询结果
|
||||||
|
GPT35CheckResult = '❌';
|
||||||
|
GPT4CheckResult = '❌';
|
||||||
|
GPT432kCheckResult = '❌';
|
||||||
|
//3.5模型查询
|
||||||
|
let GPT35CheckSuccess = false; // 初始化为 false
|
||||||
|
try {
|
||||||
|
const modelsCheckResponse = await fetch(modelsCheck, { headers });
|
||||||
|
const modelsCheckData = await modelsCheckResponse.json();
|
||||||
|
GPT35CheckSuccess = GPT35CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-3.5-turbo')) ? '✅' : '❌';
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
errors['modelsCheck'] = error.message;
|
||||||
|
}
|
||||||
|
//4模型查询
|
||||||
|
try {
|
||||||
|
const modelsCheckResponse = await fetch(modelsCheck, { headers });
|
||||||
|
const modelsCheckData = await modelsCheckResponse.json();
|
||||||
|
GPT4CheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4')) ? '✅' : '❌';
|
||||||
|
GPT432kCheckResult = Array.isArray(modelsCheckData.data) && modelsCheckData.data.some(item => item.id.includes('gpt-4-32k')) ? '✅' : '❌';
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
errors['modelsCheck'] = error.message;
|
||||||
|
}
|
||||||
|
// 是否有效查询
|
||||||
|
async function checkCompletion(apiKey, apiUrl) {
|
||||||
|
const urlCompletion = `${apiUrl}/v1/chat/completions`;
|
||||||
|
const headers = {
|
||||||
|
"Authorization": "Bearer " + apiKey,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
};
|
||||||
|
const postBody = JSON.stringify({
|
||||||
|
"model": "gpt-3.5-turbo",
|
||||||
|
"messages": [{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Hello"
|
||||||
|
}],
|
||||||
|
"max_tokens": 5
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await fetch(urlCompletion, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: postBody
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = await response.json();
|
||||||
|
// 判断请求是否成功
|
||||||
|
if (response.status === 200) {
|
||||||
|
return ['✅', data.usage.total_tokens]; // 返回状态和 total_tokens
|
||||||
|
} else {
|
||||||
|
return ['❌', null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 调用 checkCompletion 函数并获取结果
|
||||||
|
let completionCheckResult = await checkCompletion(apiKey, apiUrl);
|
||||||
|
//返回值
|
||||||
|
return [totalAmount, totalUsage, remaining, formattedDate, GPT35CheckResult, GPT4CheckResult, GPT432kCheckResult, isSubscrible, SubInformation, setid, errors, GPT35CheckSuccess, completionCheckResult, rateLimits];
|
||||||
|
} catch (error) {
|
||||||
|
return ["Error", null, null, 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 button = document.querySelector("button");
|
||||||
|
button.textContent = "加载中...";
|
||||||
|
button.disabled = true;
|
||||||
|
button.classList.add("loading")
|
||||||
|
|
||||||
|
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 GPT35CheckResult = document.createElement("td");
|
||||||
|
GPT35CheckResult.textContent = data[4];
|
||||||
|
row.appendChild(GPT35CheckResult);
|
||||||
|
|
||||||
|
let GPT4CheckResult = document.createElement("td");
|
||||||
|
GPT4CheckResult.textContent = data[5];
|
||||||
|
row.appendChild(GPT4CheckResult);
|
||||||
|
|
||||||
|
let GPT432kCheckResult = document.createElement("td");
|
||||||
|
GPT432kCheckResult.textContent = data[6];
|
||||||
|
row.appendChild(GPT432kCheckResult);
|
||||||
|
|
||||||
|
let isSubscribe = document.createElement("td");
|
||||||
|
isSubscribe.style.whiteSpace = "pre"; // 添加这一行来保留换行
|
||||||
|
isSubscribe.textContent = data[7];
|
||||||
|
row.appendChild(isSubscribe);
|
||||||
|
|
||||||
|
let SubInformation = document.createElement("td");
|
||||||
|
let SubInformationContainer = document.createElement("div");
|
||||||
|
SubInformationContainer.style.whiteSpace = "pre-wrap";
|
||||||
|
SubInformationContainer.textContent = data[8];
|
||||||
|
SubInformation.appendChild(SubInformationContainer);
|
||||||
|
row.appendChild(SubInformation);
|
||||||
|
|
||||||
|
|
||||||
|
let setidCell = document.createElement("td");
|
||||||
|
let setidCellContainer = document.createElement("div");
|
||||||
|
setidCellContainer.style.whiteSpace = "pre-wrap";
|
||||||
|
setidCellContainer.textContent = data[9];
|
||||||
|
setidCell.appendChild(setidCellContainer);
|
||||||
|
row.appendChild(setidCell);
|
||||||
|
|
||||||
|
let rateLimitsDataCell = document.createElement("td");
|
||||||
|
let rateLimitsDataContainer = document.createElement("div");
|
||||||
|
rateLimitsDataContainer.style.whiteSpace = "pre-wrap";
|
||||||
|
if (data[13]) { // checking if rateLimits data exists
|
||||||
|
let rateLimitsData = data[13];
|
||||||
|
let models = ['gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4', 'gpt-4-32k'];
|
||||||
|
for (let model of models) {
|
||||||
|
if (rateLimitsData[model]) {
|
||||||
|
rateLimitsDataContainer.textContent += `${model}:\n\tRPM: ${rateLimitsData[model].max_requests_per_1_minute}\n\tTPM: ${rateLimitsData[model].max_tokens_per_1_minute}\n\n`;
|
||||||
|
} else {
|
||||||
|
rateLimitsDataContainer.textContent += model + ": ❌\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rateLimitsDataCell.appendChild(rateLimitsDataContainer);
|
||||||
|
row.appendChild(rateLimitsDataCell);
|
||||||
|
|
||||||
|
|
||||||
|
// 是否有效列
|
||||||
|
let completionCheckResultCell = document.createElement("td");
|
||||||
|
completionCheckResultCell.innerHTML = `<span style="font-size:24px">${data[12][0]}</span><br>消耗${data[12][1]} tokens`; // 使用 innerHTML 添加两行内容
|
||||||
|
row.appendChild(completionCheckResultCell);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
tableBody.appendChild(row);
|
||||||
|
|
||||||
|
if (i === apiKeys.length - 1) {
|
||||||
|
queriedApiKeys = [];
|
||||||
|
}
|
||||||
|
serialNumber++; // 增加序列号
|
||||||
|
h2.style.display = 'block';
|
||||||
|
table.style.display = 'table';
|
||||||
|
|
||||||
|
button.textContent = "查询";
|
||||||
|
button.disabled = false;
|
||||||
|
button.classList.remove("loading")
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user