diff --git a/Makefile b/Makefile index 4a1a1e13..1d1232ca 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ npm-build: $(NPM_PREFIX) npm run build dev: - go run --tags dev . --watch + go run --tags dev . --watch --memory snapshot: npm-build goreleaser release --snapshot --rm-dist diff --git a/README.md b/README.md index e85f903e..27f277ae 100644 --- a/README.md +++ b/README.md @@ -15,51 +15,58 @@ Bridge email to other messaging services. smtpbridge ``` -## Configuration +Run with database and storage in memory. -Configuration file is located at `~/.smtpbridge.yml`. +``` +smtpbridge --memory +``` + +Restart when config file changes. + +``` +smtpbridge --watch +``` + +## Config -### Starter Configuration +Config file is located at `~/.smtpbridge.yml`. + +### Starter Config This config prints emails received via SMTP to console. The SMTP server listens on port `1025` and the HTTP server listens on port `8080`. This saves emails to `~/.smtpbridge` directory. -The `database` and `storage` keys can be removed to run this entirely in memory. ```yaml -database: - type: bolt - -storage: - type: directory - endpoints: - name: hello world type: console ``` -### Full Configuration +### Full Config ```yaml +memory: false # Run with database and storage in memory + directory: ~/.smtpbridge # Default persistence directory database: # Database - type: memory # (memory, bolt) + type: bolt # (bolt, memory) memory: limit: 100 # Max number of envelopes -storage: # Storage for attachment's data - type: memory # (memory, directory) +storage: # Storage + type: file # (file, memory) memory: size: 104857600 # Max memory allocation in bytes, 100 MiB http: # HTTP server - enable: true # (true, false) + disable: true # (false, true) host: "" port: 8080 smtp: # SMTP server - enable: true # (true, false) + disable: false # (false, true) host: "" port: 1025 size: 26214400 # Max message size in bytes, 25 MiB @@ -106,7 +113,13 @@ bridges: # Bridges to endpoints, if this is empty then envelopes will always be - console endpoint ``` +### Template + +Each template has access to the [`Envelope`](./core/envelope/envelope.go) struct. +See [`text/template`](https://pkg.go.dev/text/template) on how to template. + ## To Do - Read mail files +- Save raw emails - Windows installer diff --git a/cmd/root.go b/cmd/root.go index 5d436db3..002d90e5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -108,6 +108,12 @@ func init() { watch = rootCmd.Flags().Bool("watch", false, "restart when config file changes") + rootCmd.Flags().Bool("memory", serverConfig.Memory, "run with database and storage in memory") + viper.BindPFlag("memory", rootCmd.Flags().Lookup("memory")) + + rootCmd.Flags().String("directory", serverConfig.Directory, "persistence directory") + viper.BindPFlag("directory", rootCmd.Flags().Lookup("directory")) + rootCmd.Flags().Bool("http-disable", serverConfig.HTTP.Disable, "disable http server") viper.BindPFlag("http.disable", rootCmd.Flags().Lookup("http-disable")) diff --git a/config/config.go b/config/config.go index 649d594c..be7cc02e 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( ) type Config struct { + Memory bool `json:"memory" mapstructure:"memory"` Directory string `json:"directory" mapstructure:"directory"` Database Database `json:"database" mapstructure:"database"` Storage Storage `json:"storage" mapstructure:"storage"` @@ -28,13 +29,13 @@ func New() *Config { return &Config{ Directory: directory, Database: Database{ - Type: "memory", + Type: DatabaseTypeBolt, Memory: DatabaseMemory{ Limit: 100, }, }, Storage: Storage{ - Type: "memory", + Type: StorageTypeFile, Memory: StorageMemory{ Size: 1024 * 1024 * 100, // 100 MiB }, @@ -75,10 +76,16 @@ SUBJECT: {{ .Message.Subject }} } } - // Directory - c.Storage.Directory.Path = path.Join(c.Directory, "data") - if c.Storage.IsDirectory() { - mustCreatePath(c.Storage.Directory.Path) + // Override database and storage + if c.Memory { + c.Database.Type = DatabaseTypeMemory + c.Storage.Type = StorageTypeMemory + } + + // File + c.Storage.File.Path = path.Join(c.Directory, "attachments") + if c.Storage.IsFile() { + mustCreatePath(c.Storage.File.Path) } // Bolt diff --git a/config/database.go b/config/database.go index 27589a62..f8888a47 100644 --- a/config/database.go +++ b/config/database.go @@ -1,11 +1,18 @@ package config type Database struct { - Type string `json:"type" mapstructure:"type"` + Type DatabaseType `json:"type" mapstructure:"type"` Memory DatabaseMemory `json:"memory" mapstructure:"memory"` Bolt DatabaseBolt `json:"bolt" mapstructure:"bolt"` } +type DatabaseType string + +const ( + DatabaseTypeBolt = "bolt" + DatabaseTypeMemory = "memory" +) + type DatabaseMemory struct { Limit int64 `json:"limit" mapstructure:"limit"` } @@ -15,9 +22,9 @@ type DatabaseBolt struct { } func (d Database) IsMemory() bool { - return d.Type == "memory" + return d.Type == DatabaseTypeMemory } func (d Database) IsBolt() bool { - return d.Type == "bolt" + return d.Type == DatabaseTypeBolt } diff --git a/config/storage.go b/config/storage.go index 3a4eee20..9c8db73b 100644 --- a/config/storage.go +++ b/config/storage.go @@ -1,23 +1,30 @@ package config type Storage struct { - Type string `json:"type" mapstructure:"type"` - Memory StorageMemory `json:"memory" mapstructure:"memory"` - Directory StorageDirectory `json:"directory" mapstructure:"directory"` + Type StorageType `json:"type" mapstructure:"type"` + Memory StorageMemory `json:"memory" mapstructure:"memory"` + File StorageFile `json:"file" mapstructure:"file"` } -func (s Storage) IsMemory() bool { - return s.Type == "memory" +type StorageType string + +const ( + StorageTypeFile StorageType = "file" + StorageTypeMemory StorageType = "memory" +) + +func (s Storage) IsFile() bool { + return s.Type == StorageTypeFile } -func (s Storage) IsDirectory() bool { - return s.Type == "directory" +func (s Storage) IsMemory() bool { + return s.Type == StorageTypeMemory } type StorageMemory struct { Size int64 `json:"size" mapstructure:"size"` } -type StorageDirectory struct { +type StorageFile struct { Path string `json:"-" mapstructure:"-"` } diff --git a/server/server.go b/server/server.go index 9931213e..4545148c 100644 --- a/server/server.go +++ b/server/server.go @@ -34,8 +34,8 @@ func Start(ctx context.Context, config *config.Config) <-chan struct{} { var dataStore envelope.DataStore if config.Storage.IsMemory() { dataStore = memdb.NewData(config.Storage.Memory.Size) - } else if config.Storage.IsDirectory() { - dataStore = filedb.NewData(config.Storage.Directory.Path) + } else if config.Storage.IsFile() { + dataStore = filedb.NewData(config.Storage.File.Path) } else { log.Fatalln("server.Start: storage invalid:", config.Storage.Type) } @@ -70,6 +70,8 @@ func Start(ctx context.Context, config *config.Config) <-chan struct{} { }); err != nil { log.Fatalf("server.Start: endpoint '%s': %s", end.Name, err) } + + log.Printf("server.Start: created endpoint '%s'", end.Name) } // Create bridges from config