diff --git a/common/config/constants.go b/common/config/constants.go index 345c8b9e..f9c55061 100644 --- a/common/config/constants.go +++ b/common/config/constants.go @@ -69,6 +69,8 @@ var SMTPAccount = "" var SMTPFrom = "" var SMTPToken = "" +var ChatImageRequestProxy = "" + var GitHubClientId = "" var GitHubClientSecret = "" diff --git a/common/image/http.go b/common/image/http.go new file mode 100644 index 00000000..791ed2f7 --- /dev/null +++ b/common/image/http.go @@ -0,0 +1,26 @@ +package image + +import ( + "net/http" + "one-api/common/config" + "one-api/common/utils" + "time" +) + +var ImageHttpClients = &http.Client{ + Transport: &http.Transport{ + DialContext: utils.Socks5ProxyFunc, + Proxy: utils.ProxyFunc, + }, + Timeout: 15 * time.Second, +} + +func requestImage(url, method string) (*http.Response, error) { + res, err := utils.RequestBuilder(utils.SetProxy(config.ChatImageRequestProxy, nil), method, url, nil, nil) + + if err != nil { + return nil, err + } + + return ImageHttpClients.Do(res) +} diff --git a/common/image/image.go b/common/image/image.go index df5e578b..4ad15bb8 100644 --- a/common/image/image.go +++ b/common/image/image.go @@ -16,17 +16,8 @@ import ( _ "golang.org/x/image/webp" ) -// var ImageHttpClients = &http.Client{ -// Transport: &http.Transport{ -// DialContext: requester.Socks5ProxyFunc, -// Proxy: requester.ProxyFunc, -// }, -// // -// // // Timeout: 30 * time.Second, -// } - func IsImageUrl(url string) (bool, error) { - resp, err := http.Head(url) + resp, err := requestImage(url, http.MethodHead) if err != nil { return false, err } @@ -41,7 +32,7 @@ func GetImageSizeFromUrl(url string) (width int, height int, err error) { if !isImage { return } - resp, err := http.Get(url) + resp, err := requestImage(url, http.MethodGet) if err != nil { return } @@ -126,6 +117,6 @@ func GetImageSize(image string) (width int, height int, err error) { case strings.HasPrefix(image, "http"): return GetImageSizeFromUrl(image) default: - return 0, 0, errors.New("invalid file type, Please view request interface!") + return 0, 0, errors.New("invalid file type, please view request interface") } } diff --git a/common/requester/http_requester.go b/common/requester/http_requester.go index 328a778e..f1b6e3ae 100644 --- a/common/requester/http_requester.go +++ b/common/requester/http_requester.go @@ -20,7 +20,7 @@ import ( type HttpErrorHandler func(*http.Response) *types.OpenAIError type HTTPRequester struct { - requestBuilder RequestBuilder + // requestBuilder utils.RequestBuilder CreateFormBuilder func(io.Writer) FormBuilder ErrorHandler HttpErrorHandler proxyAddr string @@ -34,7 +34,6 @@ type HTTPRequester struct { // 如果 errorHandler 为 nil,那么会使用一个默认的错误处理函数。 func NewHTTPRequester(proxyAddr string, errorHandler HttpErrorHandler) *HTTPRequester { return &HTTPRequester{ - requestBuilder: NewRequestBuilder(), CreateFormBuilder: func(body io.Writer) FormBuilder { return NewFormBuilder(body) }, @@ -53,7 +52,7 @@ type requestOptions struct { type requestOption func(*requestOptions) func (r *HTTPRequester) setProxy() context.Context { - return utils.SetProxy(r.Context, r.proxyAddr) + return utils.SetProxy(r.proxyAddr, r.Context) } // 创建请求 @@ -65,7 +64,7 @@ func (r *HTTPRequester) NewRequest(method, url string, setters ...requestOption) for _, setter := range setters { setter(args) } - req, err := r.requestBuilder.Build(r.setProxy(), method, url, args.body, args.header) + req, err := utils.RequestBuilder(r.setProxy(), method, url, args.body, args.header) if err != nil { return nil, err } diff --git a/common/requester/marshaller.go b/common/requester/marshaller.go deleted file mode 100644 index 4577af0c..00000000 --- a/common/requester/marshaller.go +++ /dev/null @@ -1,15 +0,0 @@ -package requester - -import ( - "encoding/json" -) - -type Marshaller interface { - Marshal(value any) ([]byte, error) -} - -type JSONMarshaller struct{} - -func (jm *JSONMarshaller) Marshal(value any) ([]byte, error) { - return json.Marshal(value) -} diff --git a/common/utils/proxy.go b/common/utils/proxy.go index b0b1cf9d..a08f702b 100644 --- a/common/utils/proxy.go +++ b/common/utils/proxy.go @@ -60,7 +60,11 @@ func Socks5ProxyFunc(ctx context.Context, network, addr string) (net.Conn, error return proxyDialer.Dial(network, addr) } -func SetProxy(ctx context.Context, proxyAddr string) context.Context { +func SetProxy(proxyAddr string, ctx context.Context) context.Context { + if ctx == nil { + ctx = context.Background() + } + if proxyAddr == "" { return ctx } diff --git a/common/requester/request_builder.go b/common/utils/request_builder.go similarity index 56% rename from common/requester/request_builder.go rename to common/utils/request_builder.go index bcf7920a..7a1933c5 100644 --- a/common/requester/request_builder.go +++ b/common/utils/request_builder.go @@ -1,27 +1,14 @@ -package requester +package utils import ( "bytes" "context" + "encoding/json" "io" "net/http" ) -type RequestBuilder interface { - Build(ctx context.Context, method, url string, body any, header http.Header) (*http.Request, error) -} - -type HTTPRequestBuilder struct { - marshaller Marshaller -} - -func NewRequestBuilder() *HTTPRequestBuilder { - return &HTTPRequestBuilder{ - marshaller: &JSONMarshaller{}, - } -} - -func (b *HTTPRequestBuilder) Build( +func RequestBuilder( ctx context.Context, method string, url string, @@ -34,7 +21,7 @@ func (b *HTTPRequestBuilder) Build( bodyReader = v } else { var reqBytes []byte - reqBytes, err = b.marshaller.Marshal(body) + reqBytes, err = json.Marshal(body) if err != nil { return } diff --git a/go.mod b/go.mod index 458edc0c..1d297960 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/gin-contrib/static v1.1.0 github.com/gin-gonic/gin v1.9.1 github.com/go-co-op/gocron/v2 v2.2.9 + github.com/go-gormigrate/gormigrate/v2 v2.1.2 github.com/go-playground/validator/v10 v10.19.0 github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -35,7 +36,6 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-gormigrate/gormigrate/v2 v2.1.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect diff --git a/model/option.go b/model/option.go index 0dc6db0d..a5c881c7 100644 --- a/model/option.go +++ b/model/option.go @@ -16,8 +16,7 @@ type Option struct { func AllOption() ([]*Option, error) { var options []*Option - var err error - err = DB.Find(&options).Error + err := DB.Find(&options).Error return options, err } @@ -83,6 +82,8 @@ func InitOptionMap() { config.OptionMap["ChatCacheEnabled"] = strconv.FormatBool(config.ChatCacheEnabled) config.OptionMap["ChatCacheExpireMinute"] = strconv.Itoa(config.ChatCacheExpireMinute) + config.OptionMap["ChatImageRequestProxy"] = "" + config.OptionMapRWMutex.Unlock() loadOptionsFromDatabase() } @@ -174,6 +175,7 @@ var optionStringMap = map[string]*string{ "ChatLinks": &config.ChatLinks, "LarkClientId": &config.LarkClientId, "LarkClientSecret": &config.LarkClientSecret, + "ChatImageRequestProxy": &config.ChatImageRequestProxy, } func updateOptionMap(key string, value string) (err error) { diff --git a/web/src/views/Setting/component/OperationSetting.js b/web/src/views/Setting/component/OperationSetting.js index 045ec24d..18c65e45 100644 --- a/web/src/views/Setting/component/OperationSetting.js +++ b/web/src/views/Setting/component/OperationSetting.js @@ -35,7 +35,8 @@ const OperationSetting = () => { RetryCooldownSeconds: 0, MjNotifyEnabled: '', ChatCacheEnabled: '', - ChatCacheExpireMinute: 5 + ChatCacheExpireMinute: 5, + ChatImageRequestProxy: '' }); const [originInputs, setOriginInputs] = useState({}); let [loading, setLoading] = useState(false); @@ -173,6 +174,9 @@ const OperationSetting = () => { if (originInputs['ChatCacheExpireMinute'] !== inputs.ChatCacheExpireMinute) { await updateOption('ChatCacheExpireMinute', inputs.ChatCacheExpireMinute); } + if (originInputs['ChatImageRequestProxy'] !== inputs.ChatImageRequestProxy) { + await updateOption('ChatImageRequestProxy', inputs.ChatImageRequestProxy); + } break; } @@ -334,6 +338,26 @@ const OperationSetting = () => { /> + + + + 当用户使用vision模型并提供了图片链接时,我们的服务器需要下载这些图片并计算 tokens。为了在下载图片时保护服务器的 IP + 地址不被泄露,可以在下方配置一个代理。这个代理配置使用的是 HTTP 或 SOCKS5 + 代理。如果你是个人用户,这个配置可以不用理会。代理格式为 http://127.0.0.1:1080 或 socks5://127.0.0.1:1080 + + + 图片检测代理 + + +