Skip to content

Commit 082eebf

Browse files
AchoArnoldCopilot
andcommitted
feat: wire attachment storage and handler in DI container
- Add AttachmentStorage selection (GCS vs memory) based on GCS_BUCKET_NAME env var - Wire AttachmentHandler for public download endpoint - Pass storage and API base URL to MessageService - Add GCS_BUCKET_NAME to .env.docker Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0d7f385 commit 082eebf

2 files changed

Lines changed: 67 additions & 7 deletions

File tree

api/.env.docker

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ DATABASE_URL_DEDICATED=postgresql://dbusername:dbpassword@postgres:5432/httpsms
4848
# Redis connection string
4949
REDIS_URL=redis://@redis:6379
5050

51+
# Google Cloud Storage bucket for MMS attachments. Leave empty to use in-memory storage.
52+
GCS_BUCKET_NAME=
53+
5154
# [optional] If you would like to use uptrace.dev for distributed tracing, you can set the DSN here.
5255
# This is optional and you can leave it empty if you don't want to use uptrace
5356
UPTRACE_DSN=

api/pkg/di/container.go

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/NdoleStudio/httpsms/pkg/discord"
2727

28+
"cloud.google.com/go/storage"
2829
mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric"
2930
cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
3031
"github.com/NdoleStudio/httpsms/pkg/cache"
@@ -80,13 +81,14 @@ import (
8081

8182
// Container is used to resolve services at runtime
8283
type Container struct {
83-
projectID string
84-
db *gorm.DB
85-
dedicatedDB *gorm.DB
86-
version string
87-
app *fiber.App
88-
eventDispatcher *services.EventDispatcher
89-
logger telemetry.Logger
84+
projectID string
85+
db *gorm.DB
86+
dedicatedDB *gorm.DB
87+
version string
88+
app *fiber.App
89+
eventDispatcher *services.EventDispatcher
90+
logger telemetry.Logger
91+
attachmentStorage repositories.AttachmentStorage
9092
}
9193

9294
// NewLiteContainer creates a Container without any routes or listeners
@@ -118,6 +120,7 @@ func NewContainer(projectID string, version string) (container *Container) {
118120

119121
container.RegisterMessageListeners()
120122
container.RegisterMessageRoutes()
123+
container.RegisterAttachmentRoutes()
121124
container.RegisterBulkMessageRoutes()
122125

123126
container.RegisterMessageThreadRoutes()
@@ -1430,9 +1433,63 @@ func (container *Container) MessageService() (service *services.MessageService)
14301433
container.MessageRepository(),
14311434
container.EventDispatcher(),
14321435
container.PhoneService(),
1436+
container.AttachmentStorage(),
1437+
container.APIBaseURL(),
14331438
)
14341439
}
14351440

1441+
// AttachmentStorage creates a cached AttachmentStorage based on configuration
1442+
func (container *Container) AttachmentStorage() repositories.AttachmentStorage {
1443+
if container.attachmentStorage != nil {
1444+
return container.attachmentStorage
1445+
}
1446+
1447+
bucket := os.Getenv("GCS_BUCKET_NAME")
1448+
if bucket != "" {
1449+
container.logger.Debug("creating GCSAttachmentStorage")
1450+
client, err := storage.NewClient(context.Background())
1451+
if err != nil {
1452+
container.logger.Fatal(stacktrace.Propagate(err, "cannot create GCS client"))
1453+
}
1454+
container.attachmentStorage = repositories.NewGCSAttachmentStorage(
1455+
container.Logger(),
1456+
container.Tracer(),
1457+
client,
1458+
bucket,
1459+
)
1460+
} else {
1461+
container.logger.Debug("creating MemoryAttachmentStorage (GCS_BUCKET_NAME not set)")
1462+
container.attachmentStorage = repositories.NewMemoryAttachmentStorage(
1463+
container.Logger(),
1464+
container.Tracer(),
1465+
)
1466+
}
1467+
1468+
return container.attachmentStorage
1469+
}
1470+
1471+
// APIBaseURL returns the API base URL derived from EVENTS_QUEUE_ENDPOINT
1472+
func (container *Container) APIBaseURL() string {
1473+
endpoint := os.Getenv("EVENTS_QUEUE_ENDPOINT")
1474+
return strings.TrimSuffix(endpoint, "/v1/events")
1475+
}
1476+
1477+
// AttachmentHandler creates a new AttachmentHandler
1478+
func (container *Container) AttachmentHandler() (handler *handlers.AttachmentHandler) {
1479+
container.logger.Debug(fmt.Sprintf("creating %T", handler))
1480+
return handlers.NewAttachmentHandler(
1481+
container.Logger(),
1482+
container.Tracer(),
1483+
container.AttachmentStorage(),
1484+
)
1485+
}
1486+
1487+
// RegisterAttachmentRoutes registers routes for the /attachments prefix
1488+
func (container *Container) RegisterAttachmentRoutes() {
1489+
container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.AttachmentHandler{}))
1490+
container.AttachmentHandler().RegisterRoutes(container.App())
1491+
}
1492+
14361493
// PhoneAPIKeyService creates a new instance of services.PhoneAPIKeyService
14371494
func (container *Container) PhoneAPIKeyService() (service *services.PhoneAPIKeyService) {
14381495
container.logger.Debug(fmt.Sprintf("creating %T", service))

0 commit comments

Comments
 (0)