//go:build !dev package s3 import ( "context" "fmt" "io" "log" "os" ) var Client Uploader // FallbackToInMemory mirrors the dev-build flag so main.go can reference it // in non-dev builds. It is always false here: the production/R2 build never // uses the in-memory fallback client. var FallbackToInMemory bool // ClientIsStub reports whether the active client is the production stub that // cannot perform real S3 operations. It is always true in the !dev build: // every operation (Upload, Download, Delete) returns "not implemented" because // the AWS SDK v2 is not compiled in. main.go uses it to warn at startup so the // operator knows that profile-picture deletion will never succeed in this build. var ClientIsStub = true type Uploader interface { Upload(ctx context.Context, bucket, key string, body io.Reader, contentType string) error Download(ctx context.Context, bucket, key string, w io.Writer) error Delete(ctx context.Context, bucket, key string) error GetURL(ctx context.Context, bucket, key string) (string, error) HealthCheck(ctx context.Context) error } type S3Client struct { bucket string endpoint string region string accessKey string secretKey string publicURL string } func Connect() error { endpoint := os.Getenv("R2_ENDPOINT") if endpoint == "" { log.Println("WARNING: R2_ENDPOINT not set, S3 client not initialized") return nil } Client = &S3Client{ endpoint: endpoint, bucket: getEnv("R2_BUCKET", ""), region: getEnv("AWS_REGION", "auto"), accessKey: getEnv("R2_ACCESS_KEY", ""), secretKey: getEnv("R2_SECRET_KEY", ""), publicURL: getEnv("R2_PUBLIC_URL", ""), } log.Printf("Connected to R2: bucket=%s, endpoint=%s", Client.(*S3Client).bucket, endpoint) return nil } func (s *S3Client) Upload(ctx context.Context, bucket, key string, body io.Reader, contentType string) error { return fmt.Errorf("not implemented: production S3 upload requires AWS SDK v2 (use RUSTFS in dev)") } func (s *S3Client) Download(ctx context.Context, bucket, key string, w io.Writer) error { return fmt.Errorf("not implemented: production S3 download requires AWS SDK v2 (use RUSTFS in dev)") } func (s *S3Client) Delete(ctx context.Context, bucket, key string) error { return fmt.Errorf("not implemented: production S3 delete requires AWS SDK v2 (use RUSTFS in dev)") } func (s *S3Client) GetURL(ctx context.Context, bucket, key string) (string, error) { return fmt.Sprintf("%s/%s/%s", s.publicURL, bucket, key), nil } func (s *S3Client) HealthCheck(_ context.Context) error { if s.endpoint == "" { return fmt.Errorf("S3 client not initialized: R2_ENDPOINT not set") } // Production stub — can't perform a real check without the SDK. // Actual operations (Upload, Download, Delete) will return errors. return nil } func getEnv(key, fallback string) string { if val := os.Getenv(key); val != "" { return val } if fallback != "" { return fallback } panic("Environment variable not set: " + key) }