Skip to content

Commit

Permalink
feat: UI config auto generation
Browse files Browse the repository at this point in the history
- refactoring of the main runtime using CMD pattern
- generarte UI config at runtime
- lint code
  • Loading branch information
Nicolas Carlier authored and ncarlier committed Feb 19, 2024
1 parent 8e6f389 commit 12e7de3
Show file tree
Hide file tree
Showing 33 changed files with 442 additions and 203 deletions.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ EXPOSE 8080 9090

# Define entrypoint
ENTRYPOINT [ "readflow" ]

# Define command
CMD [ "serve" ]
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ distribution:

## Start development server (aka: a test database instance)
dev-server:
docker-compose -f docker-compose.dev.yml down
docker-compose -f docker-compose.dev.yml up
docker compose -f docker-compose.dev.yml down
docker compose -f docker-compose.dev.yml up
.PHONY: dev-server

## Deploy containers to Docker host
Expand Down
10 changes: 10 additions & 0 deletions cmd/all/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package all

import (
// activate init-config command
_ "github.com/ncarlier/readflow/cmd/init-config"
// activate serve command
_ "github.com/ncarlier/readflow/cmd/serve"
// activate version command
_ "github.com/ncarlier/readflow/cmd/version"
)
32 changes: 32 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cmd

import (
"flag"
"fmt"
"os"
)

var (
// ConfigFlag is the flag used to load the config file
ConfigFlag string
)

func init() {
defaultValue := ""
if value, ok := os.LookupEnv("READFLOW_CONFIG"); ok {
defaultValue = value
}
flag.StringVar(&ConfigFlag, "c", defaultValue, "Configuration file to load [ENV: READFLOW_CONFIG]")
flag.Usage = func() {
out := flag.CommandLine.Output()
fmt.Fprintf(out, "readflow is a news-reading (or read-it-later) solution focused on versatility and simplicity.\n")
fmt.Fprintf(out, "\nUsage:\n readflow [flags] [command]\n")
fmt.Fprintf(out, "\nAvailable Commands:\n")
for _, c := range Commands {
c.Usage()
}
fmt.Fprintf(out, "\nFlags:\n")
flag.PrintDefaults()
fmt.Fprintf(out, "\nUse \"reaflow [command] --help\" for more information about a command.\n\n")
}
}
15 changes: 15 additions & 0 deletions cmd/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmd

import "strings"

// GetFirstCommand restun first command of argument list
func GetFirstCommand(args []string) (name string, index int) {
for idx, arg := range args {
if strings.HasPrefix(arg, "-") {
// ignore flags
continue
}
return arg, idx
}
return "", -1
}
39 changes: 39 additions & 0 deletions cmd/init-config/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package initconfig

import (
"flag"
"fmt"

"github.com/ncarlier/readflow/cmd"
"github.com/ncarlier/readflow/pkg/config"
)

const cmdName = "init-config"

type InitConfigCmd struct {
filename string
flagSet *flag.FlagSet
}

func (c *InitConfigCmd) Exec(args []string, conf *config.Config) error {
if err := c.flagSet.Parse(args); err != nil {
return err
}
return conf.WriteDefaultConfigFile(c.filename)
}

func (c *InitConfigCmd) Usage() {
fmt.Fprintf(c.flagSet.Output(), " %s\tInit configuration file\n", cmdName)
}

func newInitConfigCmd() cmd.Cmd {
c := &InitConfigCmd{
flagSet: flag.NewFlagSet(cmdName, flag.ExitOnError),
}
c.flagSet.StringVar(&c.filename, "f", "config.toml", "Configuration file to create")
return c
}

func init() {
cmd.Add(cmdName, newInitConfigCmd)
}
12 changes: 12 additions & 0 deletions cmd/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cmd

// Creator function for an output
type Creator func() Cmd

// Commands registry
var Commands = map[string]Cmd{}

// Add output to the registry
func Add(name string, creator Creator) {
Commands[name] = creator()
}
35 changes: 35 additions & 0 deletions cmd/serve/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package serve

import (
"flag"
"fmt"

"github.com/ncarlier/readflow/cmd"
"github.com/ncarlier/readflow/pkg/config"
)

const cmdName = "serve"

type ServeCmd struct {
flagSet *flag.FlagSet
}

func (c *ServeCmd) Exec(args []string, conf *config.Config) error {
// no args
return startServer(conf)
}

func (c *ServeCmd) Usage() {
fmt.Fprintf(c.flagSet.Output(), " %s\t\tStart readflow server\n", cmdName)
}

