packages.wenpai.net/internal/og/upload.go
Ben Word 7cb8fef01b
WP Packages rename (#42)
* Update all import paths

* Rename directory cmd/wpcomposer/ → cmd/wppackages/

* Rename import alias wpcomposergo → wppackagesgo in main.go and migrate_test.go

* Makefile — binary name wpcomposer → wppackages

* Update Air path

* Global replace repo.wp-composer.com → repo.wp-packages.org

* Global replace cdn.wp-composer.com → cdn.wp-packages.org

* Global replace wp-composer.com → wp-packages.org (remaining)

* Composer repo key in templates/docs: repositories.wp-composer → repositories.wp-packages

* Rename columns on the existing schema

* Update all Go code referencing these column names

* Routes & SEO

* Templates & front-end

* Admin UI

* Documentation

* CI/CD

* Config defaults

* Rename role directory

* Rename all systemd template files inside the role

* Update contents of all .j2 templates — service names, binary paths, descriptions

* Update tasks/main.yml and handlers/main.yml in the role

* Update deploy/ansible/roles/app/tasks/main.yml and deploy.yml

* Update deploy/ansible/group_vars/production/main.yml

* Additional renames/fixes

* Additional renames/fixes

* Additional renames/fixes

* not needed
2026-03-19 11:50:12 -05:00

87 lines
2.2 KiB
Go

package og
import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/roots/wp-packages/internal/config"
)
// Uploader handles uploading OG images to R2 CDN or local disk.
type Uploader struct {
client *s3.Client
bucket string
publicURL string
localDir string
}
// NewUploader creates an Uploader. If R2 CDN is configured, it uploads to R2.
// Otherwise, it writes to localDir on disk.
func NewUploader(cfg config.R2Config) *Uploader {
u := &Uploader{
publicURL: cfg.CDNPublicURL,
}
if cfg.CDNBucket != "" && cfg.Endpoint != "" && cfg.AccessKeyID != "" {
u.client = s3.New(s3.Options{
Region: "auto",
Credentials: credentials.NewStaticCredentialsProvider(
cfg.AccessKeyID,
cfg.SecretAccessKey,
"",
),
BaseEndpoint: aws.String(cfg.Endpoint),
})
u.bucket = cfg.CDNBucket
} else {
u.localDir = filepath.Join("storage", "og")
}
return u
}
// Upload stores the PNG bytes at the given key (e.g. "social/plugin/akismet.png").
func (u *Uploader) Upload(ctx context.Context, key string, data []byte) error {
if u.client != nil {
_, err := u.client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(u.bucket),
Key: aws.String(key),
Body: bytes.NewReader(data),
ContentType: aws.String("image/png"),
CacheControl: aws.String("public, max-age=86400"),
})
if err != nil {
return fmt.Errorf("uploading %s to R2: %w", key, err)
}
return nil
}
// Local disk fallback
path := filepath.Join(u.localDir, key)
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return fmt.Errorf("creating directory for %s: %w", key, err)
}
if err := os.WriteFile(path, data, 0o644); err != nil {
return fmt.Errorf("writing %s: %w", key, err)
}
return nil
}
// PublicURL returns the full public URL for an OG image key.
func (u *Uploader) PublicURL(key string) string {
if u.publicURL != "" {
return u.publicURL + "/" + key
}
return ""
}
// IsR2 returns true if the uploader is configured for R2.
func (u *Uploader) IsR2() bool {
return u.client != nil
}