Skip to main content

File & Image Storage (S3 / MinIO)

Overview

Every file a user uploads through the CMS passes through a single service, internal/storage.Service, which wraps S3/MinIO. That covers rich message images, rich menu images, template hero images, images embedded in content pages, the CSV files behind audiences, customer databases, and import mappings, and user avatars.

The internal/modules/storage module has no routes of its own (RegisterRoutes is a no-op), because in the original NestJS code it was a @Global provider with no controller. The module is just a set of type aliases pointing at the shared service exposed as Deps.Storage.

Business Flow

  1. A controller receiving multipart/form-data converts the upload into a storage.File, which carries originalName, extension, mimetype, and buffer — mirroring MemoryStoredFile from nestjs-form-data.
  2. It then calls the method that matches the job.
    • PutTemp / PutTempBuffer — temporary storage before the user confirms
    • PutImage / PutImageBuffer — stores an image in the folder given by FolderName
    • PutFile — stores a general file such as a CSV
  3. Files are organized under each tenant's root folder, resolved by GetRootFolder.
  4. When reading back:
    • GetPublicUrl / GetPublicFileUrl — public URLs, needed for assets sent to LINE, which must be externally reachable
    • GeneratePresignedUrl — expiring URLs for files that should not be publicly exposed
    • GetImage / GetPathUrl — fetch a file or translate a path
  5. Additional operations include DeleteObject, IsFileExist, and CopyObjectFromPublicUrl. The last is used when duplicating content or cloning a rich menu, where the image must be copied from its existing URL.
  6. Stored CSVs are later read by the CSV Engine when paginating LINE user lists.

Key Files & Functions

FileRole
internal/storage/storage.goThe real Service — a port of src/storage/storage.service.ts
internal/storage/image.goImage handling: file-type validation and filename normalization
internal/modules/storage/service.goType aliases for Service, File, StorageType, FolderName, ImageFileType, RootFolder
internal/modules/storage/controller.goRegisterRoutes is a no-op

Full method set: GetPublicUrl, GetPathUrl, GetImage, PutTemp, PutImage, PutTempBuffer, PutImageBuffer, DeleteObject, CopyObjectFromPublicUrl, IsFileExist, GetRootFolder, PutFile, GetPublicFileUrl, GeneratePresignedUrl

Endpoints that actually upload files — they live in other modules but all funnel into this service.

RouteModule
POST /api/content-pages/upload-imageContent Page
POST /api/menu-builder/upload-imageMenu Builder
POST /api/template-message/upload-imageTemplate Message
POST /api/rich-message (form-data)Rich Message
POST /api/rich-menu (form-data)Rich Menu
POST /api/friend-track/:id/refs/uploadFriend Track
POST /api/customer-database and /previewCustomer Database
POST /api/import-mapping/uploadImport Mapping
POST /api/line-users/import-all-friendsLINE User Management
POST /api/user (avatar)User Management

Connections to Other Services

  • Permission — no routes of its own, so access control is enforced at the calling module's endpoint.
  • Infrastructure — S3 or MinIO depending on StorageType, configured from environment variables in internal/config/config.go.
  • ConsumersDeps.Storage is injected into every module, and Deps.CSVEngine receives the same storage service.