diff --git a/cmd/app/config/config.go b/cmd/app/config/config.go index 6f82966..0525dee 100644 --- a/cmd/app/config/config.go +++ b/cmd/app/config/config.go @@ -26,4 +26,7 @@ func init() { if err := env.Parse(&Config.Nats); err != nil { panic(err) } + if err := env.Parse(&Config.Storage); err != nil { + panic(err) + } } diff --git a/cmd/app/config/types.go b/cmd/app/config/types.go index 4e353a0..21b41a3 100644 --- a/cmd/app/config/types.go +++ b/cmd/app/config/types.go @@ -55,4 +55,9 @@ type config struct { Nats struct { URL string `env:"NATS_URL" envDefault:"nats://localhost:4222"` } + + // Storage provides the storage configuration. + Storage struct { + Driver string `env:"STORAGE_DRIVER" envDefault:"file"` + } } diff --git a/cmd/app/context/context.go b/cmd/app/context/context.go index d2f6062..d561378 100644 --- a/cmd/app/context/context.go +++ b/cmd/app/context/context.go @@ -8,6 +8,7 @@ import ( "github.com/iammuho/natternet/pkg/logger" "github.com/iammuho/natternet/pkg/mongodb" "github.com/iammuho/natternet/pkg/nats" + "github.com/iammuho/natternet/pkg/storage" "github.com/iammuho/natternet/pkg/utils" ) @@ -19,6 +20,7 @@ type AppContext interface { GetJwtContext() jwt.JwtContext GetMongoContext() mongodb.MongoDBContext GetNatsContext() nats.NatsContext + GetStorageContext() storage.StorageContext GetHashingFactory() hashing.HashingFactory GetUUID() utils.UUID GetTimer() utils.Timer diff --git a/cmd/app/context/mocks/mock_app_contexter.go b/cmd/app/context/mocks/mock_app_contexter.go index 8f95d88..a609236 100644 --- a/cmd/app/context/mocks/mock_app_contexter.go +++ b/cmd/app/context/mocks/mock_app_contexter.go @@ -13,6 +13,7 @@ import ( logger "github.com/iammuho/natternet/pkg/logger" mongodb "github.com/iammuho/natternet/pkg/mongodb" nats "github.com/iammuho/natternet/pkg/nats" + storage "github.com/iammuho/natternet/pkg/storage" utils "github.com/iammuho/natternet/pkg/utils" gomock "go.uber.org/mock/gomock" ) @@ -124,6 +125,20 @@ func (mr *MockAppContextMockRecorder) GetNatsContext() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNatsContext", reflect.TypeOf((*MockAppContext)(nil).GetNatsContext)) } +// GetStorageContext mocks base method. +func (m *MockAppContext) GetStorageContext() storage.StorageContext { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStorageContext") + ret0, _ := ret[0].(storage.StorageContext) + return ret0 +} + +// GetStorageContext indicates an expected call of GetStorageContext. +func (mr *MockAppContextMockRecorder) GetStorageContext() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageContext", reflect.TypeOf((*MockAppContext)(nil).GetStorageContext)) +} + // GetTimer mocks base method. func (m *MockAppContext) GetTimer() utils.Timer { m.ctrl.T.Helper() diff --git a/cmd/app/context/real_context.go b/cmd/app/context/real_context.go index 19c0cfa..3bc2794 100644 --- a/cmd/app/context/real_context.go +++ b/cmd/app/context/real_context.go @@ -8,6 +8,7 @@ import ( "github.com/iammuho/natternet/pkg/logger" "github.com/iammuho/natternet/pkg/mongodb" "github.com/iammuho/natternet/pkg/nats" + "github.com/iammuho/natternet/pkg/storage" "github.com/iammuho/natternet/pkg/utils" ) @@ -18,11 +19,12 @@ type appContext struct { mongoContext mongodb.MongoDBContext hashingFactory hashing.HashingFactory natsContext nats.NatsContext + storageContext storage.StorageContext UUID utils.UUID Timer utils.Timer } -func NewAppContext(logger *logger.Logger, jwt jwt.JwtContext, mongoContext mongodb.MongoDBContext, natsContext nats.NatsContext) AppContext { +func NewAppContext(logger *logger.Logger, jwt jwt.JwtContext, mongoContext mongodb.MongoDBContext, natsContext nats.NatsContext, storageContext storage.StorageContext) AppContext { ctx := context.Background() // Set the UUID @@ -41,6 +43,7 @@ func NewAppContext(logger *logger.Logger, jwt jwt.JwtContext, mongoContext mongo mongoContext: mongoContext, hashingFactory: hashingFactory, natsContext: natsContext, + storageContext: storageContext, UUID: uuid, Timer: timer, } @@ -77,3 +80,7 @@ func (c *appContext) GetHashingFactory() hashing.HashingFactory { func (c *appContext) GetNatsContext() nats.NatsContext { return c.natsContext } + +func (c *appContext) GetStorageContext() storage.StorageContext { + return c.storageContext +} diff --git a/cmd/app/main.go b/cmd/app/main.go index 0dc9de2..ceabdd9 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -19,6 +19,7 @@ import ( "github.com/iammuho/natternet/pkg/logger" "github.com/iammuho/natternet/pkg/mongodb" "github.com/iammuho/natternet/pkg/nats" + "github.com/iammuho/natternet/pkg/storage" "github.com/gofiber/fiber/v2" "github.com/gofiber/swagger" @@ -94,8 +95,18 @@ func main() { l.Panic("NATS Client failed to connect: %v", zap.Error(err)) } + // Add the storage + l.Info("Creating Storage", zap.String("driver", config.Config.Storage.Driver)) + storageContext, err := storage.NewStorage( + storage.WithStorageDriver(config.Config.Storage.Driver), + ) + + if err != nil { + l.Panic("Storage failed to initialize: %v", zap.Error(err)) + } + // Create the app context - ctx := context.NewAppContext(l, jwtContext, mongodbContext, natsContext) + ctx := context.NewAppContext(l, jwtContext, mongodbContext, natsContext, storageContext) // Register the routes v1 := httpServer.App.Group("/api/v1") diff --git a/pkg/storage/drivers/file/file.go b/pkg/storage/drivers/file/file.go new file mode 100644 index 0000000..7c6265a --- /dev/null +++ b/pkg/storage/drivers/file/file.go @@ -0,0 +1,111 @@ +package file + +import ( + "os" + + "github.com/iammuho/natternet/pkg/storage/drivers" +) + +type file struct{} + +// NewFileStorage returns a new file storage +func NewFileStorage() drivers.DriverContext { + return &file{} +} + +// Get returns a file +func (f *file) Get(fileName string) ([]byte, error) { + // open the file + file, err := os.Open(fileName) + + if err != nil { + return nil, err + } + + // close the file + defer file.Close() + + // get the file info + fileInfo, err := file.Stat() + + if err != nil { + return nil, err + } + + // prepare the buffer + buffer := make([]byte, fileInfo.Size()) + + // read the file + _, err = file.Read(buffer) + + if err != nil { + return nil, err + } + + return buffer, nil +} + +// Put puts a file +func (f *file) Put(fileName string, content []byte) error { + // create the file + file, err := os.Create(fileName) + + if err != nil { + return err + } + + // close the file + defer file.Close() + + // write the content + _, err = file.Write(content) + + if err != nil { + return err + } + + return nil +} + +// Delete deletes a file +func (f *file) Delete(fileName string) error { + // delete the file + err := os.Remove(fileName) + + if err != nil { + return err + } + + return nil +} + +// List lists files +func (f *file) List(path string) ([]string, error) { + // list all files in a directory + dir, err := os.Open(path) + if err != nil { + return nil, err + } + + // close the directory + defer dir.Close() + + // get the list of files + files, err := dir.Readdir(0) + + if err != nil { + return nil, err + } + + // prepare the list of files + var fileList []string + + // loop through the files + for _, file := range files { + // append the file name to the list + fileList = append(fileList, file.Name()) + } + + // return the list of files + return fileList, nil +} diff --git a/pkg/storage/drivers/interface.go b/pkg/storage/drivers/interface.go new file mode 100644 index 0000000..48c0831 --- /dev/null +++ b/pkg/storage/drivers/interface.go @@ -0,0 +1,10 @@ +package drivers + +// DriverContext is the interface for the storage driver +// TODO: add/refactor methods +type DriverContext interface { + Get(string) ([]byte, error) + Put(string, []byte) error + Delete(string) error + List(string) ([]string, error) +} diff --git a/pkg/storage/interface.go b/pkg/storage/interface.go new file mode 100644 index 0000000..a565780 --- /dev/null +++ b/pkg/storage/interface.go @@ -0,0 +1,8 @@ +package storage + +import "github.com/iammuho/natternet/pkg/storage/drivers" + +// StorageContext is the interface for the storage +type StorageContext interface { + Driver() drivers.DriverContext +} diff --git a/pkg/storage/options.go b/pkg/storage/options.go new file mode 100644 index 0000000..860ec66 --- /dev/null +++ b/pkg/storage/options.go @@ -0,0 +1,25 @@ +package storage + +// Option is the func interface to assign options +type Option func(*StorageOptions) + +type Driver string + +const ( + // DriverFile is the file driver + DriverFile Driver = "file" + // DriverAWS is the AWS driver + DriverAWS Driver = "aws" +) + +// StorageOptions defines the options for the storage +type StorageOptions struct { + Driver Driver +} + +// WithStorageDriver sets the storage driver +func WithStorageDriver(driver string) Option { + return func(o *StorageOptions) { + o.Driver = Driver(driver) + } +} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go new file mode 100644 index 0000000..60bf31f --- /dev/null +++ b/pkg/storage/storage.go @@ -0,0 +1,33 @@ +package storage + +import ( + "github.com/iammuho/natternet/pkg/storage/drivers" + "github.com/iammuho/natternet/pkg/storage/drivers/file" +) + +type storage struct { + driver drivers.DriverContext + options StorageOptions +} + +func NewStorage(opts ...Option) (StorageContext, error) { + // Setup the driver + options := StorageOptions{} + for _, o := range opts { + o(&options) + } + + switch options.Driver { + case DriverFile: + return &storage{ + driver: file.NewFileStorage(), + options: options, + }, nil + } + + return &storage{}, nil +} + +func (s *storage) Driver() drivers.DriverContext { + return s.driver +}