🔖 chore: add chat image request proxy address
This commit is contained in:
parent
3d8a51e139
commit
b5a4283b28
@ -69,6 +69,8 @@ var SMTPAccount = ""
|
|||||||
var SMTPFrom = ""
|
var SMTPFrom = ""
|
||||||
var SMTPToken = ""
|
var SMTPToken = ""
|
||||||
|
|
||||||
|
var ChatImageRequestProxy = ""
|
||||||
|
|
||||||
var GitHubClientId = ""
|
var GitHubClientId = ""
|
||||||
var GitHubClientSecret = ""
|
var GitHubClientSecret = ""
|
||||||
|
|
||||||
|
26
common/image/http.go
Normal file
26
common/image/http.go
Normal file
@ -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)
|
||||||
|
}
|
@ -16,17 +16,8 @@ import (
|
|||||||
_ "golang.org/x/image/webp"
|
_ "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) {
|
func IsImageUrl(url string) (bool, error) {
|
||||||
resp, err := http.Head(url)
|
resp, err := requestImage(url, http.MethodHead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -41,7 +32,7 @@ func GetImageSizeFromUrl(url string) (width int, height int, err error) {
|
|||||||
if !isImage {
|
if !isImage {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := http.Get(url)
|
resp, err := requestImage(url, http.MethodGet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -126,6 +117,6 @@ func GetImageSize(image string) (width int, height int, err error) {
|
|||||||
case strings.HasPrefix(image, "http"):
|
case strings.HasPrefix(image, "http"):
|
||||||
return GetImageSizeFromUrl(image)
|
return GetImageSizeFromUrl(image)
|
||||||
default:
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
type HttpErrorHandler func(*http.Response) *types.OpenAIError
|
type HttpErrorHandler func(*http.Response) *types.OpenAIError
|
||||||
|
|
||||||
type HTTPRequester struct {
|
type HTTPRequester struct {
|
||||||
requestBuilder RequestBuilder
|
// requestBuilder utils.RequestBuilder
|
||||||
CreateFormBuilder func(io.Writer) FormBuilder
|
CreateFormBuilder func(io.Writer) FormBuilder
|
||||||
ErrorHandler HttpErrorHandler
|
ErrorHandler HttpErrorHandler
|
||||||
proxyAddr string
|
proxyAddr string
|
||||||
@ -34,7 +34,6 @@ type HTTPRequester struct {
|
|||||||
// 如果 errorHandler 为 nil,那么会使用一个默认的错误处理函数。
|
// 如果 errorHandler 为 nil,那么会使用一个默认的错误处理函数。
|
||||||
func NewHTTPRequester(proxyAddr string, errorHandler HttpErrorHandler) *HTTPRequester {
|
func NewHTTPRequester(proxyAddr string, errorHandler HttpErrorHandler) *HTTPRequester {
|
||||||
return &HTTPRequester{
|
return &HTTPRequester{
|
||||||
requestBuilder: NewRequestBuilder(),
|
|
||||||
CreateFormBuilder: func(body io.Writer) FormBuilder {
|
CreateFormBuilder: func(body io.Writer) FormBuilder {
|
||||||
return NewFormBuilder(body)
|
return NewFormBuilder(body)
|
||||||
},
|
},
|
||||||
@ -53,7 +52,7 @@ type requestOptions struct {
|
|||||||
type requestOption func(*requestOptions)
|
type requestOption func(*requestOptions)
|
||||||
|
|
||||||
func (r *HTTPRequester) setProxy() context.Context {
|
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 {
|
for _, setter := range setters {
|
||||||
setter(args)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -60,7 +60,11 @@ func Socks5ProxyFunc(ctx context.Context, network, addr string) (net.Conn, error
|
|||||||
return proxyDialer.Dial(network, addr)
|
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 == "" {
|
if proxyAddr == "" {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,14 @@
|
|||||||
package requester
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RequestBuilder interface {
|
func RequestBuilder(
|
||||||
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(
|
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
method string,
|
method string,
|
||||||
url string,
|
url string,
|
||||||
@ -34,7 +21,7 @@ func (b *HTTPRequestBuilder) Build(
|
|||||||
bodyReader = v
|
bodyReader = v
|
||||||
} else {
|
} else {
|
||||||
var reqBytes []byte
|
var reqBytes []byte
|
||||||
reqBytes, err = b.marshaller.Marshal(body)
|
reqBytes, err = json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
2
go.mod
2
go.mod
@ -13,6 +13,7 @@ require (
|
|||||||
github.com/gin-contrib/static v1.1.0
|
github.com/gin-contrib/static v1.1.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-co-op/gocron/v2 v2.2.9
|
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-playground/validator/v10 v10.19.0
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
@ -35,7 +36,6 @@ require (
|
|||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // 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/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
|
@ -16,8 +16,7 @@ type Option struct {
|
|||||||
|
|
||||||
func AllOption() ([]*Option, error) {
|
func AllOption() ([]*Option, error) {
|
||||||
var options []*Option
|
var options []*Option
|
||||||
var err error
|
err := DB.Find(&options).Error
|
||||||
err = DB.Find(&options).Error
|
|
||||||
return options, err
|
return options, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +82,8 @@ func InitOptionMap() {
|
|||||||
config.OptionMap["ChatCacheEnabled"] = strconv.FormatBool(config.ChatCacheEnabled)
|
config.OptionMap["ChatCacheEnabled"] = strconv.FormatBool(config.ChatCacheEnabled)
|
||||||
config.OptionMap["ChatCacheExpireMinute"] = strconv.Itoa(config.ChatCacheExpireMinute)
|
config.OptionMap["ChatCacheExpireMinute"] = strconv.Itoa(config.ChatCacheExpireMinute)
|
||||||
|
|
||||||
|
config.OptionMap["ChatImageRequestProxy"] = ""
|
||||||
|
|
||||||
config.OptionMapRWMutex.Unlock()
|
config.OptionMapRWMutex.Unlock()
|
||||||
loadOptionsFromDatabase()
|
loadOptionsFromDatabase()
|
||||||
}
|
}
|
||||||
@ -174,6 +175,7 @@ var optionStringMap = map[string]*string{
|
|||||||
"ChatLinks": &config.ChatLinks,
|
"ChatLinks": &config.ChatLinks,
|
||||||
"LarkClientId": &config.LarkClientId,
|
"LarkClientId": &config.LarkClientId,
|
||||||
"LarkClientSecret": &config.LarkClientSecret,
|
"LarkClientSecret": &config.LarkClientSecret,
|
||||||
|
"ChatImageRequestProxy": &config.ChatImageRequestProxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOptionMap(key string, value string) (err error) {
|
func updateOptionMap(key string, value string) (err error) {
|
||||||
|
@ -35,7 +35,8 @@ const OperationSetting = () => {
|
|||||||
RetryCooldownSeconds: 0,
|
RetryCooldownSeconds: 0,
|
||||||
MjNotifyEnabled: '',
|
MjNotifyEnabled: '',
|
||||||
ChatCacheEnabled: '',
|
ChatCacheEnabled: '',
|
||||||
ChatCacheExpireMinute: 5
|
ChatCacheExpireMinute: 5,
|
||||||
|
ChatImageRequestProxy: ''
|
||||||
});
|
});
|
||||||
const [originInputs, setOriginInputs] = useState({});
|
const [originInputs, setOriginInputs] = useState({});
|
||||||
let [loading, setLoading] = useState(false);
|
let [loading, setLoading] = useState(false);
|
||||||
@ -173,6 +174,9 @@ const OperationSetting = () => {
|
|||||||
if (originInputs['ChatCacheExpireMinute'] !== inputs.ChatCacheExpireMinute) {
|
if (originInputs['ChatCacheExpireMinute'] !== inputs.ChatCacheExpireMinute) {
|
||||||
await updateOption('ChatCacheExpireMinute', inputs.ChatCacheExpireMinute);
|
await updateOption('ChatCacheExpireMinute', inputs.ChatCacheExpireMinute);
|
||||||
}
|
}
|
||||||
|
if (originInputs['ChatImageRequestProxy'] !== inputs.ChatImageRequestProxy) {
|
||||||
|
await updateOption('ChatImageRequestProxy', inputs.ChatImageRequestProxy);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +338,26 @@ const OperationSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Alert severity="info">
|
||||||
|
当用户使用vision模型并提供了图片链接时,我们的服务器需要下载这些图片并计算 tokens。为了在下载图片时保护服务器的 IP
|
||||||
|
地址不被泄露,可以在下方配置一个代理。这个代理配置使用的是 HTTP 或 SOCKS5
|
||||||
|
代理。如果你是个人用户,这个配置可以不用理会。代理格式为 http://127.0.0.1:1080 或 socks5://127.0.0.1:1080
|
||||||
|
</Alert>
|
||||||
|
<FormControl>
|
||||||
|
<InputLabel htmlFor="ChatImageRequestProxy">图片检测代理</InputLabel>
|
||||||
|
<OutlinedInput
|
||||||
|
id="ChatImageRequestProxy"
|
||||||
|
name="ChatImageRequestProxy"
|
||||||
|
value={inputs.ChatImageRequestProxy}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
label="图片检测代理"
|
||||||
|
placeholder="聊天图片检测代理设置,如果不设置可能会泄漏服务器ip"
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Stack>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -376,6 +376,9 @@ const SystemSetting = () => {
|
|||||||
</SubCard>
|
</SubCard>
|
||||||
<SubCard title="配置 SMTP" subTitle="用以支持系统的邮件发送">
|
<SubCard title="配置 SMTP" subTitle="用以支持系统的邮件发送">
|
||||||
<Grid container spacing={{ xs: 3, sm: 2, md: 4 }}>
|
<Grid container spacing={{ xs: 3, sm: 2, md: 4 }}>
|
||||||
|
<Grid xs={12}>
|
||||||
|
<Alert severity="info">请注意,有些邮箱服务商发送邮件时会携带你的服务器IP地址,非个人使用时建议使用专业的邮件服务商</Alert>
|
||||||
|
</Grid>
|
||||||
<Grid xs={12} md={4}>
|
<Grid xs={12} md={4}>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel htmlFor="SMTPServer">SMTP 服务器地址</InputLabel>
|
<InputLabel htmlFor="SMTPServer">SMTP 服务器地址</InputLabel>
|
||||||
|
Loading…
Reference in New Issue
Block a user