caidaohz commited on
Commit
77241ac
·
1 Parent(s): 7d052e7

✨ 完成Go版本API代理服务器的迁移

Browse files

- 将项目从Node.js迁移至Go语言,提升性能和并发处理能力
- 更新Dockerfile以支持Go构建流程
- 删除旧的Node.js相关文件,包括package.json和main.ts
- 更新go.mod和go.sum以反映新的依赖版本
- 更新README.md以反映项目的Go版本特性和使用说明

Files changed (8) hide show
  1. Dockerfile +43 -5
  2. Dockerfile-go +0 -48
  3. README-go.md +0 -164
  4. README.md +5 -4
  5. go.mod +24 -20
  6. go.sum +48 -85
  7. main.ts +0 -712
  8. package.json +0 -31
Dockerfile CHANGED
@@ -1,10 +1,48 @@
1
- FROM node:20-alpine
 
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
- CMD ["node", "server.js"]
 
 
 
 
 
 
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代理服务器 (Node.js 版本)
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
- npm install
29
- npm start
30
  # 默认监听8000端口
31
  ```
32
 
@@ -62,4 +63,4 @@ http://localhost:8000/proxy/https://platform.openai.com/docs
62
 
63
  ---
64
 
65
- > 本项目已完全迁移为 Node.js 版本,原 Deno 版本已废弃。
 
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.21
 
 
4
 
5
  require (
6
- github.com/gin-contrib/cors v1.4.0
7
- github.com/gin-gonic/gin v1.9.1
8
  )
9
 
10
  require (
11
- github.com/bytedance/sonic v1.9.1 // indirect
12
- github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
13
- github.com/gabriel-vasile/mimetype v1.4.2 // indirect
14
- github.com/gin-contrib/sse v0.1.0 // indirect
 
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.14.0 // indirect
18
- github.com/goccy/go-json v0.10.2 // indirect
19
  github.com/json-iterator/go v1.1.12 // indirect
20
- github.com/klauspost/cpuid/v2 v2.2.4 // indirect
21
- github.com/leodido/go-urn v1.2.4 // indirect
22
- github.com/mattn/go-isatty v0.0.19 // indirect
 
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.0.8 // indirect
26
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
27
- github.com/ugorji/go/codec v1.2.11 // indirect
28
- golang.org/x/arch v0.3.0 // indirect
29
- golang.org/x/crypto v0.9.0 // indirect
30
- golang.org/x/net v0.10.0 // indirect
31
- golang.org/x/sys v0.8.0 // indirect
32
- golang.org/x/text v0.9.0 // indirect
33
- google.golang.org/protobuf v1.30.0 // indirect
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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
2
- github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
3
- github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
4
- github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
5
- github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
6
- github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
 
 
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.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
12
- github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
13
- github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
14
- github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
15
- github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
16
- github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
17
- github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
18
- github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
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.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
30
- github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
31
- github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
32
- github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
33
- github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
34
- github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
43
- github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
44
- github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
53
- github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
54
- github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
55
- github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
64
- github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
82
- github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
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.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
87
- github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
88
- github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
89
- github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
90
- golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
91
- golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
92
- golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
93
- golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
106
- golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
107
- golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
108
- golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
109
- golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
110
- golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
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
- rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 
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
- }