chore: 并入 iotlcm-upstream-chan 历史到 upstream-chan/(原提交 1e74497)
This commit is contained in:
@@ -0,0 +1,615 @@
|
||||
package upstreamimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/service"
|
||||
sharedtoken "git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/token"
|
||||
)
|
||||
|
||||
const (
|
||||
sharedOneLinkTengyuTemplateKey = "sim_onelink_tengyu_v1"
|
||||
sharedOneLinkXuzhouTemplateKey = "sim_onelink_xuzhou_mobile_v1"
|
||||
sharedOneLinkLelianTemplateKey = "sim_onelink_lelian_v1"
|
||||
sharedOneLinkQianxunTemplateKey = "sim_onelink_qianxun_v1"
|
||||
sharedOneLinkHenanXinjingTemplateKey = "sim_onelink_henan_xinjing_v1"
|
||||
sharedOneLinkDaiheTemplateKey = "sim_onelink_daihe_v1"
|
||||
sharedOneLinkMobilePublicTemplateKey = "sim_onelink_daliuliang_v1"
|
||||
sharedOneLinkMobilePublicPreviousKey = "sim__daliuliang_v1"
|
||||
sharedOneLinkMobilePublicLegacyKey = "sim_onelink_mobile_public_v1"
|
||||
sharedOneLinkApikaTemplateKey = "sim_onelink_kedang_v1"
|
||||
sharedOneLinkYunzeTemplateKey = "sim_onelink_yunze_v1"
|
||||
sharedOneLinkLanshuTemplateKey = "sim_onelink_lanshu_v1"
|
||||
sharedOneLinkBeixinTemplateKey = "sim_onelink_beixin_v1"
|
||||
sharedOneLinkAPIBaseURL = "https://api.iot.10086.cn/v5/"
|
||||
sharedOneLinkTransIDPrefix = "ONELINK"
|
||||
sharedOneLinkXuzhouTokenTTL = 40 * time.Minute
|
||||
)
|
||||
|
||||
// SharedTokenConfig is the per-channel configuration passed to a provider
|
||||
// factory. It intentionally contains no provider implementation details.
|
||||
type SharedTokenConfig struct {
|
||||
Provider string
|
||||
TokenBaseURL string
|
||||
LoginPath string
|
||||
TokenPath string
|
||||
CarrierType int
|
||||
PwdKey string
|
||||
ChannelNo string
|
||||
AppKey string
|
||||
AppID string
|
||||
AppSecret string
|
||||
SubAccount string
|
||||
Secret string
|
||||
OperatorID string
|
||||
UserID string
|
||||
TokenType string
|
||||
UserName string
|
||||
Password string
|
||||
PasswayUID string
|
||||
Sign string
|
||||
ChannelID string
|
||||
ChannelPName string
|
||||
CardNo string
|
||||
CMCCAppID string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// TokenProviderFactory is the adapter point for the token-core/provider
|
||||
// integration. Provider branches can register factories without changing this
|
||||
// channel implementation.
|
||||
type TokenProviderFactory func(context.Context, *model.SimChannelConfigItemCache, SharedTokenConfig) (ydTokenProvider, error)
|
||||
|
||||
var sharedTokenFactories = struct {
|
||||
sync.RWMutex
|
||||
m map[string]TokenProviderFactory
|
||||
}{m: make(map[string]TokenProviderFactory)}
|
||||
|
||||
func RegisterTokenProviderFactory(provider string, factory TokenProviderFactory) {
|
||||
provider = strings.ToLower(strings.TrimSpace(provider))
|
||||
if provider == "" || factory == nil {
|
||||
return
|
||||
}
|
||||
sharedTokenFactories.Lock()
|
||||
sharedTokenFactories.m[provider] = factory
|
||||
sharedTokenFactories.Unlock()
|
||||
}
|
||||
|
||||
func tokenProviderFactory(provider string) (TokenProviderFactory, bool) {
|
||||
sharedTokenFactories.RLock()
|
||||
f, ok := sharedTokenFactories.m[strings.ToLower(strings.TrimSpace(provider))]
|
||||
sharedTokenFactories.RUnlock()
|
||||
return f, ok
|
||||
}
|
||||
|
||||
type oneLinkAPIConfig struct {
|
||||
BaseURL string
|
||||
TokenPath string
|
||||
Timeout time.Duration
|
||||
transIDPrefix string
|
||||
}
|
||||
|
||||
type sharedOneLinkChannelConfig struct {
|
||||
PwdKey string `json:"pwdKey"`
|
||||
ChannelNo string `json:"channelNo"`
|
||||
AppID string `json:"appId"`
|
||||
SubAccount string `json:"subAccount"`
|
||||
Secret string `json:"secret"`
|
||||
OperatorID string `json:"operatorId"`
|
||||
UserID string `json:"userId"`
|
||||
TokenType string `json:"tokenType"`
|
||||
UserName string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
PasswayUID string `json:"passwayUid"`
|
||||
ChannelID string `json:"channelId"`
|
||||
ChannelPName string `json:"channelPname"`
|
||||
CardNo string `json:"cardNo"`
|
||||
CMCCAppID string `json:"cmccAppId"`
|
||||
// Sign is the fixed signature issued by the LanShu open route. It is
|
||||
// passed through verbatim and is never derived locally.
|
||||
Sign string `json:"sign"`
|
||||
// TokenBaseURL lets a channel override the template's token.baseUrl, so
|
||||
// accounts living on a different upstream host can share one template.
|
||||
TokenBaseURL string `json:"tokenBaseUrl"`
|
||||
LegacyCredentials *struct {
|
||||
PwdKey string `json:"pwdKey"`
|
||||
ChannelNo string `json:"channelNo"`
|
||||
AppID string `json:"appId"`
|
||||
UserName string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
PasswayUID string `json:"passwayUid"`
|
||||
} `json:"credentials"`
|
||||
}
|
||||
|
||||
type sharedOneLinkTemplateConfig struct {
|
||||
API struct {
|
||||
BaseURL string `json:"baseUrl"`
|
||||
} `json:"api"`
|
||||
Token struct {
|
||||
Provider string `json:"provider"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
Path string `json:"path"`
|
||||
Params struct {
|
||||
LoginPath string `json:"loginPath"`
|
||||
CarrierType *int `json:"carrierType"`
|
||||
} `json:"params"`
|
||||
LegacyLoginPath string `json:"loginPath"`
|
||||
LegacyCarrierType *int `json:"carrierType"`
|
||||
} `json:"token"`
|
||||
}
|
||||
|
||||
type sharedTokenManagerProvider struct {
|
||||
manager sharedtoken.TokenManager
|
||||
chanKey string
|
||||
includeDeviceToken bool
|
||||
}
|
||||
|
||||
func (p *sharedTokenManagerProvider) GetToken(ctx context.Context) (string, error) {
|
||||
return p.manager.GetToken(ctx, p.chanKey)
|
||||
}
|
||||
|
||||
func (p *sharedTokenManagerProvider) GetDeviceToken(ctx context.Context) (string, error) {
|
||||
if !p.includeDeviceToken {
|
||||
return "", nil
|
||||
}
|
||||
token, err := p.manager.Get(ctx, p.chanKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.DeviceValue, nil
|
||||
}
|
||||
|
||||
func (p *sharedTokenManagerProvider) RecoverToken(ctx context.Context, rejected string) (string, error) {
|
||||
p.manager.InvalidateIfCurrent(p.chanKey, rejected)
|
||||
return p.manager.GetToken(ctx, p.chanKey)
|
||||
}
|
||||
|
||||
// GetSharedToken returns the shared token together with its expiry, fetching
|
||||
// from the provider on demand when the cached value is missing or expired.
|
||||
// Expiry is zero when the provider does not report one.
|
||||
func (p *sharedTokenManagerProvider) GetSharedToken(ctx context.Context) (sharedtoken.OneLinkToken, error) {
|
||||
return p.manager.Get(ctx, p.chanKey)
|
||||
}
|
||||
|
||||
type tengyuFetcher struct{ provider *TengyuTokenProvider }
|
||||
|
||||
func (f tengyuFetcher) FetchToken(ctx context.Context, _ string) (sharedtoken.OneLinkToken, error) {
|
||||
t, err := f.provider.Fetch(ctx)
|
||||
if err != nil {
|
||||
return sharedtoken.OneLinkToken{}, err
|
||||
}
|
||||
return sharedtoken.OneLinkToken{Value: t.Value, CreatedAt: t.CreatedAt, ExpiresAt: t.ExpiresAt}, nil
|
||||
}
|
||||
|
||||
type xuzhouMobileFetcher struct{ provider *XuzhouMobileTokenProvider }
|
||||
|
||||
func (f xuzhouMobileFetcher) FetchToken(ctx context.Context, _ string) (sharedtoken.OneLinkToken, error) {
|
||||
t, err := f.provider.Fetch(ctx)
|
||||
if err != nil {
|
||||
return sharedtoken.OneLinkToken{}, err
|
||||
}
|
||||
return sharedtoken.OneLinkToken{Value: t.Value, ExpiresAt: t.ExpiresAt}, nil
|
||||
}
|
||||
|
||||
type henanLelianFetcher struct{ provider *HenanLelianTokenProvider }
|
||||
|
||||
func (f henanLelianFetcher) FetchToken(ctx context.Context, _ string) (sharedtoken.OneLinkToken, error) {
|
||||
t, err := f.provider.Fetch(ctx)
|
||||
if err != nil {
|
||||
return sharedtoken.OneLinkToken{}, err
|
||||
}
|
||||
return sharedtoken.OneLinkToken{Value: t.Value, CreatedAt: t.CreatedAt}, nil
|
||||
}
|
||||
|
||||
func parseSharedOneLinkConfig(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) (SharedTokenConfig, *oneLinkAPIConfig, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil || channelConfig.Template == nil {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("channel and template config are required")
|
||||
}
|
||||
var template sharedOneLinkTemplateConfig
|
||||
if strings.TrimSpace(channelConfig.Template.TempConfig) == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("temp_config is required")
|
||||
}
|
||||
if err := json.Unmarshal([]byte(channelConfig.Template.TempConfig), &template); err != nil {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("invalid temp_config: %w", err)
|
||||
}
|
||||
var channel sharedOneLinkChannelConfig
|
||||
if strings.TrimSpace(channelConfig.Channel.ChanConfig) != "" {
|
||||
if err := json.Unmarshal([]byte(channelConfig.Channel.ChanConfig), &channel); err != nil {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("invalid chan_config: %w", err)
|
||||
}
|
||||
}
|
||||
legacy := channel.LegacyCredentials
|
||||
provider := strings.ToLower(strings.TrimSpace(template.Token.Provider))
|
||||
if provider == "" {
|
||||
provider = "tengyu"
|
||||
}
|
||||
tokenBaseURL := strings.TrimSpace(template.Token.BaseURL)
|
||||
// A channel may point token fetches at a different upstream host than the
|
||||
// template default (e.g. the 361 account lives on gxhy.ge-xing.cn while
|
||||
// the template points at hywx.xjict.com).
|
||||
if override := strings.TrimSpace(channel.TokenBaseURL); override != "" {
|
||||
tokenBaseURL = override
|
||||
}
|
||||
if tokenBaseURL == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("token.baseUrl is required")
|
||||
}
|
||||
if strings.TrimSpace(template.Token.Path) == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("token.path is required")
|
||||
}
|
||||
pwdKey := strings.TrimSpace(channel.PwdKey)
|
||||
channelNo := strings.TrimSpace(channel.ChannelNo)
|
||||
appID := strings.TrimSpace(channel.AppID)
|
||||
appSecret := strings.TrimSpace(channelConfig.Channel.AppSecret)
|
||||
subAccount := strings.TrimSpace(channel.SubAccount)
|
||||
secret := channel.Secret
|
||||
operatorID := strings.TrimSpace(channel.OperatorID)
|
||||
userID := strings.TrimSpace(channel.UserID)
|
||||
tokenType := strings.TrimSpace(channel.TokenType)
|
||||
channelID := strings.TrimSpace(channel.ChannelID)
|
||||
channelPName := strings.TrimSpace(channel.ChannelPName)
|
||||
cardNo := strings.TrimSpace(channel.CardNo)
|
||||
cmccAppID := strings.TrimSpace(channel.CMCCAppID)
|
||||
sign := strings.TrimSpace(channel.Sign)
|
||||
userName := strings.TrimSpace(channel.UserName)
|
||||
password := channel.Password
|
||||
passwayUID := strings.TrimSpace(channel.PasswayUID)
|
||||
if legacy != nil {
|
||||
if pwdKey == "" {
|
||||
pwdKey = strings.TrimSpace(legacy.PwdKey)
|
||||
}
|
||||
if channelNo == "" {
|
||||
channelNo = strings.TrimSpace(legacy.ChannelNo)
|
||||
}
|
||||
if appID == "" {
|
||||
appID = strings.TrimSpace(legacy.AppID)
|
||||
}
|
||||
if userName == "" {
|
||||
userName = strings.TrimSpace(legacy.UserName)
|
||||
}
|
||||
if password == "" {
|
||||
password = legacy.Password
|
||||
}
|
||||
if passwayUID == "" {
|
||||
passwayUID = strings.TrimSpace(legacy.PasswayUID)
|
||||
}
|
||||
g.Log().Warningf(ctx, "shared token channel %s uses deprecated chan_config.credentials; migrate fields to the root", channelConfig.Channel.ChanKey)
|
||||
}
|
||||
carrierType := 1
|
||||
if template.Token.Params.CarrierType != nil {
|
||||
carrierType = *template.Token.Params.CarrierType
|
||||
} else if template.Token.LegacyCarrierType != nil {
|
||||
carrierType = *template.Token.LegacyCarrierType
|
||||
g.Log().Warningf(ctx, "shared token template %s uses deprecated token.carrierType; move it to token.params.carrierType", channelConfig.Template.TempKey)
|
||||
}
|
||||
loginPath := strings.TrimSpace(template.Token.Params.LoginPath)
|
||||
if loginPath == "" && strings.TrimSpace(template.Token.LegacyLoginPath) != "" {
|
||||
loginPath = strings.TrimSpace(template.Token.LegacyLoginPath)
|
||||
g.Log().Warningf(ctx, "shared token template %s uses deprecated token.loginPath; move it to token.params.loginPath", channelConfig.Template.TempKey)
|
||||
}
|
||||
timeout := resolveUpstreamHTTPTimeout(ctx, channelConfig.Template.TempConfig)
|
||||
config := SharedTokenConfig{
|
||||
Provider: provider, TokenBaseURL: tokenBaseURL, LoginPath: loginPath, TokenPath: template.Token.Path,
|
||||
CarrierType: carrierType, PwdKey: pwdKey, ChannelNo: channelNo, AppKey: strings.TrimSpace(channelConfig.Channel.AppKey), AppID: appID,
|
||||
AppSecret: appSecret, SubAccount: subAccount, Secret: secret, OperatorID: operatorID, UserID: userID, TokenType: tokenType,
|
||||
UserName: userName, Password: password, PasswayUID: passwayUID,
|
||||
ChannelID: channelID, ChannelPName: channelPName, CardNo: cardNo, CMCCAppID: cmccAppID,
|
||||
Sign: sign, Timeout: timeout,
|
||||
}
|
||||
if provider == "daihe" && config.AppID == "" {
|
||||
config.AppID = strings.TrimSpace(channelConfig.Channel.AppKey)
|
||||
}
|
||||
if provider == "daihe" && (config.AppID == "" || config.AppSecret == "" || config.CMCCAppID == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.appId, app_secret and chan_config.cmccAppId are required for daihe")
|
||||
}
|
||||
if provider == "zibo" {
|
||||
config.AppID = strings.TrimSpace(channelConfig.Channel.AppKey)
|
||||
config.AppSecret = strings.TrimSpace(channelConfig.Channel.AppSecret)
|
||||
if config.AppID == "" || config.AppSecret == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("app_key and app_secret are required for zibo")
|
||||
}
|
||||
}
|
||||
if provider == "yunze" {
|
||||
config.AppID = strings.TrimSpace(channelConfig.Channel.AppKey)
|
||||
if config.AppID == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("app_key is required for yunze")
|
||||
}
|
||||
}
|
||||
if provider == "tengyu" && (config.PwdKey == "" || config.ChannelNo == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.pwdKey and chan_config.channelNo are required")
|
||||
}
|
||||
if provider == "xuzhou_mobile" && config.AppID == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.appId is required for xuzhou_mobile")
|
||||
}
|
||||
if provider == "lanshang" && (config.SubAccount == "" || config.Secret == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.subAccount and chan_config.secret are required for lanshang")
|
||||
}
|
||||
if provider == "qianxun" && (config.OperatorID == "" || config.AppID == "" || config.TokenType == "" || config.AppSecret == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.operatorId, chan_config.appId, chan_config.tokenType and app_secret are required for qianxun")
|
||||
}
|
||||
if provider == "lanshu" && (config.AppID == "" || config.Password == "" || config.Sign == "" || config.TokenType == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.appId, chan_config.password, chan_config.sign and chan_config.tokenType are required for lanshu")
|
||||
}
|
||||
if provider == "beixin" && (config.PwdKey == "" || config.ChannelNo == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.pwdKey and chan_config.channelNo are required for beixin")
|
||||
}
|
||||
if provider == "mobile_public" && (config.UserID == "" || config.AppSecret == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.userId and app_secret are required for mobile_public")
|
||||
}
|
||||
if provider == "apika" && (config.AppKey == "" || config.AppSecret == "" || config.AppID == "") {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("app_key, app_secret and chan_config.appId are required for apika")
|
||||
}
|
||||
if provider == "henan_lelian" {
|
||||
if strings.TrimSpace(config.LoginPath) == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("token.params.loginPath is required for henan_lelian")
|
||||
}
|
||||
if config.UserName == "" || strings.TrimSpace(config.Password) == "" || config.PasswayUID == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.userName, chan_config.password and chan_config.passwayUid are required for henan_lelian")
|
||||
}
|
||||
}
|
||||
if provider == "henan_xinjing" {
|
||||
if config.AppID == "" || config.AppSecret == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.appId (userId) and app_secret (apikey) are required for henan_xinjing")
|
||||
}
|
||||
if config.ChannelID == "" || config.ChannelPName == "" {
|
||||
return SharedTokenConfig{}, nil, fmt.Errorf("chan_config.channelId and chan_config.channelPname are required for henan_xinjing")
|
||||
}
|
||||
}
|
||||
transIDPrefix := sharedOneLinkTransIDPrefix
|
||||
if provider == "daihe" {
|
||||
transIDPrefix = config.CMCCAppID
|
||||
}
|
||||
if provider == "zibo" {
|
||||
transIDPrefix = config.AppID
|
||||
}
|
||||
businessBaseURL := strings.TrimSpace(template.API.BaseURL)
|
||||
if businessBaseURL == "" {
|
||||
businessBaseURL = sharedOneLinkAPIBaseURL
|
||||
}
|
||||
return config, &oneLinkAPIConfig{
|
||||
BaseURL: businessBaseURL, transIDPrefix: transIDPrefix, Timeout: timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseOneLinkTemplateAPI(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) *oneLinkAPIConfig {
|
||||
if channelConfig == nil || channelConfig.Template == nil || strings.TrimSpace(channelConfig.Template.TempConfig) == "" {
|
||||
return nil
|
||||
}
|
||||
var raw sharedOneLinkTemplateConfig
|
||||
if err := json.Unmarshal([]byte(channelConfig.Template.TempConfig), &raw); err != nil {
|
||||
g.Log().Warningf(ctx, "parse OneLink template config failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
baseURL := strings.TrimSpace(raw.API.BaseURL)
|
||||
if baseURL == "" {
|
||||
baseURL = gUrlBase
|
||||
}
|
||||
timeout := resolveUpstreamHTTPTimeout(ctx, channelConfig.Template.TempConfig)
|
||||
return &oneLinkAPIConfig{
|
||||
BaseURL: baseURL, TokenPath: raw.Token.Path, Timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
type sSimOneLinkShared struct{}
|
||||
|
||||
func NewSimOneLinkShared() *sSimOneLinkShared { return &sSimOneLinkShared{} }
|
||||
|
||||
func init() {
|
||||
shared := NewSimOneLinkShared()
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkTengyuTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkXuzhouTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkLelianTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkQianxunTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkHenanXinjingTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkDaiheTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkMobilePublicTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkMobilePublicPreviousKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkMobilePublicLegacyKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkApikaTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkYunzeTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkLanshuTemplateKey, shared)
|
||||
service.UpstreamAPI().RegisterTemp(sharedOneLinkBeixinTemplateKey, shared)
|
||||
}
|
||||
|
||||
func (s *sSimOneLinkShared) NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI {
|
||||
providerConfig, apiConfig, err := parseSharedOneLinkConfig(ctx, channelConfig)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "create shared_token_onelink failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
factory, ok := tokenProviderFactory(providerConfig.Provider)
|
||||
if !ok || factory == nil {
|
||||
g.Log().Errorf(ctx, "create shared_token_onelink failed: unsupported provider %s", providerConfig.Provider)
|
||||
return nil
|
||||
}
|
||||
provider, err := factory(ctx, channelConfig, providerConfig)
|
||||
if err != nil || provider == nil {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("factory returned nil provider")
|
||||
}
|
||||
g.Log().Errorf(ctx, "create shared_token_onelink failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
return &sSimYD{channelConfig: channelConfig, tokenProvider: provider, apiConfig: apiConfig}
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterTokenProviderFactory("mobile_public", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := mobilePublicFetcher{provider: NewMobilePublicTokenProvider(MobilePublicTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL,
|
||||
Path: cfg.TokenPath,
|
||||
UserID: cfg.UserID,
|
||||
Secret: cfg.AppSecret,
|
||||
Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithRefreshAhead(30*time.Second))
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("apika", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := apikaFetcher{provider: NewApikaTokenProvider(ApikaTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, AppKey: cfg.AppKey,
|
||||
AppSecret: cfg.AppSecret, AppID: cfg.AppID, Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithRefreshAhead(30*time.Second))
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("qianxun", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := qianxunFetcher{provider: NewQianxunTokenProvider(QianxunTokenConfig{BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, OperatorID: cfg.OperatorID, AppID: cfg.AppID, TokenType: cfg.TokenType, Key: cfg.AppSecret, Timeout: cfg.Timeout})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithUntilInvalidated())
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("henan_xinjing", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := henanXinjingFetcher{provider: NewHenanXinjingTokenProvider(HenanXinjingTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL,
|
||||
Path: cfg.TokenPath,
|
||||
UserID: cfg.AppID,
|
||||
APIKey: cfg.AppSecret,
|
||||
ChannelID: cfg.ChannelID,
|
||||
ChannelPName: cfg.ChannelPName,
|
||||
CardNo: cfg.CardNo,
|
||||
Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("tengyu", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
fetcher := tengyuFetcher{provider: NewTengyuTokenProvider(TengyuTokenConfig{BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, PwdKey: cfg.PwdKey, ChannelNo: cfg.ChannelNo, Timeout: cfg.Timeout})}
|
||||
// Tengyu does not document a refresh-ahead mechanism. Reuse the cached
|
||||
// token until its local one-hour validity expires, then fetch on demand.
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("xuzhou_mobile", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := xuzhouMobileFetcher{provider: NewXuzhouMobileTokenProvider(XuzhouMobileTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, AppID: cfg.AppID,
|
||||
CarrierType: cfg.CarrierType, TTL: sharedOneLinkXuzhouTokenTTL, Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("daihe", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := daiheTokenFetcher{provider: NewDaiheTokenProvider(DaiheTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, AppID: cfg.AppID, AppSecret: cfg.AppSecret,
|
||||
CMCCAppID: cfg.CMCCAppID, Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey, includeDeviceToken: true}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("zibo", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := ziboFetcher{provider: NewZiboTokenProvider(ZiboTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL,
|
||||
Path: cfg.TokenPath,
|
||||
AppID: cfg.AppID,
|
||||
Password: cfg.AppSecret,
|
||||
Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("yunze", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := yunzeFetcher{provider: NewYunzeTokenProvider(YunzeTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL,
|
||||
Path: cfg.TokenPath,
|
||||
AppID: cfg.AppID,
|
||||
Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithRefreshAhead(30*time.Second))
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("lanshu", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := lanshuFetcher{provider: NewLanShuTokenProvider(LanShuTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, AppID: cfg.AppID,
|
||||
Password: cfg.Password, Sign: cfg.Sign, TokenType: cfg.TokenType, Timeout: cfg.Timeout,
|
||||
})}
|
||||
// The upstream reports a real expireTime (~19-26 minutes) and issues a
|
||||
// new token per request, so refresh proactively inside the window.
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithRefreshAhead(60*time.Second))
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("beixin", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := beixinFetcher{provider: NewBeixinTokenProvider(BeixinTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath, PwdKey: cfg.PwdKey,
|
||||
ChannelNo: cfg.ChannelNo, Timeout: cfg.Timeout,
|
||||
})}
|
||||
// The upstream returns no expireTime, so the provider holds a local
|
||||
// one-hour window (mirroring tengyu). Refresh proactively inside it
|
||||
// rather than waiting for a 12021 rejection.
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithRefreshAhead(30*time.Second))
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("lanshang", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
if cfg.SubAccount == "" || cfg.Secret == "" {
|
||||
return nil, fmt.Errorf("lanshang subAccount and secret are required")
|
||||
}
|
||||
fetcher := lanshangFetcher{provider: NewLanShangTokenProvider(LanShangTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, Path: cfg.TokenPath,
|
||||
SubAccount: cfg.SubAccount, Secret: cfg.Secret, Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher)
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
RegisterTokenProviderFactory("henan_lelian", func(ctx context.Context, channelConfig *model.SimChannelConfigItemCache, cfg SharedTokenConfig) (ydTokenProvider, error) {
|
||||
if channelConfig == nil || channelConfig.Channel == nil {
|
||||
return nil, fmt.Errorf("channel config is required")
|
||||
}
|
||||
fetcher := henanLelianFetcher{provider: NewHenanLelianTokenProvider(HenanLelianTokenConfig{
|
||||
BaseURL: cfg.TokenBaseURL, LoginPath: cfg.LoginPath, Path: cfg.TokenPath,
|
||||
UserName: cfg.UserName, Password: cfg.Password, PasswayUID: cfg.PasswayUID,
|
||||
Timeout: cfg.Timeout,
|
||||
})}
|
||||
manager := sharedtoken.NewTokenManager(fetcher, sharedtoken.WithUntilInvalidated())
|
||||
manager.SeedFromConfig(channelConfig)
|
||||
return &sharedTokenManagerProvider{manager: manager, chanKey: channelConfig.Channel.ChanKey}, nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user