package upstreamimpl import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "time" sharedtoken "git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/token" ) // LanShangTokenConfig contains the endpoint from the template and credentials // from the channel configuration. type LanShangTokenConfig struct { BaseURL string Path string SubAccount string Secret string Timeout time.Duration } type LanShangTokenProvider struct { baseURL string path string subAccount string secret string client *http.Client } type lanShangTokenResponse struct { Token string `json:"token"` ExpireIn int `json:"expire_in"` } type lanshangFetcher struct{ provider *LanShangTokenProvider } func NewLanShangTokenProvider(config LanShangTokenConfig) *LanShangTokenProvider { timeout := config.Timeout if timeout <= 0 { timeout = defaultUpstreamHTTPTimeout } return &LanShangTokenProvider{ baseURL: strings.TrimRight(strings.TrimSpace(config.BaseURL), "/"), path: normalizeProviderPath(config.Path, "/get_token"), subAccount: strings.TrimSpace(config.SubAccount), secret: config.Secret, client: &http.Client{Timeout: timeout}, } } func (f lanshangFetcher) FetchToken(ctx context.Context, _ string) (sharedtoken.OneLinkToken, error) { return f.provider.Fetch(ctx) } func (p *LanShangTokenProvider) Fetch(ctx context.Context) (sharedtoken.OneLinkToken, error) { if p == nil || p.client == nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("lanshang token provider is not initialized") } if p.baseURL == "" || p.subAccount == "" || p.secret == "" { return sharedtoken.OneLinkToken{}, fmt.Errorf("lanshang token endpoint and credentials are required") } endpoint, err := url.Parse(joinProviderURL(p.baseURL, p.path)) if err != nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("parse lanshang token URL: %w", err) } query := endpoint.Query() query.Set("sub_account", p.subAccount) query.Set("secret", p.secret) endpoint.RawQuery = query.Encode() req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) if err != nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("create lanshang token request: %w", err) } req.Header.Set("Accept", "application/json") resp, err := p.client.Do(req) if err != nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("request lanshang token: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("read lanshang token response: %w", err) } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { return sharedtoken.OneLinkToken{}, fmt.Errorf("lanshang token HTTP status %d", resp.StatusCode) } var output lanShangTokenResponse if err := json.Unmarshal(body, &output); err != nil { return sharedtoken.OneLinkToken{}, fmt.Errorf("decode lanshang token response: %w", err) } value := strings.TrimSpace(output.Token) if value == "" { return sharedtoken.OneLinkToken{}, fmt.Errorf("lanshang token is empty") } now := time.Now() expiresAt := time.Time{} if output.ExpireIn > 0 { expiresAt = now.Add(time.Duration(output.ExpireIn) * time.Second) } return sharedtoken.OneLinkToken{Value: value, CreatedAt: now, ExpiresAt: expiresAt}, nil } func (p *LanShangTokenProvider) FetchToken(ctx context.Context, _ string) (sharedtoken.OneLinkToken, error) { return p.Fetch(ctx) }