mirror of
https://github.com/adams549659584/go-proxy-bingai.git
synced 2025-06-03 15:02:30 +08:00
feat: ⚡ 重构支持 vercel
This commit is contained in:
parent
91f3f44517
commit
097f583d51
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License Copyright (c) 2023 adams549659584
|
||||
|
||||
Permission is hereby granted, free
|
||||
of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
(including the next paragraph) shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
24
README.md
24
README.md
@ -22,21 +22,19 @@
|
||||
|
||||

|
||||
|
||||
### 演示站点:
|
||||
## 演示站点:
|
||||
|
||||
> 甲骨文小鸡仔,轻虐
|
||||
|
||||
- https://bing.vcanbb.top
|
||||
|
||||
> railway
|
||||
> Railway
|
||||
|
||||
- https://bing-railway.vcanbb.top
|
||||
|
||||
- https://go-proxy-bingai-production.up.railway.app
|
||||
|
||||
|
||||
|
||||
### 获取cookies
|
||||
## 获取cookies
|
||||
|
||||
- 访问 https://www.bing.com/ 或 https://cn.bing.com/ ,登录
|
||||
|
||||
@ -46,13 +44,15 @@
|
||||
|
||||

|
||||
|
||||
### 部署
|
||||
## 部署
|
||||
|
||||
> 需 https 域名 (自行配置 nginx 等)
|
||||
|
||||
> 支持 Linux (amd64 / arm64)
|
||||
|
||||
- docker 部署 , 参考 [Dockerfile](./docker/Dockerfile) 、[docker-compose.yml](./docker/docker-compose.yml)
|
||||
### docker
|
||||
|
||||
> 参考 [Dockerfile](./docker/Dockerfile) 、[docker-compose.yml](./docker/docker-compose.yml)
|
||||
|
||||
示例
|
||||
|
||||
@ -61,11 +61,11 @@
|
||||
docker run -d -p 8080:8080 --name go-proxy-bingai --restart=unless-stopped adams549659584/go-proxy-bingai
|
||||
```
|
||||
|
||||
- 直接下载 Release 运行
|
||||
### Release
|
||||
|
||||
在 [Github Releases](https://github.com/adams549659584/go-proxy-bingai/releases) 下载适用于对应平台的压缩包,解压后可得到可执行文件 go-proxy-bingai,直接运行即可。
|
||||
|
||||
- Railway
|
||||
### Railway
|
||||
|
||||
> 主要配置 Dockerfile 路径 及 端口就可以
|
||||
|
||||
@ -73,7 +73,7 @@ docker run -d -p 8080:8080 --name go-proxy-bingai --restart=unless-stopped adams
|
||||
PORT=8080
|
||||
RAILWAY_DOCKERFILE_PATH=docker/Dockerfile
|
||||
```
|
||||
使用模板部署,点这里 => [](https://railway.app/template/uIckWS?referralCode=BBs747)
|
||||
一键部署,点这里 => [](https://railway.app/template/uIckWS?referralCode=BBs747)
|
||||
|
||||

|
||||
|
||||
@ -82,3 +82,7 @@ RAILWAY_DOCKERFILE_PATH=docker/Dockerfile
|
||||

|
||||
|
||||

|
||||
|
||||
### Vercel
|
||||
|
||||
一键部署,点这里 => [](https://vercel.com/new/clone?repository-url=https://github.com/adams549659584/go-proxy-bingai&project-name=go-proxy-bingai&repository-name=go-proxy-bingai)
|
||||
|
10
api/chathub.go
Normal file
10
api/chathub.go
Normal file
@ -0,0 +1,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"adams549659584/go-proxy-bingai/common"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ChatHub(w http.ResponseWriter, r *http.Request) {
|
||||
common.NewSingleHostReverseProxy(common.BING_CHAT_URL).ServeHTTP(w, r)
|
||||
}
|
14
api/index.go
Normal file
14
api/index.go
Normal file
@ -0,0 +1,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"adams549659584/go-proxy-bingai/common"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/web/chat.html", http.StatusFound)
|
||||
} else {
|
||||
common.NewSingleHostReverseProxy(common.BING_URL).ServeHTTP(w, r)
|
||||
}
|
||||
}
|
10
api/web.go
Normal file
10
api/web.go
Normal file
@ -0,0 +1,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"adams549659584/go-proxy-bingai/web"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func WebStatic(w http.ResponseWriter, r *http.Request) {
|
||||
http.StripPrefix("/web/", http.FileServer(http.FS(web.WebFS))).ServeHTTP(w, r)
|
||||
}
|
202
common/proxy.go
Normal file
202
common/proxy.go
Normal file
@ -0,0 +1,202 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
)
|
||||
|
||||
var (
|
||||
BING_CHAT_DOMAIN = "https://sydney.bing.com"
|
||||
BING_CHAT_URL, _ = url.Parse(BING_CHAT_DOMAIN + "/sydney/ChatHub")
|
||||
BING_URL, _ = url.Parse("https://www.bing.com")
|
||||
KEEP_HEADERS = map[string]bool{
|
||||
"Accept": true,
|
||||
"Accept-Encoding": true,
|
||||
"Accept-Language": true,
|
||||
"Referer": true,
|
||||
"Connection": true,
|
||||
"Cookie": true,
|
||||
"Upgrade": true,
|
||||
"User-Agent": true,
|
||||
"Sec-Websocket-Extensions": true,
|
||||
"Sec-Websocket-Key": true,
|
||||
"Sec-Websocket-Version": true,
|
||||
"X-Request-Id": true,
|
||||
"X-Forwarded-For": true,
|
||||
}
|
||||
)
|
||||
|
||||
func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
||||
originalScheme := "http"
|
||||
httpsSchemeName := "https"
|
||||
var originalHost string
|
||||
var originalPath string
|
||||
director := func(req *http.Request) {
|
||||
if req.URL.Scheme == httpsSchemeName || req.Header.Get("X-Forwarded-Proto") == httpsSchemeName {
|
||||
originalScheme = httpsSchemeName
|
||||
}
|
||||
originalHost = req.Host
|
||||
originalPath = req.URL.Path
|
||||
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.Host = target.Host
|
||||
|
||||
req.Header.Set("Referer", fmt.Sprintf("%s/search?q=Bing+AI", BING_URL.String()))
|
||||
|
||||
// 随机ip
|
||||
randIp := fmt.Sprintf("%d.%d.%d.%d", RandInt(1, 10), RandInt(1, 255), RandInt(1, 255), RandInt(1, 255))
|
||||
req.Header.Set("X-Forwarded-For", randIp)
|
||||
|
||||
for hKey, _ := range req.Header {
|
||||
if _, isExist := KEEP_HEADERS[hKey]; !isExist {
|
||||
req.Header.Del(hKey)
|
||||
}
|
||||
}
|
||||
|
||||
// reqHeaderByte, _ := json.Marshal(req.Header)
|
||||
// log.Println("剩余请求头 : ", string(reqHeaderByte))
|
||||
}
|
||||
//改写返回信息
|
||||
modifyFunc := func(res *http.Response) error {
|
||||
contentType := res.Header.Get("Content-Type")
|
||||
if strings.Contains(contentType, "text/javascript") {
|
||||
contentEncoding := res.Header.Get("Content-Encoding")
|
||||
switch contentEncoding {
|
||||
case "gzip":
|
||||
// log.Println("ContentEncoding : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyGzipBody(res, originalScheme, originalHost)
|
||||
case "br":
|
||||
// log.Println("ContentEncoding : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyBrBody(res, originalScheme, originalHost)
|
||||
default:
|
||||
log.Println("ContentEncoding default : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyDefaultBody(res, originalScheme, originalHost)
|
||||
}
|
||||
}
|
||||
|
||||
// 修改响应 cookie 域
|
||||
// resCookies := res.Header.Values("Set-Cookie")
|
||||
// if len(resCookies) > 0 {
|
||||
// for i, v := range resCookies {
|
||||
// resCookies[i] = strings.ReplaceAll(strings.ReplaceAll(v, ".bing.com", originalHost), "bing.com", originalHost)
|
||||
// }
|
||||
// }
|
||||
res.Header.Del("Set-Cookie")
|
||||
|
||||
return nil
|
||||
}
|
||||
errorHandler := func(res http.ResponseWriter, req *http.Request, err error) {
|
||||
log.Println("代理异常 :", err)
|
||||
res.Write([]byte(err.Error()))
|
||||
}
|
||||
// 代理请求 请求回来的内容 报错自动调用
|
||||
return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc, ErrorHandler: errorHandler}
|
||||
}
|
||||
|
||||
func replaceResBody(originalBody string, originalScheme string, originalHost string) string {
|
||||
modifiedBodyStr := originalBody
|
||||
originalDomain := fmt.Sprintf("%s://%s", originalScheme, originalHost)
|
||||
|
||||
if strings.Contains(modifiedBodyStr, BING_URL.String()) {
|
||||
modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, BING_URL.String(), originalDomain)
|
||||
}
|
||||
|
||||
// 对话暂时支持国内网络,而且 Vercel 还不支持 Websocket ,先不用
|
||||
// if strings.Contains(modifiedBodyStr, BING_CHAT_DOMAIN) {
|
||||
// modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, BING_CHAT_DOMAIN, originalDomain)
|
||||
// }
|
||||
|
||||
// if strings.Contains(modifiedBodyStr, "https://www.bingapis.com") {
|
||||
// modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, "https://www.bingapis.com", "https://bing.vcanbb.top")
|
||||
// }
|
||||
return modifiedBodyStr
|
||||
}
|
||||
|
||||
func modifyGzipBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
gz, err := gzip.NewReader(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gz.Close()
|
||||
|
||||
bodyByte, err := io.ReadAll(gz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originalBody := string(bodyByte)
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
// gzip 压缩
|
||||
var buf bytes.Buffer
|
||||
writer := gzip.NewWriter(&buf)
|
||||
defer writer.Close()
|
||||
|
||||
_, err = writer.Write(modifiedBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改 Content-Length 头
|
||||
res.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyBrBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
reader := brotli.NewReader(res.Body)
|
||||
var uncompressed bytes.Buffer
|
||||
uncompressed.ReadFrom(reader)
|
||||
|
||||
originalBody := uncompressed.String()
|
||||
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
// br 压缩
|
||||
var buf bytes.Buffer
|
||||
writer := brotli.NewWriter(&buf)
|
||||
writer.Write(modifiedBody)
|
||||
writer.Close()
|
||||
|
||||
// 修改 Content-Length 头
|
||||
// res.ContentLength = int64(buf.Len())
|
||||
res.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyDefaultBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
bodyByte, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originalBody := string(bodyByte)
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
|
||||
// 修改 Content-Length 头
|
||||
// res.ContentLength = int64(buf.Len())
|
||||
res.Header.Set("Content-Length", strconv.Itoa(len(modifiedBody)))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(modifiedBody))
|
||||
|
||||
return nil
|
||||
}
|
12
common/utils.go
Normal file
12
common/utils.go
Normal file
@ -0,0 +1,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RandInt(min int, max int) int {
|
||||
seed := time.Now().UnixNano()
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
return rng.Intn(max-min+1) + min
|
||||
}
|
225
main.go
225
main.go
@ -1,238 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"adams549659584/go-proxy-bingai/api"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
)
|
||||
|
||||
//go:embed web/*
|
||||
var WebFiles embed.FS
|
||||
|
||||
var (
|
||||
ADDR = ":8080"
|
||||
BING_CHAT_DOMAIN = "https://sydney.bing.com"
|
||||
BING_CHAT_URL, _ = url.Parse(BING_CHAT_DOMAIN + "/sydney/ChatHub")
|
||||
BING_URL, _ = url.Parse("https://www.bing.com")
|
||||
KEEP_HEADERS = map[string]bool{
|
||||
"Accept": true,
|
||||
"Accept-Encoding": true,
|
||||
"Accept-Language": true,
|
||||
"Referer": true,
|
||||
"Connection": true,
|
||||
"Cookie": true,
|
||||
"Upgrade": true,
|
||||
"User-Agent": true,
|
||||
"Sec-Websocket-Extensions": true,
|
||||
"Sec-Websocket-Key": true,
|
||||
"Sec-Websocket-Version": true,
|
||||
"X-Request-Id": true,
|
||||
"X-Forwarded-For": true,
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/sydney/ChatHub", api.ChatHub)
|
||||
|
||||
http.HandleFunc("/web/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.FileServer(http.FS(WebFiles)).ServeHTTP(w, r)
|
||||
})
|
||||
http.HandleFunc("/web/", api.WebStatic)
|
||||
|
||||
http.HandleFunc("/sydney/ChatHub", func(w http.ResponseWriter, r *http.Request) {
|
||||
newSingleHostReverseProxy(BING_CHAT_URL).ServeHTTP(w, r)
|
||||
})
|
||||
http.HandleFunc("/", api.Index)
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/web/chat.html", http.StatusFound)
|
||||
} else {
|
||||
newSingleHostReverseProxy(BING_URL).ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
addr := ":8080"
|
||||
|
||||
log.Println("Starting BingAI Proxy At " + ADDR)
|
||||
log.Println("Starting BingAI Proxy At " + addr)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ADDR,
|
||||
Addr: addr,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
}
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
}
|
||||
|
||||
func newSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
||||
originalScheme := "http"
|
||||
httpsSchemeName := "https"
|
||||
var originalHost string
|
||||
var originalPath string
|
||||
director := func(req *http.Request) {
|
||||
if req.URL.Scheme == httpsSchemeName || req.Header.Get("X-Forwarded-Proto") == httpsSchemeName {
|
||||
originalScheme = httpsSchemeName
|
||||
}
|
||||
originalHost = req.Host
|
||||
originalPath = req.URL.Path
|
||||
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.Host = target.Host
|
||||
|
||||
req.Header.Set("Referer", fmt.Sprintf("%s/search?q=Bing+AI", BING_URL.String()))
|
||||
|
||||
// 随机ip
|
||||
randIp := fmt.Sprintf("%d.%d.%d.%d", randInt(1, 10), randInt(1, 255), randInt(1, 255), randInt(1, 255))
|
||||
req.Header.Set("X-Forwarded-For", randIp)
|
||||
|
||||
for hKey, _ := range req.Header {
|
||||
if _, isExist := KEEP_HEADERS[hKey]; !isExist {
|
||||
req.Header.Del(hKey)
|
||||
}
|
||||
}
|
||||
|
||||
// reqHeaderByte, _ := json.Marshal(req.Header)
|
||||
// log.Println("剩余请求头 : ", string(reqHeaderByte))
|
||||
}
|
||||
//改写返回信息
|
||||
modifyFunc := func(res *http.Response) error {
|
||||
contentType := res.Header.Get("Content-Type")
|
||||
if strings.Contains(contentType, "text/javascript") {
|
||||
contentEncoding := res.Header.Get("Content-Encoding")
|
||||
switch contentEncoding {
|
||||
case "gzip":
|
||||
// log.Println("ContentEncoding : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyGzipBody(res, originalScheme, originalHost)
|
||||
case "br":
|
||||
// log.Println("ContentEncoding : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyBrBody(res, originalScheme, originalHost)
|
||||
default:
|
||||
log.Println("ContentEncoding default : ", contentEncoding, " Path : ", originalPath)
|
||||
modifyDefaultBody(res, originalScheme, originalHost)
|
||||
}
|
||||
}
|
||||
|
||||
// 修改响应 cookie 域
|
||||
// resCookies := res.Header.Values("Set-Cookie")
|
||||
// if len(resCookies) > 0 {
|
||||
// for i, v := range resCookies {
|
||||
// resCookies[i] = strings.ReplaceAll(strings.ReplaceAll(v, ".bing.com", originalHost), "bing.com", originalHost)
|
||||
// }
|
||||
// }
|
||||
res.Header.Del("Set-Cookie")
|
||||
|
||||
return nil
|
||||
}
|
||||
errorHandler := func(res http.ResponseWriter, req *http.Request, err error) {
|
||||
log.Println("代理异常 :", err)
|
||||
res.Write([]byte(err.Error()))
|
||||
}
|
||||
// 代理请求 请求回来的内容 报错自动调用
|
||||
return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc, ErrorHandler: errorHandler}
|
||||
}
|
||||
|
||||
func replaceResBody(originalBody string, originalScheme string, originalHost string) string {
|
||||
modifiedBodyStr := originalBody
|
||||
originalDomain := fmt.Sprintf("%s://%s", originalScheme, originalHost)
|
||||
if strings.Contains(modifiedBodyStr, BING_URL.String()) {
|
||||
modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, BING_URL.String(), originalDomain)
|
||||
}
|
||||
if strings.Contains(modifiedBodyStr, BING_CHAT_DOMAIN) {
|
||||
modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, BING_CHAT_DOMAIN, originalDomain)
|
||||
}
|
||||
// if strings.Contains(modifiedBodyStr, "https://www.bingapis.com") {
|
||||
// modifiedBodyStr = strings.ReplaceAll(modifiedBodyStr, "https://www.bingapis.com", "https://bing.vcanbb.top")
|
||||
// }
|
||||
return modifiedBodyStr
|
||||
}
|
||||
|
||||
func modifyGzipBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
gz, err := gzip.NewReader(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gz.Close()
|
||||
|
||||
bodyByte, err := io.ReadAll(gz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originalBody := string(bodyByte)
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
// gzip 压缩
|
||||
var buf bytes.Buffer
|
||||
writer := gzip.NewWriter(&buf)
|
||||
defer writer.Close()
|
||||
|
||||
_, err = writer.Write(modifiedBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改 Content-Length 头
|
||||
res.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyBrBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
reader := brotli.NewReader(res.Body)
|
||||
var uncompressed bytes.Buffer
|
||||
uncompressed.ReadFrom(reader)
|
||||
|
||||
originalBody := uncompressed.String()
|
||||
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
// br 压缩
|
||||
var buf bytes.Buffer
|
||||
writer := brotli.NewWriter(&buf)
|
||||
writer.Write(modifiedBody)
|
||||
writer.Close()
|
||||
|
||||
// 修改 Content-Length 头
|
||||
// res.ContentLength = int64(buf.Len())
|
||||
res.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyDefaultBody(res *http.Response, originalScheme string, originalHost string) error {
|
||||
bodyByte, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originalBody := string(bodyByte)
|
||||
modifiedBodyStr := replaceResBody(originalBody, originalScheme, originalHost)
|
||||
// 修改响应内容
|
||||
modifiedBody := []byte(modifiedBodyStr)
|
||||
|
||||
// 修改 Content-Length 头
|
||||
// res.ContentLength = int64(buf.Len())
|
||||
res.Header.Set("Content-Length", strconv.Itoa(len(modifiedBody)))
|
||||
// 修改响应内容
|
||||
res.Body = io.NopCloser(bytes.NewReader(modifiedBody))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func randInt(min int, max int) int {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
return rand.Intn(max-min+1) + min
|
||||
}
|
||||
|
20
vercel.json
Normal file
20
vercel.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "go-proxy-bingai",
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "/api/{index,web}.go",
|
||||
"use": "@vercel/go"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"src": "/web/.*",
|
||||
"dest": "/api/web.go"
|
||||
},
|
||||
{
|
||||
"src": "/.*",
|
||||
"dest": "/api/index.go"
|
||||
}
|
||||
]
|
||||
}
|
23
web/sw.js
23
web/sw.js
@ -1,7 +1,7 @@
|
||||
// 引入workbox 框架
|
||||
importScripts('./js/sw/workbox-sw.js');
|
||||
|
||||
const SW_VERSION = '1.0.0';
|
||||
const SW_VERSION = '1.1.0';
|
||||
const CACHE_PREFIX = 'BingAI';
|
||||
|
||||
workbox.setConfig({ debug: false, logLevel: 'warn' });
|
||||
@ -34,6 +34,10 @@ workbox.precaching.precacheAndRoute([
|
||||
url: '/web/js/sw/workbox-window.prod.umd.min.js',
|
||||
revision: '2023.05.03',
|
||||
},
|
||||
{
|
||||
url: '/rp/LOB20GsbD-KR9Gwi_Ukp8-BJZCQ.br.js',
|
||||
revision: '2023.05.04',
|
||||
},
|
||||
// html
|
||||
{
|
||||
url: '/web/chat.html',
|
||||
@ -97,3 +101,20 @@ self.addEventListener('message', event => {
|
||||
replyPort.postMessage(SW_VERSION);
|
||||
}
|
||||
});
|
||||
|
||||
// 安装阶段可删除旧缓存等等
|
||||
self.addEventListener('install', async event => {
|
||||
await caches.open(`${CACHE_PREFIX}-js`).then(async cache => {
|
||||
const requests = await cache.keys();
|
||||
return await Promise.all(
|
||||
requests.map(request => {
|
||||
if (true || request.url.includes('xxx')) {
|
||||
console.log(`del old cache : `, request.url);
|
||||
return cache.delete(request);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
6
web/web.go
Normal file
6
web/web.go
Normal file
@ -0,0 +1,6 @@
|
||||
package web
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed *
|
||||
var WebFS embed.FS
|
Loading…
x
Reference in New Issue
Block a user