func newServeCmd() cmd.Cmd {
c := &ServeCmd{
flagSet: flag.NewFlagSet(cmdName, flag.ExitOnError),
}
return c
}

func init() {
cmd.Add("serve", newServeCmd)
}
117 changes: 117 additions & 0 deletions cmd/serve/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package serve

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/ncarlier/readflow/pkg/api"
"github.com/ncarlier/readflow/pkg/cache"
"github.com/ncarlier/readflow/pkg/config"
"github.com/ncarlier/readflow/pkg/db"
"github.com/ncarlier/readflow/pkg/exporter"
"github.com/ncarlier/readflow/pkg/exporter/pdf"
"github.com/ncarlier/readflow/pkg/metric"
"github.com/ncarlier/readflow/pkg/server"
"github.com/ncarlier/readflow/pkg/service"
"github.com/rs/zerolog/log"
)

func startServer(conf *config.Config) error {
log.Debug().Msg("starting readflow...")

// configure the DB
database, err := db.NewDB(conf.Database.URI)
if err != nil {
return fmt.Errorf("unable to configure the database: %w", err)
}

// configure download cache
downloadCache, err := cache.NewDefault("readflow-downloads")
if err != nil {
return fmt.Errorf("unable to configure the downloader cache storage: %w", err)
}

// configure the service registry
err = service.Configure(*conf, database, downloadCache)
if err != nil {
database.Close()
return fmt.Errorf("unable to configure the service registry: %w", err)
}

// register external exporters...
if conf.PDF.ServiceProvider != "" {
log.Info().Str("provider", conf.PDF.ServiceProvider).Msg("using PDF generator service")
exporter.Register("pdf", pdf.NewPDFExporter(conf.PDF.ServiceProvider))
}

// create HTTP server
httpServer := server.NewHTTPServer(conf)

// create and start metrics server
metricsServer := server.NewMetricsServer(conf)
if metricsServer != nil {
metric.StartCollectors(database)
go metricsServer.ListenAndServe()
}

// create and start SMTP server
smtpServer := server.NewSMTPHTTPServer(conf)
if smtpServer != nil {
go smtpServer.ListenAndServe()
}

done := make(chan bool)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)

go func() {
<-quit
log.Debug().Msg("shutting down readflow...")
api.Shutdown()

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := httpServer.Shutdown(ctx); err != nil {
log.Fatal().Err(err).Msg("unable to gracefully shutdown the HTTP server")
}
if smtpServer != nil {
if err := smtpServer.Shutdown(ctx); err != nil {
log.Fatal().Err(err).Msg("unable to gracefully shutdown the SMTP server")
}
}
if metricsServer != nil {
metric.StopCollectors()
if err := metricsServer.Shutdown(ctx); err != nil {
log.Fatal().Err(err).Msg("unable to gracefully shutdown the metrics server")
}
}

service.Shutdown()

if err := downloadCache.Close(); err != nil {
log.Error().Err(err).Msg("unable to gracefully shutdown the cache storage")
}

if err := database.Close(); err != nil {
log.Fatal().Err(err).Msg("could not gracefully shutdown database connection")
}

close(done)
}()

// set API health check as started
api.Start()

// start HTTP server
httpServer.ListenAndServe()

<-done
log.Debug().Msg("readflow stopped")

return nil
}
8 changes: 8 additions & 0 deletions cmd/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cmd

import "github.com/ncarlier/readflow/pkg/config"

type Cmd interface {
Exec(args []string, conf *config.Config) error
Usage()
}
37 changes: 37 additions & 0 deletions cmd/version/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package initconfig

import (
"flag"
"fmt"

"github.com/ncarlier/readflow/cmd"
"github.com/ncarlier/readflow/pkg/config"
"github.com/ncarlier/readflow/pkg/version"
)

const cmdName = "version"

type VersionCmd struct {
flagSet *flag.FlagSet
}

func (c *VersionCmd) Exec(args []string, conf *config.Config) error {
// no args
version.Print()
return nil
}

func (c *VersionCmd) Usage() {
fmt.Fprintf(c.flagSet.Output(), " %s\tDisplay version\n", cmdName)
}

func newVersionCmd() cmd.Cmd {
c := &VersionCmd{
flagSet: flag.NewFlagSet(cmdName, flag.ExitOnError),
}
return c
}

func init() {
cmd.Add("version", newVersionCmd)
}
Loading

0 comments on commit 12e7de3

Please sign in to comment.