完善后端联调链路与模拟器多通道支持
This commit is contained in:
96
backend/internal/platform/assets/publisher.go
Normal file
96
backend/internal/platform/assets/publisher.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OSSUtilPublisher struct {
|
||||
ossutilPath string
|
||||
configFile string
|
||||
bucketRoot string
|
||||
publicBaseURL string
|
||||
}
|
||||
|
||||
func NewOSSUtilPublisher(ossutilPath, configFile, bucketRoot, publicBaseURL string) *OSSUtilPublisher {
|
||||
return &OSSUtilPublisher{
|
||||
ossutilPath: strings.TrimSpace(ossutilPath),
|
||||
configFile: strings.TrimSpace(configFile),
|
||||
bucketRoot: strings.TrimRight(strings.TrimSpace(bucketRoot), "/"),
|
||||
publicBaseURL: strings.TrimRight(strings.TrimSpace(publicBaseURL), "/"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *OSSUtilPublisher) Enabled() bool {
|
||||
return p != nil &&
|
||||
p.ossutilPath != "" &&
|
||||
p.configFile != "" &&
|
||||
p.bucketRoot != "" &&
|
||||
p.publicBaseURL != ""
|
||||
}
|
||||
|
||||
func (p *OSSUtilPublisher) UploadJSON(ctx context.Context, publicURL string, payload []byte) error {
|
||||
if !p.Enabled() {
|
||||
return fmt.Errorf("asset publisher is not configured")
|
||||
}
|
||||
if len(payload) == 0 {
|
||||
return fmt.Errorf("payload is empty")
|
||||
}
|
||||
|
||||
objectKey, err := p.objectKeyFromPublicURL(publicURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(p.ossutilPath); err != nil {
|
||||
return fmt.Errorf("ossutil not found: %w", err)
|
||||
}
|
||||
if _, err := os.Stat(p.configFile); err != nil {
|
||||
return fmt.Errorf("ossutil config not found: %w", err)
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "cmr-manifest-*.json")
|
||||
if err != nil {
|
||||
return fmt.Errorf("create temp file: %w", err)
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if _, err := tmpFile.Write(payload); err != nil {
|
||||
tmpFile.Close()
|
||||
return fmt.Errorf("write temp file: %w", err)
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
return fmt.Errorf("close temp file: %w", err)
|
||||
}
|
||||
|
||||
target := p.bucketRoot + "/" + objectKey
|
||||
cmd := exec.CommandContext(ctx, p.ossutilPath, "cp", "-f", tmpPath, target, "--config-file", p.configFile)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload object %s failed: %w: %s", objectKey, err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OSSUtilPublisher) objectKeyFromPublicURL(publicURL string) (string, error) {
|
||||
publicURL = strings.TrimSpace(publicURL)
|
||||
if publicURL == "" {
|
||||
return "", fmt.Errorf("public url is required")
|
||||
}
|
||||
if !strings.HasPrefix(publicURL, p.publicBaseURL+"/") {
|
||||
return "", fmt.Errorf("public url %s does not match public base %s", publicURL, p.publicBaseURL)
|
||||
}
|
||||
relative := strings.TrimPrefix(publicURL, p.publicBaseURL+"/")
|
||||
relative = strings.ReplaceAll(relative, "\\", "/")
|
||||
relative = strings.TrimLeft(relative, "/")
|
||||
if relative == "" {
|
||||
return "", fmt.Errorf("public url %s resolved to empty object key", publicURL)
|
||||
}
|
||||
return filepath.ToSlash(relative), nil
|
||||
}
|
||||
Reference in New Issue
Block a user