diff --git a/backend-api/backend-api.go b/backend-api/backend-api.go index ab7e04b..b1faa4b 100644 --- a/backend-api/backend-api.go +++ b/backend-api/backend-api.go @@ -5,6 +5,7 @@ import ( "chatgpt-mirror-server/config" "chatgpt-mirror-server/modules/chatgpt/model" "chatgpt-mirror-server/modules/chatgpt/service" + "chatgpt-mirror-server/utility" "compress/gzip" "crypto/tls" "io" @@ -48,6 +49,69 @@ func NotFound(r *ghttp.Request) { r.Response.WriteStatus(http.StatusNotFound) } +// 代理请求 +func ProxyRequestGet(path string, r *ghttp.Request) (resStr string, err error) { + ctx := r.GetCtx() + userToken := "" + Authorization := r.Header.Get("Authorization") + if Authorization != "" { + userToken = r.Header.Get("Authorization")[7:] + } + userId, chatgptId, accessToken, err := ChatgptSessionService.GetAccessToken(ctx, userToken) + + g.Log().Debug(ctx, "userToken", userToken) + g.Log().Debug(ctx, "userId", userId) + g.Log().Debug(ctx, "chatgptId", chatgptId) + UpStream := config.CHATPROXY(ctx) + if err != nil { + // 处理错误 + panic(err) + } + + // 设置HTTP Transport + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + if config.Ja3Proxy != nil { + g.Log().Debug(ctx, "存在ja3proxy 重新配置") + + transport = &http.Transport{ + Proxy: http.ProxyURL(config.Ja3Proxy), + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } + // 创建HTTP客户端 + client := &http.Client{Transport: transport} + + // 设置请求头 + req, err := http.NewRequestWithContext(ctx, "GET", UpStream+"/backend-api/me", nil) + if err != nil { + // 处理错误 + panic(err) + } + req.Header.Add("Authorization", "Bearer "+accessToken) + req.Header.Add("User-Agent", r.Header.Get("User-Agent")) + req.Header.Set("Host", "chat.openai.com") + req.Header.Set("Origin", "https://chat.openai.com/chat") + req.Header.Set("Referer", "https://chat.openai.com/") + + // 使用客户端发送请求 + resp, err := client.Do(req) + if err != nil { + // 处理错误 + panic(err) + } + defer resp.Body.Close() + originalBody, shouldReturn, err := loadRespString(resp) + if err != nil || shouldReturn { + return "", err + } + return string(originalBody), nil + +} + func ProxyAll(r *ghttp.Request) { ctx := r.GetCtx() @@ -72,28 +136,43 @@ func ProxyAll(r *ghttp.Request) { r.Response.WriteStatus(http.StatusUnauthorized) return } - UpStream := config.CHATPROXY(ctx) WsUpStream := config.WS_SERVICE(ctx) - u, _ := url.Parse(UpStream) - proxy := httputil.NewSingleHostReverseProxy(u) + // g.Log().Info(ctx, "ProxyBackendApi:", path) + proxy := &httputil.ReverseProxy{} + UpStream := config.CHATPROXY(ctx) + proxy.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, } - proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, e error) { - g.Log().Error(ctx, e) - writer.WriteHeader(http.StatusBadGateway) + if config.Ja3Proxy != nil { + proxy.Transport = &http.Transport{ + Proxy: http.ProxyURL(config.Ja3Proxy), + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } } - newreq := r.Request.Clone(ctx) - newreq.URL.Host = u.Host - newreq.URL.Scheme = u.Scheme - newreq.Host = u.Host - newreq.Header.Set("authkey", config.AUTHKEY(ctx)) - - newreq.Header.Set("Host", "chat.openai.com") - newreq.Header.Set("Origin", "https://chat.openai.com/chat") + OPENAI, err := url.Parse(UpStream) + if err != nil { + g.Log().Error(ctx, err) + r.Response.WriteStatus(http.StatusServiceUnavailable) + return + } + proxy.Rewrite = func(proxyRequest *httputil.ProxyRequest) { + proxyRequest.SetURL(OPENAI) + } + + header := r.Request.Header + header.Set("Origin", "https://chat.openai.com") + header.Set("Referer", "https://chat.openai.com/") + // header.Del("Cookie") + header.Del("Accept-Encoding") if accessToken != "" { - newreq.Header.Set("Authorization", "Bearer "+accessToken) + header.Set("Authorization", "Bearer "+accessToken) } + utility.HeaderModify(&r.Request.Header) // g.Dump(newreq.URL) cdnhost := config.CDNHOST(ctx) @@ -161,7 +240,7 @@ func ProxyAll(r *ghttp.Request) { return nil } - proxy.ServeHTTP(r.Response.Writer.RawWriter(), newreq) + proxy.ServeHTTP(r.Response.RawWriter(), r.Request) } diff --git a/backend-api/me.go b/backend-api/me.go index 0d3ec17..4d7a3d3 100644 --- a/backend-api/me.go +++ b/backend-api/me.go @@ -1,7 +1,6 @@ package backendapi import ( - "chatgpt-mirror-server/config" "net/http" "github.com/gogf/gf/v2/encoding/gjson" @@ -20,39 +19,40 @@ func Me(r *ghttp.Request) { r.Response.WriteStatus(http.StatusUnauthorized) return } - if record.IsEmpty() { - g.Log().Error(ctx, "session is empty") - r.Response.WriteStatus(http.StatusUnauthorized) - return - } - AccessToken := "" - // 如果 record mode ==1 - if record["mode"].Int() == 1 { - AccessToken = record["officialSession"].String() - } else { - officialSession := gjson.New(record["officialSession"].String()) - AccessToken = officialSession.Get("accessToken").String() + // if record.IsEmpty() { + // g.Log().Error(ctx, "session is empty") + // r.Response.WriteStatus(http.StatusUnauthorized) + // return + // } + // AccessToken := "" + // // 如果 record mode ==1 + // if record["mode"].Int() == 1 { + // AccessToken = record["officialSession"].String() + // } else { + // officialSession := gjson.New(record["officialSession"].String()) + // AccessToken = officialSession.Get("accessToken").String() - } + // } + resStr, err := ProxyRequestGet("/backend-api/me", r) - UpStream := config.CHATPROXY(ctx) - // 请求后端接口 - res, err := g.Client().SetHeaderMap(map[string]string{ - "Authorization": "Bearer " + AccessToken, - "User-Agent": r.Header.Get("User-Agent"), - "authKey": config.AUTHKEY(ctx), - }).Get(ctx, UpStream+"/backend-api/me") + // UpStream := config.CHATPROXY(ctx) + // // 请求后端接口 + // res, err := g.Client().SetHeaderMap(map[string]string{ + // "Authorization": "Bearer " + AccessToken, + // "User-Agent": r.Header.Get("User-Agent"), + // "authKey": config.AUTHKEY(ctx), + // }).Get(ctx, UpStream+"/backend-api/me") if err != nil { r.Response.WriteStatus(http.StatusUnauthorized) return } - resStr := res.ReadAllString() - if res.StatusCode != http.StatusOK { - r.Response.Status = res.StatusCode - r.Response.Write(resStr) + // resStr := res.ReadAllString() + // if res.StatusCode != http.StatusOK { + // r.Response.Status = res.StatusCode + // r.Response.Write(resStr) - return - } + // return + // } resJson := gjson.New(resStr) resJson.Set("email", "__mirror@closeai.com") resJson.Set("name", record["user_username"].String()) diff --git a/config.yaml b/config.yaml index 50f8326..c046b1e 100644 --- a/config.yaml +++ b/config.yaml @@ -55,7 +55,10 @@ modules: enable: 1 # 接入网关地址 -CHATPROXY: "https://demo.xyhelper.cn" +CHATPROXY: "https://38.207.162.134:8443" +#CHATPROXY: "https://chat.openai.com" +#JA3_PROXY: "http://a:b@host.docker.internal:9988" +#JA3_PROXY: "http://a:b@host.docker.internal:9988" #CHATPROXY: "http://host.docker.internal:7999" #CHATPROXY: "https://chatgpt.ggss.club/gateway" #ARKOSE_URL: "https://chatgpt.ggss.club/arkose/v2/" diff --git a/config/config.go b/config/config.go index f44a45a..f482347 100644 --- a/config/config.go +++ b/config/config.go @@ -2,11 +2,13 @@ package config import ( "math/rand" + "net/url" "time" "github.com/gogf/gf/v2/container/garray" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gclient" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gview" @@ -33,12 +35,14 @@ func USERTOKENLOCK(ctx g.Ctx) bool { var ( DefaultModel = "text-davinci-002-render-sha" FreeModels = garray.NewStrArray() + Ja3Proxy *url.URL // ja3代理 PlusModels = garray.NewStrArray() ArkoseUrl = "https://tcr9i.closeai.biz/v2/" BuildId = "LxJWDayKNMzRjO_Ay4ljN" CacheBuildId = "LxJWDayKNMzRjO_Ay4ljN" AssetPrefix = "https://oaistatic-cdn.closeai.biz" PK40 = "35536E1E-65B4-4D96-9D97-6ADB7EFF8147" + ProxyClient *gclient.Client PK35 = "3D86FBBA-9D22-402A-B512-3420086BA6CC" envScriptTpl = `
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/discovery.html b/resource/template/4DtybnrborNWeufDD8H-a/discovery.html new file mode 100644 index 0000000..a5c26ab --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/discovery.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/editor.html b/resource/template/4DtybnrborNWeufDD8H-a/editor.html new file mode 100644 index 0000000..96ecd58 --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/editor.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/g.html b/resource/template/4DtybnrborNWeufDD8H-a/g.html new file mode 100644 index 0000000..0cbd157 --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/g.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/gc.html b/resource/template/4DtybnrborNWeufDD8H-a/gc.html new file mode 100644 index 0000000..c71a282 --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/gc.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/gpts.html b/resource/template/4DtybnrborNWeufDD8H-a/gpts.html new file mode 100644 index 0000000..a5c26ab --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/gpts.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/mine.html b/resource/template/4DtybnrborNWeufDD8H-a/mine.html new file mode 100644 index 0000000..4e0c27e --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/mine.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/4DtybnrborNWeufDD8H-a/slug.html b/resource/template/4DtybnrborNWeufDD8H-a/slug.html new file mode 100644 index 0000000..6748227 --- /dev/null +++ b/resource/template/4DtybnrborNWeufDD8H-a/slug.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/chat.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/chat.html new file mode 100644 index 0000000..f982a93 --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/chat.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/discovery.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/discovery.html new file mode 100644 index 0000000..757428e --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/discovery.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/editor.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/editor.html new file mode 100644 index 0000000..ce50303 --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/editor.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/g.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/g.html new file mode 100644 index 0000000..1371287 --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/g.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gc.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gc.html new file mode 100644 index 0000000..8c833f1 --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gc.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gpts.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gpts.html new file mode 100644 index 0000000..757428e --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/gpts.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/mine.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/mine.html new file mode 100644 index 0000000..58203c7 --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/mine.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/resource/template/PIFRBk3Rl3kZqLq3CMN4K/slug.html b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/slug.html new file mode 100644 index 0000000..302b20b --- /dev/null +++ b/resource/template/PIFRBk3Rl3kZqLq3CMN4K/slug.html @@ -0,0 +1 @@ +{{.envScript}}ChatGPT
\ No newline at end of file diff --git a/utility/headermodify.go b/utility/headermodify.go new file mode 100644 index 0000000..16b1451 --- /dev/null +++ b/utility/headermodify.go @@ -0,0 +1,36 @@ +package utility + +import "net/http" + +// HeaderModify modifies the header of the request. +func HeaderModify(headers *http.Header) { + // 移除一些错误的转发头 + headers.Del("X-Forwarded-For") + headers.Del("X-Forwarded-Host") + headers.Del("X-Forwarded-Proto") + headers.Del("X-Forwarded-Server") + headers.Del("X-Real-Ip") + headers.Del("X-Forwarded-Port") + headers.Del("X-Forwarded-Uri") + headers.Del("X-Forwarded-Path") + headers.Del("X-Forwarded-Method") + headers.Del("X-Forwarded-Protocol") + headers.Del("X-Forwarded-Scheme") + + // 移除一些CF的头 + headers.Del("Cf-Connecting-Ip") + headers.Del("Cf-Ipcountry") + headers.Del("Cf-Ray") + headers.Del("Cf-Visitor") + headers.Del("Cf-Request-Id") + headers.Del("Cf-Worker") + headers.Del("Cf-Access-Client-Id") + headers.Del("Cf-Access-Client-Device-Type") + headers.Del("Cf-Access-Client-Device-Model") + headers.Del("Cf-Access-Client-Device-Name") + headers.Del("Cf-Access-Client-Device-Brand") + + // 一些奇怪的东西 + headers.Del("x-middleware-prefetch") + +}