mirror of
https://github.com/woodchen-ink/openai-billing-query.git
synced 2025-07-18 14:01:59 +08:00
Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
55c266975c | ||
|
e5efcbd3f3 | ||
|
e17d22f6d7 | ||
|
10473ef9b5 | ||
65218d4d78 | |||
b512ae25de | |||
|
31f05dbda1 | ||
|
75a66371f0 | ||
|
896fe279cd | ||
|
1bdc566c45 | ||
|
63ff54c29d | ||
|
12885d66a1 | ||
|
82af4c8351 | ||
|
063e12e55a | ||
|
4c65ee7a71 | ||
|
853855d819 | ||
|
c93807a2f7 | ||
d63b02957a | |||
0eb792da0a | |||
|
01be618870 | ||
|
94fe780b48 | ||
|
598ee4864c | ||
|
8878b8fdc3 | ||
|
befd543d57 | ||
|
2e90adc476 | ||
6df15c2796 | |||
4cfdee7ca5 | |||
|
09c40f92a5 | ||
|
9c70af453a | ||
da6381d71f | |||
aecc0946d0 | |||
e184b1e3a9 | |||
72c754a825 | |||
|
5770c3f939 | ||
81c6c76bb5 | |||
5334b69624 | |||
8449465349 | |||
80619cb480 | |||
|
b6e65dc65a | ||
7c931a6d31 | |||
e3abc00e1b | |||
918213a64e | |||
08b6a4186b | |||
ce51f6ffca | |||
a478392113 | |||
68725fa6cc | |||
387fd638f8 | |||
596978d35f | |||
|
352f3fd129 | ||
|
2513055b20 | ||
e0c7bbe9bc | |||
|
c90b571749 | ||
407f53647b | |||
|
6ccf8048f3 | ||
2608b14b81 | |||
|
99a615d707 | ||
dbc489523f | |||
c6bfa059d0 | |||
|
d418a7be85 | ||
5eebcc886f | |||
a0e750ee56 | |||
|
bb4057a058 | ||
f1a5f4f01f | |||
45630a63e1 | |||
|
497eaa45b1 | ||
b720564fdc | |||
f33a1e538d | |||
|
4a7edfc05a | ||
c35af318d0 | |||
44b11534a7 | |||
cc1dcc6fe3 | |||
|
975c558a3d | ||
c1d8d1dc3e | |||
|
712c500681 | ||
232b613aee | |||
7f936e54e4 | |||
d1059d3b6e | |||
1aa9aeebca | |||
0db7de1f51 | |||
d8a50578bc | |||
d339ef59f7 | |||
99472ac3fd | |||
|
82a39a0f94 | ||
ff1d65de75 | |||
3252fda1fd | |||
|
598ca998c0 | ||
ca1477cfab | |||
|
0cefc281d7 | ||
17558faf49 | |||
5ccf69839e | |||
|
93af0a70f1 | ||
42aed48ee1 | |||
67a5c47c81 | |||
|
09178c011f | ||
09f405dfd1 | |||
97adf0cc04 | |||
a76ff5b674 | |||
c1ab8b7e70 | |||
9c36637cef | |||
bf7b7b1518 | |||
82b68846f3 | |||
77a338ba14 | |||
294a56fffa | |||
|
e6c0c60f85 | ||
|
bb9f6afc3c | ||
4d153c4d50 | |||
460d92cadd | |||
f88b53b325 | |||
e7aaa8de96 | |||
241ca1b67f | |||
9d9f6d4dcd | |||
|
c584807a68 | ||
7e6b926887 | |||
efff200e23 | |||
2a9d172829 | |||
f2e9b2dd2d | |||
26f74fc1e3 | |||
|
32029acfb6 | ||
fac24fb963 | |||
00e4dedd17 | |||
|
2e34e5a891 | ||
|
187295fc93 |
51
.github/workflows/jekyll-gh-pages.yml
vendored
Normal file
51
.github/workflows/jekyll-gh-pages.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
||||
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["mdui2"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./
|
||||
destination: ./_site
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.vscode/settings.json
|
52
README.md
52
README.md
@ -1,26 +1,56 @@
|
||||
# openai-billing-query
|
||||
批量可视化查询openai(chatgpt)余额,支持显示总量,已使用,剩余量,已用比例,到期时间,GPT-4,是否绑卡
|
||||
|
||||
star,please.
|
||||
|
||||
[](https://edgeone.ai/pages/new?repository-url=https%3A%2F%2Fgithub.com%2Fwoodchen-ink%2Fopenai-billing-query)
|
||||
|
||||
|
||||
|
||||
## 在线访问(使用Cloudflare Pages部署)
|
||||
新版本:https://openai-billing-query.czl.net/
|
||||
daisyUI版本:https://daisyui.o-b.pages.dev/
|
||||
|
||||
## [English](README_EN.md)
|
||||
|
||||
# 查询示例图
|
||||

|
||||
## 最新示意图(2023.08.28)
|
||||
|
||||
# 支持自定义反代接口
|
||||

|
||||
|
||||

|
||||
|
||||
## SESS ID获取方法
|
||||
|
||||
[请见我的个人博客,提供视频教程](https://woodchen.ink/archives/how-to-query-the-balance-of-openai-in-batches-expires-gpt4-whether-to-tie-cards-1ov1aw)
|
||||
|
||||
## 支持自定义反代接口
|
||||
在第361行添加自己的接口代码
|
||||
|
||||
``` html
|
||||
<option value="反代网址">【自定义名称】自定义名称</option>
|
||||
```
|
||||
## 反代代码示例
|
||||

|
||||
|
||||
# 怎么部署
|
||||
什么?这还用问?下载index.html直接打开就行,除了背景图片,没有任何外部资源。
|
||||
## 怎么部署
|
||||
下载index.html直接打开就行,除了背景图片,没有任何外部资源。
|
||||
|
||||
# 开发过程
|
||||
首先感谢Github上开源的几个查询代码,是基于他们的项目使用GPT4进行改的,全程我只手动改了几个css。
|
||||
|
||||
# 广告
|
||||
个人博客:https://woodchen.ink
|
||||
## 贡献列表
|
||||
|
||||
| 人员 | 贡献内容 |
|
||||
| ---- | ---- |
|
||||
| [qiyue](https://github.com/qiyue-rgb) | 技术协助 |
|
||||
| 🙊 | cloudflare反代地址 |
|
||||
|
||||
## CDN acceleration and security protection for this project are sponsored by Tencent EdgeOne.
|
||||
|
||||
[Best Asian CDN, Edge, and Secure Solutions - Tencent EdgeOne](https://edgeone.ai/?from=github)
|
||||
[](https://edgeone.ai/?from=github)
|
||||
|
||||
## 广告
|
||||
- [CZL Chat](https://chat.czl.net),稳定商业版AI服务。
|
||||
- [CZLOapi](https://oapi.czl.net),OPENAI代理服务,无需翻墙。
|
||||
- 个人博客:https://woodchen.ink
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://api.star-history.com/svg?repos=woodchen-ink/openai-billing-query&type=Date)
|
||||
|
@ -1,8 +1,14 @@
|
||||
# openai-billing-query
|
||||
Batch visualization query for openai (chatgpt) balance, supporting display of total amount, used amount, remaining amount, usage ratio, expiration time, GPT-4, and whether it is bound with a card.
|
||||
|
||||
# After the update on July 22nd, you need to log in to your account once. Use F12 to view the session code and use the session code for queries.
|
||||
|
||||
After the update on July 22nd, the key verification rule has been removed. You need to use the session code for queries. The key can only be used to query the total amount, binding of cards, GPT4, and organization ID.
|
||||
|
||||

|
||||
|
||||
# Query example image
|
||||

|
||||

|
||||
|
||||
# Supports custom reverse proxy interfaces
|
||||
Add your own interface code in line 361.
|
||||
|
107
get_sess.html
Normal file
107
get_sess.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="mdui-theme-light" lang="zh-cmn-Hans">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>通过access_token来获取sess</title>
|
||||
<meta name="description" content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效">
|
||||
<link rel="stylesheet" href="./static/css-1.css" type="text/css" />
|
||||
<link rel="stylesheet" href="./static/mdui/mdui.css">
|
||||
<script src="./static/mdui/mdui.global.js"></script>
|
||||
|
||||
<script>mdui.setColorScheme('#0d2d44');</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<mdui-layout>
|
||||
<mdui-layout-main>
|
||||
<div class="mdui-main-container">
|
||||
<h2>查询结果</h2>
|
||||
<div class="mdui-table">
|
||||
<table id="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>Access_Token</th>
|
||||
<th>user id</th>
|
||||
<th>email</th>
|
||||
<th>name</th>
|
||||
<th>phone number</th>
|
||||
<th>注册时间</th>
|
||||
<th>sess id</th>
|
||||
<th>sess id生成时间</th>
|
||||
<th>org id</th>
|
||||
<th>IP Country</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 表格内容 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-layout-main>
|
||||
|
||||
<mdui-top-app-bar>
|
||||
<mdui-button-icon icon="menu" close-on-overlay-click id="toggle-button"
|
||||
style="color: white;"></mdui-button-icon>
|
||||
<mdui-top-app-bar-title
|
||||
style="text-align: center;color:white;">通过access_token来获取sess</mdui-top-app-bar-title>
|
||||
</mdui-top-app-bar>
|
||||
|
||||
<mdui-navigation-drawer open class="left-drawer" close-on-overlay-click>
|
||||
<div class="left-drawer-main">
|
||||
<h3>输入 API KEY</h3>
|
||||
<p>本站不保存 KEY 信息,查询后请自行保存</p>
|
||||
<mdui-text-field id="api-key-input" placeholder="请输入access_token,多个请换行" label="access_token"
|
||||
rows="6"></mdui-text-field>
|
||||
|
||||
<mdui-select id="api-url-select" icon="airline_stops" label="查询线路" value="https://api.openai.com"
|
||||
onchange="toggleCustomUrlInput()">
|
||||
<mdui-menu-item value="https://api.openai.com">[官网线路]api.openai.com</mdui-menu-item>
|
||||
<mdui-menu-item value="https://oapi.czl.net">[CZLoapi线路]oapi.czl.net</mdui-menu-item>
|
||||
<mdui-menu-item value="https://openai.996.lat">[群友CF反代]openai.996.lat</mdui-menu-item>
|
||||
<mdui-menu-item value="https://gateway.ai.cloudflare.com/v1/feedd0aa8abd6875052d86a94f1baf83/test/openai">CF Gateway</mdui-menu-item>
|
||||
<mdui-menu-item value="custom">[已前置https]自定义</mdui-menu-item>
|
||||
</mdui-select>
|
||||
|
||||
<mdui-text-field type="text" id="custom-url-input" placeholder="输入自定义API,填域名即可,无需https://"
|
||||
class="hidden"></mdui-text-field>
|
||||
|
||||
<div style="height:2rem"></div>
|
||||
|
||||
|
||||
<mdui-button full-width id="query-button" icon="search" onclick="sendRequest()">
|
||||
查询
|
||||
</mdui-button>
|
||||
|
||||
|
||||
<!-- 下半部分 -->
|
||||
<h4>页面列表</h4>
|
||||
<mdui-menu style="width:100%;margin-top:20px;">
|
||||
<mdui-menu-item icon="search" href="index.html">查API信息</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess.html">通过access token获取sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess2.html">通过PandoraNext获取sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="refresh_see.html">刷新Platform相关信息刷新sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="build" href="https://woodchen.ink/1266.html"
|
||||
target="_blank">手动获取sess的方法</mdui-menu-item>
|
||||
</mdui-menu>
|
||||
|
||||
<div style="margin-top:20px;">
|
||||
<mdui-chip elevated icon="code"> 本网页由<a href="https://woodchen.ink"
|
||||
target="_blank">woodchen</a>开源于<a
|
||||
href="https://github.com/woodchen-ink/openai-billing-query" target="_blank">Github</a>
|
||||
</mdui-chip>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-navigation-drawer>
|
||||
|
||||
|
||||
|
||||
</mdui-layout>
|
||||
<script src="./static/getsess.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
158
get_sess2.html
Normal file
158
get_sess2.html
Normal file
@ -0,0 +1,158 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="mdui-theme-light" lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>通过PandoraNext来获取Sensitive Id</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效"
|
||||
/>
|
||||
<link rel="stylesheet" href="./static/css-1.css" type="text/css" />
|
||||
<link rel="stylesheet" href="./static/mdui/mdui.css" />
|
||||
<script src="./static/mdui/mdui.global.js"></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.3/axios.min.js"
|
||||
integrity="sha512-JWQFV6OCC2o2x8x46YrEeFEQtzoNV++r9im8O8stv91YwHNykzIS2TbvAlFdeH0GVlpnyd79W0ZGmffcRi++Bw=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
|
||||
<script>
|
||||
mdui.setColorScheme("#0d2d44");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
th,
|
||||
td {
|
||||
cursor: pointer; /* 添加鼠标指针样式 */
|
||||
white-space: normal; /* 设置为normal使内容自动换行 */
|
||||
word-wrap: break-word; /* 设置为break-word以确保长单词/链接被截断换行 */
|
||||
max-width: 300px;
|
||||
}
|
||||
/* 复制按钮样式 */
|
||||
.copy-button {
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
margin-top: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
<mdui-layout>
|
||||
<mdui-layout-main>
|
||||
<div class="mdui-main-container">
|
||||
<div style="display: flex">
|
||||
<h2 style="flex: 1">查询结果</h2>
|
||||
<button class="copy-button" onclick="copySess()">复制sess</button>
|
||||
<button class="copy-button" onclick="copyTable()">
|
||||
复制全部内容
|
||||
</button>
|
||||
</div>
|
||||
<div class="mdui-table">
|
||||
<table id="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>邮箱账户</th>
|
||||
<th>手机号</th>
|
||||
<th>Sensitive ID</th>
|
||||
<th>Refresh Token</th>
|
||||
<th>Access Token</th>
|
||||
<th>Sensitive ID创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="result-tbody" style="max-width: 100%">
|
||||
<!-- 表格内容 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-layout-main>
|
||||
|
||||
<mdui-top-app-bar>
|
||||
<mdui-button-icon
|
||||
icon="menu"
|
||||
close-on-overlay-click
|
||||
id="toggle-button"
|
||||
style="color: white"
|
||||
></mdui-button-icon>
|
||||
<mdui-top-app-bar-title style="text-align: center; color: white"
|
||||
>通过PandoraNext来获取Sensitive Id</mdui-top-app-bar-title
|
||||
>
|
||||
</mdui-top-app-bar>
|
||||
|
||||
<mdui-navigation-drawer open class="left-drawer" close-on-overlay-click>
|
||||
<div class="left-drawer-main">
|
||||
<h3>输入 API KEY</h3>
|
||||
<p>本站不保存 KEY 信息,查询后请自行保存</p>
|
||||
<mdui-text-field
|
||||
id="api-key-input"
|
||||
placeholder="请输入账号密码,格式为'账号|密码|MFA验证码',多个请换行"
|
||||
label="username|password|MFA"
|
||||
rows="6"
|
||||
></mdui-text-field>
|
||||
|
||||
<mdui-text-field
|
||||
type="text"
|
||||
id="custom-url-input"
|
||||
placeholder="输入PandoraNext的API地址和前缀"
|
||||
></mdui-text-field>
|
||||
|
||||
<div style="height: 2rem"></div>
|
||||
|
||||
<mdui-button
|
||||
full-width
|
||||
id="query-button"
|
||||
icon="search"
|
||||
onclick="sendRequest()"
|
||||
>
|
||||
查询
|
||||
</mdui-button>
|
||||
|
||||
<!-- 下半部分 -->
|
||||
<h4>页面列表</h4>
|
||||
<mdui-menu style="width: 100%; margin-top: 20px">
|
||||
<mdui-menu-item icon="search" href="index.html"
|
||||
>查API信息</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess.html"
|
||||
>通过access token获取sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess2.html"
|
||||
>通过PandoraNext获取sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="refresh_see.html"
|
||||
>刷新Platform相关信息刷新sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item
|
||||
icon="build"
|
||||
href="https://woodchen.ink/1266.html"
|
||||
target="_blank"
|
||||
>手动获取Sensitive Id的方法</mdui-menu-item
|
||||
>
|
||||
</mdui-menu>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<mdui-chip elevated icon="code">
|
||||
本网页由<a href="https://woodchen.ink" target="_blank">woodchen</a
|
||||
>开源于<a
|
||||
href="https://github.com/woodchen-ink/openai-billing-query"
|
||||
target="_blank"
|
||||
>Github</a
|
||||
>
|
||||
</mdui-chip>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-navigation-drawer>
|
||||
</mdui-layout>
|
||||
<script src="./static/getsess2.js"></script>
|
||||
</body>
|
||||
</html>
|
711
index.html
711
index.html
@ -1,649 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html class="mdui-theme-light" lang="zh-cmn-Hans">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CZL Chat-查询OpenAIAPI余额</title>
|
||||
<meta name="description" content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否绑卡">
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #1E3B7A;
|
||||
--color-primary-dark: #1E3B7A;
|
||||
--color-primary-alpha: #01847f;
|
||||
--body-color: #dadada;
|
||||
--body-bg: #000000;
|
||||
--border-color: #f8f8f800;
|
||||
}
|
||||
<title>OpenAI API 信息查询</title>
|
||||
<meta name="description" content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效">
|
||||
<link rel="stylesheet" href="./static/css-1.css" type="text/css" />
|
||||
<link rel="stylesheet" href="./static/mdui/mdui.css">
|
||||
<script src="./static/mdui/mdui.global.js"></script>
|
||||
|
||||
body {
|
||||
background: url('https://img.cdn.czl.net/i/2023/05/23/pjbczr.webp') no-repeat center center fixed;
|
||||
/* 自定义背景图 */
|
||||
background-size: cover;
|
||||
width: 90%;
|
||||
max-width: 1280px;
|
||||
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: 14px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 下面的代码定义了结果表格表头的样式 */
|
||||
#result-table th {
|
||||
background-color: var(--color-primary);
|
||||
color: #ffffff;
|
||||
font-weight: 400;
|
||||
height: 20px;
|
||||
padding: 10px 25px;
|
||||
text-align: left;
|
||||
/* border: 1px solid #dcdcdc; */
|
||||
}
|
||||
|
||||
/* 下面的代码定义了结果表格数据单元格的样式 */
|
||||
#result-table td {
|
||||
height: 20px;
|
||||
text-align: left;
|
||||
padding: 10px 25px;
|
||||
/* 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: center;
|
||||
padding: 10px 25px;
|
||||
/* 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;
|
||||
}
|
||||
</style>
|
||||
<script>mdui.setColorScheme('#0d2d44');</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1 class="text-3xl font-semibold text-center mb-8 text-gradient">查询 OpenAI API 余额</h1>
|
||||
</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,必须包含 sk-,多个可直接粘贴"></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">
|
||||
<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 协议" />
|
||||
</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">
|
||||
<mdui-layout>
|
||||
<mdui-layout-main>
|
||||
<div class="mdui-main-container">
|
||||
<h2>查询结果</h2>
|
||||
<div class="mdui-table">
|
||||
<table id="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:35px">序号</th>
|
||||
<th>序号</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-4</th>
|
||||
<th style="width: 50px">绑卡</th>
|
||||
<th>组织ID</th>
|
||||
<th>总额度</th>
|
||||
<th>已使用</th>
|
||||
<th>剩余量</th>
|
||||
<th id="progressbar-header">已用比例</th>
|
||||
<th>到期时间</th>
|
||||
<th>模型[最高]</th>
|
||||
<th>绑卡</th>
|
||||
<th id="subinfo-header">绑卡信息</th>
|
||||
<th id="setid-header">组织信息</th>
|
||||
<th>速率[RPM,TPM-RPD]</th>
|
||||
<th>有效</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tbody>
|
||||
<!-- 表格内容 -->
|
||||
</tbody>
|
||||
</table>
|
||||
<footer>
|
||||
<a href="https://chat.czl.net" style="color: white;" target="_blank">CZL Chat</a>
|
||||
<a href="https://store0.czl.net" style="color: white;" target="_blank">AI Store</a>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-layout-main>
|
||||
|
||||
</footer>
|
||||
<mdui-top-app-bar>
|
||||
<mdui-button-icon icon="menu" close-on-overlay-click id="toggle-button" style="color: white;"></mdui-button-icon>
|
||||
<mdui-top-app-bar-title style="text-align: center;color:white;">OpenAI API 信息查询</mdui-top-app-bar-title>
|
||||
</mdui-top-app-bar>
|
||||
|
||||
<mdui-navigation-drawer open class="left-drawer" close-on-overlay-click>
|
||||
<div class="left-drawer-main">
|
||||
<h3>输入 API KEY</h3>
|
||||
<p>本站不保存 KEY 信息,查询后请自行保存</p>
|
||||
<mdui-text-field id="api-key-input" placeholder="请输入 API-KEY或sessID,多个可直接粘贴" label="API-KEY或sessID"
|
||||
rows="6"></mdui-text-field>
|
||||
|
||||
<mdui-select id="api-url-select" icon="airline_stops" label="查询线路" value="https://api.openai.com"
|
||||
onchange="toggleCustomUrlInput()">
|
||||
<mdui-menu-item value="https://api.openai.com">[官网线路]api.openai.com</mdui-menu-item>
|
||||
<mdui-menu-item value="custom">[已前置https]自定义</mdui-menu-item>
|
||||
|
||||
</mdui-select>
|
||||
|
||||
<mdui-text-field type="text" id="custom-url-input" placeholder="如无前缀,自动添加https://"
|
||||
class="hidden"></mdui-text-field>
|
||||
|
||||
<div style="display: flex;">
|
||||
<div id="progressbar-toggle" style="flex-grow: 1;">
|
||||
<mdui-checkbox onchange="toggleProgressBar()">已用比例</mdui-checkbox>
|
||||
</div>
|
||||
<div id="subinfo-toggle" style="flex-grow: 1;">
|
||||
<mdui-checkbox onchange="toggleSubInfo()">绑卡信息</mdui-checkbox>
|
||||
</div>
|
||||
<div id="setid-toggle" style="flex-grow: 1;">
|
||||
<mdui-checkbox onchange="toggleSetidInfo()">组织信息</mdui-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mdui-button full-width id="query-button" icon="search" onclick="sendRequest()">
|
||||
查询
|
||||
</mdui-button>
|
||||
|
||||
|
||||
<script>
|
||||
let queriedApiKeys = [];
|
||||
let serialNumber = 1;
|
||||
<!-- 下半部分 -->
|
||||
<h4>页面列表</h4>
|
||||
<mdui-menu style="width:100%;margin-top:20px;">
|
||||
<mdui-menu-item icon="search" href="index.html">查API信息</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess.html">通过access token获取sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess2.html">通过PandoraNext获取sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="vpn_key" href="refresh_see.html">刷新Platform相关信息刷新sess</mdui-menu-item>
|
||||
<mdui-menu-item icon="build" href="https://woodchen.ink/archives/how-to-query-the-balance-of-openai-in-batches-expires-gpt4-whether-to-tie-cards-1ov1aw"
|
||||
target="_blank">手动获取sess的方法</mdui-menu-item>
|
||||
</mdui-menu>
|
||||
|
||||
async function checkBilling(apiKey, apiUrl) {
|
||||
// 计算起始日期和结束日期,当前为 90 天,最大不超过100天
|
||||
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);
|
||||
|
||||
// 设置API请求URL和请求头
|
||||
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'; //查组织id
|
||||
|
||||
try {
|
||||
// 获取API限额
|
||||
let response = await fetch(urlSubscription, { headers });
|
||||
if (!response.ok) {
|
||||
console.log("APIKEY 错误或账号被封,请登录 OpenAI 查看。");
|
||||
return;
|
||||
}
|
||||
|
||||
let currentDate = new Date();
|
||||
const subscriptionData = await response.json();
|
||||
const totalAmount = subscriptionData.system_hard_limit_usd;
|
||||
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 gpt4CheckResponse = await fetch(gpt4Check, { headers });
|
||||
const gpt4CheckData = await gpt4CheckResponse.json();
|
||||
let GPT4CheckResult = Array.isArray(gpt4CheckData.data) && gpt4CheckData.data.some(item => item.id.includes('gpt-4')) ? '✅' : '❌';
|
||||
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();
|
||||
const totalUsage = usageData.total_usage / 100;
|
||||
let remaining;
|
||||
if (currentDate > expiryDate) {
|
||||
remaining = "❌过期";
|
||||
} else {
|
||||
remaining = subscriptionData.system_hard_limit_usd - totalUsage;
|
||||
}
|
||||
|
||||
//查询组织id
|
||||
response = await fetch(urlsetid, { headers });
|
||||
const setiddata = await response.json();
|
||||
let setid = '';
|
||||
if (typeof setiddata.data[1] !== 'undefined') {
|
||||
setid = setiddata.data[0].id + '----' + setiddata.data[1].id;
|
||||
} else {
|
||||
setid = setiddata.data[0].id;
|
||||
}
|
||||
|
||||
return [totalAmount, totalUsage, remaining, formattedDate, GPT4CheckResult, isSubscrible, setid];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return ["Error", 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]+/).filter(key => key.startsWith("sk-"));
|
||||
|
||||
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) => {
|
||||
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(/^(sk-[a-zA-Z0-9]{4})[a-zA-Z0-9]*(.{6})$/, "$1***$2");
|
||||
row.appendChild(apiKeyCell);
|
||||
|
||||
console.log(data); // 添加 console.log 以查看 data 的值
|
||||
|
||||
if (data?.[0] === "Error" || data === undefined) {
|
||||
let errorMessageCell = document.createElement("td");
|
||||
errorMessageCell.colSpan = "8";
|
||||
errorMessageCell.classList.add("status-error");
|
||||
errorMessageCell.textContent = "不正确或已失效的API-KEY";
|
||||
row.appendChild(errorMessageCell);
|
||||
} else {
|
||||
let totalGrantedCell = document.createElement("td");
|
||||
totalGrantedCell.textContent = data[0].toFixed(2);
|
||||
row.appendChild(totalGrantedCell);
|
||||
|
||||
let totalUsedCell = document.createElement("td");
|
||||
totalUsedCell.textContent = data[1].toFixed(2);
|
||||
row.appendChild(totalUsedCell);
|
||||
|
||||
let totalAvailableCell = document.createElement("td");
|
||||
totalAvailableCell.textContent = typeof data[2] === 'number' ? data[2].toFixed(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);
|
||||
<div style="margin-top:20px;">
|
||||
<mdui-chip elevated icon="code"> 本网页由<a href="https://woodchen.ink" target="_blank">woodchen</a>开源于<a
|
||||
href="https://github.com/woodchen-ink/openai-billing-query" target="_blank">Github</a>
|
||||
</mdui-chip>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-navigation-drawer>
|
||||
|
||||
|
||||
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 isSubscribe = document.createElement("td");
|
||||
isSubscribe.textContent = data[5];
|
||||
row.appendChild(isSubscribe);
|
||||
|
||||
let setidCell = document.createElement("td");
|
||||
setidCell.textContent = data[6]; // 使用data[6]来获取组织ID
|
||||
row.appendChild(setidCell);
|
||||
}
|
||||
|
||||
tableBody.appendChild(row);
|
||||
|
||||
if (i === apiKeys.length - 1) {
|
||||
queriedApiKeys = [];
|
||||
}
|
||||
serialNumber++; // 增加序列号
|
||||
h2.style.display = 'block';
|
||||
table.style.display = 'table';
|
||||
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
let row = document.createElement("tr");
|
||||
|
||||
let serialNumberCell = document.createElement("td"); // 创建序列号单元格
|
||||
serialNumberCell.textContent = serialNumber; // 设置序列号文本
|
||||
row.appendChild(serialNumberCell); // 将序列号单元格添加到行中
|
||||
let apiKeyCell = document.createElement("td");
|
||||
apiKeyCell.textContent = apiKey;
|
||||
row.appendChild(apiKeyCell);
|
||||
|
||||
let errorMessageCell = document.createElement("td");
|
||||
errorMessageCell.colSpan = "8";
|
||||
errorMessageCell.style.width = "90px";
|
||||
errorMessageCell.classList.add("status-error");
|
||||
errorMessageCell.textContent = "账户疑似被封禁,请登录 OpenAI 官网确认";
|
||||
row.appendChild(errorMessageCell);
|
||||
|
||||
tableBody.appendChild(row);
|
||||
|
||||
if (i === apiKeys.length - 1) {
|
||||
queriedApiKeys = [];
|
||||
}
|
||||
serialNumber++; // 增加序列号
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
</mdui-layout>
|
||||
<script src="./static/js.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
167
refresh_see.html
Normal file
167
refresh_see.html
Normal file
@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="mdui-theme-light" lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>刷新Platform相关信息刷新sess</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="批量快速查询OPENAI的余额,支持可视化展现已用比例、额度、已用量、未用量、是否GPT-4、是否GPT4-32K、是否绑卡、绑卡信息、组织信息、是否有效"
|
||||
/>
|
||||
<link rel="stylesheet" href="./static/css-1.css" type="text/css" />
|
||||
<link rel="stylesheet" href="./static/mdui/mdui.css" />
|
||||
<script src="./static/mdui/mdui.global.js"></script>
|
||||
|
||||
<script>
|
||||
mdui.setColorScheme("#0d2d44");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
th,
|
||||
td {
|
||||
cursor: pointer; /* 添加鼠标指针样式 */
|
||||
white-space: normal; /* 设置为normal使内容自动换行 */
|
||||
word-wrap: break-word; /* 设置为break-word以确保长单词/链接被截断换行 */
|
||||
max-width: 300px;
|
||||
}
|
||||
/* 复制按钮样式 */
|
||||
.copy-button {
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
margin-top: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
<mdui-layout>
|
||||
<mdui-layout-main>
|
||||
<div class="mdui-main-container">
|
||||
<div style="display: flex">
|
||||
<h2 style="flex: 1">查询结果</h2>
|
||||
<button class="copy-button" onclick="copySess()">复制sess</button>
|
||||
<button class="copy-button" onclick="copyTable()">
|
||||
复制全部内容
|
||||
</button>
|
||||
</div>
|
||||
<div class="mdui-table">
|
||||
<table id="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>邮箱账户</th>
|
||||
<th>手机号</th>
|
||||
<th>Sensitive ID</th>
|
||||
<th>Refresh Token</th>
|
||||
<th>Access Token</th>
|
||||
<th>Sensitive ID创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="result-tbody" style="max-width: 100%">
|
||||
<!-- 表格内容 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-layout-main>
|
||||
|
||||
<mdui-top-app-bar>
|
||||
<mdui-button-icon
|
||||
icon="menu"
|
||||
close-on-overlay-click
|
||||
id="toggle-button"
|
||||
style="color: white"
|
||||
></mdui-button-icon>
|
||||
<mdui-top-app-bar-title style="text-align: center; color: white"
|
||||
>刷新Platform相关信息刷新sess</mdui-top-app-bar-title
|
||||
>
|
||||
</mdui-top-app-bar>
|
||||
|
||||
<mdui-navigation-drawer open class="left-drawer" close-on-overlay-click>
|
||||
<div class="left-drawer-main">
|
||||
<h3>输入 API KEY</h3>
|
||||
<p>本站不保存 KEY 信息,查询后请自行保存</p>
|
||||
<mdui-text-field
|
||||
id="api-key-input"
|
||||
placeholder="请输入 refresh_token,多个请回车换行"
|
||||
label="refresh_token"
|
||||
rows="6"
|
||||
></mdui-text-field>
|
||||
|
||||
<mdui-select
|
||||
id="api-url-select"
|
||||
icon="airline_stops"
|
||||
label="查询线路"
|
||||
value="https://ai.fakeopen.com"
|
||||
onchange="toggleCustomUrlInput()"
|
||||
>
|
||||
<mdui-menu-item value="https://ai.fakeopen.com"
|
||||
>[FkopenAi]ai.fakeopen.com</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item value="custom">[已前置https]自定义PandoraNext API</mdui-menu-item>
|
||||
</mdui-select>
|
||||
|
||||
<mdui-text-field
|
||||
type="text"
|
||||
id="custom-url-input"
|
||||
placeholder="请输入PandoraNext的API地址和前缀"
|
||||
label="PandoraNext API地址和前缀"
|
||||
class="hidden"
|
||||
></mdui-text-field>
|
||||
|
||||
<div style="height: 2rem"></div>
|
||||
|
||||
<mdui-button
|
||||
full-width
|
||||
id="query-button"
|
||||
icon="search"
|
||||
onclick="sendRequest()"
|
||||
>
|
||||
查询
|
||||
</mdui-button>
|
||||
|
||||
<!-- 下半部分 -->
|
||||
<h4>页面列表</h4>
|
||||
<mdui-menu style="width: 100%; margin-top: 20px">
|
||||
<mdui-menu-item icon="search" href="index.html"
|
||||
>查API信息</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess.html"
|
||||
>通过access token获取sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="get_sess2.html"
|
||||
>通过PandoraNext获取sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item icon="vpn_key" href="refresh_see.html"
|
||||
>刷新Platform相关信息刷新sess</mdui-menu-item
|
||||
>
|
||||
<mdui-menu-item
|
||||
icon="build"
|
||||
href="https://woodchen.ink/1266.html"
|
||||
target="_blank"
|
||||
>手动获取sess的方法</mdui-menu-item
|
||||
>
|
||||
</mdui-menu>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<mdui-chip elevated icon="code">
|
||||
本网页由<a href="https://woodchen.ink" target="_blank">woodchen</a
|
||||
>开源于<a
|
||||
href="https://github.com/woodchen-ink/openai-billing-query"
|
||||
target="_blank"
|
||||
>Github</a
|
||||
>
|
||||
</mdui-chip>
|
||||
</div>
|
||||
</div>
|
||||
</mdui-navigation-drawer>
|
||||
</mdui-layout>
|
||||
<script src="./static/refresh_see.js"></script>
|
||||
</body>
|
||||
</html>
|
55
static/css-1.css
Normal file
55
static/css-1.css
Normal file
File diff suppressed because one or more lines are too long
453
static/css.css
Normal file
453
static/css.css
Normal file
@ -0,0 +1,453 @@
|
||||
: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://random-api.czl.net/pic/ecy') 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: -apple-system,BlinkMacSystemFont,'Helvetica Neue',Helvetica,Segoe UI,Arial,Roboto,'PingFang SC',miui,'Hiragino Sans GB','Microsoft Yahei',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 {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
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 tr{
|
||||
border-radius: 8px;
|
||||
} */
|
||||
|
||||
/* 下面的代码定义了结果表格奇数行的背景颜色 */
|
||||
#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;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
/* 在小于768px的屏幕上生效的样式 */
|
||||
|
||||
/* 可以减小字体和表格单元格的大小,使其适应小屏设备 */
|
||||
#result-table th,
|
||||
#result-table td {
|
||||
font-size: 8px;
|
||||
padding: 5px 3px;
|
||||
}
|
||||
|
||||
/* 可以隐藏一些不是非常重要的列,以便更有效地利用屏幕空间 */
|
||||
#result-table th:nth-child(n+12),
|
||||
#result-table td:nth-child(n+12) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#result-table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 5px;
|
||||
/* padding: 5px; */
|
||||
}
|
||||
|
||||
|
||||
#result-table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
padding-top: 100px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fefefe;
|
||||
color: #000000;
|
||||
margin: auto;
|
||||
width: 410px;
|
||||
height: auto;
|
||||
max-height: 80%;
|
||||
border-radius: 12px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.modal p {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #FFF;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
align-self: flex-end;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 0px;
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 10px 16px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#modalText {
|
||||
margin: 0;
|
||||
}
|
224
static/getsess.js
Normal file
224
static/getsess.js
Normal file
@ -0,0 +1,224 @@
|
||||
let queriedApiKeys = [];
|
||||
let serialNumber = 1;
|
||||
|
||||
function toggleCustomUrlInput() {
|
||||
// 获取id为"api-url-select"的元素
|
||||
const selectElement = document.getElementById("api-url-select");
|
||||
// 获取id为"custom-url-input"的元素
|
||||
const customUrlInput = document.getElementById("custom-url-input");
|
||||
|
||||
// 如果selectElement的值为"custom"
|
||||
if (selectElement.value === "custom") {
|
||||
// 从customUrlInput的classList中移除"hidden"
|
||||
customUrlInput.classList.remove("hidden");
|
||||
} else {
|
||||
// 给customUrlInput的classList添加"hidden"
|
||||
customUrlInput.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
async function checkBilling(apiKey, apiUrl) {
|
||||
const headers = {
|
||||
"Authorization": "Bearer " + apiKey,
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
const urlGetsess = `${apiUrl}/dashboard/onboarding/login`;
|
||||
|
||||
try {
|
||||
const response = await fetch(urlGetsess, {
|
||||
method: "POST", // 设置请求方法为 POST
|
||||
headers: headers,
|
||||
body: JSON.stringify({}) // 此处放置要发送的数据
|
||||
});
|
||||
|
||||
const getsessdata = await response.json();
|
||||
console.log(getsessdata);
|
||||
if (getsessdata && getsessdata.user && getsessdata.user.session) {
|
||||
return getsessdata; // 直接返回整个getsessdata对象
|
||||
} else {
|
||||
console.error("Unexpected data structure: user or session property not found in the response.", getsessdata);
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//查询函数
|
||||
async 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");
|
||||
|
||||
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() === "") {
|
||||
mdui.alert({
|
||||
headline: "无查询线路",
|
||||
description: "请选择或自定义",
|
||||
confirmText: "OK",
|
||||
})
|
||||
return;
|
||||
} else {
|
||||
apiUrl = customUrlInput.value.trim();
|
||||
|
||||
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
||||
apiUrl = "https://" + apiUrl;
|
||||
}
|
||||
|
||||
if (!apiUrl.startsWith("https://gateway.ai.cloudflare.com")) {
|
||||
apiUrl += "/v1"; // 如果不是,则添加路径‘/v1’
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apiUrl = apiUrlSelect.value;
|
||||
|
||||
if (apiUrlSelect.value === "https://gateway.ai.cloudflare.com/v1/feedd0aa8abd6875052d86a94f1baf83/test/openai") {
|
||||
apiUrl = apiUrl.replace("/v1", ""); // 如果用户选择的选项是https://gateway.ai.cloudflare.com开头,则删除/v1
|
||||
} else {
|
||||
apiUrl += "/v1"; // 如果不是,则添加路径‘/v1’
|
||||
}
|
||||
}
|
||||
|
||||
let apiKeys = apiKeyInput.value.split(/[,\s,\n]+/);
|
||||
|
||||
if (apiKeys.length === 0) {
|
||||
mdui.alert({
|
||||
headline: "未匹配到 access_token",
|
||||
description: "请检查输入内容",
|
||||
confirmText: "OK",
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
mdui.alert({
|
||||
headline: "成功匹配到 access_token",
|
||||
description: apiKeys,
|
||||
confirmText: "OK",
|
||||
});
|
||||
|
||||
showLoadingAnimation();
|
||||
|
||||
let tableBody = document.querySelector("#result-table tbody");
|
||||
for (let i = 0; i < apiKeys.length; i++) {
|
||||
let apiKey = apiKeys[i].trim();
|
||||
|
||||
let data = await checkBilling(apiKey, apiUrl); // 获取 checkBilling 的结果
|
||||
|
||||
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(/^(.{10}).*(.{8})$/, "$1***$2");
|
||||
row.appendChild(apiKeyCell);
|
||||
|
||||
if (data.user && data.user.session) {
|
||||
let user = data.user;
|
||||
let session = user.session;
|
||||
let ip_country = data.ip_country;
|
||||
|
||||
let orgId = user.orgs.data[0].id;
|
||||
|
||||
let properties = ['id', 'email', 'name', 'phone_number', 'created', 'sensitive_id', 'session_created', orgId, 'ip_country'];
|
||||
properties.forEach(prop => {
|
||||
let cell = document.createElement("td");
|
||||
if (prop === 'created' || prop === 'session_created') {
|
||||
let timestamp = prop === 'created' ? user[prop] : session['created'];
|
||||
cell.textContent = new Date(timestamp * 1000).toLocaleString();
|
||||
} else if (prop === 'sensitive_id') {
|
||||
cell.textContent = session[prop]; // 获取 session 对象中的 sensitive_id
|
||||
} else if (prop === orgId) {
|
||||
cell.textContent = orgId; // 直接使用 orgId 变量
|
||||
} else if (prop === 'ip_country') {
|
||||
cell.textContent = ip_country ? ip_country : 'N/A'; // 改为使用 ip_country 变量
|
||||
} else {
|
||||
cell.textContent = user[prop];
|
||||
}
|
||||
row.appendChild(cell);
|
||||
});
|
||||
|
||||
} else {
|
||||
// 接口返回的数据结构不符合预期
|
||||
console.error("Unexpected data structure: data[0] or data.user is undefined.");
|
||||
let errorMessageCell = document.createElement("td");
|
||||
errorMessageCell.colSpan = "8";
|
||||
errorMessageCell.classList.add("status-error");
|
||||
errorMessageCell.textContent = "不正确或已失效的API-KEY";
|
||||
row.appendChild(errorMessageCell);
|
||||
}
|
||||
|
||||
tableBody.appendChild(row);
|
||||
|
||||
if (i === apiKeys.length - 1) {
|
||||
queriedApiKeys = [];
|
||||
}
|
||||
serialNumber++;
|
||||
}
|
||||
hideLoadingAnimation();
|
||||
}
|
||||
|
||||
|
||||
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";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function showLoadingAnimation() {
|
||||
const button = document.getElementById("query-button");
|
||||
|
||||
// 创建一个新的 <mdui-linear-progress> 元素
|
||||
const progressElement = document.createElement("mdui-linear-progress");
|
||||
progressElement.id = "query-progress";
|
||||
|
||||
// 将新元素替代原始按钮元素
|
||||
button.parentElement.replaceChild(progressElement, button);
|
||||
}
|
||||
|
||||
function hideLoadingAnimation() {
|
||||
const progressElement = document.querySelector("mdui-linear-progress");
|
||||
|
||||
if (progressElement) {
|
||||
const button = document.createElement("mdui-button");
|
||||
button.id = "query-button";
|
||||
button.innerHTML = "查询";
|
||||
button.setAttribute("full-width", "");
|
||||
button.setAttribute("icon", "search");
|
||||
button.setAttribute("onclick", "sendRequest()");
|
||||
|
||||
// 将原始按钮元素替代回来
|
||||
progressElement.parentElement.replaceChild(button, progressElement);
|
||||
}
|
||||
}
|
||||
|
||||
let isOpen = true;
|
||||
|
||||
toggleButton.addEventListener("click", () => {
|
||||
isOpen = !isOpen;
|
||||
if (isOpen) {
|
||||
navigationDrawer.open = true;
|
||||
} else {
|
||||
navigationDrawer.open = false;
|
||||
}
|
||||
});
|
247
static/getsess2.js
Normal file
247
static/getsess2.js
Normal file
@ -0,0 +1,247 @@
|
||||
let serialNumber = 1;
|
||||
|
||||
function checkBilling(apiKey, apiUrl) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
};
|
||||
const urlGetsess = `${apiUrl}/api/auth/platform/login`;
|
||||
var urlencoded = new URLSearchParams();
|
||||
for (var i in apiKey) {
|
||||
urlencoded.append(i, apiKey[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(urlGetsess, urlencoded, {
|
||||
headers: headers,
|
||||
maxRedirects: 0,
|
||||
});
|
||||
|
||||
const getsessdata = response.data;
|
||||
if (getsessdata && getsessdata.login_info && getsessdata.token_info) {
|
||||
resolve(getsessdata); // 返回getsessdata对象
|
||||
} else {
|
||||
reject(getsessdata);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//查询函数
|
||||
async function sendRequest() {
|
||||
let apiKeyInput = document.getElementById("api-key-input");
|
||||
let customUrlInput = document.getElementById("custom-url-input");
|
||||
|
||||
document
|
||||
.getElementById("result-table")
|
||||
.getElementsByTagName("tbody")[0].innerHTML = "";
|
||||
|
||||
let apiUrl = customUrlInput.value.trim();
|
||||
if (apiUrl.endsWith('/')) {
|
||||
apiUrl = apiUrl.slice(0, -1); // 去掉末尾的"/"
|
||||
}
|
||||
let userList = apiKeyInput.value.split(/[,\s,\n]+/);
|
||||
if (!apiUrl) {
|
||||
mdui.alert({
|
||||
headline: "无查询线路",
|
||||
description: "请输入PandoraNext Api的地址",
|
||||
confirmText: "OK",
|
||||
});
|
||||
return;
|
||||
} else if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
||||
apiUrl = "https://" + apiUrl;
|
||||
}
|
||||
|
||||
if (userList.length === 0) {
|
||||
mdui.alert({
|
||||
headline: "请检查输入内容",
|
||||
description: "请填写账户信息",
|
||||
confirmText: "OK",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = [];
|
||||
for (var i = 0; i < userList.length; i++) {
|
||||
var userInfo = userList[i].split(/\|/);
|
||||
if (userInfo.length > 3 || userInfo.length < 2) {
|
||||
mdui.alert({
|
||||
headline: "请检查输入内容",
|
||||
description: "请按格式输入账户信息",
|
||||
confirmText: "OK",
|
||||
});
|
||||
return;
|
||||
}
|
||||
userData.push({
|
||||
username: userInfo[0],
|
||||
password: userInfo[1],
|
||||
mfa_code: userInfo[2] ? userInfo[2] : "",
|
||||
prompt: "login",
|
||||
});
|
||||
}
|
||||
|
||||
showLoadingAnimation();
|
||||
|
||||
let tableBody = document.querySelector("#result-table tbody");
|
||||
let properties = [
|
||||
"email",
|
||||
"phone_number",
|
||||
"sensitive_id",
|
||||
"refresh_token",
|
||||
"access_token",
|
||||
"created",
|
||||
];
|
||||
for (let i = 0; i < userData.length; i++) {
|
||||
let userInfo = userData[i];
|
||||
let row = document.createElement("tr");
|
||||
let serialNumberCell = document.createElement("td");
|
||||
serialNumberCell.textContent = serialNumber;
|
||||
row.appendChild(serialNumberCell);
|
||||
|
||||
try {
|
||||
let data = await checkBilling(userInfo, apiUrl);
|
||||
let user = data.login_info.user;
|
||||
let session = user.session;
|
||||
let token_info = data.token_info;
|
||||
let cell = document.createElement("td");
|
||||
let cellValue = '';
|
||||
properties.forEach((prop) => {
|
||||
console.log(prop);
|
||||
let cell = document.createElement("td");
|
||||
if (prop === "created") {
|
||||
cellValue = new Date(session["created"] * 1000).toLocaleString();
|
||||
} else if (prop === "sensitive_id") {
|
||||
cellValue = session[prop]; // 获取 session 对象中的 sensitive_id
|
||||
cell.onclick = function () {
|
||||
copyCell(cell, `Sensitive ID复制成功`);
|
||||
};
|
||||
} else if (prop === "refresh_token" || prop === "access_token") {
|
||||
cellValue = token_info[prop];
|
||||
cell.onclick = function () {
|
||||
copyCell(cell, `${prop === "refresh_token" ? 'Refresh Token' : 'Access Token'}复制成功`);
|
||||
};
|
||||
} else {
|
||||
cellValue = user[prop] ? user[prop] : ''; // 确保在user[prop]为空时,cellValue被赋予空字符串
|
||||
}
|
||||
|
||||
cell.textContent = cellValue && cellValue.length > 50 ? cellValue.substring(0, 57) + "..." : cellValue; // 如果长度超过60,显示"..."
|
||||
cell.innerHTML = `<span title="${cellValue}">${cell.textContent}</span>`; // 在悬停时显示全部内容
|
||||
row.appendChild(cell);
|
||||
});
|
||||
} catch (error) {
|
||||
let username = document.createElement("td");
|
||||
username.textContent = userInfo.username;
|
||||
row.appendChild(username);
|
||||
let errorMessageCell = document.createElement("td");
|
||||
errorMessageCell.colSpan = "8";
|
||||
errorMessageCell.classList.add("status-error");
|
||||
// 在这里检查错误信息是否为 "error request login url"
|
||||
if (error === 'error request login url') {
|
||||
errorMessageCell.textContent = '请求错误,请稍后重试';
|
||||
} else {
|
||||
errorMessageCell.textContent = error && error.detail ? error.detail : error;
|
||||
}
|
||||
row.appendChild(errorMessageCell);
|
||||
}
|
||||
|
||||
tableBody.appendChild(row);
|
||||
serialNumber++;
|
||||
}
|
||||
hideLoadingAnimation();
|
||||
}
|
||||
|
||||
function copyCell(cell, message) {
|
||||
// 创建一个新的textarea元素
|
||||
var textarea = document.createElement("textarea");
|
||||
// 设置textarea的值为单元格的文本内容
|
||||
textarea.value = cell.innerText;
|
||||
// 将textarea元素添加到body中
|
||||
document.body.appendChild(textarea);
|
||||
// 选择textarea的文本内容
|
||||
textarea.select();
|
||||
// 执行复制命令
|
||||
document.execCommand("copy");
|
||||
// 移除textarea元素
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: message,
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function copyTable() {
|
||||
// 这个函数可以保留,以便你仍然可以复制整个表格内容
|
||||
var tableBody = document.getElementById("result-tbody");
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.value = tableBody.innerText;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: "复制成功",
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function copySess() {
|
||||
var sensitiveCells = document.querySelectorAll("tbody td:nth-child(4) span"); // 选择所有的Sensitive ID单元格
|
||||
var sensitiveIds = Array.from(sensitiveCells).map((cell) => cell.title); // 从单元格中获取所有的Sensitive ID
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.value = sensitiveIds.join("\n"); // 用换行符连接所有的Sensitive ID
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: "Sensitive ID复制成功",
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function showLoadingAnimation() {
|
||||
const button = document.getElementById("query-button");
|
||||
|
||||
// 创建一个新的 <mdui-linear-progress> 元素
|
||||
const progressElement = document.createElement("mdui-linear-progress");
|
||||
progressElement.id = "query-progress";
|
||||
|
||||
// 将新元素替代原始按钮元素
|
||||
button.parentElement.replaceChild(progressElement, button);
|
||||
}
|
||||
|
||||
function hideLoadingAnimation() {
|
||||
const progressElement = document.querySelector("mdui-linear-progress");
|
||||
|
||||
if (progressElement) {
|
||||
const button = document.createElement("mdui-button");
|
||||
button.id = "query-button";
|
||||
button.innerHTML = "查询";
|
||||
button.setAttribute("full-width", "");
|
||||
button.setAttribute("icon", "search");
|
||||
button.setAttribute("onclick", "sendRequest()");
|
||||
|
||||
// 将原始按钮元素替代回来
|
||||
progressElement.parentElement.replaceChild(button, progressElement);
|
||||
}
|
||||
}
|
||||
|
||||
const navigationDrawer = document.querySelector(".left-drawer");
|
||||
const toggleButton = document.getElementById("toggle-button");
|
||||
|
||||
let isOpen = true;
|
||||
|
||||
toggleButton.addEventListener("click", () => {
|
||||
isOpen = !isOpen;
|
||||
if (isOpen) {
|
||||
navigationDrawer.open = true;
|
||||
} else {
|
||||
navigationDrawer.open = false;
|
||||
}
|
||||
});
|
655
static/js.js
Normal file
655
static/js.js
Normal file
@ -0,0 +1,655 @@
|
||||
function toggleProgressBar() {
|
||||
let progressBarHeader = document.getElementById("progressbar-header");
|
||||
let progressBarCells = document.querySelectorAll("td.progressbar");
|
||||
let toggle = document.querySelector("#progressbar-toggle mdui-checkbox");
|
||||
let display = toggle.checked ? "" : "none";
|
||||
progressBarHeader.style.display = display;
|
||||
progressBarCells.forEach(function (cell) { cell.style.display = display; });
|
||||
}
|
||||
function toggleSubInfo() {
|
||||
let toggle = document.querySelector("#subinfo-toggle mdui-checkbox");
|
||||
let display = toggle.checked ? "" : "none";
|
||||
|
||||
let subInfoHeader = document.getElementById("subinfo-header");
|
||||
subInfoHeader.style.display = display;
|
||||
|
||||
let subInfoCells = document.querySelectorAll("td.subinfo");
|
||||
subInfoCells.forEach(function (cell) { cell.style.display = display; });
|
||||
}
|
||||
|
||||
function toggleSetidInfo() {
|
||||
let toggle = document.querySelector("#setid-toggle mdui-checkbox");
|
||||
let display = toggle.checked ? "" : "none";
|
||||
|
||||
let setIdHeader = document.getElementById("setid-header");
|
||||
setIdHeader.style.display = display;
|
||||
|
||||
let setIdCells = document.querySelectorAll("td.setid");
|
||||
setIdCells.forEach(function (cell) { cell.style.display = display; });
|
||||
}
|
||||
toggleProgressBar();
|
||||
toggleSubInfo();
|
||||
toggleSetidInfo();
|
||||
|
||||
|
||||
// 线路选择框
|
||||
function toggleCustomUrlInput() {
|
||||
// 获取id为"api-url-select"的元素
|
||||
const selectElement = document.getElementById("api-url-select");
|
||||
// 获取id为"custom-url-input"的元素
|
||||
const customUrlInput = document.getElementById("custom-url-input");
|
||||
|
||||
// 如果selectElement的值为"custom"
|
||||
if (selectElement.value === "custom") {
|
||||
// 从customUrlInput的classList中移除"hidden"
|
||||
customUrlInput.classList.remove("hidden");
|
||||
} else {
|
||||
// 给customUrlInput的classList添加"hidden"
|
||||
customUrlInput.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
let queriedApiKeys = [];
|
||||
let serialNumber = 1;
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param {Date} date - 需要格式化的日期对象
|
||||
* @returns {string} - 格式化后的日期字符串(年-月-日)
|
||||
*/
|
||||
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}`;
|
||||
}
|
||||
|
||||
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}/models`;
|
||||
const urlSubscription = `${apiUrl}/dashboard/billing/subscription`;
|
||||
let urlUsage = `${apiUrl}/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`;
|
||||
const urlsetid = apiUrl + '/organizations';
|
||||
const urlPaymentmethods = `${apiUrl}/dashboard/billing/payment_methods`;
|
||||
const urlRatelimits = `${apiUrl}/dashboard/rate_limits`;
|
||||
const urlAdvanceData = `${apiUrl}/dashboard/billing/credit_grants`; // 预付费查询接口
|
||||
|
||||
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变量
|
||||
totalAmount = subscriptionData.hard_limit_usd;
|
||||
|
||||
// 通过fetch函数获取advanceData数据,传入urlAdvanceData和headers参数
|
||||
const advanceDataResponse = await fetch(urlAdvanceData, { headers });
|
||||
// 将响应解析为json格式
|
||||
const advanceData = await advanceDataResponse.json();
|
||||
|
||||
// 如果订阅数据的计费机制为'advance',或者总金额小于等于6并且订阅数据中没有信用卡信息
|
||||
if ((subscriptionData.billing_mechanism === 'advance') || (totalAmount <= 6 && !subscriptionData.has_credit_card)) {
|
||||
// 将advanceData中的total_granted属性值赋值给totalAmount变量
|
||||
totalAmount = advanceData.total_granted;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 捕获错误并打印到控制台
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
// 如果总金额大于6
|
||||
if (totalAmount > 6) {
|
||||
// 设置开始日期为子日期
|
||||
startDate = subDate;
|
||||
// 构建请求使用数据的URL
|
||||
urlUsage = `${apiUrl}/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`;
|
||||
// 发送请求获取使用数据的响应
|
||||
response = await fetch(urlUsage, { headers });
|
||||
// 将响应解析为JSON格式的数据
|
||||
const usageData = await response.json();
|
||||
}
|
||||
// 发送请求获取使用数据的响应
|
||||
response = await fetch(urlUsage, { headers });
|
||||
// 将响应解析为JSON格式的数据
|
||||
const usageData = await response.json();
|
||||
|
||||
// 计算总使用量并除以100
|
||||
totalUsage = usageData.total_usage / 100;
|
||||
// 计算剩余金额并保留3位小数
|
||||
remaining = (totalAmount - totalUsage).toFixed(3);
|
||||
|
||||
} catch (error) {
|
||||
// 输出错误信息
|
||||
console.error(error);
|
||||
|
||||
}
|
||||
|
||||
//获取是否绑卡
|
||||
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 {
|
||||
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;
|
||||
let businessAddress = subscriptionData.business_address;
|
||||
|
||||
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')) ? '✅' : '❌';
|
||||
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);
|
||||
}
|
||||
|
||||
// 发起请求查有效
|
||||
async function checkCompletion(apiKey, apiUrl) {
|
||||
// 设置请求的url
|
||||
const urlCompletion = `${apiUrl}/chat/completions`;
|
||||
// 设置请求头
|
||||
const headers = {
|
||||
"Authorization": "Bearer " + apiKey,
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
// 设置请求体
|
||||
const postBody = JSON.stringify({
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [{
|
||||
"role": "user",
|
||||
"content": "hi"
|
||||
}],
|
||||
"max_tokens": 1
|
||||
});
|
||||
|
||||
// 发起请求
|
||||
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 sendRequest() {
|
||||
toggleProgressBar();
|
||||
toggleSubInfo();
|
||||
toggleSetidInfo();
|
||||
|
||||
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");
|
||||
table.style.visibility = "visible";
|
||||
|
||||
if (apiKeyInput.value.trim() === "") {
|
||||
mdui.alert({
|
||||
headline: "未匹配到 API-KEY",
|
||||
description: "请检查输入内容",
|
||||
confirmText: "OK",
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById("result-table").getElementsByTagName('tbody')[0].innerHTML = "";
|
||||
|
||||
let apiUrl = "";
|
||||
// 判断用户选择的API URL选项
|
||||
if (apiUrlSelect.value === "custom") {
|
||||
if (customUrlInput.value.trim() === "") {
|
||||
mdui.alert({
|
||||
headline: "无查询线路",
|
||||
description: "请选择或自定义",
|
||||
confirmText: "OK",
|
||||
})
|
||||
return;
|
||||
} else {
|
||||
apiUrl = customUrlInput.value.trim();
|
||||
|
||||
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
||||
apiUrl = "https://" + apiUrl;
|
||||
}
|
||||
|
||||
if (!apiUrl.startsWith("https://gateway.ai.cloudflare.com")) {
|
||||
apiUrl += "/v1"; // 如果不是,则添加路径‘/v1’
|
||||
}
|
||||
}
|
||||
} else {
|
||||
apiUrl = apiUrlSelect.value;
|
||||
|
||||
if (apiUrlSelect.value === "https://gateway.ai.cloudflare.com/v1/feedd0aa8abd6875052d86a94f1baf83/test/openai") {
|
||||
apiUrl = apiUrl.replace("/v1", ""); // 如果用户选择的选项是https://gateway.ai.cloudflare.com开头,则删除/v1
|
||||
} else {
|
||||
apiUrl += "/v1"; // 如果不是,则添加路径‘/v1’
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let apiKeys = parseKeys(apiKeyInput.value);
|
||||
|
||||
mdui.alert({
|
||||
headline: "成功匹配到 API Key",
|
||||
description: apiKeys,
|
||||
confirmText: "OK",
|
||||
});
|
||||
|
||||
|
||||
|
||||
showLoadingAnimation();
|
||||
|
||||
function parseKeys(input) {
|
||||
let lines = input.split(/\n/);
|
||||
let result = [];
|
||||
for (let line of lines) {
|
||||
let sessKeyMatch = line.match(/sess-[^-]+/);
|
||||
let skKeyMatch = line.match(/sk-[^-]+/);
|
||||
|
||||
if (sessKeyMatch !== null && skKeyMatch !== null) {
|
||||
result.push(sessKeyMatch[0]);
|
||||
}
|
||||
else if (skKeyMatch !== null) {
|
||||
result.push(skKeyMatch[0]);
|
||||
}
|
||||
else if (sessKeyMatch !== null) {
|
||||
result.push(sessKeyMatch[0]);
|
||||
}else {
|
||||
// 如果没有匹配到任何内容,保留原始行
|
||||
result.push(line);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
let lastQueryPromise = null;
|
||||
|
||||
let tableBody = document.querySelector("#result-table tbody");
|
||||
for (let i = 0; i < apiKeys.length; i++) {
|
||||
let apiKey = apiKeys[i].trim();
|
||||
|
||||
if (queriedApiKeys.includes(apiKey)) {
|
||||
mdui.alert({
|
||||
headline: "重复提示",
|
||||
description: `API KEY ${apiKey} 已查询过,跳过此次查询`,
|
||||
confirmText: "OK"
|
||||
})
|
||||
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 的值
|
||||
|
||||
let totalAmount = document.createElement("td");
|
||||
totalAmount.textContent = typeof data[0] === "number" ? data[0] : "无值";
|
||||
row.appendChild(totalAmount);
|
||||
|
||||
let totalUsedCell = document.createElement("td");
|
||||
typeof data[1] === "number" ? data[1].toFixed(3) : '无值';
|
||||
if (isNaN(data[1])) {
|
||||
totalUsedCell.textContent = "无值";
|
||||
} else {
|
||||
totalUsedCell.textContent = data[1].toFixed(3);
|
||||
}
|
||||
row.appendChild(totalUsedCell);
|
||||
|
||||
let totalAvailableCell = document.createElement("td");
|
||||
if (isNaN(data[2])) {
|
||||
totalAvailableCell.textContent = "无值";
|
||||
} else {
|
||||
totalAvailableCell.textContent = data[2];
|
||||
}
|
||||
row.appendChild(totalAvailableCell);
|
||||
|
||||
// 进度条
|
||||
let progressCell = document.createElement("td");
|
||||
progressCell.classList.add("progressbar");
|
||||
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);
|
||||
progressCell.style.display = document.querySelector("#progressbar-toggle mdui-checkbox").checked ? "" : "none";
|
||||
|
||||
|
||||
// 到期时间
|
||||
let expireTime = document.createElement("td");
|
||||
if (data[3] === "1970-01-01") {
|
||||
expireTime.textContent = "永久有效";
|
||||
} else if (data[3] === "NaN-NaN-NaN") {
|
||||
expireTime.textContent = "无值";
|
||||
} else {
|
||||
expireTime.textContent = data[3];
|
||||
}
|
||||
row.appendChild(expireTime);
|
||||
|
||||
|
||||
let GPT35CheckResult = document.createElement("td");
|
||||
GPT35CheckResult.textContent = data[4];
|
||||
let GPT4CheckResult = document.createElement("td");
|
||||
GPT4CheckResult.textContent = data[5];
|
||||
let GPT432kCheckResult = document.createElement("td");
|
||||
GPT432kCheckResult.textContent = data[6];
|
||||
let highestModel = document.createElement("td");
|
||||
if (GPT35CheckResult.textContent === "✅" && GPT4CheckResult.textContent === "❌" && GPT432kCheckResult.textContent === "❌") {
|
||||
highestModel.textContent = "gpt3.5";
|
||||
} else if (GPT35CheckResult.textContent === "✅" && GPT4CheckResult.textContent === "✅" && GPT432kCheckResult.textContent === "❌") {
|
||||
highestModel.textContent = "gpt4";
|
||||
} else if (GPT35CheckResult.textContent === "✅" && GPT4CheckResult.textContent === "✅" && GPT432kCheckResult.textContent === "✅") {
|
||||
highestModel.textContent = "gpt4-32K";
|
||||
} else {
|
||||
highestModel.textContent = "❌";
|
||||
}
|
||||
|
||||
row.appendChild(highestModel);
|
||||
|
||||
let isSubscribe = document.createElement("td");
|
||||
isSubscribe.style.whiteSpace = "pre"; // 添加这一行来保留换行
|
||||
isSubscribe.textContent = data[7];
|
||||
if (data[7] === "Not Found.") {
|
||||
isSubscribe.textContent = "无值";
|
||||
} else {
|
||||
isSubscribe.textContent = data[7];
|
||||
}
|
||||
|
||||
row.appendChild(isSubscribe);
|
||||
|
||||
let SubInformation = document.createElement("td");
|
||||
SubInformation.classList.add("subinfo");
|
||||
let SubInformationContainer = document.createElement("div");
|
||||
SubInformationContainer.style.whiteSpace = "pre-wrap";
|
||||
SubInformationContainer.textContent = data[8];
|
||||
|
||||
SubInformation.appendChild(SubInformationContainer);
|
||||
row.appendChild(SubInformation);
|
||||
SubInformation.style.display = document.querySelector("#subinfo-toggle mdui-checkbox").checked ? "" : "none";
|
||||
|
||||
|
||||
let setidCell = document.createElement("td");
|
||||
setidCell.classList.add("setid");
|
||||
let setidCellContainer = document.createElement("div");
|
||||
setidCellContainer.style.whiteSpace = "pre-wrap";
|
||||
setidCellContainer.textContent = data[9];
|
||||
setidCell.appendChild(setidCellContainer);
|
||||
row.appendChild(setidCell);
|
||||
setidCell.style.display = document.querySelector("#setid-toggle mdui-checkbox").checked ? "" : "none";
|
||||
|
||||
|
||||
let rateLimitsDataCell = document.createElement("td");
|
||||
let rateLimitsDataContainer = document.createElement("div");
|
||||
rateLimitsDataContainer.style.whiteSpace = "pre-wrap";
|
||||
if (data[13]) {
|
||||
let rateLimitsData = data[13];
|
||||
let models = ['gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4', 'gpt-4-32k'];
|
||||
let rateLimitsText = '';
|
||||
for (let model of models) {
|
||||
if (rateLimitsData[model]) {
|
||||
let modelName = '';
|
||||
if (model === 'gpt-3.5-turbo') {
|
||||
modelName = 'gpt3.5';
|
||||
} else if (model === 'gpt-3.5-turbo-16k') {
|
||||
modelName = 'gpt3.5-16K';
|
||||
} else if (model === 'gpt-4') {
|
||||
modelName = 'gpt4';
|
||||
} else if (model === 'gpt-4-32k') {
|
||||
modelName = 'gpt4-32K';
|
||||
}
|
||||
rateLimitsText += `${modelName}: ${rateLimitsData[model].max_requests_per_1_minute}, ${rateLimitsData[model].max_tokens_per_1_minute}-${rateLimitsData[model].max_requests_per_1_day}\n`;
|
||||
} else {
|
||||
rateLimitsText += model + ": ❌\n";
|
||||
}
|
||||
}
|
||||
rateLimitsDataContainer.textContent = rateLimitsText;
|
||||
}
|
||||
|
||||
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++; // 增加序列号
|
||||
table.style.display = 'table';
|
||||
|
||||
})
|
||||
lastQueryPromise = checkBilling(apiKey, apiUrl).then((data) => {
|
||||
// 查询完成后的代码...
|
||||
|
||||
hideLoadingAnimation();
|
||||
});
|
||||
}
|
||||
if (lastQueryPromise) {
|
||||
lastQueryPromise.then(() => {
|
||||
hideLoadingAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function showLoadingAnimation() {
|
||||
const button = document.getElementById("query-button");
|
||||
|
||||
// 创建一个新的 <mdui-linear-progress> 元素
|
||||
const progressElement = document.createElement("mdui-linear-progress");
|
||||
progressElement.id = "query-progress";
|
||||
|
||||
// 将新元素替代原始按钮元素
|
||||
button.parentElement.replaceChild(progressElement, button);
|
||||
}
|
||||
|
||||
function hideLoadingAnimation() {
|
||||
const progressElement = document.querySelector("mdui-linear-progress");
|
||||
|
||||
if (progressElement) {
|
||||
const button = document.createElement("mdui-button");
|
||||
button.id = "query-button";
|
||||
button.innerHTML = "查询";
|
||||
button.setAttribute("full-width", "");
|
||||
button.setAttribute("icon", "search");
|
||||
button.setAttribute("onclick", "sendRequest()");
|
||||
|
||||
// 将原始按钮元素替代回来
|
||||
progressElement.parentElement.replaceChild(button, progressElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const navigationDrawer = document.querySelector(".left-drawer");
|
||||
const toggleButton = document.getElementById("toggle-button");
|
||||
|
||||
let isOpen = true;
|
||||
|
||||
toggleButton.addEventListener("click", () => {
|
||||
isOpen = !isOpen;
|
||||
if (isOpen) {
|
||||
navigationDrawer.open = true;
|
||||
} else {
|
||||
navigationDrawer.open = false;
|
||||
}
|
||||
});
|
1
static/mdui/mdui.css
Normal file
1
static/mdui/mdui.css
Normal file
File diff suppressed because one or more lines are too long
22
static/mdui/mdui.global.js
Normal file
22
static/mdui/mdui.global.js
Normal file
File diff suppressed because one or more lines are too long
279
static/refresh_see.js
Normal file
279
static/refresh_see.js
Normal file
@ -0,0 +1,279 @@
|
||||
let serialNumber = 1;
|
||||
|
||||
// 线路选择框
|
||||
function toggleCustomUrlInput() {
|
||||
// 获取id为"api-url-select"的元素
|
||||
const selectElement = document.getElementById("api-url-select");
|
||||
// 获取id为"custom-url-input"的元素
|
||||
const customUrlInput = document.getElementById("custom-url-input");
|
||||
|
||||
// 如果selectElement的值为"custom"
|
||||
if (selectElement.value === "custom") {
|
||||
// 从customUrlInput的classList中移除"hidden"
|
||||
customUrlInput.classList.remove("hidden");
|
||||
} else {
|
||||
// 给customUrlInput的classList添加"hidden"
|
||||
customUrlInput.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function checkBilling(apiKey, apiUrl) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// 拼接url
|
||||
var tokenUrl = `${apiUrl}`;
|
||||
var loginUrl = `${apiUrl}/v1/dashboard/onboarding/login`;
|
||||
// 使用"/auth/platform/refresh",潘多拉则需要加上/api
|
||||
if (!apiUrl.startsWith("https://ai.fakeopen.com")) {
|
||||
tokenUrl += "/api";
|
||||
}
|
||||
tokenUrl += "/auth/platform/refresh";
|
||||
|
||||
var urlencoded = new URLSearchParams();
|
||||
urlencoded.append("refresh_token", apiKey);
|
||||
|
||||
let response = await fetch(tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: urlencoded,
|
||||
redirect: "follow",
|
||||
});
|
||||
const rdata = await response.json();
|
||||
if (rdata && rdata.access_token && rdata.refresh_token) {
|
||||
// 查询sess
|
||||
const get_sess = await fetch(loginUrl, {
|
||||
method: "POST", // 设置请求方法为 POST
|
||||
headers: {
|
||||
Authorization: "Bearer " + rdata.access_token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}), // 此处放置要发送的数据
|
||||
});
|
||||
const getsessdata = await get_sess.json();
|
||||
if (getsessdata && getsessdata.user && getsessdata.user.session) {
|
||||
resolve({ token_info: rdata, ...getsessdata });
|
||||
} else {
|
||||
reject(getsessdata);
|
||||
}
|
||||
} else {
|
||||
reject(rdata);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//查询函数
|
||||
async function sendRequest() {
|
||||
let apiKeyInput = document.getElementById("api-key-input");
|
||||
let apiUrlSelect = document.getElementById("api-url-select");
|
||||
let customUrlInput = document.getElementById("custom-url-input");
|
||||
|
||||
document
|
||||
.getElementById("result-table")
|
||||
.getElementsByTagName("tbody")[0].innerHTML = "";
|
||||
let apiUrl = apiUrlSelect.value;
|
||||
if (apiUrlSelect.value === "custom") {
|
||||
apiUrl = customUrlInput.value.trim();
|
||||
}
|
||||
if (!apiUrl) {
|
||||
mdui.alert({
|
||||
headline: "无查询线路",
|
||||
description: "请选择或自定义配置查询Sess线路",
|
||||
confirmText: "OK",
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
||||
apiUrl = "https://" + apiUrl;
|
||||
}
|
||||
if (apiUrl && apiUrl.endsWith("/")) {
|
||||
apiUrl = apiUrl.slice(0, -1); // 去掉末尾的"/"
|
||||
}
|
||||
}
|
||||
|
||||
let tokenList = apiKeyInput.value.split(/[,\s,\n]+/);
|
||||
if (!apiKeyInput.value || tokenList.length === 0) {
|
||||
mdui.alert({
|
||||
headline: "请输入Token",
|
||||
description: "请检查输入refresh_token",
|
||||
confirmText: "OK",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
showLoadingAnimation();
|
||||
|
||||
let tableBody = document.querySelector("#result-table tbody");
|
||||
|
||||
let properties = [
|
||||
"email",
|
||||
"phone_number",
|
||||
"sensitive_id",
|
||||
"refresh_token",
|
||||
"access_token",
|
||||
"created",
|
||||
];
|
||||
|
||||
for (let i = 0; i < tokenList.length; i++) {
|
||||
let token = tokenList[i].trim();
|
||||
if (token) {
|
||||
let row = document.createElement("tr");
|
||||
let serialNumberCell = document.createElement("td");
|
||||
serialNumberCell.textContent = serialNumber;
|
||||
row.appendChild(serialNumberCell);
|
||||
try {
|
||||
let data = await checkBilling(token, apiUrl);
|
||||
let user = data.user;
|
||||
let session = user.session;
|
||||
let token_info = data.token_info;
|
||||
properties.forEach((prop) => {
|
||||
let cellValue = "";
|
||||
let cell = document.createElement("td");
|
||||
if (prop === "created") {
|
||||
cellValue = new Date(session["created"] * 1000).toLocaleString();
|
||||
} else if (prop === "sensitive_id") {
|
||||
cellValue = session[prop]; // 获取 session 对象中的 sensitive_id
|
||||
cell.onclick = function () {
|
||||
copyCell(cell, `Sensitive ID复制成功`);
|
||||
};
|
||||
} else if (prop === "refresh_token" || prop === "access_token") {
|
||||
cellValue = token_info[prop];
|
||||
cell.onclick = function () {
|
||||
copyCell(
|
||||
cell,
|
||||
`${
|
||||
prop === "refresh_token" ? "Refresh Token" : "Access Token"
|
||||
}复制成功`
|
||||
);
|
||||
};
|
||||
} else {
|
||||
cellValue = user[prop] ? user[prop] : ""; // 确保在user[prop]为空时,cellValue被赋予空字符串
|
||||
}
|
||||
|
||||
cell.textContent =
|
||||
cellValue && cellValue.length > 50
|
||||
? cellValue.substring(0, 57) + "..."
|
||||
: cellValue; // 如果长度超过60,显示"..."
|
||||
cell.innerHTML = `<span title="${cellValue}">${cell.textContent}</span>`; // 在悬停时显示全部内容
|
||||
row.appendChild(cell);
|
||||
});
|
||||
} catch (error) {
|
||||
let username = document.createElement("td");
|
||||
username.textContent = token.replace(/^(.{10}).*(.{8})$/, "$1***$2");
|
||||
row.appendChild(username);
|
||||
let errorMessageCell = document.createElement("td");
|
||||
errorMessageCell.colSpan = "8";
|
||||
errorMessageCell.classList.add("status-error");
|
||||
// 在这里检查错误信息是否为 "error request login url"
|
||||
if (error === "error request login url") {
|
||||
errorMessageCell.textContent = "请求错误,请稍后重试";
|
||||
} else {
|
||||
errorMessageCell.textContent =
|
||||
error && error.detail ? error.detail : error;
|
||||
}
|
||||
row.appendChild(errorMessageCell);
|
||||
}
|
||||
|
||||
tableBody.appendChild(row);
|
||||
serialNumber++;
|
||||
}
|
||||
}
|
||||
hideLoadingAnimation();
|
||||
}
|
||||
|
||||
function copyCell(cell, message) {
|
||||
// 创建一个新的textarea元素
|
||||
var textarea = document.createElement("textarea");
|
||||
// 设置textarea的值为单元格的文本内容
|
||||
textarea.value = cell.innerText;
|
||||
// 将textarea元素添加到body中
|
||||
document.body.appendChild(textarea);
|
||||
// 选择textarea的文本内容
|
||||
textarea.select();
|
||||
// 执行复制命令
|
||||
document.execCommand("copy");
|
||||
// 移除textarea元素
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: message,
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function copyTable() {
|
||||
// 这个函数可以保留,以便你仍然可以复制整个表格内容
|
||||
var tableBody = document.getElementById("result-tbody");
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.value = tableBody.innerText;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: "复制成功",
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function copySess() {
|
||||
var sensitiveCells = document.querySelectorAll("tbody td:nth-child(4) span"); // 选择所有的Sensitive ID单元格
|
||||
var sensitiveIds = Array.from(sensitiveCells).map((cell) => cell.title); // 从单元格中获取所有的Sensitive ID
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.value = sensitiveIds.join("\n"); // 用换行符连接所有的Sensitive ID
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
mdui.alert({
|
||||
headline: "提示",
|
||||
description: "Sensitive ID复制成功",
|
||||
confirmText: "OK",
|
||||
});
|
||||
}
|
||||
|
||||
function showLoadingAnimation() {
|
||||
const button = document.getElementById("query-button");
|
||||
|
||||
// 创建一个新的 <mdui-linear-progress> 元素
|
||||
const progressElement = document.createElement("mdui-linear-progress");
|
||||
progressElement.id = "query-progress";
|
||||
|
||||
// 将新元素替代原始按钮元素
|
||||
button.parentElement.replaceChild(progressElement, button);
|
||||
}
|
||||
|
||||
function hideLoadingAnimation() {
|
||||
const progressElement = document.querySelector("mdui-linear-progress");
|
||||
|
||||
if (progressElement) {
|
||||
const button = document.createElement("mdui-button");
|
||||
button.id = "query-button";
|
||||
button.innerHTML = "查询";
|
||||
button.setAttribute("full-width", "");
|
||||
button.setAttribute("icon", "search");
|
||||
button.setAttribute("onclick", "sendRequest()");
|
||||
|
||||
// 将原始按钮元素替代回来
|
||||
progressElement.parentElement.replaceChild(button, progressElement);
|
||||
}
|
||||
}
|
||||
|
||||
const navigationDrawer = document.querySelector(".left-drawer");
|
||||
const toggleButton = document.getElementById("toggle-button");
|
||||
|
||||
let isOpen = true;
|
||||
|
||||
toggleButton.addEventListener("click", () => {
|
||||
isOpen = !isOpen;
|
||||
if (isOpen) {
|
||||
navigationDrawer.open = true;
|
||||
} else {
|
||||
navigationDrawer.open = false;
|
||||
}
|
||||
});
|
28
tailwind.config.js
Normal file
28
tailwind.config.js
Normal file
@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
|
||||
theme: {
|
||||
screens: {
|
||||
"sm": "640px",
|
||||
"md": "768px",
|
||||
"lg": "1024px",
|
||||
"xl": "1280px",
|
||||
"2xl": "1600px",
|
||||
'sm': '576px',
|
||||
"2xl": { 'max': '1600px' },
|
||||
// => @media (min-width: 576px) { ... }
|
||||
|
||||
'md': '960px',
|
||||
// => @media (min-width: 960px) { ... }
|
||||
|
||||
'lg': '1560px',
|
||||
// => @media (min-width: 1440px) { ... }
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [require("daisyui")],
|
||||
variants: {
|
||||
extend: {
|
||||
borderColor: ['responsive', 'hover', 'focus']
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user