internal/handler/handler.go

Summary

Maintainability
A
1 hr
Test Coverage
package handler

import (
    "context"
    "fmt"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/rdoorn/gohelper/app"
    "github.com/rdoorn/gohelper/ginhelper"
    "github.com/rdoorn/gohelper/logging"
    "github.com/rdoorn/ixxi/internal/models"
    "github.com/spf13/viper"
)

type Handler struct {
    app.App

    // log is the default log handler
    logger logging.SimpleLogger

    // router is the gin handler
    router gin.Context

    // server is the http server handler
    server   http.Server
    shutdown chan struct{}

    users models.UserInterface
    files models.FileInterface
    db    models.DBInterface

    salt string
}

func New() *Handler {
    return &Handler{
        shutdown: make(chan struct{}),
    }
}

func (h *Handler) Start(c *Config) error {
    if err := h.setLogging(viper.GetString("log_level"), viper.GetStringSlice("log_output")...); err != nil {
        return err
    }

    h.salt = c.PasswordSalt

    // user store
    h.Debugf("setting up user DB", "provider", fmt.Sprintf("%s", c.Users.Provider))
    usersInterface, err := c.Users.Setup()
    if err != nil {
        return err
    }
    h.users = usersInterface.(models.UserInterface)
    h.Debugf("setup user DB", "provider", fmt.Sprintf("%s", usersInterface))

    // file store
    fileInterface, err := c.File.Setup()
    if err != nil {
        return err
    }
    h.files = fileInterface.(models.FileInterface)

    // db store
    dbInterface, err := c.DB.Setup()
    if err != nil {
        return err
    }
    h.db = dbInterface.(models.DBInterface)

    router := gin.New()
    router.Use(gin.Recovery(), ginhelper.Logger(h.logger))
    v1 := router.Group("/v1")
    {
        v1.POST("/version", h.apiV1Version)
        v1.POST("/login", h.apiV1Login)  // login
        v1.POST("/users", h.apiV1Signup) // new user
        // v1.PUT("/users", h.apiV1UpdateUser) // update existing user
    }
    {
        hasAccount := v1.Group("/")
        hasAccount.Use(JWTAuthenticationRequired())
        hasAccount.GET("/users/:username/activate/:token", h.apiV1AccountActivate) // login
    }
    {
        standardUser := v1.Group("/")
        standardUser.Use(JWTAuthenticationRequired("user"))
        standardUser.GET("/users/:username", h.apiV1AccountGet)         // login
        standardUser.POST("/files/:username", h.apiV1FileUploadRequest) // request url to upload file
        standardUser.POST("/albums/:album/:key", h.apiV1FileUpload)     // upload file with key
    }
    router.Static("/static", "./html")

    // start https server
    h.server = http.Server{
        Addr:      c.Addr(),
        Handler:   router,
        TLSConfig: c.TLS.ParseValid(),
    }

    h.logger.Infof("Webservice started", "listener", c.Addr())
    if err := h.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        return err
    }
    <-h.shutdown
    return nil
}

func (h *Handler) Stop() {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    h.server.SetKeepAlivesEnabled(false)
    if err := h.server.Shutdown(ctx); err != nil {
        h.logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
    }
    close(h.shutdown)
    h.logger.Infof("Webservice shutdown")
}

func (h *Handler) setLogging(level string, output ...string) error {
    h.logger, _ = logging.NewZap(output...)
    logWrapper, err := h.logWrapper("core", level)
    if err != nil {
        return err
    }
    h.WithLogging(logWrapper)
    return nil
}

func (h *Handler) logWrapper(description, level string) (logging.SimpleLogger, error) {
    logLevel, err := logging.ToLevel(level)
    if err != nil {
        return nil, fmt.Errorf("error setting log level: %s\n", err)
    }
    var prefix []interface{}
    prefix = append(prefix, "func")
    prefix = append(prefix, description)
    return &logging.Wrapper{
        Log:    h.logger,
        Level:  logLevel,
        Prefix: prefix,
    }, nil
}