|
- package service
-
- import (
- "bytes"
- "context"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/QuantumNous/new-api/model"
- "github.com/QuantumNous/new-api/setting/system_setting"
- )
-
- type CompatibleAssetAdapter struct {
- name string
- operation map[AssetOperation]struct{}
- }
-
- func NewCompatibleAssetAdapter(name string, operations []AssetOperation) AssetAdapter {
- supported := make(map[AssetOperation]struct{}, len(operations))
- for _, op := range operations {
- supported[op] = struct{}{}
- }
- return &CompatibleAssetAdapter{name: name, operation: supported}
- }
-
- func (a *CompatibleAssetAdapter) Name() string {
- return a.name
- }
-
- func (a *CompatibleAssetAdapter) Supports(operation AssetOperation) bool {
- _, ok := a.operation[operation]
- return ok
- }
-
- func (a *CompatibleAssetAdapter) DoAssetRequest(ctx context.Context, channel *model.Channel, req AssetRequest) (*AssetUpstreamResponse, *AssetError) {
- if !a.Supports(req.Action.Operation) {
- return nil, newAssetError(AssetErrorOperationNotSupported, fmt.Sprintf("asset operation %s is not supported", req.Action.Operation), http.StatusBadRequest)
- }
- upstreamURL, err := buildCompatibleAssetURL(channel, req.Action.Action, req.Version)
- if err != nil {
- return nil, newAssetError(AssetErrorServer, err.Error(), http.StatusInternalServerError)
- }
- fetchSetting := system_setting.GetFetchSetting()
- if err := common.ValidateURLWithFetchSetting(upstreamURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainFilterMode, fetchSetting.IpFilterMode, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts, fetchSetting.ApplyIPFilterForDomain); err != nil {
- return nil, newAssetError(AssetErrorServer, fmt.Sprintf("request blocked: %v", err), http.StatusForbidden)
- }
- httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, upstreamURL, bytes.NewReader(req.RawBody))
- if err != nil {
- return nil, newAssetError(AssetErrorServer, err.Error(), http.StatusInternalServerError)
- }
- httpReq.Header.Set("Content-Type", "application/json")
- httpReq.Header.Set("Accept", "application/json")
- httpReq.Header.Set("Authorization", "Bearer "+strings.TrimSpace(channel.Key))
- client, err := GetHttpClientWithProxy(channel.GetSetting().Proxy)
- if err != nil {
- return nil, newAssetError(AssetErrorServer, err.Error(), http.StatusInternalServerError)
- }
- if client == nil {
- client = http.DefaultClient
- }
- resp, err := client.Do(httpReq)
- if err != nil {
- return nil, newAssetError(AssetErrorUpstream, err.Error(), http.StatusBadGateway)
- }
- defer resp.Body.Close()
- data, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, newAssetError(AssetErrorUpstream, err.Error(), http.StatusBadGateway)
- }
- if resp.StatusCode < 200 || resp.StatusCode >= 300 {
- return nil, newAssetError(AssetErrorUpstream, string(data), http.StatusBadGateway)
- }
- return &AssetUpstreamResponse{StatusCode: resp.StatusCode, Header: resp.Header, Body: data}, nil
- }
-
- func buildCompatibleAssetURL(channel *model.Channel, action string, version string) (string, error) {
- baseURL := strings.TrimSpace(channel.GetBaseURL())
- if baseURL == "" {
- baseURL = "https://ark.cn-beijing.volcengineapi.com"
- }
- u, err := url.Parse(baseURL)
- if err != nil {
- return "", err
- }
- u.Path = strings.TrimRight(u.Path, "/") + "/api/v1/multimodal/sd/assets"
- q := u.Query()
- q.Set("Action", action)
- if strings.TrimSpace(version) == "" {
- version = "2024-01-01"
- }
- q.Set("Version", version)
- u.RawQuery = q.Encode()
- return u.String(), nil
- }
|