✨ 完成Go版本API代理服务器的迁移
Browse files- 将项目从Node.js迁移至Go语言,提升性能和并发处理能力
- 更新Dockerfile以支持Go构建流程
- 删除旧的Node.js相关文件,包括package.json和main.ts
- 更新go.mod和go.sum以反映新的依赖版本
- 更新README.md以反映项目的Go版本特性和使用说明
- Dockerfile +43 -5
- Dockerfile-go +0 -48
- README-go.md +0 -164
- README.md +5 -4
- go.mod +24 -20
- go.sum +48 -85
- main.ts +0 -712
- package.json +0 -31
Dockerfile
CHANGED
|
@@ -1,10 +1,48 @@
|
|
| 1 |
-
|
|
|
|
| 2 |
|
|
|
|
| 3 |
WORKDIR /app
|
| 4 |
-
COPY package.json package-lock.json* ./
|
| 5 |
-
RUN npm install --production || npm install --legacy-peer-deps --production
|
| 6 |
-
COPY . .
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
EXPOSE 8000
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Go版本的高性能API代理服务器Dockerfile
|
| 2 |
+
FROM golang:alpine AS builder
|
| 3 |
|
| 4 |
+
# 设置工作目录
|
| 5 |
WORKDIR /app
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
+
# 复制go模块文件
|
| 8 |
+
COPY go.mod go.sum ./
|
| 9 |
+
|
| 10 |
+
# 下载依赖
|
| 11 |
+
RUN go mod download
|
| 12 |
+
|
| 13 |
+
# 复制源代码
|
| 14 |
+
COPY *.go ./
|
| 15 |
+
|
| 16 |
+
# 编译应用
|
| 17 |
+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o api-proxy .
|
| 18 |
+
|
| 19 |
+
# 运行阶段
|
| 20 |
+
FROM alpine:latest
|
| 21 |
+
|
| 22 |
+
# 安装CA证书
|
| 23 |
+
RUN apk --no-cache add ca-certificates
|
| 24 |
+
|
| 25 |
+
# 创建非root用户
|
| 26 |
+
RUN addgroup -g 1001 -S apiproxy && \
|
| 27 |
+
adduser -u 1001 -S apiproxy -G apiproxy
|
| 28 |
+
|
| 29 |
+
WORKDIR /home/apiproxy/
|
| 30 |
+
|
| 31 |
+
# 从构建阶段复制二进制文件
|
| 32 |
+
COPY --from=builder /app/api-proxy .
|
| 33 |
+
|
| 34 |
+
# 设置文件权限
|
| 35 |
+
RUN chown apiproxy:apiproxy api-proxy
|
| 36 |
+
|
| 37 |
+
# 切换到非root用户
|
| 38 |
+
USER apiproxy
|
| 39 |
+
|
| 40 |
+
# 暴露端口
|
| 41 |
EXPOSE 8000
|
| 42 |
|
| 43 |
+
# 健康检查
|
| 44 |
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
| 45 |
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8000/stats || exit 1
|
| 46 |
+
|
| 47 |
+
# 启动应用
|
| 48 |
+
CMD ["./api-proxy"]
|
Dockerfile-go
DELETED
|
@@ -1,48 +0,0 @@
|
|
| 1 |
-
# Go版本的高性能API代理服务器Dockerfile
|
| 2 |
-
FROM golang:1.21-alpine AS builder
|
| 3 |
-
|
| 4 |
-
# 设置工作目录
|
| 5 |
-
WORKDIR /app
|
| 6 |
-
|
| 7 |
-
# 复制go模块文件
|
| 8 |
-
COPY go.mod go.sum ./
|
| 9 |
-
|
| 10 |
-
# 下载依赖
|
| 11 |
-
RUN go mod download
|
| 12 |
-
|
| 13 |
-
# 复制源代码
|
| 14 |
-
COPY *.go ./
|
| 15 |
-
|
| 16 |
-
# 编译应用
|
| 17 |
-
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o api-proxy .
|
| 18 |
-
|
| 19 |
-
# 运行阶段
|
| 20 |
-
FROM alpine:latest
|
| 21 |
-
|
| 22 |
-
# 安装CA证书
|
| 23 |
-
RUN apk --no-cache add ca-certificates
|
| 24 |
-
|
| 25 |
-
# 创建非root用户
|
| 26 |
-
RUN addgroup -g 1001 -S apiproxy && \
|
| 27 |
-
adduser -u 1001 -S apiproxy -G apiproxy
|
| 28 |
-
|
| 29 |
-
WORKDIR /home/apiproxy/
|
| 30 |
-
|
| 31 |
-
# 从构建阶段复制二进制文件
|
| 32 |
-
COPY --from=builder /app/api-proxy .
|
| 33 |
-
|
| 34 |
-
# 设置文件权限
|
| 35 |
-
RUN chown apiproxy:apiproxy api-proxy
|
| 36 |
-
|
| 37 |
-
# 切换到非root用户
|
| 38 |
-
USER apiproxy
|
| 39 |
-
|
| 40 |
-
# 暴露端口
|
| 41 |
-
EXPOSE 8000
|
| 42 |
-
|
| 43 |
-
# 健康检查
|
| 44 |
-
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
| 45 |
-
CMD wget --no-verbose --tries=1 --spider http://localhost:8000/stats || exit 1
|
| 46 |
-
|
| 47 |
-
# 启动应用
|
| 48 |
-
CMD ["./api-proxy"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README-go.md
DELETED
|
@@ -1,164 +0,0 @@
|
|
| 1 |
-
# API代理服务器 - Go版本
|
| 2 |
-
|
| 3 |
-
🚀 **高性能Go语言实现的API代理服务器**
|
| 4 |
-
|
| 5 |
-
## 🎯 特性
|
| 6 |
-
|
| 7 |
-
### 🔥 Go版本优势
|
| 8 |
-
- **极高并发**: 支持数万并发连接
|
| 9 |
-
- **超低延迟**: 响应时间 < 5ms
|
| 10 |
-
- **内存优化**: 使用原子操作和读写锁
|
| 11 |
-
- **连接池**: HTTP连接复用和Keep-Alive
|
| 12 |
-
- **智能缓存**: 统计数据缓存机制
|
| 13 |
-
- **批量清理**: 性能优化的内存管理
|
| 14 |
-
|
| 15 |
-
### 📊 功能特性
|
| 16 |
-
- ✅ 支持18+个主流AI API代理
|
| 17 |
-
- ✅ 实时统计API调用次数
|
| 18 |
-
- ✅ 多时间维度统计(24h/7d/30d/总计)
|
| 19 |
-
- ✅ 完整网页代理模式
|
| 20 |
-
- ✅ Gemini NoThink模式
|
| 21 |
-
- ✅ CORS跨域支持
|
| 22 |
-
- ✅ 安全响应头设置
|
| 23 |
-
|
| 24 |
-
## 🚀 快速开始
|
| 25 |
-
|
| 26 |
-
### 方式1: 直接运行
|
| 27 |
-
```bash
|
| 28 |
-
# 安装依赖
|
| 29 |
-
go mod download
|
| 30 |
-
|
| 31 |
-
# 运行服务
|
| 32 |
-
go run .
|
| 33 |
-
|
| 34 |
-
# 服务启动在 http://localhost:8000
|
| 35 |
-
```
|
| 36 |
-
|
| 37 |
-
### 方式2: 编译运行
|
| 38 |
-
```bash
|
| 39 |
-
# 编译
|
| 40 |
-
go build -o api-proxy .
|
| 41 |
-
|
| 42 |
-
# 运行
|
| 43 |
-
./api-proxy
|
| 44 |
-
```
|
| 45 |
-
|
| 46 |
-
### 方式3: Docker运行
|
| 47 |
-
```bash
|
| 48 |
-
# 构建镜像
|
| 49 |
-
docker build -t api-proxy-go .
|
| 50 |
-
|
| 51 |
-
# 运行容器
|
| 52 |
-
docker run -p 8000:8000 api-proxy-go
|
| 53 |
-
```
|
| 54 |
-
|
| 55 |
-
## 📋 支持的API
|
| 56 |
-
|
| 57 |
-
| API提供商 | 端点 | 示例 |
|
| 58 |
-
|-----------|------|------|
|
| 59 |
-
| OpenAI | `/openai` | `/openai/v1/chat/completions` |
|
| 60 |
-
| Claude | `/claude` | `/claude/v1/messages` |
|
| 61 |
-
| Gemini | `/gemini` | `/gemini/v1/models` |
|
| 62 |
-
| Gemini NoThink | `/gnothink` | `/gnothink/v1/models/...` |
|
| 63 |
-
| XAI | `/xai` | `/xai/v1/chat/completions` |
|
| 64 |
-
| Groq | `/groq` | `/groq/openai/v1/chat/completions` |
|
| 65 |
-
| Together | `/together` | `/together/v1/chat/completions` |
|
| 66 |
-
| 更多... | ... | 见Web界面 |
|
| 67 |
-
|
| 68 |
-
## 🔧 使用方法
|
| 69 |
-
|
| 70 |
-
### API代理
|
| 71 |
-
将原始API地址的域名部分替换为代理服务器地址+端点前缀:
|
| 72 |
-
|
| 73 |
-
```bash
|
| 74 |
-
# 原始OpenAI API
|
| 75 |
-
https://api.openai.com/v1/chat/completions
|
| 76 |
-
|
| 77 |
-
# 代理地址
|
| 78 |
-
http://localhost:8000/openai/v1/chat/completions
|
| 79 |
-
```
|
| 80 |
-
|
| 81 |
-
### 网页代理
|
| 82 |
-
```bash
|
| 83 |
-
# 代理任意网站
|
| 84 |
-
http://localhost:8000/proxy/https://example.com
|
| 85 |
-
|
| 86 |
-
# 代理API文档
|
| 87 |
-
http://localhost:8000/proxy/https://platform.openai.com/docs
|
| 88 |
-
```
|
| 89 |
-
|
| 90 |
-
### 统计API
|
| 91 |
-
```bash
|
| 92 |
-
# 获取JSON格式统计数据
|
| 93 |
-
GET http://localhost:8000/stats
|
| 94 |
-
```
|
| 95 |
-
|
| 96 |
-
## ⚙️ 配置
|
| 97 |
-
|
| 98 |
-
### 环境变量
|
| 99 |
-
- `PORT`: 服务端口 (默认: 8000)
|
| 100 |
-
- `GIN_MODE`: Gin模式 (release/debug)
|
| 101 |
-
|
| 102 |
-
## 🔧 开发
|
| 103 |
-
|
| 104 |
-
### 项目结构
|
| 105 |
-
```
|
| 106 |
-
.
|
| 107 |
-
├── main.go # 主程序文件
|
| 108 |
-
├── html.go # HTML页面生成
|
| 109 |
-
├── go.mod # Go模块文件
|
| 110 |
-
├── go.sum # 依赖校验和
|
| 111 |
-
├── Dockerfile # Docker构建文件
|
| 112 |
-
└── README-go.md # 本文档
|
| 113 |
-
```
|
| 114 |
-
|
| 115 |
-
### 性能优化
|
| 116 |
-
1. **原子操作**: 使用`sync/atomic`避免锁竞争
|
| 117 |
-
2. **读写锁**: `sync.RWMutex`优化并发读取
|
| 118 |
-
3. **连接池**: HTTP客户端连接复用
|
| 119 |
-
4. **批量清理**: 减少内存分配和GC压力
|
| 120 |
-
5. **缓存机制**: 统计数据5秒缓存
|
| 121 |
-
|
| 122 |
-
## 🆚 性能对比
|
| 123 |
-
|
| 124 |
-
| 指标 | Node.js版本 | Go版本 |
|
| 125 |
-
|------|-------------|--------|
|
| 126 |
-
| 并发处理 | ~10K | ~50K+ |
|
| 127 |
-
| 内存使用 | 50-100MB | 20-50MB |
|
| 128 |
-
| 响应延迟 | 5-10ms | 1-5ms |
|
| 129 |
-
| CPU使用 | 中等 | 低 |
|
| 130 |
-
| 启动时间 | 1-2s | 0.1s |
|
| 131 |
-
|
| 132 |
-
## 📈 监控
|
| 133 |
-
|
| 134 |
-
访问 `http://localhost:8000` 查看实时统计面板:
|
| 135 |
-
- 📊 实时调用统计
|
| 136 |
-
- 📈 多维度数据展示
|
| 137 |
-
- 🎯 重点API监控
|
| 138 |
-
- 🔄 自动刷新数据
|
| 139 |
-
|
| 140 |
-
## 🐳 Docker部署
|
| 141 |
-
|
| 142 |
-
```dockerfile
|
| 143 |
-
FROM golang:alpine AS builder
|
| 144 |
-
WORKDIR /app
|
| 145 |
-
COPY . .
|
| 146 |
-
RUN go mod download && go build -o api-proxy .
|
| 147 |
-
|
| 148 |
-
FROM alpine:latest
|
| 149 |
-
RUN apk --no-cache add ca-certificates
|
| 150 |
-
WORKDIR /root/
|
| 151 |
-
COPY --from=builder /app/api-proxy .
|
| 152 |
-
EXPOSE 8000
|
| 153 |
-
CMD ["./api-proxy"]
|
| 154 |
-
```
|
| 155 |
-
|
| 156 |
-
## 🔗 相关链接
|
| 157 |
-
|
| 158 |
-
- [主分支 (Node.js版本)](../README.md)
|
| 159 |
-
- [性能测试报告](benchmark.md)
|
| 160 |
-
- [API文档](api-docs.md)
|
| 161 |
-
|
| 162 |
-
## �� 许可证
|
| 163 |
-
|
| 164 |
-
MIT License
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -8,7 +8,7 @@ pinned: false
|
|
| 8 |
app_port: 8000
|
| 9 |
---
|
| 10 |
|
| 11 |
-
# API代理服务器 (
|
| 12 |
|
| 13 |
⚡ 支持多种AI API代理、网页代理与实时统计面板。
|
| 14 |
|
|
@@ -20,13 +20,14 @@ app_port: 8000
|
|
| 20 |
- 支持CORS跨域、自动转发常用请求头
|
| 21 |
- Gemini NoThink模式:自动为Gemini请求添加thinkingBudget: 0
|
| 22 |
- 安全特性:安全响应头、禁止爬虫、自动处理预检请求
|
|
|
|
| 23 |
|
| 24 |
## 快速开始
|
| 25 |
|
| 26 |
### 本地运行
|
| 27 |
```bash
|
| 28 |
-
|
| 29 |
-
|
| 30 |
# 默认监听8000端口
|
| 31 |
```
|
| 32 |
|
|
@@ -62,4 +63,4 @@ http://localhost:8000/proxy/https://platform.openai.com/docs
|
|
| 62 |
|
| 63 |
---
|
| 64 |
|
| 65 |
-
> 本项目已完全迁移为
|
|
|
|
| 8 |
app_port: 8000
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# API代理服务器 (Go 版本)
|
| 12 |
|
| 13 |
⚡ 支持多种AI API代理、网页代理与实时统计面板。
|
| 14 |
|
|
|
|
| 20 |
- 支持CORS跨域、自动转发常用请求头
|
| 21 |
- Gemini NoThink模式:自动为Gemini请求添加thinkingBudget: 0
|
| 22 |
- 安全特性:安全响应头、禁止爬虫、自动处理预检请求
|
| 23 |
+
- 高性能:基于Go语言,支持高并发处理
|
| 24 |
|
| 25 |
## 快速开始
|
| 26 |
|
| 27 |
### 本地运行
|
| 28 |
```bash
|
| 29 |
+
go mod download
|
| 30 |
+
go run main.go
|
| 31 |
# 默认监听8000端口
|
| 32 |
```
|
| 33 |
|
|
|
|
| 63 |
|
| 64 |
---
|
| 65 |
|
| 66 |
+
> 本项目已完全迁移为 Go 版本,提供更高性能和更好的并发处理能力。
|
go.mod
CHANGED
|
@@ -1,35 +1,39 @@
|
|
| 1 |
module deno-proxy
|
| 2 |
|
| 3 |
-
go 1.
|
|
|
|
|
|
|
| 4 |
|
| 5 |
require (
|
| 6 |
-
github.com/gin-contrib/cors v1.
|
| 7 |
-
github.com/gin-gonic/gin v1.
|
| 8 |
)
|
| 9 |
|
| 10 |
require (
|
| 11 |
-
github.com/bytedance/sonic v1.
|
| 12 |
-
github.com/
|
| 13 |
-
github.com/
|
| 14 |
-
github.com/
|
|
|
|
| 15 |
github.com/go-playground/locales v0.14.1 // indirect
|
| 16 |
github.com/go-playground/universal-translator v0.18.1 // indirect
|
| 17 |
-
github.com/go-playground/validator/v10 v10.
|
| 18 |
-
github.com/goccy/go-json v0.10.
|
| 19 |
github.com/json-iterator/go v1.1.12 // indirect
|
| 20 |
-
github.com/klauspost/cpuid/v2 v2.2.
|
| 21 |
-
github.com/
|
| 22 |
-
github.com/
|
|
|
|
| 23 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
| 24 |
github.com/modern-go/reflect2 v1.0.2 // indirect
|
| 25 |
-
github.com/pelletier/go-toml/v2 v2.
|
| 26 |
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
| 27 |
-
github.com/ugorji/go/codec v1.
|
| 28 |
-
golang.org/x/arch v0.
|
| 29 |
-
golang.org/x/crypto v0.
|
| 30 |
-
golang.org/x/net v0.
|
| 31 |
-
golang.org/x/sys v0.
|
| 32 |
-
golang.org/x/text v0.
|
| 33 |
-
google.golang.org/protobuf v1.
|
| 34 |
gopkg.in/yaml.v3 v3.0.1 // indirect
|
| 35 |
)
|
|
|
|
| 1 |
module deno-proxy
|
| 2 |
|
| 3 |
+
go 1.23.0
|
| 4 |
+
|
| 5 |
+
toolchain go1.24.4
|
| 6 |
|
| 7 |
require (
|
| 8 |
+
github.com/gin-contrib/cors v1.7.6
|
| 9 |
+
github.com/gin-gonic/gin v1.10.1
|
| 10 |
)
|
| 11 |
|
| 12 |
require (
|
| 13 |
+
github.com/bytedance/sonic v1.13.3 // indirect
|
| 14 |
+
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
| 15 |
+
github.com/cloudwego/base64x v0.1.5 // indirect
|
| 16 |
+
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
| 17 |
+
github.com/gin-contrib/sse v1.1.0 // indirect
|
| 18 |
github.com/go-playground/locales v0.14.1 // indirect
|
| 19 |
github.com/go-playground/universal-translator v0.18.1 // indirect
|
| 20 |
+
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
| 21 |
+
github.com/goccy/go-json v0.10.5 // indirect
|
| 22 |
github.com/json-iterator/go v1.1.12 // indirect
|
| 23 |
+
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
| 24 |
+
github.com/kr/text v0.2.0 // indirect
|
| 25 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
| 26 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
| 27 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
| 28 |
github.com/modern-go/reflect2 v1.0.2 // indirect
|
| 29 |
+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
| 30 |
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
| 31 |
+
github.com/ugorji/go/codec v1.3.0 // indirect
|
| 32 |
+
golang.org/x/arch v0.18.0 // indirect
|
| 33 |
+
golang.org/x/crypto v0.39.0 // indirect
|
| 34 |
+
golang.org/x/net v0.41.0 // indirect
|
| 35 |
+
golang.org/x/sys v0.33.0 // indirect
|
| 36 |
+
golang.org/x/text v0.26.0 // indirect
|
| 37 |
+
google.golang.org/protobuf v1.36.6 // indirect
|
| 38 |
gopkg.in/yaml.v3 v3.0.1 // indirect
|
| 39 |
)
|
go.sum
CHANGED
|
@@ -1,129 +1,92 @@
|
|
| 1 |
-
github.com/bytedance/sonic v1.
|
| 2 |
-
github.com/bytedance/sonic v1.
|
| 3 |
-
github.com/bytedance/sonic
|
| 4 |
-
github.com/
|
| 5 |
-
github.com/
|
| 6 |
-
github.com/
|
|
|
|
|
|
|
| 7 |
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
| 8 |
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 9 |
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 10 |
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 11 |
-
github.com/gabriel-vasile/mimetype v1.4.
|
| 12 |
-
github.com/gabriel-vasile/mimetype v1.4.
|
| 13 |
-
github.com/gin-contrib/cors v1.
|
| 14 |
-
github.com/gin-contrib/cors v1.
|
| 15 |
-
github.com/gin-contrib/sse
|
| 16 |
-
github.com/gin-contrib/sse
|
| 17 |
-
github.com/gin-gonic/gin v1.
|
| 18 |
-
github.com/gin-gonic/gin v1.
|
| 19 |
-
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
| 20 |
-
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
| 21 |
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
| 22 |
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
| 23 |
-
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
| 24 |
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
| 25 |
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
| 26 |
-
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
| 27 |
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
| 28 |
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
| 29 |
-
github.com/go-playground/validator/v10 v10.
|
| 30 |
-
github.com/go-playground/validator/v10 v10.
|
| 31 |
-
github.com/go-
|
| 32 |
-
github.com/goccy/go-json v0.
|
| 33 |
-
github.com/
|
| 34 |
-
github.com/
|
| 35 |
-
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
| 36 |
-
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
| 37 |
-
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
| 38 |
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 39 |
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
| 40 |
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
| 41 |
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
| 42 |
-
github.com/klauspost/cpuid/v2 v2.2.
|
| 43 |
-
github.com/klauspost/cpuid/v2 v2.2.
|
| 44 |
-
github.com/
|
| 45 |
-
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
| 46 |
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
| 47 |
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
| 48 |
-
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
| 49 |
-
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
| 50 |
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
| 51 |
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
| 52 |
-
github.com/leodido/go-urn v1.
|
| 53 |
-
github.com/leodido/go-urn v1.
|
| 54 |
-
github.com/
|
| 55 |
-
github.com/mattn/go-isatty v0.0.
|
| 56 |
-
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
| 57 |
-
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
| 58 |
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 59 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
| 60 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 61 |
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
| 62 |
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
| 63 |
-
github.com/pelletier/go-toml/v2 v2.
|
| 64 |
-
github.com/pelletier/go-toml/v2 v2.
|
| 65 |
-
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
| 66 |
-
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
| 67 |
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 68 |
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
| 69 |
-
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
| 70 |
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
| 71 |
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
| 72 |
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 73 |
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 74 |
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 75 |
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
| 76 |
-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 77 |
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 78 |
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 79 |
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 80 |
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
| 81 |
-
github.com/stretchr/testify v1.
|
| 82 |
-
github.com/stretchr/testify v1.
|
| 83 |
-
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 84 |
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
| 85 |
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
| 86 |
-
github.com/ugorji/go v1.
|
| 87 |
-
github.com/ugorji/go/codec v1.
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
golang.org/x/
|
| 91 |
-
golang.org/x/
|
| 92 |
-
golang.org/x/
|
| 93 |
-
golang.org/x/
|
| 94 |
-
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
| 95 |
-
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
| 96 |
-
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
| 97 |
-
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
| 98 |
-
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
| 99 |
-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 100 |
-
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 101 |
-
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 102 |
-
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 103 |
-
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 104 |
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 105 |
-
golang.org/x/sys v0.
|
| 106 |
-
golang.org/x/sys v0.
|
| 107 |
-
golang.org/x/
|
| 108 |
-
golang.org/x/text v0.
|
| 109 |
-
golang.org/
|
| 110 |
-
golang.org/
|
| 111 |
-
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
| 112 |
-
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
| 113 |
-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
| 114 |
-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 115 |
-
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
| 116 |
-
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
| 117 |
-
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
| 118 |
-
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
| 119 |
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 120 |
-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 121 |
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
| 122 |
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
| 123 |
-
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
| 124 |
-
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
| 125 |
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 126 |
-
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 127 |
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 128 |
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 129 |
-
|
|
|
|
| 1 |
+
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
| 2 |
+
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
| 3 |
+
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
| 4 |
+
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
| 5 |
+
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
| 6 |
+
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
| 7 |
+
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
| 8 |
+
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
| 9 |
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
| 10 |
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 11 |
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 12 |
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 13 |
+
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
| 14 |
+
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
| 15 |
+
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
| 16 |
+
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
| 17 |
+
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
| 18 |
+
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
| 19 |
+
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
| 20 |
+
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
|
|
|
|
|
|
| 21 |
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
| 22 |
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
|
|
| 23 |
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
| 24 |
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
|
|
|
| 25 |
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
| 26 |
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
| 27 |
+
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
| 28 |
+
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
| 29 |
+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
| 30 |
+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
| 31 |
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
| 32 |
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
|
|
|
|
|
|
|
|
| 33 |
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 34 |
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
| 35 |
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
| 36 |
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
| 37 |
+
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
| 38 |
+
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
| 39 |
+
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
|
|
|
| 40 |
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
| 41 |
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
|
|
|
|
|
| 42 |
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
| 43 |
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
| 44 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
| 45 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
| 46 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
| 47 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
|
|
|
|
|
| 48 |
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 49 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
| 50 |
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 51 |
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
| 52 |
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
| 53 |
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
| 54 |
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
|
|
|
|
|
|
| 55 |
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 56 |
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
|
|
| 57 |
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
| 58 |
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
| 59 |
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 60 |
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 61 |
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 62 |
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
|
|
| 63 |
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 64 |
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 65 |
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 66 |
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
| 67 |
+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
| 68 |
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
|
|
| 69 |
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
| 70 |
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
| 71 |
+
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
| 72 |
+
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
| 73 |
+
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
| 74 |
+
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
| 75 |
+
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
| 76 |
+
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
| 77 |
+
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
| 78 |
+
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 80 |
+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
| 81 |
+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
| 82 |
+
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
| 83 |
+
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
| 84 |
+
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
| 85 |
+
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
|
|
| 87 |
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
| 88 |
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
|
|
|
|
|
| 89 |
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
|
|
| 90 |
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 91 |
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 92 |
+
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
main.ts
DELETED
|
@@ -1,712 +0,0 @@
|
|
| 1 |
-
import { serve } from "https://deno.land/std/http/server.ts";
|
| 2 |
-
|
| 3 |
-
const apiMapping = {
|
| 4 |
-
"/discord": "https://discord.com/api",
|
| 5 |
-
"/telegram": "https://api.telegram.org",
|
| 6 |
-
"/openai": "https://api.openai.com",
|
| 7 |
-
"/claude": "https://api.anthropic.com",
|
| 8 |
-
"/gemini": "https://generativelanguage.googleapis.com",
|
| 9 |
-
"/gnothink": "https://generativelanguage.googleapis.com",
|
| 10 |
-
"/meta": "https://www.meta.ai/api",
|
| 11 |
-
"/groq": "https://api.groq.com/openai",
|
| 12 |
-
"/xai": "https://api.x.ai",
|
| 13 |
-
"/cohere": "https://api.cohere.ai",
|
| 14 |
-
"/huggingface": "https://api-inference.huggingface.co",
|
| 15 |
-
"/together": "https://api.together.xyz",
|
| 16 |
-
"/novita": "https://api.novita.ai",
|
| 17 |
-
"/portkey": "https://api.portkey.ai",
|
| 18 |
-
"/fireworks": "https://api.fireworks.ai",
|
| 19 |
-
"/openrouter": "https://openrouter.ai/api",
|
| 20 |
-
"/sophnet": "https://sophnet.com/",
|
| 21 |
-
"/cerebras":"https://api.cerebras.ai"
|
| 22 |
-
};
|
| 23 |
-
|
| 24 |
-
// Stats storage
|
| 25 |
-
const stats = {
|
| 26 |
-
total: 0,
|
| 27 |
-
endpoints: {} as Record<string, { total: number; today: number; week: number; month: number }>,
|
| 28 |
-
requests: [] as Array<{ endpoint: string; timestamp: number }>
|
| 29 |
-
};
|
| 30 |
-
|
| 31 |
-
// Initialize stats
|
| 32 |
-
for (const endpoint of Object.keys(apiMapping)) {
|
| 33 |
-
stats.endpoints[endpoint] = {
|
| 34 |
-
total: 0,
|
| 35 |
-
today: 0, // Aggregated count for last 24h
|
| 36 |
-
week: 0, // Aggregated count for last 7d
|
| 37 |
-
month: 0 // Aggregated count for last 30d
|
| 38 |
-
};
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
function recordRequest(endpoint) {
|
| 42 |
-
const now = Date.now();
|
| 43 |
-
stats.total++;
|
| 44 |
-
stats.endpoints[endpoint].total++;
|
| 45 |
-
stats.requests.push({ endpoint, timestamp: now });
|
| 46 |
-
|
| 47 |
-
// Clean up old requests (older than 30 days)
|
| 48 |
-
const thirtyDaysAgo = now - (30 * 24 * 60 * 60 * 1000);
|
| 49 |
-
stats.requests = stats.requests.filter(req => req.timestamp > thirtyDaysAgo);
|
| 50 |
-
|
| 51 |
-
updateSummaryStats(); // Update summary stats like today, week, month totals
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
function updateSummaryStats() {
|
| 55 |
-
const now = Date.now();
|
| 56 |
-
const oneDayAgo = now - (24 * 60 * 60 * 1000);
|
| 57 |
-
const sevenDaysAgo = now - (7 * 24 * 60 * 60 * 1000);
|
| 58 |
-
const thirtyDaysAgo = now - (30 * 24 * 60 * 60 * 1000);
|
| 59 |
-
|
| 60 |
-
for (const endpointKey of Object.keys(stats.endpoints)) {
|
| 61 |
-
stats.endpoints[endpointKey].today = 0;
|
| 62 |
-
stats.endpoints[endpointKey].week = 0;
|
| 63 |
-
stats.endpoints[endpointKey].month = 0;
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
for (const req of stats.requests) {
|
| 67 |
-
const endpointStats = stats.endpoints[req.endpoint];
|
| 68 |
-
if (!endpointStats) continue; // Should not happen if initialized correctly
|
| 69 |
-
|
| 70 |
-
if (req.timestamp > oneDayAgo) {
|
| 71 |
-
endpointStats.today++;
|
| 72 |
-
}
|
| 73 |
-
if (req.timestamp > sevenDaysAgo) {
|
| 74 |
-
endpointStats.week++;
|
| 75 |
-
}
|
| 76 |
-
if (req.timestamp > thirtyDaysAgo) { // This will always be true due to cleanup, but good for clarity
|
| 77 |
-
endpointStats.month++;
|
| 78 |
-
}
|
| 79 |
-
}
|
| 80 |
-
}
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
function generateStatsHTML(request) {
|
| 84 |
-
updateSummaryStats(); // Ensure summary stats are up-to-date
|
| 85 |
-
|
| 86 |
-
const url = new URL(request.url);
|
| 87 |
-
const currentDomain = `${url.protocol}//${url.host}`;
|
| 88 |
-
|
| 89 |
-
const sophnetStats = stats.endpoints["/sophnet"] || { today: 0, week: 0, month: 0, total: 0 };
|
| 90 |
-
const openaiStats = stats.endpoints["/openai"] || { today: 0, week: 0, month: 0, total: 0 };
|
| 91 |
-
const geminiStats = stats.endpoints["/gemini"] || { today: 0, week: 0, month: 0, total: 0 };
|
| 92 |
-
const claudeStats = stats.endpoints["/claude"] || { today: 0, week: 0, month: 0, total: 0 };
|
| 93 |
-
const xaiStats = stats.endpoints["/xai"] || { today: 0, week: 0, month: 0, total: 0 };
|
| 94 |
-
|
| 95 |
-
return `
|
| 96 |
-
<!DOCTYPE html>
|
| 97 |
-
<html lang="zh-CN">
|
| 98 |
-
<head>
|
| 99 |
-
<meta charset="UTF-8">
|
| 100 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 101 |
-
<title>API代理服务器 - 统计面板</title>
|
| 102 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
|
| 103 |
-
<style>
|
| 104 |
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 105 |
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; }
|
| 106 |
-
.container { max-width: 1400px; margin: 0 auto; }
|
| 107 |
-
.header { text-align: center; color: white; margin-bottom: 40px; }
|
| 108 |
-
.header h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 0 2px 4px rgba(0,0,0,0.3); }
|
| 109 |
-
.header p { font-size: 1.1rem; opacity: 0.9; }
|
| 110 |
-
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 40px; }
|
| 111 |
-
.chart-section { background: rgba(255, 255, 255, 0.95); border-radius: 16px; padding: 24px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); margin-bottom: 40px; }
|
| 112 |
-
.chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 16px; }
|
| 113 |
-
.chart-title { font-size: 1.5rem; color: #333; font-weight: 600; }
|
| 114 |
-
.time-tabs { display: flex; gap: 8px; background: #f1f5f9; padding: 4px; border-radius: 12px; }
|
| 115 |
-
.time-tab { padding: 8px 16px; border: none; background: transparent; border-radius: 8px; cursor: pointer; font-size: 0.9rem; font-weight: 500; color: #64748b; transition: all 0.3s ease; }
|
| 116 |
-
.time-tab.active { background: #6366f1; color: white; box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3); }
|
| 117 |
-
.time-tab:hover:not(.active) { background: #e2e8f0; color: #334155; }
|
| 118 |
-
.chart-container { position: relative; height: 400px; margin-bottom: 20px; }
|
| 119 |
-
.chart-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 20px; }
|
| 120 |
-
.stat-card { background: rgba(255, 255, 255, 0.95); border-radius: 16px; padding: 24px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
| 121 |
-
.stat-card:hover { transform: translateY(-5px); box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15); }
|
| 122 |
-
.stat-card h3 { font-size: 1.2rem; color: #333; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
|
| 123 |
-
.api-icon { width: 24px; height: 24px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: white; }
|
| 124 |
-
.openai-icon { background: #10a37f; } .gemini-icon { background: #4285f4; } .claude-icon { background: #d97706; } .xai-icon { background: #000000; } .total-icon { background: #6366f1; }
|
| 125 |
-
.stat-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid #eee; }
|
| 126 |
-
.stat-row:last-child { border-bottom: none; }
|
| 127 |
-
.stat-label { color: #666; font-size: 0.9rem; }
|
| 128 |
-
.stat-value { font-size: 1.1rem; font-weight: 600; color: #333; }
|
| 129 |
-
.usage-guide { background: rgba(255, 255, 255, 0.95); border-radius: 16px; padding: 32px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); }
|
| 130 |
-
.usage-guide h2 { color: #333; margin-bottom: 20px; font-size: 1.5rem; }
|
| 131 |
-
.endpoint-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px; margin: 20px 0; }
|
| 132 |
-
.endpoint-item { background: #f8f9fa; padding: 16px; border-radius: 8px; border-left: 4px solid #6366f1; transition: all 0.3s ease; cursor: pointer; }
|
| 133 |
-
.endpoint-item:hover { background: #f1f5f9; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }
|
| 134 |
-
.endpoint-path { font-weight: bold; color: #6366f1; margin-bottom: 4px; font-family: 'Courier New', monospace; }
|
| 135 |
-
.endpoint-url { font-size: 0.8rem; color: #666; word-break: break-all; font-family: 'Courier New', monospace; }
|
| 136 |
-
.example-section { margin-top: 24px; padding-top: 24px; border-top: 1px solid #eee; }
|
| 137 |
-
.example-section h3 { color: #333; margin-bottom: 12px; }
|
| 138 |
-
.code-block { background: #1a1a1a; color: #f8f8f2; padding: 16px; border-radius: 8px; font-family: 'Courier New', monospace; font-size: 0.9rem; overflow-x: auto; margin: 12px 0; white-space: pre-wrap; word-wrap: break-word; line-height: 1.4; }
|
| 139 |
-
.refresh-btn { position: fixed; bottom: 30px; right: 30px; background: #6366f1; color: white; border: none; border-radius: 50px; padding: 12px 24px; font-size: 1rem; cursor: pointer; box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3); transition: all 0.3s ease; z-index: 1000; }
|
| 140 |
-
.refresh-btn:hover { background: #5855eb; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4); }
|
| 141 |
-
.toast { position: fixed; top: 20px; right: 20px; background: #10b981; color: white; padding: 12px 20px; border-radius: 8px; font-size: 14px; z-index: 1001; opacity: 0; transform: translateX(100%); transition: all 0.3s ease; }
|
| 142 |
-
.toast.show { opacity: 1; transform: translateX(0); }
|
| 143 |
-
.chart-legend { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; padding-top: 16px; border-top: 1px solid #e2e8f0; }
|
| 144 |
-
.legend-item { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; }
|
| 145 |
-
.legend-color { width: 12px; height: 12px; border-radius: 2px; }
|
| 146 |
-
.legend-line { width: 16px; height: 3px; border-radius: 2px; }
|
| 147 |
-
.no-data { text-align: center; color: #64748b; font-style: italic; padding: 40px 0; }
|
| 148 |
-
.chart-info { background: #f8fafc; padding: 12px 16px; border-radius: 8px; margin-bottom: 16px; border-left: 4px solid #6366f1; }
|
| 149 |
-
.chart-info p { color: #64748b; font-size: 0.9rem; margin: 0; }
|
| 150 |
-
@media (max-width: 768px) {
|
| 151 |
-
.stats-grid { grid-template-columns: 1fr; }
|
| 152 |
-
.endpoint-list { grid-template-columns: 1fr; }
|
| 153 |
-
.header h1 { font-size: 2rem; }
|
| 154 |
-
.chart-grid { grid-template-columns: 1fr; }
|
| 155 |
-
.chart-header { flex-direction: column; align-items: stretch; }
|
| 156 |
-
.time-tabs { justify-self: stretch; }
|
| 157 |
-
}
|
| 158 |
-
</style>
|
| 159 |
-
</head>
|
| 160 |
-
<body>
|
| 161 |
-
<div class="container">
|
| 162 |
-
<div class="header"><h1>🚀 API代理服务器</h1><p>实时统计与使用指南</p></div>
|
| 163 |
-
|
| 164 |
-
<div class="chart-section">
|
| 165 |
-
<div class="chart-header">
|
| 166 |
-
<h2 class="chart-title">📊 API调用统计图表</h2>
|
| 167 |
-
<div class="time-tabs">
|
| 168 |
-
<button class="time-tab active" data-period="today">24小时</button>
|
| 169 |
-
<button class="time-tab" data-period="week">7天</button>
|
| 170 |
-
<button class="time-tab" data-period="month">30天</button>
|
| 171 |
-
<button class="time-tab" data-period="total">总计</button>
|
| 172 |
-
</div>
|
| 173 |
-
</div>
|
| 174 |
-
<div class="chart-info"><p>📊 组合图表:蓝色柱状图显示总API调用次数,红色折线图显示总体调用趋势。选择上方时间范围查看不同维度数据。</p></div>
|
| 175 |
-
<div class="chart-grid">
|
| 176 |
-
<div class="chart-container"><canvas id="apiChart"></canvas></div>
|
| 177 |
-
<div><div class="chart-legend" id="chartLegend"></div></div>
|
| 178 |
-
</div>
|
| 179 |
-
</div>
|
| 180 |
-
|
| 181 |
-
<div class="stats-grid">
|
| 182 |
-
<div class="stat-card"><h3><div class="api-icon openai-icon">AI</div>sophnet API 调用统计</h3><div class="stat-row"><span class="stat-label">24小时</span><span class="stat-value">${sophnetStats.today}</span></div><div class="stat-row"><span class="stat-label">7天</span><span class="stat-value">${sophnetStats.week}</span></div><div class="stat-row"><span class="stat-label">30天</span><span class="stat-value">${sophnetStats.month}</span></div><div class="stat-row"><span class="stat-label">总计</span><span class="stat-value">${sophnetStats.total}</span></div></div>
|
| 183 |
-
<div class="stat-card"><h3><div class="api-icon gemini-icon">G</div>Gemini API 调用统计</h3><div class="stat-row"><span class="stat-label">24小时</span><span class="stat-value">${geminiStats.today}</span></div><div class="stat-row"><span class="stat-label">7天</span><span class="stat-value">${geminiStats.week}</span></div><div class="stat-row"><span class="stat-label">30天</span><span class="stat-value">${geminiStats.month}</span></div><div class="stat-row"><span class="stat-label">总计</span><span class="stat-value">${geminiStats.total}</span></div></div>
|
| 184 |
-
<div class="stat-card"><h3><div class="api-icon claude-icon">C</div>Claude API 调用统计</h3><div class="stat-row"><span class="stat-label">24小时</span><span class="stat-value">${claudeStats.today}</span></div><div class="stat-row"><span class="stat-label">7天</span><span class="stat-value">${claudeStats.week}</span></div><div class="stat-row"><span class="stat-label">30天</span><span class="stat-value">${claudeStats.month}</span></div><div class="stat-row"><span class="stat-label">总计</span><span class="stat-value">${claudeStats.total}</span></div></div>
|
| 185 |
-
<div class="stat-card"><h3><div class="api-icon xai-icon">X</div>XAI API 调用统计</h3><div class="stat-row"><span class="stat-label">24小时</span><span class="stat-value">${xaiStats.today}</span></div><div class="stat-row"><span class="stat-label">7天</span><span class="stat-value">${xaiStats.week}</span></div><div class="stat-row"><span class="stat-label">30天</span><span class="stat-value">${xaiStats.month}</span></div><div class="stat-row"><span class="stat-label">总计</span><span class="stat-value">${xaiStats.total}</span></div></div>
|
| 186 |
-
<div class="stat-card"><h3><div class="api-icon total-icon">📊</div>总体统计</h3><div class="stat-row"><span class="stat-label">总请求数</span><span class="stat-value">${stats.total}</span></div><div class="stat-row"><span class="stat-label">活跃端点</span><span class="stat-value">${Object.keys(stats.endpoints).filter(k => stats.endpoints[k].total > 0).length}</span></div><div class="stat-row"><span class="stat-label">服务状态</span><span class="stat-value" style="color: #10b981;">🟢 运行中</span></div></div>
|
| 187 |
-
</div>
|
| 188 |
-
|
| 189 |
-
<div class="usage-guide">
|
| 190 |
-
<h2>📖 使用说明</h2>
|
| 191 |
-
<h3>支持的API端点</h3>
|
| 192 |
-
<div class="endpoint-list">${Object.keys(apiMapping).map(endpoint => `<div class="endpoint-item" title="点击复制完整地址"><div class="endpoint-path">${endpoint}</div><div class="endpoint-url">${currentDomain}${endpoint}</div></div>`).join('')}</div>
|
| 193 |
-
<div class="example-section">
|
| 194 |
-
<h3>🔧 使用方法</h3><p style="margin-bottom: 16px; color: #666;">将原始API地址替换为代理地址,例如:</p>
|
| 195 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">OpenAI API 示例:</h4><div class="code-block"># 原始地址\nhttps://api.openai.com/v1/chat/completions\n\n# 代理地址\n${currentDomain}/openai/v1/chat/completions</div>
|
| 196 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">Gemini API 示例:</h4><div class="code-block"># 原始地址\nhttps://generativelanguage.googleapis.com/v1/models\n\n# 代理地址\n${currentDomain}/gemini/v1/models</div>
|
| 197 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">Gemini NoThink API 示例:</h4><div class="code-block"># 原始地址\nhttps://generativelanguage.googleapis.com/v1/models/gemini-2.0-flash-thinking-exp:generateContent\n\n# 代理地址(自动禁用思考模式)\n${currentDomain}/gnothink/v1/models/gemini-2.0-flash-thinking-exp:generateContent</div>
|
| 198 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">Claude API 示例:</h4><div class="code-block"># 原始地址\nhttps://api.anthropic.com/v1/messages\n\n# 代理地址\n${currentDomain}/claude/v1/messages</div>
|
| 199 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">XAI API 示例:</h4><div class="code-block"># 原始地址\nhttps://api.x.ai/v1/chat/completions\n\n# 代理地址\n${currentDomain}/xai/v1/chat/completions</div>
|
| 200 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">cURL 示例:</h4><div class="code-block">curl -X POST ${currentDomain}/openai/v1/chat/completions \\\n -H "Content-Type: application/json" \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -d '{\n "model": "gpt-3.5-turbo",\n "messages": [{"role": "user", "content": "Hello!"}]\n }'</div>
|
| 201 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">JavaScript 示例:</h4><div class="code-block">// 使用 fetch API\nconst response = await fetch('${currentDomain}/openai/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer YOUR_API_KEY'\n },\n body: JSON.stringify({\n model: 'gpt-3.5-turbo',\n messages: [{ role: 'user', content: 'Hello!' }]\n })\n});\n\nconst data = await response.json();\nconsole.log(data);</div>
|
| 202 |
-
<h4 style="margin: 16px 0 8px 0; color: #333;">Python 示例:</h4><div class="code-block">import requests\n\nurl = "${currentDomain}/openai/v1/chat/completions"\nheaders = {\n "Content-Type": "application/json",\n "Authorization": "Bearer YOUR_API_KEY"\n}\ndata = {\n "model": "gpt-3.5-turbo",\n "messages": [{"role": "user", "content": "Hello!"}]\n}\n\nresponse = requests.post(url, headers=headers, json=data)\nprint(response.json())</div>
|
| 203 |
-
</div>
|
| 204 |
-
<div class="example-section"><h3>🌐 代理模式</h3><p style="margin-bottom: 16px; color: #666;">支持完整网页代理,可以直接在浏览器中访问被代理的网站:</p><div class="code-block"># 代理任意网站\n${currentDomain}/proxy/https://example.com\n\n# 代理API文档\n${currentDomain}/proxy/https://platform.openai.com/docs</div></div>
|
| 205 |
-
<div class="example-section"><h3>⚡ 特性</h3><ul style="margin-left: 20px; color: #666; line-height: 1.6;"><li>✅ 支持所有HTTP方法 (GET, POST, PUT, DELETE等)</li><li>✅ 自动转发请求头和响应头</li><li>✅ 支持CORS跨域请求</li><li>✅ 实时统计API调用次数</li><li>✅ 代理模式支持完整网页浏览</li><li>✅ 自动获取当前域名,无需手动配置</li><li>✅ 组合图表展示调用统计和趋势</li><li>✅ Gemini NoThink模式:自动为Gemini请求添加thinkingBudget: 0禁用思考模式</li></ul></div>
|
| 206 |
-
<div class="example-section"><h3>🔒 安全特性</h3><ul style="margin-left: 20px; color: #666; line-height: 1.6;"><li>🛡️ 设置安全响应头 (X-Frame-Options, X-Content-Type-Options等)</li><li>🛡️ 过滤和转发指定的请求头</li><li>🛡️ 禁止搜索引擎爬取 (robots.txt)</li><li>🛡️ 自动处理CORS预检请求</li></ul></div>
|
| 207 |
-
<div class="example-section"><h3>📊 统计功能</h3><ul style="margin-left: 20px; color: #666; line-height: 1.6;"><li>📈 实时统计API调用次数</li><li>📈 支持多时间维度统计(24h/7d/30d/总计)</li><li>📈 重点监控OpenAI、Gemini、Claude和XAI API使用量</li><li>📈 提供JSON格式统计API: <code style="background: #f1f5f9; padding: 2px 6px; border-radius: 4px;">${currentDomain}/stats</code></li><li>📈 组合图表展示,柱状图+折线图显示数据和趋势</li></ul></div>
|
| 208 |
-
</div>
|
| 209 |
-
</div>
|
| 210 |
-
<button class="refresh-btn" onclick="location.reload()">🔄 刷新数据</button>
|
| 211 |
-
<div id="toast" class="toast"></div>
|
| 212 |
-
|
| 213 |
-
<script>
|
| 214 |
-
const rawStatsData = ${JSON.stringify(stats)};
|
| 215 |
-
let chartInstance = null;
|
| 216 |
-
let currentPeriod = 'today';
|
| 217 |
-
const barColors = ['#6366f1', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#84cc16', '#f97316', '#ec4899', '#64748b', '#14b8a6', '#a855f7', '#eab308', '#22c55e', '#3b82f6'];
|
| 218 |
-
|
| 219 |
-
function getChartDataForPeriod(period, allRequests, endpointDetails) {
|
| 220 |
-
const now = Date.now();
|
| 221 |
-
let labels = [];
|
| 222 |
-
let aggregatedData = [];
|
| 223 |
-
|
| 224 |
-
if (period === 'today') {
|
| 225 |
-
const hourlyCounts = Array(24).fill(0);
|
| 226 |
-
const firstHourTimestamp = new Date(now - 23 * 60 * 60 * 1000);
|
| 227 |
-
firstHourTimestamp.setMinutes(0, 0, 0);
|
| 228 |
-
|
| 229 |
-
for (let i = 0; i < 24; i++) {
|
| 230 |
-
const hour = new Date(firstHourTimestamp);
|
| 231 |
-
hour.setHours(firstHourTimestamp.getHours() + i);
|
| 232 |
-
labels.push(hour.getHours().toString().padStart(2, '0') + ':00');
|
| 233 |
-
}
|
| 234 |
-
|
| 235 |
-
const twentyFourHoursAgo = now - 24 * 60 * 60 * 1000;
|
| 236 |
-
allRequests.filter(req => req.timestamp >= twentyFourHoursAgo)
|
| 237 |
-
.forEach(req => {
|
| 238 |
-
const reqHour = new Date(req.timestamp);
|
| 239 |
-
// Find the correct bucket relative to the firstHourTimestamp
|
| 240 |
-
const diffHours = Math.floor((reqHour.getTime() - firstHourTimestamp.getTime()) / (60 * 60 * 1000));
|
| 241 |
-
if (diffHours >= 0 && diffHours < 24) {
|
| 242 |
-
hourlyCounts[diffHours]++;
|
| 243 |
-
}
|
| 244 |
-
});
|
| 245 |
-
aggregatedData = hourlyCounts;
|
| 246 |
-
} else if (period === 'week' || period === 'month') {
|
| 247 |
-
const numDays = period === 'week' ? 7 : 30;
|
| 248 |
-
const dailyCounts = Array(numDays).fill(0);
|
| 249 |
-
const firstDayTimestamp = new Date(now);
|
| 250 |
-
firstDayTimestamp.setDate(firstDayTimestamp.getDate() - (numDays - 1));
|
| 251 |
-
firstDayTimestamp.setHours(0, 0, 0, 0);
|
| 252 |
-
|
| 253 |
-
for (let i = 0; i < numDays; i++) {
|
| 254 |
-
const day = new Date(firstDayTimestamp);
|
| 255 |
-
day.setDate(firstDayTimestamp.getDate() + i);
|
| 256 |
-
labels.push(day.getFullYear() + '-' + (day.getMonth() + 1).toString().padStart(2, '0') + '-' + day.getDate().toString().padStart(2, '0'));
|
| 257 |
-
}
|
| 258 |
-
|
| 259 |
-
const periodStartTimestamp = firstDayTimestamp.getTime();
|
| 260 |
-
allRequests.filter(req => req.timestamp >= periodStartTimestamp)
|
| 261 |
-
.forEach(req => {
|
| 262 |
-
const reqDay = new Date(req.timestamp);
|
| 263 |
-
reqDay.setHours(0,0,0,0);
|
| 264 |
-
const diffDays = Math.floor((reqDay.getTime() - firstDayTimestamp.getTime()) / (24 * 60 * 60 * 1000));
|
| 265 |
-
if (diffDays >= 0 && diffDays < numDays) {
|
| 266 |
-
dailyCounts[diffDays]++;
|
| 267 |
-
}
|
| 268 |
-
});
|
| 269 |
-
aggregatedData = dailyCounts;
|
| 270 |
-
} else if (period === 'total') {
|
| 271 |
-
const activeEndpoints = Object.keys(endpointDetails).filter(ep => endpointDetails[ep].total > 0);
|
| 272 |
-
labels = activeEndpoints.map(ep => ep.replace('/', ''));
|
| 273 |
-
aggregatedData = activeEndpoints.map(ep => endpointDetails[ep].total);
|
| 274 |
-
}
|
| 275 |
-
return { labels, data: aggregatedData };
|
| 276 |
-
}
|
| 277 |
-
|
| 278 |
-
function createCombinedChart(period) {
|
| 279 |
-
const ctx = document.getElementById('apiChart').getContext('2d');
|
| 280 |
-
if (chartInstance) chartInstance.destroy();
|
| 281 |
-
|
| 282 |
-
const chartData = getChartDataForPeriod(period, rawStatsData.requests, rawStatsData.endpoints);
|
| 283 |
-
|
| 284 |
-
if (chartData.labels.length === 0) {
|
| 285 |
-
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
| 286 |
-
ctx.fillStyle = '#64748b'; ctx.font = '16px Arial'; ctx.textAlign = 'center';
|
| 287 |
-
ctx.fillText('暂无数据', ctx.canvas.width / 2, ctx.canvas.height / 2);
|
| 288 |
-
updateLegend(period, { labels: [], barData: [] }); // Pass empty barData for legend
|
| 289 |
-
return;
|
| 290 |
-
}
|
| 291 |
-
|
| 292 |
-
const xAxisTitle = period === 'total' ? 'API 端点' : (period === 'today' ? '小时 (过去24小时)' : '日期');
|
| 293 |
-
|
| 294 |
-
const chartConfig = {
|
| 295 |
-
type: 'bar',
|
| 296 |
-
data: {
|
| 297 |
-
labels: chartData.labels,
|
| 298 |
-
datasets: [
|
| 299 |
-
{
|
| 300 |
-
type: 'bar',
|
| 301 |
-
label: 'API调用次数',
|
| 302 |
-
data: chartData.data,
|
| 303 |
-
backgroundColor: period === 'total'
|
| 304 |
-
? chartData.labels.map((_, i) => barColors[i % barColors.length] + 'B3') // B3 for ~70% opacity
|
| 305 |
-
: '#6366f1B3',
|
| 306 |
-
borderColor: period === 'total'
|
| 307 |
-
? chartData.labels.map((_, i) => barColors[i % barColors.length])
|
| 308 |
-
: '#6366f1',
|
| 309 |
-
borderWidth: 1.5,
|
| 310 |
-
yAxisID: 'y',
|
| 311 |
-
order: 2 // Ensure bars are behind the line
|
| 312 |
-
},
|
| 313 |
-
{
|
| 314 |
-
type: 'line',
|
| 315 |
-
label: '调用趋势',
|
| 316 |
-
data: chartData.data,
|
| 317 |
-
borderColor: '#ef4444',
|
| 318 |
-
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
| 319 |
-
borderWidth: 2.5,
|
| 320 |
-
pointBackgroundColor: '#ef4444',
|
| 321 |
-
pointBorderColor: 'white',
|
| 322 |
-
pointBorderWidth: 1.5,
|
| 323 |
-
pointRadius: period === 'total' ? 4 : (period === 'today' ? 3 : 4),
|
| 324 |
-
pointHoverRadius: period === 'total' ? 6 : (period === 'today' ? 5 : 6),
|
| 325 |
-
fill: false,
|
| 326 |
-
tension: (period === 'today' || period === 'total') ? 0.1 : 0.3, // Smoother for daily
|
| 327 |
-
yAxisID: 'y', // Use the same Y-axis for simplicity
|
| 328 |
-
order: 1 // Ensure line is in front
|
| 329 |
-
}
|
| 330 |
-
]
|
| 331 |
-
},
|
| 332 |
-
options: {
|
| 333 |
-
responsive: true, maintainAspectRatio: false,
|
| 334 |
-
interaction: { mode: 'index', intersect: false },
|
| 335 |
-
plugins: {
|
| 336 |
-
legend: { display: true, position: 'top', labels: { usePointStyle: true, padding: 20, font: { size: 12 } } },
|
| 337 |
-
tooltip: {
|
| 338 |
-
backgroundColor: 'rgba(0,0,0,0.85)', titleColor: 'white', bodyColor: 'white',
|
| 339 |
-
borderColor: '#6366f1', borderWidth: 1,
|
| 340 |
-
callbacks: {
|
| 341 |
-
label: function(context) {
|
| 342 |
-
let label = context.dataset.label || '';
|
| 343 |
-
if (label) label += ': ';
|
| 344 |
-
if (context.parsed.y !== null) label += context.parsed.y + ' 次';
|
| 345 |
-
|
| 346 |
-
if (period === 'total') {
|
| 347 |
-
const total = chartData.data.reduce((a, b) => a + b, 0);
|
| 348 |
-
if (total > 0) {
|
| 349 |
-
const percentage = ((context.raw / total) * 100).toFixed(1);
|
| 350 |
-
label += ' (' + percentage + '%)';
|
| 351 |
-
}
|
| 352 |
-
}
|
| 353 |
-
return label;
|
| 354 |
-
}
|
| 355 |
-
}
|
| 356 |
-
}
|
| 357 |
-
},
|
| 358 |
-
scales: {
|
| 359 |
-
x: {
|
| 360 |
-
title: { display: true, text: xAxisTitle, color: '#333', font: { weight: 'bold' } },
|
| 361 |
-
ticks: { color: '#64748b', maxRotation: period === 'month' ? 45 : 0, minRotation: 0 },
|
| 362 |
-
grid: { color: '#e2e8f0' }
|
| 363 |
-
},
|
| 364 |
-
y: {
|
| 365 |
-
beginAtZero: true,
|
| 366 |
-
ticks: { color: '#64748b', precision: 0 }, // Ensure whole numbers for counts
|
| 367 |
-
grid: { color: '#e2e8f0' },
|
| 368 |
-
title: { display: true, text: '调用次数', color: '#333', font: { weight: 'bold' } }
|
| 369 |
-
}
|
| 370 |
-
// Removed y1 axis for simplicity, both datasets use 'y'
|
| 371 |
-
},
|
| 372 |
-
animation: { duration: 800, easing: 'easeOutQuart' }
|
| 373 |
-
}
|
| 374 |
-
};
|
| 375 |
-
chartInstance = new Chart(ctx, chartConfig);
|
| 376 |
-
updateLegend(period, chartData);
|
| 377 |
-
}
|
| 378 |
-
|
| 379 |
-
function updateLegend(period, chartData) {
|
| 380 |
-
const legendContainer = document.getElementById('chartLegend');
|
| 381 |
-
legendContainer.innerHTML = ''; // Clear previous legend
|
| 382 |
-
|
| 383 |
-
if (chartData.labels.length === 0) {
|
| 384 |
-
legendContainer.innerHTML = '<div class="no-data">期间内无调用数据</div>';
|
| 385 |
-
return;
|
| 386 |
-
}
|
| 387 |
-
|
| 388 |
-
if (period === 'total') {
|
| 389 |
-
const totalOverall = chartData.data.reduce((sum, item) => sum + item, 0);
|
| 390 |
-
const legendItemsHtml = chartData.labels.map((label, index) => {
|
| 391 |
-
const value = chartData.data[index];
|
| 392 |
-
const percentage = totalOverall > 0 ? ((value / totalOverall) * 100).toFixed(1) : 0;
|
| 393 |
-
return '<div class="legend-item">' +
|
| 394 |
-
'<div class="legend-color" style="background-color: ' + barColors[index % barColors.length] + '"></div>' +
|
| 395 |
-
'<span>' + label + ': ' + value + ' 次 (' + percentage + '%)</span>' +
|
| 396 |
-
'</div>';
|
| 397 |
-
}).join('');
|
| 398 |
-
legendContainer.innerHTML = legendItemsHtml;
|
| 399 |
-
} else { // For 'today', 'week', 'month'
|
| 400 |
-
const periodText = period === 'today' ? '24小时' : (period === 'week' ? '7天' : '30天');
|
| 401 |
-
legendContainer.innerHTML =
|
| 402 |
-
'<div class="legend-item">' +
|
| 403 |
-
'<div class="legend-color" style="background-color: #6366f1"></div>' +
|
| 404 |
-
'<span>总调用次数 (柱状)</span>' +
|
| 405 |
-
'</div>' +
|
| 406 |
-
'<div class="legend-item">' +
|
| 407 |
-
'<div class="legend-line" style="background-color: #ef4444"></div>' +
|
| 408 |
-
'<span>调用趋势 (折线)</span>' +
|
| 409 |
-
'</div>' +
|
| 410 |
-
'<p style="font-size: 0.85rem; color: #666; margin-top: 10px;">' +
|
| 411 |
-
'显示过去 ' + periodText + ' 的总调��数据。' +
|
| 412 |
-
'</p>';
|
| 413 |
-
}
|
| 414 |
-
}
|
| 415 |
-
|
| 416 |
-
function switchPeriod(newPeriod) {
|
| 417 |
-
currentPeriod = newPeriod;
|
| 418 |
-
document.querySelectorAll('.time-tab').forEach(tab => tab.classList.remove('active'));
|
| 419 |
-
document.querySelector('[data-period="' + newPeriod + '"]').classList.add('active');
|
| 420 |
-
createCombinedChart(currentPeriod);
|
| 421 |
-
}
|
| 422 |
-
|
| 423 |
-
setInterval(() => { location.reload(); }, 60000);
|
| 424 |
-
|
| 425 |
-
function showToast(message) {
|
| 426 |
-
const toast = document.getElementById('toast');
|
| 427 |
-
toast.textContent = message; toast.classList.add('show');
|
| 428 |
-
setTimeout(() => { toast.classList.remove('show'); }, 3000);
|
| 429 |
-
}
|
| 430 |
-
|
| 431 |
-
function copyToClipboard(text) {
|
| 432 |
-
if (navigator.clipboard) {
|
| 433 |
-
navigator.clipboard.writeText(text).then(() => showToast('已复制: ' + text), () => fallbackCopy(text));
|
| 434 |
-
} else { fallbackCopy(text); }
|
| 435 |
-
}
|
| 436 |
-
function fallbackCopy(text) {
|
| 437 |
-
const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta);
|
| 438 |
-
ta.select();
|
| 439 |
-
try { document.execCommand('copy'); showToast('已复制: ' + text); }
|
| 440 |
-
catch (err) { showToast('复制失败'); }
|
| 441 |
-
document.body.removeChild(ta);
|
| 442 |
-
}
|
| 443 |
-
|
| 444 |
-
document.addEventListener('DOMContentLoaded', function() {
|
| 445 |
-
createCombinedChart(currentPeriod);
|
| 446 |
-
document.querySelectorAll('.time-tab').forEach(tab => {
|
| 447 |
-
tab.addEventListener('click', function() { switchPeriod(this.dataset.period); });
|
| 448 |
-
});
|
| 449 |
-
document.querySelectorAll('.endpoint-item').forEach(item => {
|
| 450 |
-
item.addEventListener('click', function() {
|
| 451 |
-
const url = this.querySelector('.endpoint-url').textContent.trim();
|
| 452 |
-
copyToClipboard(url);
|
| 453 |
-
const originalBg = this.style.backgroundColor, originalBorder = this.style.borderLeftColor;
|
| 454 |
-
this.style.backgroundColor = '#dcfce7'; this.style.borderLeftColor = '#16a34a';
|
| 455 |
-
setTimeout(() => { this.style.backgroundColor = originalBg; this.style.borderLeftColor = originalBorder; }, 1000);
|
| 456 |
-
});
|
| 457 |
-
});
|
| 458 |
-
});
|
| 459 |
-
</script>
|
| 460 |
-
</body>
|
| 461 |
-
</html>`;
|
| 462 |
-
}
|
| 463 |
-
|
| 464 |
-
// Deno server logic (serve, recordRequest, apiMapping etc.) remains largely the same
|
| 465 |
-
// Ensure updateSummaryStats is called when generating HTML or if stats data is fetched via /stats API
|
| 466 |
-
serve(async (request) => {
|
| 467 |
-
const url = new URL(request.url);
|
| 468 |
-
const pathname = url.pathname;
|
| 469 |
-
|
| 470 |
-
if (pathname === "/" || pathname === "/index.html") {
|
| 471 |
-
return new Response(generateStatsHTML(request), {
|
| 472 |
-
status: 200,
|
| 473 |
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
| 474 |
-
});
|
| 475 |
-
}
|
| 476 |
-
|
| 477 |
-
if (pathname === "/robots.txt") {
|
| 478 |
-
return new Response("User-agent: *\nDisallow: /", {
|
| 479 |
-
status: 200,
|
| 480 |
-
headers: { "Content-Type": "text/plain" },
|
| 481 |
-
});
|
| 482 |
-
}
|
| 483 |
-
|
| 484 |
-
if (pathname === "/stats") {
|
| 485 |
-
updateSummaryStats(); // Make sure summary is up-to-date for the API
|
| 486 |
-
return new Response(JSON.stringify(stats, null, 2), {
|
| 487 |
-
status: 200,
|
| 488 |
-
headers: {
|
| 489 |
-
"Content-Type": "application/json",
|
| 490 |
-
"Access-Control-Allow-Origin": "*" // Allow CORS for stats API
|
| 491 |
-
},
|
| 492 |
-
});
|
| 493 |
-
}
|
| 494 |
-
|
| 495 |
-
// Proxy mode
|
| 496 |
-
if (pathname.startsWith("/proxy/")) {
|
| 497 |
-
try {
|
| 498 |
-
const fullUrl = url.href;
|
| 499 |
-
// Correctly extract targetUrl, considering potential query params in currentDomain part
|
| 500 |
-
const proxyPathIndex = url.pathname.indexOf("/proxy/");
|
| 501 |
-
const targetUrlString = url.pathname.substring(proxyPathIndex + "/proxy/".length) + url.search + url.hash;
|
| 502 |
-
|
| 503 |
-
if (!targetUrlString || !targetUrlString.startsWith("http")) {
|
| 504 |
-
return new Response("Invalid proxy URL. Must start with http:// or https:// after /proxy/", { status: 400 });
|
| 505 |
-
}
|
| 506 |
-
const targetUrl = new URL(targetUrlString);
|
| 507 |
-
const baseUrl = `${targetUrl.protocol}//${targetUrl.host}`;
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
const headers = new Headers();
|
| 511 |
-
const allowedHeaders = ["accept", "content-type", "authorization", "user-agent", "accept-encoding", "accept-language", "cache-control", "pragma", "x-requested-with"];
|
| 512 |
-
request.headers.forEach((value, key) => {
|
| 513 |
-
if (allowedHeaders.includes(key.toLowerCase()) || key.toLowerCase().startsWith("sec-") || key.toLowerCase().startsWith("x-")) {
|
| 514 |
-
headers.set(key, value);
|
| 515 |
-
}
|
| 516 |
-
});
|
| 517 |
-
// Crucial for some sites to work, but be mindful of security implications if any
|
| 518 |
-
if (request.headers.has("referer")) {
|
| 519 |
-
headers.set("Referer", request.headers.get("referer").replace(url.origin, targetUrl.origin));
|
| 520 |
-
}
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
const response = await fetch(targetUrl.toString(), {
|
| 524 |
-
method: request.method,
|
| 525 |
-
headers: headers,
|
| 526 |
-
body: request.method !== "GET" && request.method !== "HEAD" ? request.body : undefined,
|
| 527 |
-
redirect: "manual" // Handle redirects manually if needed, or 'follow'
|
| 528 |
-
});
|
| 529 |
-
|
| 530 |
-
const responseHeaders = new Headers(response.headers);
|
| 531 |
-
const origin = request.headers.get("Origin");
|
| 532 |
-
if (origin) {
|
| 533 |
-
responseHeaders.set("Access-Control-Allow-Origin", origin);
|
| 534 |
-
responseHeaders.set("Access-Control-Allow-Credentials", "true");
|
| 535 |
-
} else {
|
| 536 |
-
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
| 537 |
-
}
|
| 538 |
-
responseHeaders.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH");
|
| 539 |
-
responseHeaders.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, " + allowedHeaders.join(", "));
|
| 540 |
-
responseHeaders.set("Access-Control-Max-Age", "86400");
|
| 541 |
-
|
| 542 |
-
// Security headers (can be adjusted)
|
| 543 |
-
responseHeaders.set("X-Content-Type-Options", "nosniff");
|
| 544 |
-
responseHeaders.delete("X-Frame-Options"); // Or set to SAMEORIGIN if proxying own content
|
| 545 |
-
responseHeaders.set("Referrer-Policy", "no-referrer-when-downgrade"); // Common policy
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
if (request.method === "OPTIONS") {
|
| 549 |
-
return new Response(null, { status: 204, headers: responseHeaders });
|
| 550 |
-
}
|
| 551 |
-
|
| 552 |
-
// Handle redirects by rewriting Location header
|
| 553 |
-
if (response.status >= 300 && response.status < 400 && response.headers.has("location")) {
|
| 554 |
-
let newLocation = response.headers.get("location");
|
| 555 |
-
// If location is relative, prepend the target's base URL
|
| 556 |
-
if (newLocation && newLocation.startsWith("/")) {
|
| 557 |
-
newLocation = `${baseUrl}${newLocation}`;
|
| 558 |
-
}
|
| 559 |
-
// Rewrite the location to go through the proxy
|
| 560 |
-
if (newLocation) {
|
| 561 |
-
responseHeaders.set("Location", `${url.origin}/proxy/${newLocation}`);
|
| 562 |
-
}
|
| 563 |
-
return new Response(null, { status: response.status, headers: responseHeaders });
|
| 564 |
-
}
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
const contentType = responseHeaders.get("content-type") || "";
|
| 568 |
-
if (contentType.includes("text/html")) {
|
| 569 |
-
let text = await response.text();
|
| 570 |
-
// Basic HTML rewriting (can be very complex for modern SPAs)
|
| 571 |
-
const currentProxyBase = `${url.origin}/proxy/`;
|
| 572 |
-
text = text.replace(/(href|src|action)=["']\/(?!\/)/gi, `$1="${currentProxyBase}${baseUrl}/`);
|
| 573 |
-
text = text.replace(/(href|src|action)=["'](https?:\/\/[^"']+)/gi, (match, attr, originalUrl) => {
|
| 574 |
-
return `${attr}="${currentProxyBase}${originalUrl}"`;
|
| 575 |
-
});
|
| 576 |
-
// Rewrite srcset
|
| 577 |
-
text = text.replace(/srcset=["']([^"']+)["']/gi, (match, srcset) => {
|
| 578 |
-
const newSrcset = srcset.split(',').map(s => {
|
| 579 |
-
const parts = s.trim().split(/\s+/);
|
| 580 |
-
let u = parts[0];
|
| 581 |
-
if (u.startsWith('/')) u = `${baseUrl}${u}`;
|
| 582 |
-
return `${currentProxyBase}${u}${parts[1] ? ' ' + parts[1] : ''}`;
|
| 583 |
-
}).join(', ');
|
| 584 |
-
return `srcset="${newSrcset}"`;
|
| 585 |
-
});
|
| 586 |
-
// Remove integrity attributes as content is modified
|
| 587 |
-
text = text.replace(/\s+integrity=["'][^"']+["']/gi, '');
|
| 588 |
-
// Attempt to fix base href if present
|
| 589 |
-
text = text.replace(/<base\s+href=["']([^"']+)["'][^>]*>/gi, (match, baseHrefVal) => {
|
| 590 |
-
let newBase = baseHrefVal;
|
| 591 |
-
if(baseHrefVal.startsWith('/')) newBase = `${baseUrl}${baseHrefVal}`;
|
| 592 |
-
return `<base href="${currentProxyBase}${newBase}">`;
|
| 593 |
-
});
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
return new Response(text, { status: response.status, headers: responseHeaders });
|
| 597 |
-
} else if (contentType.includes("text/css")) {
|
| 598 |
-
let text = await response.text();
|
| 599 |
-
// Rewrite url() in CSS
|
| 600 |
-
const currentProxyBase = `${url.origin}/proxy/`;
|
| 601 |
-
text = text.replace(/url\(([^)]+)\)/gi, (match, cssUrl) => {
|
| 602 |
-
let u = cssUrl.trim().replace(/["']/g, '');
|
| 603 |
-
if (u.startsWith('data:') || u.startsWith('#')) return match; // Skip data URIs and fragments
|
| 604 |
-
if (u.startsWith('/')) u = `${baseUrl}${u}`;
|
| 605 |
-
else if (!u.startsWith('http')) u = `${new URL(u, targetUrl.toString()).href}`; // Resolve relative URLs
|
| 606 |
-
return `url(${currentProxyBase}${u})`;
|
| 607 |
-
});
|
| 608 |
-
return new Response(text, { status: response.status, headers: responseHeaders });
|
| 609 |
-
}
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
return new Response(response.body, { status: response.status, headers: responseHeaders });
|
| 613 |
-
} catch (error) {
|
| 614 |
-
console.error("Proxy request failed:", error.message, error.stack);
|
| 615 |
-
return new Response("Proxy Request Failed: " + error.message, { status: 502 }); // Bad Gateway
|
| 616 |
-
}
|
| 617 |
-
}
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
const [prefix, rest] = extractPrefixAndRest(pathname, Object.keys(apiMapping));
|
| 621 |
-
if (!prefix) {
|
| 622 |
-
return new Response("Not Found", { status: 404 });
|
| 623 |
-
}
|
| 624 |
-
|
| 625 |
-
recordRequest(prefix);
|
| 626 |
-
const targetApiUrl = `${apiMapping[prefix]}${rest}${url.search}`;
|
| 627 |
-
|
| 628 |
-
try {
|
| 629 |
-
const headers = new Headers();
|
| 630 |
-
// Forward specific headers, be selective for security
|
| 631 |
-
const commonApiHeaders = ["content-type", "authorization", "accept", "anthropic-version"];
|
| 632 |
-
request.headers.forEach((value, key) => {
|
| 633 |
-
if (commonApiHeaders.includes(key.toLowerCase()) || key.toLowerCase().startsWith("x-")) {
|
| 634 |
-
headers.set(key, value);
|
| 635 |
-
}
|
| 636 |
-
});
|
| 637 |
-
|
| 638 |
-
// Add required headers for specific APIs
|
| 639 |
-
if (prefix === "/claude" && !headers.has("anthropic-version")) {
|
| 640 |
-
headers.set("anthropic-version", "2023-06-01");
|
| 641 |
-
}
|
| 642 |
-
|
| 643 |
-
// Add user-agent if not present, some APIs might require it
|
| 644 |
-
if (!headers.has("user-agent")) {
|
| 645 |
-
headers.set("user-agent", "Deno-API-Proxy/1.0");
|
| 646 |
-
}
|
| 647 |
-
|
| 648 |
-
// Handle special processing for gnothink
|
| 649 |
-
let requestBody: BodyInit | null = null;
|
| 650 |
-
if (prefix === "/gnothink" && request.method === "POST" && request.body && headers.get("content-type")?.includes("application/json")) {
|
| 651 |
-
const originalBodyText = await request.text();
|
| 652 |
-
if (originalBodyText) {
|
| 653 |
-
const bodyJson = JSON.parse(originalBodyText);
|
| 654 |
-
|
| 655 |
-
// Add thinkingBudget: 0 to disable thinking mode
|
| 656 |
-
bodyJson.generationConfig = {
|
| 657 |
-
...(bodyJson.generationConfig || {}),
|
| 658 |
-
thinkingConfig: {
|
| 659 |
-
thinkingBudget: 0
|
| 660 |
-
}
|
| 661 |
-
};
|
| 662 |
-
|
| 663 |
-
requestBody = JSON.stringify(bodyJson);
|
| 664 |
-
} else {
|
| 665 |
-
requestBody = null;
|
| 666 |
-
}
|
| 667 |
-
} else if (request.method !== "GET" && request.method !== "HEAD" && request.body) {
|
| 668 |
-
requestBody = request.body;
|
| 669 |
-
}
|
| 670 |
-
|
| 671 |
-
const apiResponse = await fetch(targetApiUrl, {
|
| 672 |
-
method: request.method,
|
| 673 |
-
headers: headers,
|
| 674 |
-
body: requestBody,
|
| 675 |
-
});
|
| 676 |
-
|
| 677 |
-
const responseHeaders = new Headers(apiResponse.headers);
|
| 678 |
-
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
| 679 |
-
responseHeaders.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH");
|
| 680 |
-
responseHeaders.set("Access-Control-Allow-Headers", "Content-Type, Authorization, anthropic-version, " + commonApiHeaders.join(", "));
|
| 681 |
-
|
| 682 |
-
// Security headers
|
| 683 |
-
responseHeaders.set("X-Content-Type-Options", "nosniff");
|
| 684 |
-
responseHeaders.set("X-Frame-Options", "DENY"); // APIs shouldn't be framed
|
| 685 |
-
responseHeaders.set("Referrer-Policy", "no-referrer");
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
if (request.method === "OPTIONS") {
|
| 689 |
-
return new Response(null, { status: 204, headers: responseHeaders });
|
| 690 |
-
}
|
| 691 |
-
|
| 692 |
-
return new Response(apiResponse.body, {
|
| 693 |
-
status: apiResponse.status,
|
| 694 |
-
headers: responseHeaders,
|
| 695 |
-
});
|
| 696 |
-
} catch (error) {
|
| 697 |
-
console.error("API proxy fetch failed:", error);
|
| 698 |
-
return new Response("Internal Server Error during API proxy", { status: 500 });
|
| 699 |
-
}
|
| 700 |
-
});
|
| 701 |
-
|
| 702 |
-
function extractPrefixAndRest(pathname, prefixes) {
|
| 703 |
-
for (const prefix of prefixes) {
|
| 704 |
-
if (pathname.startsWith(prefix)) {
|
| 705 |
-
return [prefix, pathname.slice(prefix.length)];
|
| 706 |
-
}
|
| 707 |
-
}
|
| 708 |
-
return [null, null];
|
| 709 |
-
}
|
| 710 |
-
|
| 711 |
-
console.log("🚀 API代理服务器已启动 (Deno)");
|
| 712 |
-
console.log("🕒 统计数据每分钟自动刷新页面");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "api-proxy-server",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"description": "API代理服务器 - 支持多种AI API代理和统计功能",
|
| 5 |
-
"main": "server.js",
|
| 6 |
-
"type": "module",
|
| 7 |
-
"scripts": {
|
| 8 |
-
"start": "node server.js",
|
| 9 |
-
"dev": "node --watch server.js"
|
| 10 |
-
},
|
| 11 |
-
"keywords": [
|
| 12 |
-
"api",
|
| 13 |
-
"proxy",
|
| 14 |
-
"openai",
|
| 15 |
-
"gemini",
|
| 16 |
-
"claude",
|
| 17 |
-
"xai",
|
| 18 |
-
"cors"
|
| 19 |
-
],
|
| 20 |
-
"author": "",
|
| 21 |
-
"license": "MIT",
|
| 22 |
-
"dependencies": {
|
| 23 |
-
"express": "^4.18.3",
|
| 24 |
-
"node-fetch": "^3.3.2",
|
| 25 |
-
"cors": "^2.8.5",
|
| 26 |
-
"http-proxy-middleware": "^3.0.0"
|
| 27 |
-
},
|
| 28 |
-
"engines": {
|
| 29 |
-
"node": ">=18.0.0"
|
| 30 |
-
}
|
| 31 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|