hunterlong/statup

View on GitHub
handlers/handlers.go

Summary

Maintainability
A
1 hr
Test Coverage
package handlers

import (
    "encoding/json"
    "fmt"
    "github.com/statping/statping/types/errors"
    "html/template"
    "net/http"
    "path"
    "time"

    "github.com/statping/statping/source"
    "github.com/statping/statping/utils"
)

const (
    cookieName = "statping_auth"

    timeout = time.Second * 30
)

var (
    jwtKey     []byte
    httpServer *http.Server
    usingSSL   bool
    mainTmpl   = `{{define "main" }} {{ template "base" . }} {{ end }}`
    templates  = []string{"base.gohtml"}
)

func StopHTTPServer(err error) {
    log.Infoln("Stopping HTTP Server")
}

// RunHTTPServer will start a HTTP server on a specific IP and port
func RunHTTPServer() error {
    if utils.Params.GetBool("DISABLE_HTTP") {
        return nil
    }

    ip := utils.Params.GetString("SERVER_IP")
    host := fmt.Sprintf("%v:%v", ip, utils.Params.GetInt("SERVER_PORT"))
    key := utils.FileExists(utils.Directory + "/server.key")
    cert := utils.FileExists(utils.Directory + "/server.crt")

    if key && cert {
        log.Infoln("server.cert and server.key was found in root directory! Starting in SSL mode.")
        log.Infoln(fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
        usingSSL = true
    } else {
        log.Infoln("Statping HTTP Server running on http://" + host + basePath)
    }

    router = Router()
    resetCookies()

    if utils.Params.GetBool("LETSENCRYPT_ENABLE") {
        return startLetsEncryptServer(ip)
    } else if usingSSL {
        return startSSLServer(ip)
    } else {
        return startServer(host)
    }
}

// IsReadAuthenticated will allow Read Only authentication for some routes
func IsReadAuthenticated(r *http.Request) bool {
    if ok := hasSetupEnv(); ok {
        return true
    }
    if ok := hasAPIQuery(r); ok {
        return true
    }
    if ok := hasAuthorizationHeader(r); ok {
        return true
    }
    return IsFullAuthenticated(r)
}

// IsFullAuthenticated returns true if the HTTP request is authenticated. You can set the environment variable GO_ENV=test
// to bypass the admin authenticate to the dashboard features.
func IsFullAuthenticated(r *http.Request) bool {
    if ok := hasSetupEnv(); ok {
        return true
    }
    if ok := hasAPIQuery(r); ok {
        return true
    }
    if ok := hasAuthorizationHeader(r); ok {
        return true
    }
    claim, err := getJwtToken(r)
    if err != nil {
        return false
    }
    return claim.Admin
}

// ScopeName will show private JSON fields in the API.
// It will return "admin" if request has valid admin authentication.
func ScopeName(r *http.Request) string {
    if ok := hasAPIQuery(r); ok {
        return "admin"
    }
    if ok := hasAuthorizationHeader(r); ok {
        return "admin"
    }
    claim, err := getJwtToken(r)
    if err != nil {
        return ""
    }
    if claim.Admin {
        return "admin"
    }
    return "user"
}

// IsAdmin returns true if the user session is an administrator
func IsAdmin(r *http.Request) bool {
    claim, err := getJwtToken(r)
    if err != nil {
        return false
    }
    return claim.Admin
}

// IsUser returns true if the user is registered
func IsUser(r *http.Request) bool {
    if ok := hasSetupEnv(); ok {
        return true
    }
    tk, err := getJwtToken(r)
    if err != nil {
        return false
    }
    if err := tk.Valid(); err != nil {
        return false
    }
    return true
}

func loadTemplate(w http.ResponseWriter, r *http.Request) (*template.Template, error) {
    var err error
    mainTemplate := template.New("main")
    mainTemplate, err = mainTemplate.Parse(mainTmpl)
    if err != nil {
        log.Errorln(err)
        return nil, err
    }
    mainTemplate.Funcs(handlerFuncs(w, r))
    // render all templates
    for _, temp := range templates {
        tmp, _ := source.TmplBox.String(temp)
        mainTemplate, err = mainTemplate.Parse(tmp)
        if err != nil {
            log.Errorln(err)
            return nil, err
        }
    }
    return mainTemplate, err
}

// ExecuteResponse will render a HTTP response for the front end user
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
    if url, ok := redirect.(string); ok {
        http.Redirect(w, r, path.Join(basePath, url), http.StatusSeeOther)
        return
    }
    if usingSSL {
        w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
    }
    mainTemplate, err := loadTemplate(w, r)
    if err != nil {
        log.Errorln(err)
    }
    render, err := source.TmplBox.String(file)
    if err != nil {
        log.Errorln(err)
    }
    // render the page requested
    if _, err := mainTemplate.Parse(render); err != nil {
        log.Errorln(err)
    }
    // execute the template
    if err := mainTemplate.Execute(w, data); err != nil {
        log.Errorln(err)
    }
}

func returnJson(d interface{}, w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    if e, ok := d.(errors.Error); ok {
        w.WriteHeader(e.Status())
        json.NewEncoder(w).Encode(e)
        return
    }
    if e, ok := d.(error); ok {
        w.WriteHeader(500)
        json.NewEncoder(w).Encode(errors.New(e.Error()))
        return
    }
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(d)
}

// error404Handler is a HTTP handler for 404 error pages
func error404Handler(w http.ResponseWriter, r *http.Request) {
    if usingSSL {
        w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
    }
    w.WriteHeader(http.StatusNotFound)
    ExecuteResponse(w, r, "base.gohtml", nil, nil)
}