Address production-readiness review: clearer errors, safer votes, and ops hardening.
Distinguish auth/lookup failures, make votes idempotent on visible questions, bound shutdown, page admin users, LRU throttle, trusted-proxy CIDRs, avatar cleanup, versioned migrations, and session cleanup logging.
This commit is contained in:
+41
-4
@@ -17,6 +17,7 @@ import (
|
||||
type Uploader interface {
|
||||
Enabled() bool
|
||||
Upload(ctx context.Context, obj FileUpload) (publicURL string, err error)
|
||||
Delete(ctx context.Context, key string) error
|
||||
}
|
||||
|
||||
// FileUpload is a file body to store (e.g. an avatar).
|
||||
@@ -51,6 +52,8 @@ func (Disabled) Upload(context.Context, FileUpload) (string, error) {
|
||||
return "", fmt.Errorf("avatar uploads are not configured")
|
||||
}
|
||||
|
||||
func (Disabled) Delete(context.Context, string) error { return nil }
|
||||
|
||||
// FromEnv builds an Uploader from SPACES_* environment variables.
|
||||
func FromEnv() Uploader {
|
||||
return NewSpaces(SpacesConfig{
|
||||
@@ -99,11 +102,45 @@ func (s *spaces) Upload(ctx context.Context, obj FileUpload) (string, error) {
|
||||
if _, err := s.client.PutObject(ctx, input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if s.cfg.CDNBase != "" {
|
||||
return s.cfg.CDNBase + "/" + key, nil
|
||||
return s.publicURL(key), nil
|
||||
}
|
||||
|
||||
func (s *spaces) Delete(ctx context.Context, key string) error {
|
||||
key = strings.TrimPrefix(key, "/")
|
||||
if key == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(s.cfg.Bucket),
|
||||
Key: aws.String(key),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *spaces) publicURL(key string) string {
|
||||
if s.cfg.CDNBase != "" {
|
||||
return s.cfg.CDNBase + "/" + key
|
||||
}
|
||||
// Virtual-hosted–style Spaces URL.
|
||||
host := strings.TrimPrefix(s.cfg.Endpoint, "https://")
|
||||
host = strings.TrimPrefix(host, "http://")
|
||||
return fmt.Sprintf("https://%s.%s/%s", s.cfg.Bucket, host, key), nil
|
||||
return fmt.Sprintf("https://%s.%s/%s", s.cfg.Bucket, host, key)
|
||||
}
|
||||
|
||||
// KeyFromPublicURL extracts the object key from a Spaces/CDN URL when possible.
|
||||
func KeyFromPublicURL(publicURL, cdnBase, bucket, endpoint string) string {
|
||||
publicURL = strings.TrimSpace(publicURL)
|
||||
if publicURL == "" {
|
||||
return ""
|
||||
}
|
||||
cdnBase = strings.TrimRight(strings.TrimSpace(cdnBase), "/")
|
||||
if cdnBase != "" && strings.HasPrefix(publicURL, cdnBase+"/") {
|
||||
return strings.TrimPrefix(publicURL, cdnBase+"/")
|
||||
}
|
||||
host := strings.TrimPrefix(strings.TrimSpace(endpoint), "https://")
|
||||
host = strings.TrimPrefix(host, "http://")
|
||||
prefix := fmt.Sprintf("https://%s.%s/", bucket, host)
|
||||
if strings.HasPrefix(publicURL, prefix) {
|
||||
return strings.TrimPrefix(publicURL, prefix)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user