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
+
+
+ 图片检测代理
+
+
+