├── .gitignore ├── LICENSE ├── README.md ├── about.html ├── assets ├── css │ └── style.css ├── img │ └── Authorization.png └── js │ └── openai.js ├── cf_worker.js ├── cf_worker_old.js ├── check.sh ├── docs ├── cloudflare_pages.md ├── cloudflare_proxy_pages.md ├── cloudflare_workers.md └── img │ ├── Cloudflare_Worker1.png │ ├── Cloudflare_Worker2.png │ ├── Cloudflare_Worker3.png │ ├── Cloudflare_Worker4.png │ ├── Cloudflare_Worker5.png │ ├── Cloudflare_Worker6.png │ ├── Cloudflare_pages1.png │ ├── Cloudflare_pages2.png │ ├── Cloudflare_pages3.png │ ├── Cloudflare_pages4.png │ ├── Cloudflare_pages5.png │ ├── Cloudflare_pages6.png │ ├── Cloudflare_pages7.png │ ├── jpg.webp │ ├── pages │ ├── pages1.png │ ├── pages2.png │ ├── pages3.png │ ├── pages4.png │ └── pages5.png │ ├── post.png │ └── worker.png ├── functions ├── helloworld.js └── v1 │ └── [[path]].js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | 133 | test* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 x-dr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *经测试有些ip访问时会显示下图* 2 | 3 | 4 | 5 | 6 | --- 7 | ### Demo 8 | 9 | [https://chatai.451024.xyz](https://chatai.451024.xyz) 10 | 11 | > api 12 | 13 | ```url 14 | https://openai.451024.xyz 15 | ``` 16 | 17 | 18 | ```url 19 | https://openai-proxy-api.pages.dev/api 20 | ``` 21 | *** 22 | 23 | > 新项目 [基于OpenAI的微信机器人](https://github.com/x-dr/wechat-bot) 24 | 25 | *** 26 | 27 | ### 演示站为公共服务,如有大规模使用需求请自行部署,演示站有点不堪重负 28 | 29 | ![worker](./docs/img/worker.png) 30 | 31 | 32 | ## 利用Cloudflare Worker中转api.openai.com 33 | 34 | 1. 新建一个 Cloudflare Worker 35 | 2. 复制 [cf_worker.js](https://cdn.jsdelivr.net/gh/x-dr/chatgptProxyAPI@main/cf_worker.js) 里的代码粘贴到 Worker 中并部署 36 | 3. 给 Worker 绑定一个没有被墙的域名 37 | 4. 使用自己的域名代替 api.openai.com 38 | 39 | 40 | **[详细教程](./docs/cloudflare_workers.md)** 41 | 42 | 43 | 44 | *** 45 | 46 | 47 | 48 | ## 使用CloudFlare Pages进行中转 49 | 50 | ### 1、部署中转API+ Openai API余额查询 (使用sess-xxxx的Authorization查询,有效时间未知) 51 | 52 | > [官方文档](https://developers.cloudflare.com/pages) 53 | 54 | 1. ~~Fork本项目~~ 点击[Use this template](https://github.com/x-dr/chatgptProxyAPI/generate)按钮创建一个新的代码库。 55 | 2. 登录到[Cloudflare](https://dash.cloudflare.com/)控制台. 56 | 3. 在帐户主页中,选择`pages`> ` Create a project` > `Connect to Git` 57 | 4. 选择你 Fork 的项目存储库,在`Set up builds and deployments`部分中,全部默认即可。 58 | 59 | 60 | 5. 点击`Save and Deploy`部署,然后点`Continue to project`即可看到访问域名 61 | 62 | 63 | > 把官方接口的`https://api.openai.com`替换为`https://xxx.pages.dev` 即可 64 | 65 | **Demo** 66 | 67 | [https://chatai.451024.xyz](https://chatai.451024.xyz) 68 | 69 | **[详细教程](./docs/cloudflare_pages.md)** 70 | 71 | 72 | ### 2、只部署中转API 73 | 74 | 75 | **[详细教程](./docs/cloudflare_proxy_pages.md)** 76 | 77 | 78 | 79 | 80 | ## docker 部署(要境外vps) 81 | 82 | > 好像不支持sse 所以不建议 83 | 84 |
85 | 86 | e.g. 87 | 88 | ```bash 89 | docker run -itd --name openaiproxy \ 90 | -p 3000:3000 \ 91 | --restart=always \ 92 | gindex/openaiproxy:latest 93 | ``` 94 | 95 | #### 使用 96 | 97 | *api : http://vpsip:3000/proxy/v1/chat/completions* 98 | 99 | ```bash 100 | curl --location 'http://vpsip:3000/proxy/v1/chat/completions' \ 101 | --header 'Authorization: Bearer sk-xxxxxxxxxxxxxxx' \ 102 | --header 'Content-Type: application/json' \ 103 | --data '{ 104 | "model": "gpt-3.5-turbo", 105 | "messages": [{"role": "user", "content": "Hello!"}] 106 | }' 107 | 108 | ``` 109 | 110 |
111 | 112 | 113 | 114 | ## 用法 115 | 116 | 117 |
118 | 119 | JavaScript用fetch 120 | 121 | ```javascript 122 | const requestOptions = { 123 | method: 'POST', 124 | headers: { 125 | "Authorization": "Bearer sk-xxxxxxxxxxxx", 126 | "Content-Type": "application/json" 127 | }, 128 | body: JSON.stringify({ 129 | "model": "gpt-3.5-turbo", 130 | "messages": [ 131 | { 132 | "role": "user", 133 | "content": "hello word" 134 | } 135 | ] 136 | }) 137 | }; 138 | 139 | fetch("https://openai.1rmb.tk/v1/chat/completions", requestOptions) 140 | .then(response => response.text()) 141 | .then(result => console.log(result)) 142 | .catch(error => console.log('error', error)); 143 | 144 | ``` 145 | 146 |
147 | 148 | 149 | 150 |
151 | 152 | 用python 153 | 154 | ```python 155 | import requests 156 | 157 | url = "https://openai.1rmb.tk/v1/chat/completions" 158 | api_key = 'sk-xxxxxxxxxxxxxxxxxxxx' 159 | 160 | headers = { 161 | 'Authorization': f'Bearer {api_key}', 162 | 'Content-Type': 'application/json' 163 | } 164 | 165 | payload = { 166 | "model": "gpt-3.5-turbo", 167 | "messages": [ 168 | { 169 | "role": "user", 170 | "content": "hello word" 171 | } 172 | ] 173 | } 174 | 175 | try: 176 | response = requests.post(url, headers=headers, json=payload) 177 | response.raise_for_status() # 抛出异常,如果响应码不是200 178 | data = response.json() 179 | print(data) 180 | except requests.exceptions.RequestException as e: 181 | print(f"请求错误: {e}") 182 | except json.JSONDecodeError as e: 183 | print(f"无效的 JSON 响应: {e}") 184 | ``` 185 | 186 |
187 | 188 | 189 | 190 |
191 | 用nodejs chatgpt库 192 | 193 | [transitive-bullshit/chatgpt-api](https://github.com/transitive-bullshit/chatgpt-api) 194 | 195 | ```javascript 196 | import { ChatGPTAPI } from 'chatgpt' 197 | 198 | async function example() { 199 | const api = new ChatGPTAPI({ 200 | apiKey: "sk-xxxxxxxxxxxxxx", 201 | // proxy+/v1 202 | apiBaseUrl:"https://openai.1rmb.tk/v1" 203 | 204 | 205 | }) 206 | 207 | const res = await api.sendMessage('Hello World!') 208 | console.log(res.text) 209 | } 210 | 211 | example() 212 | 213 | ``` 214 | 215 |
216 | 217 | 218 | 219 |
220 | 221 | 查询余额 222 | 223 | ```javascript 224 | const headers = { 225 | 'content-type': 'application/json', 226 | 'Authorization': `Bearer sk-xxxxxxxxxxxxxxxxx` 227 | } 228 | // 查是否订阅 229 | const subscription = await fetch("https://openai.1rmb.tk/v1/dashboard/billing/subscription", { 230 | method: 'get', 231 | headers: headers 232 | }) 233 | if (!subscription.ok) { 234 | const data = await subscription.json() 235 | // console.log(data); 236 | return data 237 | // throw new Error('API request failed') 238 | } else { 239 | const subscriptionData = await subscription.json() 240 | const endDate = subscriptionData.access_until 241 | const startDate = new Date(endDate - 90 * 24 * 60 * 60); 242 | console.log(formatDate(endDate, "YYYY-MM-DD")); 243 | console.log(formatDate(startDate, "YYYY-MM-DD")); 244 | const response = await fetch(`https://openai.1rmb.tk/v1/dashboard/billing/usage?start_date=${formatDate(startDate, "YYYY-MM-DD")}&end_date=${formatDate(endDate, "YYYY-MM-DD")}`, { 245 | method: 'get', 246 | headers: headers 247 | }) 248 | 249 | const usageData = await response.json(); 250 | // 账号类型 251 | const plan = subscriptionData.plan.id 252 | console.log(usageData); 253 | } 254 | 255 | ``` 256 | 257 |
258 | 259 | ## Star History 260 | 261 | [![Star History Chart](https://api.star-history.com/svg?repos=x-dr/chatgptProxyAPI&type=Date)](https://star-history.com/#x-dr/chatgptProxyAPI&Date) 262 | 263 | -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | OpenAI/ChatGPT 工具箱 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 33 | 34 |
35 | 38 | 39 | 45 |
46 | 47 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /assets/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-primary: #5c7cfa; 3 | --color-primary-dark: #4263eb; 4 | --color-primary-alpha: #5c7cfa50; 5 | 6 | --body-color: #495057; 7 | --body-bg: #f8f9fa; 8 | 9 | --border-color: #dee2e6; 10 | } 11 | 12 | * { 13 | font-family: -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Helvetica, pingfang sc, hiragino sans gb, microsoft yahei, SimSun, sans-serif; 14 | font-size: 16px; 15 | font-weight: 400 16 | } 17 | 18 | body {} 19 | 20 | .footer { 21 | width: 100%; 22 | background-color: #f5f5f5; 23 | z-index: 9999; 24 | padding: 10px 10px; 25 | position: fixed; 26 | bottom: 0 27 | } 28 | 29 | .krajee-default .file-caption-info, 30 | .krajee-default .file-size-info { 31 | height: auto !important 32 | } 33 | 34 | .krajee-default .file-thumb-progress { 35 | top: 50px !important 36 | } 37 | 38 | .kv-main { 39 | margin-bottom: 100px !important 40 | } 41 | 42 | .kv-main h2 { 43 | font-size: 25px 44 | } 45 | 46 | .page-header { 47 | padding-bottom: 9px; 48 | margin: 30px 0 20px; 49 | border-bottom: 1px solid #eee 50 | } 51 | 52 | .page-footer { 53 | padding-bottom: 9px; 54 | margin: 30px 0 20px; 55 | border-top: 1px solid #eee 56 | } 57 | 58 | .container .text-muted { 59 | line-height: 20px; 60 | padding-left: 0; 61 | position: relative; 62 | margin-bottom: 10px; 63 | text-align: center; 64 | } 65 | 66 | .text-muted span { 67 | display: block; 68 | font-size: 11px 69 | } 70 | 71 | .text-muted { 72 | margin-bottom: 10px !important 73 | } 74 | 75 | .kv-main h2 em { 76 | font-size: .5em; 77 | color: #aaa 78 | } 79 | 80 | .footer>.container { 81 | padding-right: 15px; 82 | padding-left: 15px 83 | } 84 | 85 | /* pre { 86 | display: block; 87 | padding: 10px; 88 | margin: 0 0 10.5px; 89 | font-size: 14px; 90 | line-height: 1.42857143; 91 | word-break: break-all; 92 | word-wrap: break-word; 93 | color: #333; 94 | background-color: #f5f5f5; 95 | border: 1px solid #ccc; 96 | border-radius: 0 97 | } 98 | 99 | code { 100 | font-size: 80% 101 | } 102 | 103 | #uploadconfig { 104 | border-radius: 5px; 105 | border: 1px solid #ddd; 106 | padding: 10px; 107 | width: 100%; 108 | margin-bottom: 5px 109 | } 110 | 111 | .panel-heading-sm { 112 | height: 28px; 113 | padding: 5px 10px 114 | } 115 | 116 | .panel-body-sm { 117 | padding: 5px 10px 118 | } 119 | 120 | .file-preview-thumbnails { 121 | max-height: 1000px; 122 | overflow: overlay 123 | } 124 | 125 | fieldset { 126 | border: 1px solid rgba(0, 0, 0, .125); 127 | padding: 15px 20px; 128 | } 129 | 130 | legend { 131 | font-size: 14px; 132 | font-weight: 700; 133 | color: #232323; 134 | padding-left: 8px; 135 | padding-right: 8px; 136 | margin-bottom: 0; 137 | border-bottom: 0; 138 | width: inherit 139 | } 140 | 141 | fieldset legend img { 142 | vertical-align: middle; 143 | line-height: 16px; 144 | height: 16px; 145 | padding-right: 5px 146 | } 147 | 148 | .padding10 { 149 | padding-left: 10px 150 | } 151 | 152 | .tab-content { 153 | border-left: 1px solid #ddd; 154 | border-right: 1px solid #ddd 155 | } 156 | 157 | .dlinput_header { 158 | font-size: 11px; 159 | margin-top: 10px 160 | } 161 | 162 | .accepr_box { 163 | margin-bottom: 130px 164 | } 165 | 166 | .content-box { 167 | background-color: #c4e3ff; 168 | padding: 10px; 169 | margin-top: 20px; 170 | border: 1px solid #a6cef3; 171 | border-radius: 3px; 172 | padding: 10px 20px 173 | } 174 | 175 | .content-box::before { 176 | font-size: 100px 177 | } 178 | 179 | .content-box span { 180 | padding: 5px; 181 | margin-right: 20px; 182 | color: #2b557c 183 | } 184 | 185 | .content-box a { 186 | color: #155724; 187 | background-color: #90caff; 188 | border-radius: 3px; 189 | margin: 0 5px; 190 | font-size: 14px; 191 | padding: 2px 9px; 192 | text-decoration: none; 193 | transition: .3s all ease 194 | } 195 | 196 | .content-box a:hover { 197 | background-color: #78bfff 198 | } */ 199 | 200 | 201 | .fixed-top { 202 | position: unset; 203 | top: 0; 204 | right: 0; 205 | left: 0 206 | } 207 | 208 | 209 | /* main { 210 | padding-left: 2rem; 211 | padding-right: 2rem; 212 | } */ 213 | 214 | main[x-cloak] { 215 | opacity: 0; 216 | } 217 | 218 | main:not([x-cloak]) { 219 | opacity: 1; 220 | transition: opacity .3s; 221 | } 222 | 223 | 224 | textarea { 225 | -webkit-appearance: none; 226 | appearance: none; 227 | display: block; 228 | width: 100%; 229 | padding: .5rem 1rem; 230 | border: 1px solid var(--border-color); 231 | border-radius: .25rem; 232 | box-sizing: border-box; 233 | color: #33404d; 234 | line-height: inherit; 235 | font-size: 1rem; 236 | transition: border .3s, box-shadow .3s; 237 | } 238 | 239 | textarea:focus { 240 | box-shadow: 0 0 0 .25rem var(--color-primary-alpha); 241 | border-color: var(--color-primary); 242 | outline: 0; 243 | } 244 | 245 | 246 | .success, 247 | .error { 248 | margin-bottom: 1rem; 249 | padding: .5rem 1rem; 250 | border-radius: .25rem; 251 | color: #fff; 252 | text-align: center; 253 | opacity: 1; 254 | transition: opacity .3s; 255 | } 256 | 257 | .success { 258 | border: 1px solid #12b886; 259 | background: #38d9a9; 260 | } 261 | 262 | .error { 263 | border: 1px solid #fa5252; 264 | background: #ff8787; 265 | } 266 | 267 | @keyframes rotate { 268 | 100% { 269 | transform: rotate(360deg); 270 | } 271 | } 272 | 273 | 274 | /* button { 275 | appearance: none; 276 | display: flex; 277 | justify-content: center; 278 | align-items: center; 279 | margin-bottom: 5rem; 280 | padding: .5rem .75rem; 281 | border: 1px solid var(--color-primary); 282 | border-radius: .25rem; 283 | background: var(--color-primary); 284 | color: #fff; 285 | font-size: 1rem; 286 | font-weight: 500; 287 | line-height: inherit; 288 | cursor: pointer; 289 | user-select: none; 290 | transition: border .3s, background .3s, ; 291 | } */ 292 | 293 | button:hover { 294 | border-color: var(--color-primary-dark); 295 | background: var(--color-primary-dark); 296 | } 297 | 298 | button:focus { 299 | box-shadow: 0 0 0 .25rem var(--color-primary-alpha); 300 | border-color: var(--color-primary); 301 | outline: 0; 302 | } 303 | 304 | button:disabled { 305 | background: var(--color-primary); 306 | border-color: var(--color-primary); 307 | opacity: .6; 308 | cursor: not-allowed; 309 | } 310 | 311 | button.loading::before { 312 | content: ''; 313 | display: inline-block; 314 | margin-right: .5rem; 315 | border: 2px solid #fff; 316 | border-top-color: transparent; 317 | border-bottom-color: transparent; 318 | border-radius: 50%; 319 | width: .75rem; 320 | height: .75rem; 321 | animation: rotate .5s linear infinite; 322 | } 323 | 324 | 325 | .proxy{ 326 | color: #0077cc; 327 | text-decoration: none; 328 | background-color: #f0f0f0; 329 | padding: 5px 10px; 330 | border-radius: 5px; 331 | box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); 332 | } 333 | 334 | 335 | -------------------------------------------------------------------------------- /assets/img/Authorization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/assets/img/Authorization.png -------------------------------------------------------------------------------- /assets/js/openai.js: -------------------------------------------------------------------------------- 1 | //参考自 https://github.com/open-tdp/openai-chat 2 | const app = { 3 | u: '', 4 | alert: null, 5 | total: null, 6 | loading: false, 7 | isValidated() { 8 | const inputText = this.u.trim(); 9 | // 使用正则表达式匹配以 "sk-" 开头的密钥,并排除非匹配项 10 | const regex = /^(sk-|sess-).{21,}$/; 11 | const keys = inputText.split('\n').filter(key => regex.test(key.trim())); 12 | return keys 13 | }, 14 | 15 | clear() { 16 | this.total = this.total.filter(item => { 17 | return item.total_available && (item.total_available > 0 || item.total_available === "查询失败"); 18 | }); 19 | this.u = this.total.map(item => item.key).join('\n'); 20 | this.alert = { type: 'success', message: '清理完成' }; 21 | }, 22 | 23 | async submit($refs) { 24 | if (!this.u) { 25 | this.alert = { type: 'error', message: '请输入以sess-开头的Authorization...' } 26 | return 27 | } 28 | const keys = this.isValidated(); 29 | if (keys.length === 0) { 30 | this.alert = { type: 'error', message: '非法key格式' } 31 | return 32 | } 33 | 34 | keys.forEach(async (key) => { 35 | await this.checkBilling(key); 36 | }); 37 | 38 | 39 | }, 40 | 41 | async fetch(path, body, key) { 42 | key = key || 'this.defaultKey'; 43 | 44 | const opts = { 45 | method: 'GET', 46 | headers: { 47 | 'content-type': 'application/json', 48 | Authorization: 'Bearer ' + key, 49 | }, 50 | credentials: 'omit', // 添加此行以取消发送凭据 51 | } 52 | 53 | if (body != null) { 54 | opts.method = 'POST'; 55 | opts.body = JSON.stringify(body); 56 | } 57 | 58 | return fetch(path, opts).then(async r => { 59 | 60 | const data = await r.json(); 61 | if (!r.ok) { 62 | if (data && data.error) { 63 | // console.log(data.error); 64 | // throw new Error(data.error.message); 65 | return data 66 | } 67 | throw new Error(r.statusText || '请求失败'); 68 | } 69 | return data; 70 | }) 71 | }, 72 | 73 | 74 | async checkBilling(key) { 75 | this.alert = null 76 | this.loading = true 77 | this.total = null 78 | const today = new Date(); 79 | const formatDate = function (timestamp) { 80 | const date = new Date(timestamp * 1000); 81 | return [date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'); 82 | }; 83 | 84 | const headers = { 85 | 'content-type': 'application/json', 86 | 'Authorization': `Bearer ${key}` 87 | } 88 | const subscription = await this.fetch('/v1/dashboard/billing/subscription', null, key); 89 | if (!subscription || !subscription.plan) { 90 | if (!this.total) { 91 | this.total = []; 92 | } 93 | this.total.push({ 94 | key: key, 95 | msgkey: key.replace(/(sess-.{5}).+(.{5})/, '$1****$2'), 96 | total_granted: 0, 97 | total_used: 0, 98 | total_available: 0, 99 | plan: 'API Key 无效', 100 | endDate: '', 101 | latest_gpt: subscription.error.code 102 | }); 103 | this.loading = false 104 | this.alert = { type: 'success', message: "查询成功" } 105 | } else { 106 | const start_date = subscription.hard_limit_usd > 20 107 | ? [today.getFullYear(), today.getMonth() + 1, '1'].join('-') : formatDate(today / 1000 - 90 * 86400); 108 | const end_date = formatDate(today / 1000 + 86400); 109 | const usageData = await this.fetch(`/v1/dashboard/billing/usage?start_date=${start_date}&end_date=${end_date}`, null, key); 110 | 111 | const modelData = await this.fetch('/v1/models', null, key); 112 | 113 | 114 | const gptModels = modelData.data.filter(model => model.id.includes("gpt")); 115 | const highestGPTModel = gptModels.reduce((prev, current) => { 116 | const prevVersion = parseFloat(prev.id.split("-")[1]); 117 | const currentVersion = parseFloat(current.id.split("-")[1]); 118 | return (currentVersion > prevVersion) ? current : prev; 119 | }); 120 | const GPTModel = highestGPTModel.id 121 | const plan = (subscription.plan.title === "Pay-as-you-go") ? "Pay-as-you-go" : subscription.plan.id; 122 | //总 123 | const total_granted = subscription.hard_limit_usd; 124 | //已用 125 | // const total_used = usageData.total_usage / 100 || -1 126 | const total_used = typeof usageData.total_usage === "number" ? usageData.total_usage / 100 : "查询失败"; 127 | 128 | // 剩余额度 129 | const total_available = typeof total_used === "number" ? total_granted - total_used : "查询失败"; 130 | 131 | //剩余额度 132 | // const total_available = total_granted - total_used; 133 | 134 | if (!this.total) { 135 | this.total = []; 136 | } 137 | this.total.push({ 138 | key: key, 139 | msgkey: key.replace(/(sess-.{5}).+(.{5})/, '$1****$2'), 140 | total_granted: total_granted, 141 | total_used: total_used, 142 | total_available: total_available, 143 | plan: plan, 144 | endDate: formatDate(subscription.access_until), 145 | latest_gpt: GPTModel, 146 | }); 147 | 148 | 149 | } 150 | 151 | this.loading = false 152 | this.alert = { type: 'success', message: "查询成功" } 153 | return 154 | 155 | } 156 | } 157 | 158 | 159 | const ip = { 160 | ipinfo: '', 161 | getipinfo() { 162 | fetch('https://forge.speedtest.cn/api/location/info') 163 | .then(res => res.json()) 164 | .then(res => { 165 | // console.log(res); 166 | this.ipinfo = `当前IP: ${res.ip} (${res.province} ${res.city} ${res.distinct} ${res.isp}) ` 167 | 168 | }) 169 | .catch(err => { 170 | console.log(err); 171 | }) 172 | } 173 | } 174 | 175 | 176 | 177 | 178 | function copyToClipboard(text) { 179 | navigator.clipboard.writeText(text) 180 | .then(() => { 181 | alert('API已复制到剪贴板'); 182 | }) 183 | .catch((error) => { 184 | console.error('API复制到剪贴板时出错:', error); 185 | }); 186 | } 187 | -------------------------------------------------------------------------------- /cf_worker.js: -------------------------------------------------------------------------------- 1 | const TELEGRAPH_URL = 'https://api.openai.com'; 2 | 3 | addEventListener('fetch', event => { 4 | event.respondWith(handleRequest(event.request)) 5 | }) 6 | 7 | async function handleRequest(request) { 8 | const url = new URL(request.url); 9 | const headers_Origin = request.headers.get("Access-Control-Allow-Origin") || "*" 10 | url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); 11 | const modifiedRequest = new Request(url.toString(), { 12 | headers: request.headers, 13 | method: request.method, 14 | body: request.body, 15 | redirect: 'follow' 16 | }); 17 | const response = await fetch(modifiedRequest); 18 | const modifiedResponse = new Response(response.body, response); 19 | // 添加允许跨域访问的响应头 20 | modifiedResponse.headers.set('Access-Control-Allow-Origin', headers_Origin); 21 | return modifiedResponse; 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /cf_worker_old.js: -------------------------------------------------------------------------------- 1 | const TELEGRAPH_URL = 'https://api.openai.com'; 2 | 3 | addEventListener('fetch', event => { 4 | event.respondWith(handleRequest(event.request)) 5 | }) 6 | 7 | async function handleRequest(request) { 8 | const url = new URL(request.url); 9 | const headers_Origin = request.headers.get("Access-Control-Allow-Origin") || "*" 10 | url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); 11 | const modifiedRequest = new Request(url.toString(), { 12 | headers: request.headers, 13 | method: request.method, 14 | body: request.body, 15 | redirect: 'follow' 16 | }); 17 | const response = await fetch(modifiedRequest); 18 | const modifiedResponse = new Response(response.body, response); 19 | // 添加允许跨域访问的响应头 20 | modifiedResponse.headers.set('Access-Control-Allow-Origin', headers_Origin); 21 | return modifiedResponse; 22 | } -------------------------------------------------------------------------------- /check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | Font_Black="\033[30m"; 3 | Font_Red="\033[31m"; 4 | Font_Green="\033[32m"; 5 | Font_Yellow="\033[33m"; 6 | Font_Blue="\033[34m"; 7 | Font_Purple="\033[35m"; 8 | Font_SkyBlue="\033[36m"; 9 | Font_White="\033[37m"; 10 | Font_Suffix="\033[0m"; 11 | RED='\033[0;31m' 12 | GREEN='\033[0;32m' 13 | YELLOW='\033[0;33m' 14 | PLAIN='\033[0m' 15 | BLUE="\033[36m" 16 | SUPPORT_COUNTRY=(AL DZ AD AO AG AR AM AU AT AZ BS BD BB BE BZ BJ BT BA BW BR BG BF CV CA CL CO KM CR HR CY DK DJ DM DO EC SV EE FJ FI FR GA GM GE DE GH GR GD GT GN GW GY HT HN HU IS IN ID IQ IE IL IT JM JP JO KZ KE KI KW KG LV LB LS LR LI LT LU MG MW MY MV ML MT MH MR MU MX MC MN ME MA MZ MM NA NR NP NL NZ NI NE NG MK NO OM PK PW PA PG PE PH PL PT QA RO RW KN LC VC WS SM ST SN RS SC SL SG SK SI SB ZA ES LK SR SE CH TH TG TO TT TN TR TV UG AE US UY VU ZM BO BN CG CZ VA FM MD PS KR TW TZ TL GB) 17 | 18 | openai_v4() { 19 | echo -e "OpenAI:" 20 | if [[ $(curl -sS https://chat.openai.com/ -I | grep "text/plain") != "" ]] 21 | then 22 | echo "您的 IP 已被封锁!" 23 | else 24 | check4=`ping 1.1.1.1 -c 1 2>&1`; 25 | if [[ "$check4" != *"received"* ]] && [[ "$check4" != *"transmitted"* ]];then 26 | echo -e "\033[34mIPv4 is not supported on the current host. Skip...\033[0m"; 27 | else 28 | iso2_code4=$(curl -4 -sS https://chat.openai.com/cdn-cgi/trace | grep "loc=" | awk -F= '{print $2}') 29 | if [[ "${SUPPORT_COUNTRY[@]}" =~ "${iso2_code4}" ]]; 30 | then 31 | echo -e "${GREEN}您的 IP 支持访问 OpenAI。 地区: ${iso2_code4}${PLAIN}" 32 | else 33 | echo -e "${RED}地区: ${iso2_code4}. 目前不支持 OpenAI。${PLAIN}" 34 | fi 35 | fi 36 | fi 37 | } 38 | 39 | openai_v6() { 40 | echo -e "OpenAI:" 41 | if [[ $(curl -sS https://chat.openai.com/ -I | grep "text/plain") != "" ]] 42 | then 43 | echo "您的 IP 已被封锁!" 44 | else 45 | 46 | check6=`ping6 240c::6666 -c 1 2>&1`; 47 | if [[ "$check6" != *"received"* ]] && [[ "$check6" != *"transmitted"* ]];then 48 | echo -e "\033[34m当前主机不支持 IPv6。 跳过...\033[0m"; 49 | else 50 | iso2_code6=$(curl -6 -sS https://chat.openai.com/cdn-cgi/trace | grep "loc=" | awk -F= '{print $2}') 51 | if [[ "${SUPPORT_COUNTRY[@]}" =~ "${iso2_code6}" ]]; 52 | then 53 | echo -e "${GREEN}您的 IP 支持访问 OpenAI。 地区: ${iso2_code6}${PLAIN}" 54 | else 55 | echo -e "${RED}地区: ${iso2_code6}. 目前不支持 OpenAI。${PLAIN}" 56 | fi 57 | fi 58 | fi 59 | } 60 | 61 | 62 | echo "-------------------------------------" 63 | echo -e "${Font_SkyBlue} OpenAI解锁判断${Font_Suffix}" 64 | echo -e "${Font_SkyBlue} https://github.com/x-dr/chatgptProxyAPI ${Font_Suffix}" 65 | echo -e "${Font_SkyBlue} 脚本截取至GitHub:https://github.com/xb0or/nftest ${Font_Suffix}" 66 | echo " ** 正在测试 IPv4 解锁情况"; 67 | check4=`ping 1.1.1.1 -c 1 2>&1`; 68 | if [[ "$check4" != *"received"* ]] && [[ "$check4" != *"transmitted"* ]];then 69 | echo -e "\033[34m当前主机不支持IPv4,跳过...\033[0m"; 70 | else 71 | local_ipv4=$(curl -4 -s --max-time 10 api64.ipify.org) 72 | local_isp4=$(curl -s -4 --max-time 10 --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" "https://api.ip.sb/geoip/${local_ipv4}" | grep organization | cut -f4 -d '"') 73 | echo -e "${BLUE}您的 IPv4: ${local_ipv4} - ${local_isp4}${PLAIN}" 74 | openai_v4 75 | fi 76 | echo "=====================================" 77 | echo " ** 正在测试 IPv6 解锁情况"; 78 | check6=`ping6 240c::6666 -c 1 2>&1`; 79 | if [[ "$check6" != *"received"* ]] && [[ "$check6" != *"transmitted"* ]];then 80 | echo -e "\033[34m当前主机不支持IPv6,跳过...\033[0m"; 81 | echo "-------------------------------------" 82 | else 83 | local_ipv6=$(curl -6 -s --max-time 20 api64.ipify.org) 84 | local_isp6=$(curl -s -6 --max-time 10 --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36" "https://api.ip.sb/geoip/${local_ipv6}" | grep organization | cut -f4 -d '"') 85 | echo -e "${BLUE}您的 IPv6: ${local_ipv6} - ${local_isp6}${PLAIN}" 86 | openai_v6 87 | echo "-------------------------------------" 88 | fi 89 | -------------------------------------------------------------------------------- /docs/cloudflare_pages.md: -------------------------------------------------------------------------------- 1 | ## 利用Cloudflare pages部署 2 | 3 | > 参考自 https://github.com/open-tdp/openai-chat 4 | 5 | 1. ~~Fork本项目 [x-dr/chatgptProxyAPI](https://github.com/x-dr/chatgptProxyAPI/fork)~~ 点击[Use this template](https://github.com/x-dr/chatgptProxyAPI/generate)按钮创建一个新的代码库。 6 | 2. 登录到[Cloudflare](https://dash.cloudflare.com/)控制台. 7 | 3. 在帐户主页中,选择`pages`> ` Create a project` > `Connect to Git` 8 | ![Cloudflare_pages1.png](./img/Cloudflare_pages1.png) 9 | 10 | 4. 选择你 Fork 的项目存储库,在`Set up builds and deployments`部分中,全部默认即可。 11 | 12 | ![Cloudflare_pages2.png](./img/Cloudflare_pages2.png) 13 | 14 | 15 | 16 | ![Cloudflare_pages4.png](./img/Cloudflare_pages7.png) 17 | 18 | 19 | 20 | 5. 点击`Save and Deploy`部署 21 | 22 | 23 | > 然后点`Continue to project`即可看到访问域名 24 | 25 | ![Cloudflare_pages6.png](./img/Cloudflare_pages6.png) 26 | 27 | **至此便大功告成。等待片刻,应该就可以通过你自己的域名来代替 OpenAI 的 API 地址了,比如在本文搭建的api:`https://openapi-dev.pages.dev`为例子,想要请求 ChatGPT 的 API ,即是把官方 API 地址 https://api.openai.com/v1/chat/completions 换为我自己的域名 https://openapi-dev.pages.dev/v1/chat/completions ,其他参数均参照官方示例即可。由于 Cloudflare 有每天免费 10 万次的请求额度,所以轻度使用基本是零成本的。** 28 | 29 | ### 使用 30 | 31 | > 以我的搭建的服务`https://openapi-dev.pages.dev/` 为例 32 | 33 | 1. 对话 34 | 35 | ```bash 36 | curl --location 'https://openapi-dev.pages.dev/v1/chat/completions' \ 37 | --header 'Authorization: Bearer sk-xxxxxxxxxxxxxxx' \ 38 | --header 'Content-Type: application/json' \ 39 | --data '{ 40 | "model": "gpt-3.5-turbo", 41 | "messages": [{"role": "user", "content": "Hello!"}] 42 | }' 43 | 44 | ``` 45 | 46 |
47 | 48 | 响应 49 | 50 | ```json 51 | { 52 | "id": "chatcmpl-6rMlZybwjMQIhFAEaiCmWvMP1BXld", 53 | "object": "chat.completion", 54 | "created": 1678176917, 55 | "model": "gpt-3.5-turbo-0301", 56 | "usage": { 57 | "prompt_tokens": 9, 58 | "completion_tokens": 11, 59 | "total_tokens": 20 60 | }, 61 | "choices": [ 62 | { 63 | "message": { 64 | "role": "assistant", 65 | "content": "\n\nHello! How can I assist you today?" 66 | }, 67 | "finish_reason": "stop", 68 | "index": 0 69 | } 70 | ] 71 | } 72 | 73 | ``` 74 | 75 |
76 | 77 | 2. 查询key余额 78 | 79 | ```js 80 | const headers = { 81 | 'content-type': 'application/json', 82 | 'Authorization': `Bearer sk-xxxxxxxxxxxxxxxxx` 83 | } 84 | // 查是否订阅 85 | const subscription = await fetch("https://openai.1rmb.tk/v1/dashboard/billing/subscription", { 86 | method: 'get', 87 | headers: headers 88 | }) 89 | if (!subscription.ok) { 90 | const data = await subscription.json() 91 | // console.log(data); 92 | return data 93 | // throw new Error('API request failed') 94 | } else { 95 | const subscriptionData = await subscription.json() 96 | const endDate = subscriptionData.access_until 97 | const startDate = new Date(endDate - 90 * 24 * 60 * 60); 98 | console.log(formatDate(endDate, "YYYY-MM-DD")); 99 | console.log(formatDate(startDate, "YYYY-MM-DD")); 100 | const response = await fetch(`https://openai.1rmb.tk/v1/dashboard/billing/usage?start_date=${formatDate(startDate, "YYYY-MM-DD")}&end_date=${formatDate(endDate, "YYYY-MM-DD")}`, { 101 | method: 'get', 102 | headers: headers 103 | }) 104 | 105 | const usageData = await response.json(); 106 | console.log(usageData); 107 | } 108 | 109 | ``` 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docs/cloudflare_proxy_pages.md: -------------------------------------------------------------------------------- 1 | 2 | 由于Workers的域名已经被污染了。在CloudFlare Workers自定义域名的方案之后,我来和大家一起在CloudFlare Pages利用其的Functions部署中转,作为一种备用方法。 3 | 4 | 5 | ### 打开Github,新建一个仓库 6 | 7 | ![pages1](../docs/img/pages/pages1.png) 8 | ![pages1](../docs/img/pages/pages2.png) 9 | 10 | 11 | ### 点击creating a new file按钮 12 | 13 | 1. 创建一个文件名为_worker.js的文件,然后复制以下代码 14 | 15 | ```js 16 | export default { 17 | async fetch(request, env) { 18 | const url = new URL(request.url); 19 | url.host = "api.openai.com"; 20 | // openai is already set all CORS heasders 21 | return fetch(url, { 22 | headers: request.headers, 23 | method: request.method, 24 | body: request.body, 25 | redirect: 'follow' 26 | }); 27 | } 28 | } 29 | 30 | ``` 31 | ![pages1](../docs/img/pages/pages3.png) 32 | ![pages1](../docs/img/pages/pages4.png) 33 | 34 | 2. 然后点`Commit new file`保存 35 | 36 | ![pages1](../docs/img/pages/pages5.png) 37 | 38 | #### 扩展 39 | 40 | > 如果想中转别的网站把下面代码中的`TELEGRAPH_URL`换为别网站就行 41 | 42 |
43 | 44 | 代码 45 | 46 | ```js 47 | 48 | const TELEGRAPH_URL = 'https://api.openai.com'; 49 | 50 | 51 | export default { 52 | async fetch(request, env) { 53 | const NewResponse = await handleRequest(request) 54 | return NewResponse 55 | }, 56 | 57 | }; 58 | 59 | async function handleRequest(request) { 60 | const url = new URL(request.url); 61 | const headers_Origin = request.headers.get("Access-Control-Allow-Origin") || "*" 62 | url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); 63 | const modifiedRequest = new Request(url.toString(), { 64 | headers: request.headers, 65 | method: request.method, 66 | body: request.body, 67 | redirect: 'follow' 68 | }); 69 | const response = await fetch(modifiedRequest); 70 | const modifiedResponse = new Response(response.body, response); 71 | // 添加允许跨域访问的响应头 72 | modifiedResponse.headers.set('Access-Control-Allow-Origin', headers_Origin); 73 | return modifiedResponse; 74 | } 75 | 76 | ``` 77 |
78 | 79 | ### 部署 80 | 81 | 1. 登录到[Cloudflare](https://dash.cloudflare.com/)控制台. 82 | 2. 在帐户主页中,选择`pages`> ` Create a project` > `Connect to Git` 83 | ![Cloudflare_pages1.png](./img/Cloudflare_pages1.png) 84 | 85 | 3. 选择你 Fork 的项目存储库,在`Set up builds and deployments`部分中,选择None作为您的框架预设。其他为空。(即保持默认即可) 86 | 87 | 4. 点击`Save and Deploy`部署 88 | 89 | 90 | > 然后点`Continue to project`即可看到访问域名 91 | 92 | ![Cloudflare_pages6.png](./img/Cloudflare_pages6.png) 93 | 94 | 95 | 96 | ### 用法 97 | 98 | **[参考](https://github.com/x-dr/chatgptProxyAPI#用法)** 99 | -------------------------------------------------------------------------------- /docs/cloudflare_workers.md: -------------------------------------------------------------------------------- 1 | 2 | ## 利用Cloudflare Worker中转api.openai.com 3 | 4 | 5 | 由于 Cloudflare Worker的域名`workers.dev`被墙境内要绑定自己的域名才能访问,但`CloudFlare Pages`的域名还是可以访问,所以我们可以用CloudFlare Pages的Functions部署中转 **[部署方法](./cloudflare_proxy_pages.md)** 6 | 7 | 8 | 9 | **本文以下内容来自[noobnooc/discussions/9](https://github.com/noobnooc/noobnooc/discussions/9)有修改** 10 | 11 | 1. 新建一个 Cloudflare Worker 12 | 2. 复制 [cf_worker.js](https://cdn.jsdelivr.net/gh/x-dr/chatgptProxyAPI@main/cf_worker.js) 里的代码粘贴到 Worker 中并部署 13 | 3. 给 Worker 绑定一个没有被 GFW 墙的域名 14 | 4. 使用自己的域名代替 api.openai.com 15 | 16 | ### 具体操作 17 | 18 | #### 创建一个 Cloudflare Worker 19 | 20 | 1. 登录到 Cloudflare 的管理界面后,点击侧边栏的 `Workers` 选项,然后点击 `Create a Service` 创建一个 Worker。 21 | 22 | ![Create a Service](./img/Cloudflare_Worker1.png) 23 | 24 | > 然后在创建界面中输入 `Service name` 后点击 `Create Service` 按钮新建 Worker。`Select a starter` 项不用管。 25 | 26 | ![Create a Service](./img/Cloudflare_Worker2.png) 27 | 28 | > 至此 Cloudflare 的 Worker 便创建好了,下面开始修改 Worker 的代码,使其能代理 OpenAI 的 API。 29 | 30 | #### 修改 Cloudflare Worker 的代码 31 | 32 | > 在 Worker 的管理界面,点击右上角的 “Quick Edit” 按钮编辑代码 Worker 的代码。 33 | 34 | ![Create a Service](./img/Cloudflare_Worker3.png) 35 | 36 | > 在左侧的代码编辑器中,删除现有的所有代码,然后复制粘贴以下内容到代码编辑器: 37 | 38 | ```js 39 | const TELEGRAPH_URL = 'https://api.openai.com'; 40 | 41 | addEventListener('fetch', event => { 42 | event.respondWith(handleRequest(event.request)) 43 | }) 44 | 45 | async function handleRequest(request) { 46 | const url = new URL(request.url); 47 | const headers_Origin = request.headers.get("Access-Control-Allow-Origin") || "*" 48 | url.host = TELEGRAPH_URL.replace(/^https?:\/\//, ''); 49 | const modifiedRequest = new Request(url.toString(), { 50 | headers: request.headers, 51 | method: request.method, 52 | body: request.body, 53 | redirect: 'follow' 54 | }); 55 | const response = await fetch(modifiedRequest); 56 | const modifiedResponse = new Response(response.body, response); 57 | // 添加允许跨域访问的响应头 58 | modifiedResponse.headers.set('Access-Control-Allow-Origin', headers_Origin); 59 | return modifiedResponse; 60 | } 61 | 62 | ``` 63 | 64 | 65 | ![Create a Service](./img/Cloudflare_Worker4.png) 66 | 67 | > 最后点击编辑器右下角的 `Save and deploy` 按钮部署该代码,在弹出的对话框中继续选择 `Save and deploy` 确认部署。 68 | 69 | 70 | 71 | **但是你可能会发现,这样做了依然还是没有解决问题,因为 Cloudflare Workers 的 workers.dev 域名也是被墙。但是好在只是墙了 workers.dev 域名,而 ip 还是幸存的状态,所以我们可以给 Worker 绑定一个自己的域名。** 72 | 73 | #### 绑定域名 74 | 75 | > 在 Cloudflare Workers 的管理界面中,点击 `Triggers` 选项卡,然后点击 `Custom Domians` 中的 `Add Custom Domain` 按钮以绑定域名。 76 | 77 | ![Create a Service](./img/Cloudflare_Worker5.png) 78 | 79 | > 输入域名后点击 `Add Custom Domain` (目前只支持 NS 托管在 Cloudflare 上的域名,如果不介意,可以点击 Cloudflare 侧边栏的 “Websites”,然后点击 “Add a Site” 按钮,根据提示将域名的 NS 记录指定到 Cloudflare。) 80 | 81 | ![Create a Service](./img/Cloudflare_Worker6.png) 82 | 83 | 84 | 85 | **至此便大功告成。等待片刻,应该就可以通过你自己的域名来代替 OpenAI 的 API 地址了,比如在本文的例子中,想要请求 ChatGPT 的 API ,即是把官方 API 地址 https://api.openai.com/v1/chat/completions 换为我自己的域名 https://openai.1rmb.tk/v1/chat/completions ,其他参数均参照官方示例即可。由于 Cloudflare 有每天免费 10 万次的请求额度,所以轻度使用基本是零成本的。** 86 | 87 | 88 | 89 | ## 使用 90 | 91 | 1. 对话 92 | 93 | ```bash 94 | curl --location 'https://openai.1rmb.tk/v1/chat/completions' \ 95 | --header 'Authorization: Bearer sk-xxxxxxxxxxxxxxx' \ 96 | --header 'Content-Type: application/json' \ 97 | --data '{ 98 | "model": "gpt-3.5-turbo", 99 | "messages": [{"role": "user", "content": "Hello!"}] 100 | }' 101 | 102 | ``` 103 | 104 |
105 | 106 | 响应 107 | 108 | ```json 109 | { 110 | "id": "chatcmpl-6rMlZybwjMQIhFAEaiCmWvMP1BXld", 111 | "object": "chat.completion", 112 | "created": 1678176917, 113 | "model": "gpt-3.5-turbo-0301", 114 | "usage": { 115 | "prompt_tokens": 9, 116 | "completion_tokens": 11, 117 | "total_tokens": 20 118 | }, 119 | "choices": [ 120 | { 121 | "message": { 122 | "role": "assistant", 123 | "content": "\n\nHello! How can I assist you today?" 124 | }, 125 | "finish_reason": "stop", 126 | "index": 0 127 | } 128 | ] 129 | } 130 | 131 | ``` 132 | 133 |
134 | 135 | 2. 查询key余额 136 | 137 | ```bash 138 | curl --location 'https://openai.1rmb.tk/dashboard/billing/credit_grants' \ 139 | --header 'Authorization: Bearer sk-xxxxxx' 140 | 141 | ``` 142 | 143 |
144 | 145 | 响应 146 | 147 | ```json 148 | { 149 | "object": "credit_summary", 150 | "total_granted": 18.0, 151 | "total_used": 9.543368000000001, 152 | "total_available": 8.456631999999999, 153 | "grants": { 154 | "object": "list", 155 | "data": [ 156 | { 157 | "object": "credit_grant", 158 | "id": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx", 159 | "grant_amount": 18.0, 160 | "used_amount": 18, 161 | "effective_at": 1666666200.0, 162 | "expires_at": 1666666600.0 163 | } 164 | ] 165 | } 166 | } 167 | ``` 168 | 169 |
170 | 171 | 172 | 173 | ### 查询 OPENAI API 余额 174 | 175 | [https://checkbilling.1rmb.tk/](https://checkbilling.1rmb.tk/) 176 | -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker1.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker2.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker3.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker4.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker5.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_Worker6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_Worker6.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages1.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages2.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages3.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages4.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages5.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages6.png -------------------------------------------------------------------------------- /docs/img/Cloudflare_pages7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/Cloudflare_pages7.png -------------------------------------------------------------------------------- /docs/img/jpg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/jpg.webp -------------------------------------------------------------------------------- /docs/img/pages/pages1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/pages/pages1.png -------------------------------------------------------------------------------- /docs/img/pages/pages2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/pages/pages2.png -------------------------------------------------------------------------------- /docs/img/pages/pages3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/pages/pages3.png -------------------------------------------------------------------------------- /docs/img/pages/pages4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/pages/pages4.png -------------------------------------------------------------------------------- /docs/img/pages/pages5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/pages/pages5.png -------------------------------------------------------------------------------- /docs/img/post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/post.png -------------------------------------------------------------------------------- /docs/img/worker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x-dr/chatgptProxyAPI/bb22961fe2239fbc41e8a9317e522f189ebf0105/docs/img/worker.png -------------------------------------------------------------------------------- /functions/helloworld.js: -------------------------------------------------------------------------------- 1 | export function onRequest(context) { 2 | return new Response("Hello, world!") 3 | } -------------------------------------------------------------------------------- /functions/v1/[[path]].js: -------------------------------------------------------------------------------- 1 | export async function onRequest(context) { 2 | const { 3 | request, // 与现有 Worker API 中的 request 相同 4 | env, // 与现有 Worker API 中的 env 相同 5 | params, // 如果文件名包含 [id] 或 [[path]],则与现有 Worker API 中的 params 相同 6 | waitUntil, // 与现有 Worker API 中的 ctx.waitUntil 相同 7 | next, // 用于中间件或获取资源 8 | data, // 在中间件之间传递数据的任意空间 9 | } = context; 10 | // const newResponse = request.clone(); 11 | const url = new URL(request.url); 12 | const headers_Origin = request.headers.get("Access-Control-Allow-Origin") || "*" 13 | 14 | // console.log('https://api.openai.com' + url.pathname + url.search); 15 | 16 | const modifiedRequest = new Request('https://api.openai.com' + url.pathname + url.search, { 17 | method: request.method, 18 | headers: request.headers, 19 | body: request.body, 20 | }); 21 | 22 | const response = await fetch(modifiedRequest); 23 | 24 | const modifiedResponse = new Response(response.body, response); 25 | modifiedResponse.headers.set('Access-Control-Allow-Origin', headers_Origin); 26 | 27 | return modifiedResponse; 28 | } 29 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | OpenAI/ChatGPT 工具箱 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 39 |
40 | 45 |
46 |

47 |

48 |
49 | 51 | 52 |
53 |

54 |
55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 82 | 83 | 84 |
#API KEY账号类型最高模型额度总量已用额度剩余额度过期时间
85 |
86 |
87 |
88 | 89 | 90 |
91 | 99 | 100 | 101 | 102 | 103 | 104 | --------------------------------------------------------------------------------