Ctrl K
ComunidadGuías…

internal/cdn/cloudfront.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cdn

import (
	"bytes"
	"crypto"
	"errors"
	"fmt"
	"strings"
	"time"
	"xprem/config"
	"xprem/internal/bucket"
	"xprem/internal/keyStore"

	"github.com/aws/aws-sdk-go-v2/feature/cloudfront/sign"
)

type CloudfrontCDN struct{}

func getCloudfrontDomain() string {
	domain := config.GetEnv("CLOUDFRONT_DOMAIN")
	// The AWS SDK URL signer requires a scheme; tolerate the bare domain
	// shown in the documentation examples.
	if domain != "" && !strings.HasPrefix(domain, "http://") && !strings.HasPrefix(domain, "https://") {
		domain = "https://" + domain
	}
	return domain
}

func getCloudfrontKeyPairId() string {
	return config.GetEnv("CLOUDFRONT_KEY_PAIR_ID")
}

func (c *CloudfrontCDN) isCDNAvailable() bool {
	privateCloudfrontCert := keyStore.GetPrivateCloudfrontKey()
	domain := getCloudfrontDomain()
	keyPairId := getCloudfrontKeyPairId()
	return privateCloudfrontCert != "" && domain != "" && keyPairId != ""
}

func getSigner(key string) (crypto.Signer, error) {
	privateKey, err := sign.LoadPEMPrivKeyPKCS8AsSigner(bytes.NewReader([]byte(key)))
	if err != nil {
		// A fresh reader is required: the failed PKCS#8 attempt above has
		// already consumed the previous one, so reusing it would make this
		// fallback always fail with "no valid PEM data provided".
		privateKey, err = sign.LoadPEMPrivKey(bytes.NewReader([]byte(key)))
		if err != nil {
			return nil, fmt.Errorf("error parsing private key: %w", err)
		}
	}
	return privateKey, nil
}

func (c *CloudfrontCDN) ComputeRedirectionURLForAsset(appId, branch, runtimeVersion, updateId, asset string) (string, error) {
	// Must match the v2 bucket layout exactly, if the CloudFront origin is
	// an S3 bucket, the object sits at {BUCKET_KEY_PREFIX}{appId}/{branch}/…
	// Operators using BUCKET_KEY_PREFIX must NOT also configure a
	// CloudFront Origin Path equal to the prefix; the path is part of the
	// signed resource and would be applied twice.
	endpoint := bucket.ResolveKeyPrefix() + fmt.Sprintf("%s/%s/%s/%s/%s", appId, branch, runtimeVersion, updateId, asset)
	return c.signObjectKey(endpoint)
}

func (c *CloudfrontCDN) ComputeRedirectionURLForBlob(appId, hash string) (string, error) {
	return c.signObjectKey(bucket.ResolveKeyPrefix() + bucket.BlobObjectKey(appId, hash))
}

func (c *CloudfrontCDN) ComputeRedirectionURLForPatch(appId, branch, targetUpdateUUID, sourceUpdateUUID string) (string, error) {
	return c.signObjectKey(bucket.ResolveKeyPrefix() + bucket.BSDiffObjectKey(appId, branch, targetUpdateUUID, sourceUpdateUUID))
}

func (c *CloudfrontCDN) signObjectKey(endpoint string) (string, error) {
	domain := getCloudfrontDomain()
	keyPairId := getCloudfrontKeyPairId()
	privateCloudfrontCert := keyStore.GetPrivateCloudfrontKey()

	if domain == "" || keyPairId == "" || privateCloudfrontCert == "" {
		return "", errors.New("CloudFront configuration is incomplete")
	}

	privateKey, err := getSigner(privateCloudfrontCert)
	if err != nil {
		return "", fmt.Errorf("error parsing private key: %w", err)
	}

	resource := fmt.Sprintf("%s/%s", domain, endpoint)
	policy := sign.NewCannedPolicy(resource, time.Now().Add(10*time.Minute))
	signer := sign.NewURLSigner(keyPairId, privateKey)
	return signer.SignWithPolicy(resource, policy)
}