package services import ( "context" "fmt" "mailsrv/mail" "mailsrv/runtime" "os" "path" "strings" "time" "github.com/rs/zerolog/log" ) const ( TickerInterval time.Duration = 10 * time.Second JSONSuffix string = ".json" ErrorSuffix string = ".err" ) type Watcher interface { Watch() Done() <-chan struct{} } // DirectoryWatch watches a directory every `tick` interval and collect email files. type DirectoryWatch struct { ctx context.Context fnCancel context.CancelFunc outboxPath string queue *runtime.Queue } func NewDirectoryWatch(ctx context.Context, outboxPath string, queue *runtime.Queue) DirectoryWatch { ctxChild, fnCancel := context.WithCancel(ctx) return DirectoryWatch{ ctx: ctxChild, fnCancel: fnCancel, outboxPath: outboxPath, queue: queue, } } func (dw DirectoryWatch) Done() <-chan struct{} { return dw.ctx.Done() } // Watch reads the `outbox` directory every `TickInterval` and put JSON format e-mail in the queue. func (dw DirectoryWatch) Watch() { log.Info().Str("outbox", dw.outboxPath).Msg("watching outbox directory...") ticker := time.NewTicker(TickerInterval) go func() { for { select { case <-dw.Done(): log.Err(dw.ctx.Err()).Msg("context done") return case <-ticker.C: log.Debug().Str("action", "retrieving json e-mail format...").Str("path", dw.outboxPath) files, err := os.ReadDir(dw.outboxPath) if err != nil && !os.IsExist(err) { log.Err(err).Msg("outbox directory does not exist") dw.fnCancel() return } for _, file := range files { filename := file.Name() if !strings.HasSuffix(filename, JSONSuffix) { continue } emailPath := path.Join(dw.outboxPath, filename) email, err := mail.FromJSON(emailPath) if err != nil { log.Err(err).Str("path", emailPath).Msg("unable to parse JSON email") // if JSON parsing failed the `path` is renamed with an error suffix to not watch it again newPath := fmt.Sprintf("%s%s", emailPath, ErrorSuffix) if err := os.Rename(emailPath, newPath); err != nil { log.Err(err).Str("path", emailPath).Str("new path", newPath).Msg("unable to rename bad JSON email path") } continue } email.Path = emailPath dw.queue.Add(email) } } } }() }