diff --git a/cmd/Dockerfile b/cmd/Dockerfile
index 145d1991..c0a23d45 100644
--- a/cmd/Dockerfile
+++ b/cmd/Dockerfile
@@ -1,6 +1,6 @@
FROM alpine:latest
-ENV VERSION=v0.4
+ENV VERSION=v0.41
RUN apk --no-cache add libstdc++ ca-certificates
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
diff --git a/cmd/cli_test.go b/cmd/cli_test.go
index 0af5ee34..0a94d144 100644
--- a/cmd/cli_test.go
+++ b/cmd/cli_test.go
@@ -3,7 +3,6 @@ package main
import (
"github.com/rendon/testcli"
"github.com/stretchr/testify/assert"
- "os"
"testing"
)
@@ -52,9 +51,6 @@ func TestAssetsCLI(t *testing.T) {
}
func TestSassCLI(t *testing.T) {
- if os.Getenv("IS_DOCKER") == "true" {
- os.Setenv("SASS", "/usr/local/bin/sass")
- }
run := CatchCLI([]string{"statup", "sass"})
assert.Nil(t, run)
assert.FileExists(t, dir+"/assets/css/base.css")
diff --git a/cmd/main.go b/cmd/main.go
index b064079e..906e7513 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -45,7 +45,7 @@ func main() {
if err != nil {
utils.Log(3, err)
core.SetupMode = true
- handlers.RunHTTPServer()
+ handlers.RunHTTPServer("localhost", 8080)
}
mainProcess()
}
@@ -71,7 +71,7 @@ func mainProcess() {
if !core.SetupMode {
LoadPlugins(false)
- handlers.RunHTTPServer()
+ handlers.RunHTTPServer("localhost", 8080)
}
}
diff --git a/cmd/main_test.go b/cmd/main_test.go
index 301defcb..ffe7aff2 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -454,6 +454,7 @@ func RunPrometheusHandler(t *testing.T) {
route.ServeHTTP(rr, req)
t.Log(rr.Body.String())
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 6"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunFailingPrometheusHandler(t *testing.T) {
@@ -461,7 +462,8 @@ func RunFailingPrometheusHandler(t *testing.T) {
assert.Nil(t, err)
rr := httptest.NewRecorder()
route.ServeHTTP(rr, req)
- assert.Equal(t, 401, rr.Result().StatusCode)
+ assert.Equal(t, 303, rr.Result().StatusCode)
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunLoginHandler(t *testing.T) {
@@ -474,6 +476,7 @@ func RunLoginHandler(t *testing.T) {
rr := httptest.NewRecorder()
route.ServeHTTP(rr, req)
assert.Equal(t, 303, rr.Result().StatusCode)
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunDashboardHandler(t *testing.T) {
@@ -483,6 +486,7 @@ func RunDashboardHandler(t *testing.T) {
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "
Statup | Dashboard"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunUsersHandler(t *testing.T) {
@@ -492,6 +496,7 @@ func RunUsersHandler(t *testing.T) {
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "Statup | Users"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunUserViewHandler(t *testing.T) {
@@ -501,6 +506,7 @@ func RunUserViewHandler(t *testing.T) {
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "Statup | Users"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunServicesHandler(t *testing.T) {
@@ -510,6 +516,7 @@ func RunServicesHandler(t *testing.T) {
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "Statup | Services"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunHelpHandler(t *testing.T) {
@@ -519,6 +526,7 @@ func RunHelpHandler(t *testing.T) {
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "Statup | Help"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func RunSettingsHandler(t *testing.T) {
@@ -529,6 +537,7 @@ func RunSettingsHandler(t *testing.T) {
assert.True(t, strings.Contains(rr.Body.String(), "Statup | Settings"))
assert.True(t, strings.Contains(rr.Body.String(), "Theme Editor"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
+ assert.True(t, handlers.IsAuthenticated(req))
}
func fileExists(file string) bool {
diff --git a/core/core.go b/core/core.go
index accbdb73..cd96fa13 100644
--- a/core/core.go
+++ b/core/core.go
@@ -4,6 +4,7 @@ import (
"github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types"
+ "github.com/hunterlong/statup/utils"
"github.com/pkg/errors"
"os"
"time"
@@ -79,21 +80,21 @@ func (c Core) SassVars() string {
if !source.UsingAssets {
return ""
}
- return source.OpenAsset(".", "scss/variables.scss")
+ return source.OpenAsset(utils.Directory, "scss/variables.scss")
}
func (c Core) BaseSASS() string {
if !source.UsingAssets {
return ""
}
- return source.OpenAsset(".", "scss/base.scss")
+ return source.OpenAsset(utils.Directory, "scss/base.scss")
}
func (c Core) MobileSASS() string {
if !source.UsingAssets {
return ""
}
- return source.OpenAsset(".", "scss/mobile.scss")
+ return source.OpenAsset(utils.Directory, "scss/mobile.scss")
}
func (c Core) AllOnline() bool {
diff --git a/handlers/handlers.go b/handlers/handlers.go
index 98210279..a853ac3d 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -21,9 +21,9 @@ var (
Store *sessions.CookieStore
)
-func RunHTTPServer() {
- utils.Log(1, "Statup HTTP Server running on http://localhost:8080")
- r := Router()
+func RunHTTPServer(ip string, port int) error {
+ host := fmt.Sprintf("%v:%v", ip, port)
+ utils.Log(1, "Statup HTTP Server running on http://"+host)
for _, p := range core.CoreApp.AllPlugins {
info := p.GetInfo()
for _, route := range p.Routes() {
@@ -33,16 +33,13 @@ func RunHTTPServer() {
}
}
srv := &http.Server{
- Addr: "0.0.0.0:8080",
+ Addr: host,
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
- Handler: r,
- }
- err := srv.ListenAndServe()
- if err != nil {
- utils.Log(4, err)
+ Handler: Router(),
}
+ return srv.ListenAndServe()
}
func IsAuthenticated(r *http.Request) bool {
@@ -118,4 +115,9 @@ func ExecuteJSResponse(w http.ResponseWriter, r *http.Request, file string, data
t.Execute(w, data)
}
+func Error404Handler(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ ExecuteResponse(w, r, "error_404.html", nil)
+}
+
type DbConfig types.DbConfig
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 08af9b3a..01a076e2 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -1,7 +1,6 @@
package handlers
import (
- "fmt"
"github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/utils"
@@ -21,16 +20,11 @@ func init() {
func IsRouteAuthenticated(req *http.Request) bool {
os.Setenv("GO_ENV", "production")
- req, err := http.NewRequest(req.Method, req.URL.String(), req.Body)
- if err != nil {
- os.Setenv("GO_ENV", "test")
- return false
- }
rr := httptest.NewRecorder()
+ req.Header.Set("Authorization", "badkey")
Router().ServeHTTP(rr, req)
- fmt.Println(req.URL.String(), rr.Code)
code := rr.Code
- if code != 303 {
+ if code == 200 {
os.Setenv("GO_ENV", "test")
return false
}
@@ -38,6 +32,11 @@ func IsRouteAuthenticated(req *http.Request) bool {
return true
}
+func TestFailedHTTPServer(t *testing.T) {
+ err := RunHTTPServer("missinghost", 0)
+ assert.Error(t, err)
+}
+
func TestIndexHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/", nil)
assert.Nil(t, err)
@@ -386,6 +385,7 @@ func TestDeleteServiceHandler(t *testing.T) {
rr := httptest.NewRecorder()
Router().ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestLogsHandler(t *testing.T) {
@@ -397,6 +397,7 @@ func TestLogsHandler(t *testing.T) {
assert.Equal(t, 200, rr.Code)
assert.Contains(t, body, "Statup | Logs")
assert.Contains(t, body, "Statup made with ❤️")
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestLogsLineHandler(t *testing.T) {
@@ -408,6 +409,7 @@ func TestLogsLineHandler(t *testing.T) {
assert.Equal(t, 200, rr.Code)
t.Log(body)
assert.NotEmpty(t, body)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestSaveSettingsHandler(t *testing.T) {
@@ -420,6 +422,7 @@ func TestSaveSettingsHandler(t *testing.T) {
rr := httptest.NewRecorder()
Router().ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestViewSettingsHandler(t *testing.T) {
@@ -445,6 +448,7 @@ func TestSaveAssetsHandler(t *testing.T) {
assert.FileExists(t, utils.Directory+"/assets/js/main.js")
assert.DirExists(t, utils.Directory+"/assets")
assert.True(t, source.UsingAssets)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestDeleteAssetsHandler(t *testing.T) {
@@ -454,6 +458,7 @@ func TestDeleteAssetsHandler(t *testing.T) {
Router().ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
assert.False(t, source.UsingAssets)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestPrometheusHandler(t *testing.T) {
@@ -465,6 +470,7 @@ func TestPrometheusHandler(t *testing.T) {
body := rr.Body.String()
assert.Equal(t, 200, rr.Code)
assert.Contains(t, body, "statup_total_services 6")
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestSaveNotificationHandler(t *testing.T) {
@@ -485,6 +491,7 @@ func TestSaveNotificationHandler(t *testing.T) {
rr := httptest.NewRecorder()
Router().ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
}
func TestViewNotificationSettingsHandler(t *testing.T) {
@@ -515,6 +522,7 @@ func TestSaveFooterHandler(t *testing.T) {
rr := httptest.NewRecorder()
Router().ServeHTTP(rr, req)
assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
req, err = http.NewRequest("GET", "/", nil)
assert.Nil(t, err)
@@ -540,3 +548,32 @@ func TestLogoutHandler(t *testing.T) {
Router().ServeHTTP(rr, req)
assert.Equal(t, 303, rr.Code)
}
+
+func TestBuildAssetsHandler(t *testing.T) {
+ req, err := http.NewRequest("GET", "/settings/build", nil)
+ assert.Nil(t, err)
+ rr := httptest.NewRecorder()
+ Router().ServeHTTP(rr, req)
+ assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
+ assert.FileExists(t, "../assets/scss/base.scss")
+}
+
+func TestSaveSassHandler(t *testing.T) {
+ base := source.OpenAsset(utils.Directory, "scss/base.scss")
+ vars := source.OpenAsset(utils.Directory, "scss/variables.scss")
+
+ form := url.Values{}
+ form.Add("theme", base+"\n .test_design { color: $test-design; }")
+ form.Add("variables", vars+"\n $test-design: #ffffff; ")
+ req, err := http.NewRequest("POST", "/settings/css", strings.NewReader(form.Encode()))
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ assert.Nil(t, err)
+ rr := httptest.NewRecorder()
+ Router().ServeHTTP(rr, req)
+ assert.Equal(t, 200, rr.Code)
+ assert.True(t, IsRouteAuthenticated(req))
+
+ newBase := source.OpenAsset(utils.Directory, "css/base.css")
+ assert.Contains(t, newBase, ".test_design {")
+}
diff --git a/handlers/misc.go b/handlers/misc.go
deleted file mode 100644
index 03ec423c..00000000
--- a/handlers/misc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package handlers
-
-import (
- "net/http"
-)
-
-func Error404Handler(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusNotFound)
- ExecuteResponse(w, r, "error_404.html", nil)
-}
diff --git a/handlers/prometheus.go b/handlers/prometheus.go
index efdace07..c365934f 100644
--- a/handlers/prometheus.go
+++ b/handlers/prometheus.go
@@ -21,7 +21,7 @@ import (
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
utils.Log(1, fmt.Sprintf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr))
if !isAuthorized(r) {
- http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+ http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
metrics := []string{}
diff --git a/handlers/settings.go b/handlers/settings.go
index 0b8fffce..df85f02e 100644
--- a/handlers/settings.go
+++ b/handlers/settings.go
@@ -58,9 +58,9 @@ func SaveSASSHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
theme := r.PostForm.Get("theme")
variables := r.PostForm.Get("variables")
- source.SaveAsset(theme, ".", "scss/base.scss")
- source.SaveAsset(variables, ".", "scss/variables.scss")
- source.CompileSASS(".")
+ source.SaveAsset([]byte(theme), utils.Directory, "scss/base.scss")
+ source.SaveAsset([]byte(variables), utils.Directory, "scss/variables.scss")
+ source.CompileSASS(utils.Directory)
ExecuteResponse(w, r, "settings.html", core.CoreApp)
}
@@ -70,8 +70,12 @@ func SaveAssetsHandler(w http.ResponseWriter, r *http.Request) {
return
}
dir := utils.Directory
- source.CreateAllAssets(dir)
- err := source.CompileSASS(dir)
+ err := source.CreateAllAssets(dir)
+ if err != nil {
+ utils.Log(3, err)
+ return
+ }
+ err = source.CompileSASS(dir)
if err != nil {
source.CopyToPublic(source.CssBox, dir+"/assets/css", "base.css")
utils.Log(2, "Default 'base.css' was insert because SASS did not work.")
diff --git a/source/source.go b/source/source.go
index a14eda72..5a29bf4a 100644
--- a/source/source.go
+++ b/source/source.go
@@ -104,12 +104,14 @@ func HasAssets(folder string) bool {
return false
}
-func SaveAsset(data, folder, file string) {
+func SaveAsset(data []byte, folder, file string) error {
utils.Log(1, fmt.Sprintf("Saving %v/%v into assets folder", folder, file))
- err := ioutil.WriteFile(folder+"/assets/"+file, []byte(data), 0644)
+ err := ioutil.WriteFile(folder+"/assets/"+file, data, 0644)
if err != nil {
utils.Log(3, fmt.Sprintf("Failed to save %v/%v, %v", folder, file, err))
+ return err
}
+ return nil
}
func OpenAsset(folder, file string) string {
@@ -155,27 +157,32 @@ func DeleteAllAssets(folder string) error {
return err
}
-func CopyToPublic(box *rice.Box, folder, file string) {
+func CopyToPublic(box *rice.Box, folder, file string) error {
assetFolder := fmt.Sprintf("%v/%v", folder, file)
utils.Log(1, fmt.Sprintf("Copying %v to %v", file, assetFolder))
base, err := box.String(file)
if err != nil {
utils.Log(3, fmt.Sprintf("Failed to copy %v to %v, %v.", file, assetFolder, err))
+ return err
}
err = ioutil.WriteFile(assetFolder, []byte(base), 0644)
if err != nil {
utils.Log(3, fmt.Sprintf("Failed to write file %v to %v, %v.", file, assetFolder, err))
+ return err
}
+ return nil
}
-func MakePublicFolder(folder string) {
+func MakePublicFolder(folder string) error {
utils.Log(1, fmt.Sprintf("Creating folder '%v'", folder))
if _, err := os.Stat(folder); os.IsNotExist(err) {
err = os.MkdirAll(folder, 0755)
if err != nil {
utils.Log(3, fmt.Sprintf("Failed to created %v directory, %v", folder, err))
+ return err
}
}
+ return nil
}
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
@@ -199,7 +206,5 @@ func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
return out, err
}
}
- // never reached
- panic(true)
return nil, nil
}
diff --git a/source/source_test.go b/source/source_test.go
index 07c56d82..2434e343 100644
--- a/source/source_test.go
+++ b/source/source_test.go
@@ -30,14 +30,29 @@ func TestCreateAssets(t *testing.T) {
}
func TestCompileSASS(t *testing.T) {
- if os.Getenv("IS_DOCKER") == "true" {
- os.Setenv("SASS", "/usr/local/bin/sass")
- }
assert.Nil(t, CompileSASS(dir))
assert.True(t, HasAssets(dir))
}
+func TestSaveAsset(t *testing.T) {
+ data := []byte("BODY { color: black; }")
+ asset := SaveAsset(data, dir, "scss/theme.scss")
+ assert.Nil(t, asset)
+ assert.FileExists(t, dir+"/assets/scss/theme.scss")
+}
+
+func TestOpenAsset(t *testing.T) {
+ asset := OpenAsset(dir, "scss/theme.scss")
+ assert.NotEmpty(t, asset)
+}
+
func TestDeleteAssets(t *testing.T) {
assert.Nil(t, DeleteAllAssets(dir))
assert.False(t, HasAssets(dir))
}
+
+func TestCopyToPluginFailed(t *testing.T) {
+
+ assert.Nil(t, DeleteAllAssets(dir))
+ assert.False(t, HasAssets(dir))
+}
diff --git a/utils/log.go b/utils/log.go
index 2c81a163..d3aa2c74 100644
--- a/utils/log.go
+++ b/utils/log.go
@@ -17,23 +17,29 @@ var (
LastLine interface{}
)
-func InitLogs() error {
+func createLog(dir string) error {
var err error
- _, err = os.Stat(Directory + "/logs")
+ _, err = os.Stat(dir + "/logs")
if err != nil {
if os.IsNotExist(err) {
- os.Mkdir(Directory+"/logs", 0777)
+ os.Mkdir(dir+"/logs", 0777)
} else {
- fmt.Println(err)
+ return err
}
}
-
- file, err := os.Create(Directory + "/logs/statup.log")
+ file, err := os.Create(dir + "/logs/statup.log")
if err != nil {
return err
}
defer file.Close()
+ return err
+}
+func InitLogs() error {
+ err := createLog(Directory)
+ if err != nil {
+ return err
+ }
logFile, err = os.OpenFile(Directory+"/logs/statup.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
if err != nil {
log.Printf("ERROR opening file: %v", err)
diff --git a/utils/utils.go b/utils/utils.go
index c1f65a83..8db42553 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -72,20 +72,14 @@ func UnderScoreString(str string) string {
return newStr
}
-func DeleteFile(file string) bool {
+func DeleteFile(file string) error {
err := os.Remove(file)
if err != nil {
- Log(3, err)
- return false
+ return err
}
- return true
+ return nil
}
-func DeleteDirectory(directory string) bool {
- err := os.RemoveAll(directory)
- if err != nil {
- Log(3, err)
- return false
- }
- return true
+func DeleteDirectory(directory string) error {
+ return os.RemoveAll(directory)
}
diff --git a/utils/utils_test.go b/utils/utils_test.go
index 43e5b7af..3d655591 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -8,8 +8,14 @@ import (
"time"
)
+func TestCreateLog(t *testing.T) {
+ err := createLog(Directory)
+ assert.Nil(t, err)
+}
+
func TestInitLogs(t *testing.T) {
assert.Nil(t, InitLogs())
+ assert.FileExists(t, "../logs/statup.log")
}
func TestDir(t *testing.T) {
@@ -25,12 +31,12 @@ func TestLog(t *testing.T) {
assert.Nil(t, Log(5, errors.New("this is a 5 level error")))
}
-func TestLogFileCreation(t *testing.T) {
- assert.FileExists(t, "../logs/statup.log")
+func TestDeleteFile(t *testing.T) {
+ assert.Nil(t, DeleteFile(Directory+"/logs/statup.log"))
}
-func TestDeleteFile(t *testing.T) {
- assert.True(t, DeleteFile(Directory+"/logs/statup.log"))
+func TestFailedDeleteFile(t *testing.T) {
+ assert.Error(t, DeleteFile(Directory+"/missingfilehere.txt"))
}
func TestLogHTTP(t *testing.T) {
@@ -73,5 +79,5 @@ func TestSha256(t *testing.T) {
}
func TestDeleteDirectory(t *testing.T) {
- assert.True(t, DeleteDirectory(Directory+"/logs"))
+ assert.Nil(t, DeleteDirectory(Directory+"/logs"))
}