feat: initial http server and logger

This commit is contained in:
Felipe M 2022-08-13 11:14:42 +02:00
parent 66826ebfdd
commit 01c8a69b89
Signed by: fmartingr
GPG Key ID: 716BC147715E716F
8 changed files with 316 additions and 2 deletions

View File

@ -1,7 +1,35 @@
package main
import "fmt"
import (
"context"
"github.com/fmartingr/notion2ical/internal/server"
"go.uber.org/zap"
)
func main() {
fmt.Println("I come in peace.")
ctx := context.Background()
logger, err := zap.NewProduction()
if err != nil {
panic(err)
}
// TODO: set log level
defer func() {
if err := logger.Sync(); err != nil {
panic(err)
}
}()
server := server.NewServer(
logger,
server.ParseServerConfiguration(ctx, logger),
)
if err := server.Start(ctx); err != nil {
logger.Panic("error starting server", zap.Error(err))
}
server.WaitStop()
}

17
go.mod
View File

@ -1,3 +1,20 @@
module github.com/fmartingr/notion2ical
go 1.19
require (
github.com/gofiber/fiber/v2 v2.36.0
github.com/sethvargo/go-envconfig v0.8.2
go.uber.org/zap v1.22.0
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/klauspost/compress v1.15.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.38.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
)

47
go.sum Normal file
View File

@ -0,0 +1,47 @@
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofiber/fiber/v2 v2.36.0 h1:1qLMe5rhXFLPa2SjK10Wz7WFgLwYi4TYg7XrjztJHqA=
github.com/gofiber/fiber/v2 v2.36.0/go.mod h1:tgCr+lierLwLoVHHO/jn3Niannv34WRkQETU8wiL9fQ=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sethvargo/go-envconfig v0.8.2 h1:DDUVuG21RMgeB/bn4leclUI/837y6cQCD4w8hb5797k=
github.com/sethvargo/go-envconfig v0.8.2/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.38.0 h1:yTjSSNjuDi2PPvXY2836bIwLmiTS2T4T9p1coQshpco=
github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -0,0 +1,8 @@
package models
import "context"
type Server interface {
Start(ctx context.Context) error
Stop(ctx context.Context) error
}

65
internal/server/config.go Normal file
View File

@ -0,0 +1,65 @@
package server
import (
"bufio"
"context"
"os"
"strings"
"github.com/sethvargo/go-envconfig"
"go.uber.org/zap"
)
// readDotEnv reads the configuration from variables in a .env file (only for contributing)
func readDotEnv(logger *zap.Logger) map[string]string {
file, err := os.Open(".env")
if err != nil {
return nil
}
defer file.Close()
result := make(map[string]string)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "#") {
continue
}
keyval := strings.SplitN(line, "=", 2)
result[keyval[0]] = keyval[1]
}
if err := scanner.Err(); err != nil {
logger.Fatal("error reading dotenv", zap.Error(err))
}
return result
}
type ServerConfig struct {
Hostname string `env:"HOSTNAME,required"`
Http struct {
Enabled bool `env:"HTTP_ENABLED,default=True"`
Port int `env:"HTTP_PORT,default=8080"`
}
LogLevel string `env:"LOG_LEVEL,default=info"`
}
func ParseServerConfiguration(ctx context.Context, logger *zap.Logger) *ServerConfig {
var cfg ServerConfig
lookuper := envconfig.MultiLookuper(
envconfig.MapLookuper(map[string]string{"HOSTNAME": os.Getenv("HOSTNAME")}),
envconfig.MapLookuper(readDotEnv(logger)),
envconfig.PrefixLookuper("NOTION2ICAL_", envconfig.OsLookuper()),
envconfig.OsLookuper(),
)
if err := envconfig.ProcessWith(ctx, &cfg, lookuper); err != nil {
logger.Fatal("Error parsing configuration: %s", zap.Error(err))
}
return &cfg
}

62
internal/server/http.go Normal file
View File

@ -0,0 +1,62 @@
package server
import (
"context"
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type httpServer struct {
http *fiber.App
addr string
logger *zap.Logger
}
func (s *httpServer) Start(_ context.Context) error {
s.http.
Static("/", "./public").
Get("/calendar", s.calendarHandler).
Get("/liveness", s.livenessHandler).
Use(s.notFound)
s.logger.Info("starting http server", zap.String("addr", s.addr))
return s.http.Listen(s.addr)
}
func (s *httpServer) Stop(ctx context.Context) error {
s.logger.Info("stoppping http server")
return s.http.Shutdown()
}
func (s *httpServer) notFound(c *fiber.Ctx) error {
return c.SendStatus(404)
}
func (s *httpServer) livenessHandler(c *fiber.Ctx) error {
return c.SendStatus(200)
}
func (s *httpServer) calendarHandler(c *fiber.Ctx) error {
return c.SendStatus(501)
}
func NewHttpServer(logger *zap.Logger, port int) *httpServer {
return &httpServer{
logger: logger,
addr: fmt.Sprintf(":%d", port),
http: fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
logger.Error(
"handler error",
zap.String("method", c.Method()),
zap.String("path", c.Path()),
zap.Error(err),
)
return c.SendStatus(500)
},
}),
}
}

72
internal/server/server.go Normal file
View File

@ -0,0 +1,72 @@
package server
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"time"
internalModels "github.com/fmartingr/notion2ical/internal/models"
"go.uber.org/zap"
)
type Server struct {
Http internalModels.Server
config *ServerConfig
logger *zap.Logger
cancel context.CancelFunc
}
func (s *Server) Start(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
s.cancel = cancel
if s.config.Http.Enabled {
go func() {
if err := s.Http.Start(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Fatal("error starting server", zap.Error(err))
}
}()
}
return nil
}
func (s *Server) WaitStop() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
sig := <-signals
s.logger.Info("signal received, shutting down", zap.String("signal", sig.String()))
s.Stop()
}
func (s *Server) Stop() {
s.cancel()
shuwdownContext, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if s.config.Http.Enabled {
if err := s.Http.Stop(shuwdownContext); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Fatal("error shutting down http server", zap.Error(err))
}
}
}
func NewServer(logger *zap.Logger, conf *ServerConfig) *Server {
server := &Server{
logger: logger,
config: conf,
}
if conf.Http.Enabled {
server.Http = NewHttpServer(logger, conf.Http.Port)
}
return server
}

15
public/index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=320, initial-scale=1.0">
<title>Notion to iCal</title>
</head>
<body>
Notion to ical
</body>
</html>