mirror of https://github.com/statping/statping
new 2
parent
4ff0ee5a0d
commit
ae92209808
|
@ -18,7 +18,7 @@ services:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- VERSION=0.27.4
|
- VERSION=0.27.6
|
||||||
- DB_HOST=localhost
|
- DB_HOST=localhost
|
||||||
- DB_USER=travis
|
- DB_USER=travis
|
||||||
- DB_PASS=
|
- DB_PASS=
|
||||||
|
@ -51,8 +51,6 @@ deploy:
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
||||||
before_install:
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||||
- psql -c 'create database test;' -U postgres
|
- psql -c 'create database test;' -U postgres
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
# RENDERING CSS
|
# RENDERING CSS
|
||||||
gem install sass
|
gem install sass
|
||||||
sass html/scss/base.scss html/css/base.css
|
sass source/scss/base.scss source/css/base.css
|
||||||
|
|
||||||
# MIGRATION SQL FILE FOR CURRENT VERSION
|
# MIGRATION SQL FILE FOR CURRENT VERSION
|
||||||
printf "UPDATE core SET version='$VERSION';\n" >> sql/upgrade.sql
|
printf "UPDATE core SET version='$VERSION';\n" >> source/sql/upgrade.sql
|
||||||
|
|
||||||
# COMPILE SRC INTO BIN
|
# COMPILE SRC INTO BIN
|
||||||
rice embed-go
|
rice embed-go
|
||||||
|
|
|
@ -20,7 +20,6 @@ curl -s -X POST \
|
||||||
-d "$body" \
|
-d "$body" \
|
||||||
https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
||||||
|
|
||||||
|
|
||||||
if [ "$TRAVIS_BRANCH" == "master" ]
|
if [ "$TRAVIS_BRANCH" == "master" ]
|
||||||
then
|
then
|
||||||
curl -X POST $DOCKER > /dev/null
|
curl -X POST $DOCKER > /dev/null
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
ENV VERSION=v0.27.4
|
ENV VERSION=v0.27.6
|
||||||
|
|
||||||
RUN apk --no-cache add libstdc++ ca-certificates
|
RUN apk --no-cache add libstdc++ ca-certificates
|
||||||
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
||||||
|
|
92
assets.go
92
assets.go
|
@ -1,92 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/GeertJohan/go.rice"
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
useAssets bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func CopyToPublic(box *rice.Box, folder, file string) {
|
|
||||||
base, err := box.String(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
ioutil.WriteFile("assets/"+folder+"/"+file, []byte(base), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakePublicFolder(folder string) {
|
|
||||||
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(folder, 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CompileSASS() {
|
|
||||||
sassBin := os.Getenv("SASS")
|
|
||||||
shell := os.Getenv("CMD_FILE")
|
|
||||||
log.Send(1, fmt.Sprintf("Compiling SASS into /css/base.css..."))
|
|
||||||
command := fmt.Sprintf("%v %v %v", sassBin, "assets/scss/base.scss", "assets/css/base.css")
|
|
||||||
testCmd := exec.Command(shell, command)
|
|
||||||
_, err := testCmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
log.Send(1, "SASS Compiling is complete!")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasAssets() bool {
|
|
||||||
if _, err := os.Stat("assets"); err == nil {
|
|
||||||
useAssets = true
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
assetEnv := os.Getenv("USE_ASSETS")
|
|
||||||
if assetEnv == "true" {
|
|
||||||
CreateAllAssets()
|
|
||||||
useAssets = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveAsset(data, file string) {
|
|
||||||
ioutil.WriteFile("assets/"+file, []byte(data), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OpenAsset(file string) string {
|
|
||||||
dat, err := ioutil.ReadFile("assets/" + file)
|
|
||||||
log.Send(2, err)
|
|
||||||
return string(dat)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateAllAssets() {
|
|
||||||
log.Send(1, "Creating folder 'assets' in current directory..")
|
|
||||||
MakePublicFolder("assets")
|
|
||||||
MakePublicFolder("assets/js")
|
|
||||||
MakePublicFolder("assets/css")
|
|
||||||
MakePublicFolder("assets/scss")
|
|
||||||
MakePublicFolder("assets/emails")
|
|
||||||
log.Send(1, "Inserting scss, css, emails, and javascript files into assets..")
|
|
||||||
CopyToPublic(scssBox, "scss", "base.scss")
|
|
||||||
CopyToPublic(scssBox, "scss", "variables.scss")
|
|
||||||
CopyToPublic(emailBox, "emails", "error.html")
|
|
||||||
CopyToPublic(emailBox, "emails", "failure.html")
|
|
||||||
CopyToPublic(cssBox, "css", "bootstrap.min.css")
|
|
||||||
CopyToPublic(jsBox, "js", "bootstrap.min.js")
|
|
||||||
CopyToPublic(jsBox, "js", "Chart.bundle.min.js")
|
|
||||||
CopyToPublic(jsBox, "js", "jquery-3.3.1.slim.min.js")
|
|
||||||
CopyToPublic(jsBox, "js", "main.js")
|
|
||||||
CopyToPublic(jsBox, "js", "setup.js")
|
|
||||||
log.Send(1, "Compiling CSS from SCSS style...")
|
|
||||||
CompileSASS()
|
|
||||||
log.Send(1, "Statup assets have been inserted")
|
|
||||||
}
|
|
76
cli.go
76
cli.go
|
@ -2,9 +2,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CatchCLI(args []string) {
|
func CatchCLI(args []string) {
|
||||||
|
@ -13,65 +13,67 @@ func CatchCLI(args []string) {
|
||||||
fmt.Printf("Statup v%v\n", VERSION)
|
fmt.Printf("Statup v%v\n", VERSION)
|
||||||
case "assets":
|
case "assets":
|
||||||
RenderBoxes()
|
RenderBoxes()
|
||||||
CreateAllAssets()
|
core.CreateAllAssets()
|
||||||
case "sass":
|
case "sass":
|
||||||
CompileSASS()
|
core.CompileSASS()
|
||||||
case "export":
|
case "export":
|
||||||
var err error
|
var err error
|
||||||
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION)
|
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||||
RenderBoxes()
|
RenderBoxes()
|
||||||
configs, err = LoadConfig()
|
core.Configs, err = core.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(3, "config.yml file not found")
|
utils.Log(4, "config.yml file not found")
|
||||||
}
|
}
|
||||||
setupMode = true
|
RunOnce()
|
||||||
mainProcess()
|
indexSource := core.ExportIndexHTML()
|
||||||
time.Sleep(10 * time.Second)
|
err = core.SaveFile("./index.html", []byte(indexSource))
|
||||||
indexSource := ExportIndexHTML()
|
|
||||||
err = SaveFile("./index.html", []byte(indexSource))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(4, err)
|
||||||
}
|
}
|
||||||
log.Send(1, "Exported Statup index page: 'index.html'")
|
utils.Log(1, "Exported Statup index page: 'index.html'")
|
||||||
case "help":
|
case "help":
|
||||||
HelpEcho()
|
HelpEcho()
|
||||||
case "update":
|
case "update":
|
||||||
fmt.Println("Sorry updating isn't available yet!")
|
fmt.Println("Sorry updating isn't available yet!")
|
||||||
case "run":
|
case "run":
|
||||||
log.Send(1, "Running 1 time and saving to database...")
|
utils.Log(1, "Running 1 time and saving to database...")
|
||||||
var err error
|
RunOnce()
|
||||||
configs, err = LoadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, "config.yml file not found")
|
|
||||||
}
|
|
||||||
err = DbConnection(configs.Connection)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
}
|
|
||||||
core, err = SelectCore()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Core database was not found, Statup is not setup yet.")
|
|
||||||
}
|
|
||||||
services, err = SelectAllServices()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
}
|
|
||||||
for _, s := range services {
|
|
||||||
out := s.Check()
|
|
||||||
fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online)
|
|
||||||
}
|
|
||||||
fmt.Println("Check is complete.")
|
fmt.Println("Check is complete.")
|
||||||
case "env":
|
case "env":
|
||||||
fmt.Println("Statup Environment Variables")
|
fmt.Println("Statup Environment Variables")
|
||||||
envs, err := godotenv.Read(".env")
|
envs, err := godotenv.Read(".env")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(3, "No .env file found in current directory.")
|
utils.Log(4, "No .env file found in current directory.")
|
||||||
}
|
}
|
||||||
for k, e := range envs {
|
for k, e := range envs {
|
||||||
fmt.Printf("%v=%v\n", k, e)
|
fmt.Printf("%v=%v\n", k, e)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Send(3, "Statup does not have the command you entered.")
|
utils.Log(3, "Statup does not have the command you entered.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunOnce() {
|
||||||
|
var err error
|
||||||
|
core.Configs, err = core.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, "config.yml file not found")
|
||||||
|
}
|
||||||
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, err)
|
||||||
|
}
|
||||||
|
core.CoreApp, err = core.SelectCore()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Core database was not found, Statup is not setup yet.")
|
||||||
|
}
|
||||||
|
core.CoreApp.Services, err = core.SelectAllServices()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, err)
|
||||||
|
}
|
||||||
|
for _, s := range core.CoreApp.Services {
|
||||||
|
out := s.Check()
|
||||||
|
fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"github.com/hunterlong/statup/types"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LoadDefaultCommunications() {
|
|
||||||
emailer := SelectCommunication(1)
|
|
||||||
if emailer.Enabled {
|
|
||||||
LoadMailer(emailer)
|
|
||||||
go EmailerQueue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadComms() {
|
|
||||||
for _, c := range core.Communications {
|
|
||||||
if c.Enabled {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Run(c *types.Communication) {
|
|
||||||
|
|
||||||
sample := &types.Email{
|
|
||||||
To: "info@socialeck.com",
|
|
||||||
Subject: "Test Email from Statup",
|
|
||||||
}
|
|
||||||
|
|
||||||
AddEmail(sample)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectAllCommunications() ([]*types.Communication, error) {
|
|
||||||
var c []*types.Communication
|
|
||||||
col := dbSession.Collection("communication").Find()
|
|
||||||
err := col.All(&c)
|
|
||||||
core.Communications = c
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func Create(c *types.Communication) (int64, error) {
|
|
||||||
c.CreatedAt = time.Now()
|
|
||||||
uuid, err := dbSession.Collection("communication").Insert(c)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
}
|
|
||||||
if uuid == nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
c.Id = uuid.(int64)
|
|
||||||
if core != nil {
|
|
||||||
core.Communications = append(core.Communications, c)
|
|
||||||
}
|
|
||||||
return uuid.(int64), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func Disable(c *types.Communication) {
|
|
||||||
c.Enabled = false
|
|
||||||
Update(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Enable(c *types.Communication) {
|
|
||||||
c.Enabled = true
|
|
||||||
Update(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Update(c *types.Communication) *types.Communication {
|
|
||||||
col := dbSession.Collection("communication").Find("id", c.Id)
|
|
||||||
col.Update(c)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectCommunication(id int64) *types.Communication {
|
|
||||||
for _, c := range core.Communications {
|
|
||||||
if c.Id == id {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
68
core.go
68
core.go
|
@ -1,68 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hunterlong/statup/plugin"
|
|
||||||
"github.com/hunterlong/statup/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Core struct {
|
|
||||||
Name string `db:"name"`
|
|
||||||
Description string `db:"description"`
|
|
||||||
Config string `db:"config"`
|
|
||||||
ApiKey string `db:"api_key"`
|
|
||||||
ApiSecret string `db:"api_secret"`
|
|
||||||
Style string `db:"style"`
|
|
||||||
Footer string `db:"footer"`
|
|
||||||
Domain string `db:"domain"`
|
|
||||||
Version string `db:"version"`
|
|
||||||
Plugins []plugin.Info
|
|
||||||
Repos []PluginJSON
|
|
||||||
PluginFields []PluginSelect
|
|
||||||
Communications []*types.Communication
|
|
||||||
OfflineAssets bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) Update() (*Core, error) {
|
|
||||||
res := dbSession.Collection("core").Find().Limit(1)
|
|
||||||
res.Update(c)
|
|
||||||
core = c
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Core) UsingAssets() bool {
|
|
||||||
return useAssets
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Core) SassVars() string {
|
|
||||||
if !useAssets {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return OpenAsset("scss/variables.scss")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Core) BaseSASS() string {
|
|
||||||
if !useAssets {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return OpenAsset("scss/base.scss")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Core) AllOnline() bool {
|
|
||||||
for _, s := range services {
|
|
||||||
if !s.Online {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectCore() (*Core, error) {
|
|
||||||
var c *Core
|
|
||||||
err := dbSession.Collection("core").Find().One(&c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
core = c
|
|
||||||
//store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
|
||||||
return core, err
|
|
||||||
}
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/GeertJohan/go.rice"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CopyToPublic(box *rice.Box, folder, file string) {
|
||||||
|
utils.Log(1, fmt.Sprintf("Copying %v to %v...", file, folder))
|
||||||
|
base, err := box.String(file)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to copy %v to %v, %v.", file, folder, err))
|
||||||
|
}
|
||||||
|
ioutil.WriteFile("assets/"+folder+"/"+file, []byte(base), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakePublicFolder(folder string) {
|
||||||
|
utils.Log(1, fmt.Sprintf("Creating folder '%v' in current directory...", 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileSASS() error {
|
||||||
|
sassBin := os.Getenv("SASS")
|
||||||
|
shell := os.Getenv("CMD_FILE")
|
||||||
|
utils.Log(1, fmt.Sprintf("Compiling SASS into /css/base.css..."))
|
||||||
|
command := fmt.Sprintf("%v %v %v", sassBin, "assets/scss/base.scss", "assets/css/base.css")
|
||||||
|
testCmd := exec.Command(shell, command)
|
||||||
|
_, err := testCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
||||||
|
utils.Log(3, fmt.Sprintf("SASS: %v | CMD_FILE:%v", sassBin, shell))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
utils.Log(1, "SASS Compiling is complete!")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasAssets() bool {
|
||||||
|
if _, err := os.Stat("assets"); err == nil {
|
||||||
|
utils.Log(1, "Assets folder was found!")
|
||||||
|
UsingAssets = true
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
assetEnv := os.Getenv("USE_ASSETS")
|
||||||
|
if assetEnv == "true" {
|
||||||
|
utils.Log(1, "Environment variable USE_ASSETS was found.")
|
||||||
|
CreateAllAssets()
|
||||||
|
UsingAssets = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveAsset(data, file string) {
|
||||||
|
utils.Log(1, fmt.Sprintf("Saving %v into assets folder", file))
|
||||||
|
err := ioutil.WriteFile("assets/"+file, []byte(data), 0644)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to save %v, %v", file, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenAsset(file string) string {
|
||||||
|
dat, err := ioutil.ReadFile("assets/" + file)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to open %v, %v", file, err))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(dat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAllAssets() {
|
||||||
|
utils.Log(1, "Dump Statup assets into current directory...")
|
||||||
|
MakePublicFolder("assets")
|
||||||
|
MakePublicFolder("assets/js")
|
||||||
|
MakePublicFolder("assets/css")
|
||||||
|
MakePublicFolder("assets/scss")
|
||||||
|
MakePublicFolder("assets/emails")
|
||||||
|
utils.Log(1, "Inserting scss, css, emails, and javascript files into assets..")
|
||||||
|
CopyToPublic(ScssBox, "scss", "base.scss")
|
||||||
|
CopyToPublic(ScssBox, "scss", "variables.scss")
|
||||||
|
CopyToPublic(EmailBox, "emails", "error.html")
|
||||||
|
CopyToPublic(EmailBox, "emails", "failure.html")
|
||||||
|
CopyToPublic(CssBox, "css", "bootstrap.min.css")
|
||||||
|
CopyToPublic(JsBox, "js", "bootstrap.min.js")
|
||||||
|
CopyToPublic(JsBox, "js", "Chart.bundle.min.js")
|
||||||
|
CopyToPublic(JsBox, "js", "jquery-3.3.1.slim.min.js")
|
||||||
|
CopyToPublic(JsBox, "js", "main.js")
|
||||||
|
CopyToPublic(JsBox, "js", "setup.js")
|
||||||
|
utils.Log(1, "Compiling CSS from SCSS style...")
|
||||||
|
err := CompileSASS()
|
||||||
|
if err == nil {
|
||||||
|
utils.Log(1, "Statup assets have been inserted")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,21 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FailureData types.FailureData
|
||||||
|
|
||||||
func CheckServices() {
|
func CheckServices() {
|
||||||
services, _ = SelectAllServices()
|
CoreApp.Services, _ = SelectAllServices()
|
||||||
log.Send(1, fmt.Sprintf("Loaded %v Services", len(services)))
|
utils.Log(1, fmt.Sprintf("Loaded %v Services", len(CoreApp.Services)))
|
||||||
for _, v := range services {
|
for _, v := range CoreApp.Services {
|
||||||
obj := v
|
obj := v
|
||||||
go obj.StartCheckins()
|
go obj.StartCheckins()
|
||||||
go obj.CheckQueue()
|
go obj.CheckQueue()
|
||||||
|
@ -26,7 +29,7 @@ func (s *Service) CheckQueue() {
|
||||||
s.Interval = 1
|
s.Interval = 1
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf("Service: %v | Online: %v | Latency: %0.0fms", s.Name, s.Online, (s.Latency * 1000))
|
msg := fmt.Sprintf("Service: %v | Online: %v | Latency: %0.0fms", s.Name, s.Online, (s.Latency * 1000))
|
||||||
log.Send(0, msg)
|
utils.Log(1, msg)
|
||||||
time.Sleep(time.Duration(s.Interval) * time.Second)
|
time.Sleep(time.Duration(s.Interval) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,17 +83,13 @@ func (s *Service) Record(response *http.Response) {
|
||||||
OnSuccess(s)
|
OnSuccess(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FailureData struct {
|
|
||||||
Issue string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Failure(issue string) {
|
func (s *Service) Failure(issue string) {
|
||||||
s.Online = false
|
s.Online = false
|
||||||
data := FailureData{
|
data := FailureData{
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
}
|
}
|
||||||
log.Send(1, fmt.Sprintf("Service %v Failing: %v", s.Name, issue))
|
utils.Log(2, fmt.Sprintf("Service %v Failing: %v", s.Name, issue))
|
||||||
s.CreateFailure(data)
|
s.CreateFailure(data)
|
||||||
SendFailureEmail(s)
|
//SendFailureEmail(s)
|
||||||
OnFailure(s)
|
OnFailure(s)
|
||||||
}
|
}
|
|
@ -1,25 +1,33 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ararog/timeago"
|
"github.com/ararog/timeago"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Checkin struct {
|
type Checkin types.Checkin
|
||||||
Id int `db:"id,omitempty"`
|
|
||||||
Service int64 `db:"service"`
|
func (c *Checkin) String() string {
|
||||||
Interval int64 `db:"check_interval"`
|
return c.Api
|
||||||
Api string `db:"api"`
|
}
|
||||||
CreatedAt time.Time `db:"created_at"`
|
|
||||||
Hits int64 `json:"hits"`
|
func FindCheckin(api string) *Checkin {
|
||||||
Last time.Time `json:"last"`
|
for _, s := range CoreApp.Services {
|
||||||
|
for _, c := range s.Checkins {
|
||||||
|
if c.Api == api {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) SelectAllCheckins() []*Checkin {
|
func (s *Service) SelectAllCheckins() []*Checkin {
|
||||||
var checkins []*Checkin
|
var checkins []*Checkin
|
||||||
col := dbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id")
|
col := DbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id")
|
||||||
col.All(&checkins)
|
col.All(&checkins)
|
||||||
s.Checkins = checkins
|
s.Checkins = checkins
|
||||||
return checkins
|
return checkins
|
||||||
|
@ -27,9 +35,9 @@ func (s *Service) SelectAllCheckins() []*Checkin {
|
||||||
|
|
||||||
func (u *Checkin) Create() (int64, error) {
|
func (u *Checkin) Create() (int64, error) {
|
||||||
u.CreatedAt = time.Now()
|
u.CreatedAt = time.Now()
|
||||||
uuid, err := dbSession.Collection("checkins").Insert(u)
|
uuid, err := DbSession.Collection("checkins").Insert(u)
|
||||||
if uuid == nil {
|
if uuid == nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
fmt.Println("new checkin: ", uuid)
|
fmt.Println("new checkin: ", uuid)
|
||||||
|
@ -38,7 +46,7 @@ func (u *Checkin) Create() (int64, error) {
|
||||||
|
|
||||||
func SelectCheckinApi(api string) *Checkin {
|
func SelectCheckinApi(api string) *Checkin {
|
||||||
var checkin *Checkin
|
var checkin *Checkin
|
||||||
dbSession.Collection("checkins").Find("api", api).One(&checkin)
|
DbSession.Collection("checkins").Find("api", api).One(&checkin)
|
||||||
return checkin
|
return checkin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,17 +78,6 @@ func (f *Checkin) Ago() string {
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindCheckin(api string) *Checkin {
|
|
||||||
for _, s := range services {
|
|
||||||
for _, c := range s.Checkins {
|
|
||||||
if c.Api == api {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checkin) Run() {
|
func (c *Checkin) Run() {
|
||||||
if c.Interval == 0 {
|
if c.Interval == 0 {
|
||||||
return
|
return
|
||||||
|
@ -104,7 +101,7 @@ func (s *Service) StartCheckins() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckinProcess() {
|
func CheckinProcess() {
|
||||||
for _, s := range services {
|
for _, s := range CoreApp.Services {
|
||||||
for _, c := range s.Checkins {
|
for _, c := range s.Checkins {
|
||||||
checkin := c
|
checkin := c
|
||||||
go checkin.Run()
|
go checkin.Run()
|
|
@ -0,0 +1,85 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Communication types.Communication
|
||||||
|
|
||||||
|
func LoadDefaultCommunications() {
|
||||||
|
emailer := SelectCommunication(1)
|
||||||
|
if emailer.Enabled {
|
||||||
|
//LoadMailer(emailer)
|
||||||
|
//go EmailerQueue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadComms() {
|
||||||
|
for _, c := range CoreApp.Communications {
|
||||||
|
if c.Enabled {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(c *Communication) {
|
||||||
|
|
||||||
|
//sample := &Email{
|
||||||
|
// To: "info@socialeck.com",
|
||||||
|
// Subject: "Test Email from Statup",
|
||||||
|
//}
|
||||||
|
|
||||||
|
//AddEmail(sample)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectAllCommunications() ([]*Communication, error) {
|
||||||
|
var c []*Communication
|
||||||
|
col := DbSession.Collection("communication").Find()
|
||||||
|
err := col.All(&c)
|
||||||
|
CoreApp.Communications = c
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(c *Communication) (int64, error) {
|
||||||
|
c.CreatedAt = time.Now()
|
||||||
|
uuid, err := DbSession.Collection("communication").Insert(c)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, err)
|
||||||
|
}
|
||||||
|
if uuid == nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
c.Id = uuid.(int64)
|
||||||
|
if CoreApp != nil {
|
||||||
|
CoreApp.Communications = append(CoreApp.Communications, c)
|
||||||
|
}
|
||||||
|
return uuid.(int64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Disable(c *Communication) {
|
||||||
|
c.Enabled = false
|
||||||
|
Update(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Enable(c *Communication) {
|
||||||
|
c.Enabled = true
|
||||||
|
Update(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Update(c *Communication) *Communication {
|
||||||
|
col := DbSession.Collection("communication").Find("id", c.Id)
|
||||||
|
col.Update(c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectCommunication(id int64) *Communication {
|
||||||
|
for _, c := range CoreApp.Communications {
|
||||||
|
if c.Id == id {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config types.Config
|
||||||
|
|
||||||
|
func LoadConfig() (*Config, error) {
|
||||||
|
var config Config
|
||||||
|
file, err := ioutil.ReadFile("config.yml")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(file, &config)
|
||||||
|
Configs = &config
|
||||||
|
return &config, err
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GeertJohan/go.rice"
|
||||||
|
"github.com/hunterlong/statup/plugin"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginJSON types.PluginJSON
|
||||||
|
type PluginRepos types.PluginRepos
|
||||||
|
|
||||||
|
type Core struct {
|
||||||
|
Name string `db:"name"`
|
||||||
|
Description string `db:"description"`
|
||||||
|
Config string `db:"config"`
|
||||||
|
ApiKey string `db:"api_key"`
|
||||||
|
ApiSecret string `db:"api_secret"`
|
||||||
|
Style string `db:"style"`
|
||||||
|
Footer string `db:"footer"`
|
||||||
|
Domain string `db:"domain"`
|
||||||
|
Version string `db:"version"`
|
||||||
|
Services []*Service
|
||||||
|
Plugins []plugin.Info
|
||||||
|
Repos []PluginJSON
|
||||||
|
//PluginFields []PluginSelect
|
||||||
|
Communications []*Communication
|
||||||
|
OfflineAssets bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
Configs *Config
|
||||||
|
CoreApp *Core
|
||||||
|
SqlBox *rice.Box
|
||||||
|
CssBox *rice.Box
|
||||||
|
ScssBox *rice.Box
|
||||||
|
JsBox *rice.Box
|
||||||
|
TmplBox *rice.Box
|
||||||
|
EmailBox *rice.Box
|
||||||
|
SetupMode bool
|
||||||
|
AllPlugins []plugin.PluginActions
|
||||||
|
UsingAssets bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
CoreApp = new(Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) Update() (*Core, error) {
|
||||||
|
res := DbSession.Collection("core").Find().Limit(1)
|
||||||
|
res.Update(c)
|
||||||
|
CoreApp = c
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Core) UsingAssets() bool {
|
||||||
|
return UsingAssets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Core) SassVars() string {
|
||||||
|
if !UsingAssets {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return OpenAsset("scss/variables.scss")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Core) BaseSASS() string {
|
||||||
|
if !UsingAssets {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return OpenAsset("scss/base.scss")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Core) AllOnline() bool {
|
||||||
|
for _, s := range CoreApp.Services {
|
||||||
|
if !s.Online {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectCore() (*Core, error) {
|
||||||
|
var c *Core
|
||||||
|
err := DbSession.Collection("core").Find().One(&c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
CoreApp = c
|
||||||
|
//store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
||||||
|
return CoreApp, err
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"upper.io/db.v3"
|
||||||
|
"upper.io/db.v3/lib/sqlbuilder"
|
||||||
|
"upper.io/db.v3/mysql"
|
||||||
|
"upper.io/db.v3/postgresql"
|
||||||
|
"upper.io/db.v3/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbServer string
|
||||||
|
sqliteSettings sqlite.ConnectionURL
|
||||||
|
postgresSettings postgresql.ConnectionURL
|
||||||
|
mysqlSettings mysql.ConnectionURL
|
||||||
|
DbSession sqlbuilder.Database
|
||||||
|
)
|
||||||
|
|
||||||
|
type DbConfig types.DbConfig
|
||||||
|
|
||||||
|
func DbConnection(dbType string) error {
|
||||||
|
var err error
|
||||||
|
if dbType == "sqlite" {
|
||||||
|
sqliteSettings = sqlite.ConnectionURL{
|
||||||
|
Database: "statup.db",
|
||||||
|
}
|
||||||
|
DbSession, err = sqlite.Open(sqliteSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if dbType == "mysql" {
|
||||||
|
if Configs.Port == "" {
|
||||||
|
Configs.Port = "3306"
|
||||||
|
}
|
||||||
|
mysqlSettings = mysql.ConnectionURL{
|
||||||
|
Database: Configs.Database,
|
||||||
|
Host: Configs.Host,
|
||||||
|
User: Configs.User,
|
||||||
|
Password: Configs.Password,
|
||||||
|
}
|
||||||
|
DbSession, err = mysql.Open(mysqlSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if Configs.Port == "" {
|
||||||
|
Configs.Port = "5432"
|
||||||
|
}
|
||||||
|
host := fmt.Sprintf("%v:%v", Configs.Host, Configs.Port)
|
||||||
|
postgresSettings = postgresql.ConnectionURL{
|
||||||
|
Database: Configs.Database,
|
||||||
|
Host: host,
|
||||||
|
User: Configs.User,
|
||||||
|
Password: Configs.Password,
|
||||||
|
}
|
||||||
|
DbSession, err = postgresql.Open(postgresSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//dbSession.SetLogging(true)
|
||||||
|
dbServer = dbType
|
||||||
|
OnLoad(DbSession)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DatabaseMaintence() {
|
||||||
|
defer DatabaseMaintence()
|
||||||
|
utils.Log(1, "Checking for database records older than 7 days...")
|
||||||
|
since := time.Now().AddDate(0, 0, -7)
|
||||||
|
DeleteAllSince("failures", since)
|
||||||
|
DeleteAllSince("hits", since)
|
||||||
|
time.Sleep(60 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteAllSince(table string, date time.Time) {
|
||||||
|
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
|
||||||
|
_, err := DbSession.Exec(db.Raw(sql))
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DbConfig) Save() error {
|
||||||
|
var err error
|
||||||
|
config, err := os.Create("config.yml")
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, err := yaml.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.WriteString(string(data))
|
||||||
|
config.Close()
|
||||||
|
|
||||||
|
Configs, err = LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = DbConnection(Configs.Connection)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
DropDatabase()
|
||||||
|
CreateDatabase()
|
||||||
|
|
||||||
|
newCore := Core{
|
||||||
|
Name: c.Project,
|
||||||
|
Description: c.Description,
|
||||||
|
Config: "config.yml",
|
||||||
|
ApiKey: utils.NewSHA1Hash(5),
|
||||||
|
ApiSecret: utils.NewSHA1Hash(10),
|
||||||
|
Domain: c.Domain,
|
||||||
|
}
|
||||||
|
|
||||||
|
col := DbSession.Collection("core")
|
||||||
|
_, err = col.Insert(newCore)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunDatabaseUpgrades() {
|
||||||
|
utils.Log(1, "Running Database Upgrade from 'upgrade.sql'...")
|
||||||
|
upgrade, _ := SqlBox.String("upgrade.sql")
|
||||||
|
requests := strings.Split(upgrade, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := DbSession.Exec(db.Raw(request + ";"))
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.Log(1, "Database Upgraded")
|
||||||
|
}
|
||||||
|
|
||||||
|
func DropDatabase() {
|
||||||
|
fmt.Println("Dropping Tables...")
|
||||||
|
down, _ := SqlBox.String("down.sql")
|
||||||
|
requests := strings.Split(down, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := DbSession.Exec(request)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateDatabase() {
|
||||||
|
fmt.Println("Creating Tables...")
|
||||||
|
sql := "postgres_up.sql"
|
||||||
|
if dbServer == "mysql" {
|
||||||
|
sql = "mysql_up.sql"
|
||||||
|
} else if dbServer == "sqlite3" {
|
||||||
|
sql = "sqlite_up.sql"
|
||||||
|
}
|
||||||
|
up, _ := SqlBox.String(sql)
|
||||||
|
requests := strings.Split(up, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := DbSession.Exec(request)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//secret := NewSHA1Hash()
|
||||||
|
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan()
|
||||||
|
fmt.Println("Database Created")
|
||||||
|
//SampleData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DbConfig) Clean() *DbConfig {
|
||||||
|
if os.Getenv("DB_PORT") != "" {
|
||||||
|
if c.DbConn == "postgres" {
|
||||||
|
c.DbHost = c.DbHost + ":" + os.Getenv("DB_PORT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fatih/structs"
|
"github.com/fatih/structs"
|
||||||
|
@ -7,55 +7,55 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func OnLoad(db sqlbuilder.Database) {
|
func OnLoad(db sqlbuilder.Database) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnLoad(db)
|
p.OnLoad(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnSuccess(s *Service) {
|
func OnSuccess(s *Service) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnSuccess(structs.Map(s))
|
p.OnSuccess(structs.Map(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnFailure(s *Service) {
|
func OnFailure(s *Service) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnFailure(structs.Map(s))
|
p.OnFailure(structs.Map(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnSettingsSaved(c *Core) {
|
func OnSettingsSaved(c *Core) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnSettingsSaved(structs.Map(c))
|
p.OnSettingsSaved(structs.Map(c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnNewUser(u *User) {
|
func OnNewUser(u *User) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnNewUser(structs.Map(u))
|
p.OnNewUser(structs.Map(u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnNewService(s *Service) {
|
func OnNewService(s *Service) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnNewService(structs.Map(s))
|
p.OnNewService(structs.Map(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnDeletedService(s *Service) {
|
func OnDeletedService(s *Service) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnDeletedService(structs.Map(s))
|
p.OnDeletedService(structs.Map(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnUpdateService(s *Service) {
|
func OnUpdateService(s *Service) {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
p.OnUpdatedService(structs.Map(s))
|
p.OnUpdatedService(structs.Map(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectPlugin(name string) plugin.PluginActions {
|
func SelectPlugin(name string) plugin.PluginActions {
|
||||||
for _, p := range allPlugins {
|
for _, p := range AllPlugins {
|
||||||
if p.GetInfo().Name == name {
|
if p.GetInfo().Name == name {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/utils"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExportIndexHTML() string {
|
func ExportIndexHTML() string {
|
||||||
core.OfflineAssets = true
|
CoreApp.OfflineAssets = true
|
||||||
out := index{*core, services}
|
//out := index{*CoreApp, CoreApp.Services}
|
||||||
nav, _ := tmplBox.String("nav.html")
|
nav, _ := TmplBox.String("nav.html")
|
||||||
footer, _ := tmplBox.String("footer.html")
|
footer, _ := TmplBox.String("footer.html")
|
||||||
render, err := tmplBox.String("index.html")
|
render, err := TmplBox.String("index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(3, err)
|
utils.Log(3, err)
|
||||||
}
|
}
|
||||||
t := template.New("message")
|
t := template.New("message")
|
||||||
t.Funcs(template.FuncMap{
|
t.Funcs(template.FuncMap{
|
||||||
|
@ -25,18 +25,18 @@ func ExportIndexHTML() string {
|
||||||
return template.HTML(html)
|
return template.HTML(html)
|
||||||
},
|
},
|
||||||
"VERSION": func() string {
|
"VERSION": func() string {
|
||||||
return VERSION
|
return "version here"
|
||||||
},
|
},
|
||||||
"underscore": func(html string) string {
|
"underscore": func(html string) string {
|
||||||
return UnderScoreString(html)
|
return utils.UnderScoreString(html)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
t, _ = t.Parse(nav)
|
t, _ = t.Parse(nav)
|
||||||
t, _ = t.Parse(footer)
|
t, _ = t.Parse(footer)
|
||||||
t.Parse(render)
|
t.Parse(render)
|
||||||
var tpl bytes.Buffer
|
var tpl bytes.Buffer
|
||||||
if err := t.Execute(&tpl, out); err != nil {
|
if err := t.Execute(&tpl, nil); err != nil {
|
||||||
log.Send(3, err)
|
utils.Log(3, err)
|
||||||
}
|
}
|
||||||
result := tpl.String()
|
result := tpl.String()
|
||||||
return result
|
return result
|
|
@ -1,21 +1,13 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ararog/timeago"
|
"github.com/ararog/timeago"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/utils"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Failure struct {
|
|
||||||
Id int `db:"id,omitempty"`
|
|
||||||
Issue string `db:"issue"`
|
|
||||||
Method string `db:"method"`
|
|
||||||
Service int64 `db:"service"`
|
|
||||||
CreatedAt time.Time `db:"created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
||||||
fail := &Failure{
|
fail := &Failure{
|
||||||
Issue: data.Issue,
|
Issue: data.Issue,
|
||||||
|
@ -23,7 +15,7 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
s.Failures = append(s.Failures, fail)
|
s.Failures = append(s.Failures, fail)
|
||||||
col := dbSession.Collection("failures")
|
col := DbSession.Collection("failures")
|
||||||
uuid, err := col.Insert(fail)
|
uuid, err := col.Insert(fail)
|
||||||
if uuid == nil {
|
if uuid == nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -33,14 +25,14 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
||||||
|
|
||||||
func (s *Service) SelectAllFailures() []*Failure {
|
func (s *Service) SelectAllFailures() []*Failure {
|
||||||
var fails []*Failure
|
var fails []*Failure
|
||||||
col := dbSession.Collection("failures").Find("service", s.Id).OrderBy("-id")
|
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id")
|
||||||
col.All(&fails)
|
col.All(&fails)
|
||||||
return fails
|
return fails
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) DeleteFailures() {
|
func (u *Service) DeleteFailures() {
|
||||||
var fails []*Failure
|
var fails []*Failure
|
||||||
col := dbSession.Collection("failures")
|
col := DbSession.Collection("failures")
|
||||||
col.Find("service", u.Id).All(&fails)
|
col.Find("service", u.Id).All(&fails)
|
||||||
for _, fail := range fails {
|
for _, fail := range fails {
|
||||||
fail.Delete()
|
fail.Delete()
|
||||||
|
@ -49,7 +41,7 @@ func (u *Service) DeleteFailures() {
|
||||||
|
|
||||||
func (s *Service) LimitedFailures() []*Failure {
|
func (s *Service) LimitedFailures() []*Failure {
|
||||||
var fails []*Failure
|
var fails []*Failure
|
||||||
col := dbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10)
|
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10)
|
||||||
col.All(&fails)
|
col.All(&fails)
|
||||||
return fails
|
return fails
|
||||||
}
|
}
|
||||||
|
@ -67,28 +59,28 @@ func (f *Failure) Ago() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Failure) Delete() error {
|
func (f *Failure) Delete() error {
|
||||||
col := dbSession.Collection("failures").Find("id", f.Id)
|
col := DbSession.Collection("failures").Find("id", f.Id)
|
||||||
return col.Delete()
|
return col.Delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CountFailures() uint64 {
|
func CountFailures() uint64 {
|
||||||
col := dbSession.Collection("failures").Find()
|
col := DbSession.Collection("failures").Find()
|
||||||
amount, err := col.Count()
|
amount, err := col.Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) TotalFailures() (uint64, error) {
|
func (s *Service) TotalFailures() (uint64, error) {
|
||||||
col := dbSession.Collection("failures").Find("service", s.Id)
|
col := DbSession.Collection("failures").Find("service", s.Id)
|
||||||
amount, err := col.Count()
|
amount, err := col.Count()
|
||||||
return amount, err
|
return amount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) TotalFailures24Hours() (uint64, error) {
|
func (s *Service) TotalFailures24Hours() (uint64, error) {
|
||||||
col := dbSession.Collection("failures").Find("service", s.Id)
|
col := DbSession.Collection("failures").Find("service", s.Id)
|
||||||
amount, err := col.Count()
|
amount, err := col.Count()
|
||||||
return amount, err
|
return amount, err
|
||||||
}
|
}
|
||||||
|
@ -110,5 +102,9 @@ func (f *Failure) ParseError() string {
|
||||||
if err {
|
if err {
|
||||||
return fmt.Sprintf("Incorrect HTTP Status Code")
|
return fmt.Sprintf("Incorrect HTTP Status Code")
|
||||||
}
|
}
|
||||||
|
err = strings.Contains(f.Issue, "connection refused")
|
||||||
|
if err {
|
||||||
|
return fmt.Sprintf("Connection Failed")
|
||||||
|
}
|
||||||
return f.Issue
|
return f.Issue
|
||||||
}
|
}
|
|
@ -1,20 +1,16 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"time"
|
"time"
|
||||||
"upper.io/db.v3"
|
"upper.io/db.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hit struct {
|
type Hit types.Hit
|
||||||
Id int `db:"id,omitempty"`
|
|
||||||
Service int64 `db:"service"`
|
|
||||||
Latency float64 `db:"latency"`
|
|
||||||
CreatedAt time.Time `db:"created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func hitCol() db.Collection {
|
func hitCol() db.Collection {
|
||||||
return dbSession.Collection("hits")
|
return DbSession.Collection("hits")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreateHit(d HitData) (int64, error) {
|
func (s *Service) CreateHit(d HitData) (int64, error) {
|
||||||
|
@ -25,7 +21,7 @@ func (s *Service) CreateHit(d HitData) (int64, error) {
|
||||||
}
|
}
|
||||||
uuid, err := hitCol().Insert(h)
|
uuid, err := hitCol().Insert(h)
|
||||||
if uuid == nil {
|
if uuid == nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return uuid.(int64), err
|
return uuid.(int64), err
|
||||||
|
@ -68,6 +64,9 @@ func (s *Service) TotalHits() (uint64, error) {
|
||||||
func (s *Service) Sum() (float64, error) {
|
func (s *Service) Sum() (float64, error) {
|
||||||
var amount float64
|
var amount float64
|
||||||
hits, err := s.Hits()
|
hits, err := s.Hits()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
for _, h := range hits {
|
for _, h := range hits {
|
||||||
amount += h.Latency
|
amount += h.Latency
|
||||||
}
|
}
|
|
@ -1,17 +1,16 @@
|
||||||
package main
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"upper.io/db.v3"
|
"upper.io/db.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Failure types.Failure
|
||||||
services []*Service
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Id int64 `db:"id,omitempty" json:"id"`
|
Id int64 `db:"id,omitempty" json:"id"`
|
||||||
|
@ -40,11 +39,11 @@ type Service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceCol() db.Collection {
|
func serviceCol() db.Collection {
|
||||||
return dbSession.Collection("services")
|
return DbSession.Collection("services")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectService(id int64) *Service {
|
func SelectService(id int64) *Service {
|
||||||
for _, s := range services {
|
for _, s := range CoreApp.Services {
|
||||||
if s.Id == id {
|
if s.Id == id {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -56,10 +55,14 @@ func SelectAllServices() ([]*Service, error) {
|
||||||
var srvcs []*Service
|
var srvcs []*Service
|
||||||
col := serviceCol().Find()
|
col := serviceCol().Find()
|
||||||
err := col.All(&srvcs)
|
err := col.All(&srvcs)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, err)
|
||||||
|
}
|
||||||
for _, s := range srvcs {
|
for _, s := range srvcs {
|
||||||
s.Checkins = s.SelectAllCheckins()
|
s.Checkins = s.SelectAllCheckins()
|
||||||
s.Failures = s.SelectAllFailures()
|
s.Failures = s.SelectAllFailures()
|
||||||
}
|
}
|
||||||
|
CoreApp.Services = srvcs
|
||||||
return srvcs, err
|
return srvcs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,9 +109,9 @@ func (s *Service) GraphData() string {
|
||||||
increment := "minute"
|
increment := "minute"
|
||||||
since := time.Now().Add(time.Hour*-12 + time.Minute*0 + time.Second*0)
|
since := time.Now().Add(time.Hour*-12 + time.Minute*0 + time.Second*0)
|
||||||
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
|
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
|
||||||
dated, err := dbSession.Query(db.Raw(sql))
|
dated, err := DbSession.Query(db.Raw(sql))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
for dated.Next() {
|
for dated.Next() {
|
||||||
|
@ -120,7 +123,7 @@ func (s *Service) GraphData() string {
|
||||||
}
|
}
|
||||||
data, err := json.Marshal(d)
|
data, err := json.Marshal(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return string(data)
|
return string(data)
|
||||||
|
@ -151,18 +154,22 @@ func (s *Service) AvgUptime() string {
|
||||||
|
|
||||||
func (u *Service) RemoveArray() []*Service {
|
func (u *Service) RemoveArray() []*Service {
|
||||||
var srvcs []*Service
|
var srvcs []*Service
|
||||||
for _, s := range services {
|
for _, s := range CoreApp.Services {
|
||||||
if s.Id != u.Id {
|
if s.Id != u.Id {
|
||||||
srvcs = append(srvcs, s)
|
srvcs = append(srvcs, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
services = srvcs
|
CoreApp.Services = srvcs
|
||||||
return srvcs
|
return srvcs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) Delete() error {
|
func (u *Service) Delete() error {
|
||||||
res := serviceCol().Find("id", u.Id)
|
res := serviceCol().Find("id", u.Id)
|
||||||
err := res.Delete()
|
err := res.Delete()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
u.RemoveArray()
|
u.RemoveArray()
|
||||||
OnDeletedService(u)
|
OnDeletedService(u)
|
||||||
return err
|
return err
|
||||||
|
@ -176,10 +183,11 @@ func (u *Service) Create() (int64, error) {
|
||||||
u.CreatedAt = time.Now()
|
u.CreatedAt = time.Now()
|
||||||
uuid, err := serviceCol().Insert(u)
|
uuid, err := serviceCol().Insert(u)
|
||||||
if uuid == nil {
|
if uuid == nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to create service %v. %v", u.Name, err))
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
u.Id = uuid.(int64)
|
u.Id = uuid.(int64)
|
||||||
services = append(services, u)
|
CoreApp.Services = append(CoreApp.Services, u)
|
||||||
go u.CheckQueue()
|
go u.CheckQueue()
|
||||||
OnNewService(u)
|
OnNewService(u)
|
||||||
return uuid.(int64), err
|
return uuid.(int64), err
|
||||||
|
@ -187,7 +195,7 @@ func (u *Service) Create() (int64, error) {
|
||||||
|
|
||||||
func CountOnline() int {
|
func CountOnline() int {
|
||||||
amount := 0
|
amount := 0
|
||||||
for _, v := range services {
|
for _, v := range CoreApp.Services {
|
||||||
if v.Online {
|
if v.Online {
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InsertDefaultComms() {
|
||||||
|
emailer := &Communication{
|
||||||
|
Method: "email",
|
||||||
|
Removable: false,
|
||||||
|
Enabled: false,
|
||||||
|
}
|
||||||
|
Create(emailer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteConfig() {
|
||||||
|
err := os.Remove("./config.yml")
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSampleData() error {
|
||||||
|
fmt.Println("Inserting Sample Data...")
|
||||||
|
s1 := &Service{
|
||||||
|
Name: "Google",
|
||||||
|
Domain: "https://google.com",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 10,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
s2 := &Service{
|
||||||
|
Name: "Statup.io",
|
||||||
|
Domain: "https://statup.io",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
s3 := &Service{
|
||||||
|
Name: "Statup.io SSL Check",
|
||||||
|
Domain: "https://statup.io",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 443,
|
||||||
|
Type: "tcp",
|
||||||
|
}
|
||||||
|
s4 := &Service{
|
||||||
|
Name: "Github Failing Check",
|
||||||
|
Domain: "https://github.com/thisisnotausernamemaybeitis",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
s1.Create()
|
||||||
|
s2.Create()
|
||||||
|
s3.Create()
|
||||||
|
s4.Create()
|
||||||
|
|
||||||
|
checkin := &Checkin{
|
||||||
|
Service: s2.Id,
|
||||||
|
Interval: 30,
|
||||||
|
Api: utils.NewSHA1Hash(18),
|
||||||
|
}
|
||||||
|
checkin.Create()
|
||||||
|
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
s1.Check()
|
||||||
|
s2.Check()
|
||||||
|
s3.Check()
|
||||||
|
s4.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User types.User
|
||||||
|
|
||||||
|
func SelectUser(id int64) (*User, error) {
|
||||||
|
var user User
|
||||||
|
col := DbSession.Collection("users")
|
||||||
|
res := col.Find("id", id)
|
||||||
|
err := res.One(&user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectUsername(username string) (*User, error) {
|
||||||
|
var user User
|
||||||
|
col := DbSession.Collection("users")
|
||||||
|
res := col.Find("username", username)
|
||||||
|
err := res.One(&user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) Delete() error {
|
||||||
|
col := DbSession.Collection("users")
|
||||||
|
user := col.Find("id", u.Id)
|
||||||
|
return user.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) Create() (int64, error) {
|
||||||
|
u.CreatedAt = time.Now()
|
||||||
|
u.Password = utils.HashPassword(u.Password)
|
||||||
|
u.ApiKey = utils.NewSHA1Hash(5)
|
||||||
|
u.ApiSecret = utils.NewSHA1Hash(10)
|
||||||
|
col := DbSession.Collection("users")
|
||||||
|
uuid, err := col.Insert(u)
|
||||||
|
if uuid == nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to create user %v. %v", u.Username, err))
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
OnNewUser(u)
|
||||||
|
return uuid.(int64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectAllUsers() ([]User, error) {
|
||||||
|
var users []User
|
||||||
|
col := DbSession.Collection("users").Find()
|
||||||
|
err := col.All(&users)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(3, fmt.Sprintf("Failed to load all users. %v", err))
|
||||||
|
}
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthUser(username, password string) (*User, bool) {
|
||||||
|
var auth bool
|
||||||
|
user, err := SelectUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if CheckHash(password, user.Password) {
|
||||||
|
auth = true
|
||||||
|
}
|
||||||
|
return user, auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckHash(password, hash string) bool {
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||||
|
return err == nil
|
||||||
|
}
|
89
database.go
89
database.go
|
@ -1,89 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"time"
|
|
||||||
"upper.io/db.v3"
|
|
||||||
"upper.io/db.v3/lib/sqlbuilder"
|
|
||||||
"upper.io/db.v3/mysql"
|
|
||||||
"upper.io/db.v3/postgresql"
|
|
||||||
"upper.io/db.v3/sqlite"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
dbServer string
|
|
||||||
sqliteSettings sqlite.ConnectionURL
|
|
||||||
postgresSettings postgresql.ConnectionURL
|
|
||||||
mysqlSettings mysql.ConnectionURL
|
|
||||||
dbSession sqlbuilder.Database
|
|
||||||
)
|
|
||||||
|
|
||||||
func DbConnection(dbType string) error {
|
|
||||||
var err error
|
|
||||||
if dbType == "sqlite" {
|
|
||||||
sqliteSettings = sqlite.ConnectionURL{
|
|
||||||
Database: "statup.db",
|
|
||||||
}
|
|
||||||
dbSession, err = sqlite.Open(sqliteSettings)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if dbType == "mysql" {
|
|
||||||
if configs.Port == "" {
|
|
||||||
configs.Port = "3306"
|
|
||||||
}
|
|
||||||
mysqlSettings = mysql.ConnectionURL{
|
|
||||||
Database: configs.Database,
|
|
||||||
Host: configs.Host,
|
|
||||||
User: configs.User,
|
|
||||||
Password: configs.Password,
|
|
||||||
}
|
|
||||||
dbSession, err = mysql.Open(mysqlSettings)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if configs.Port == "" {
|
|
||||||
configs.Port = "5432"
|
|
||||||
}
|
|
||||||
host := fmt.Sprintf("%v:%v", configs.Host, configs.Port)
|
|
||||||
postgresSettings = postgresql.ConnectionURL{
|
|
||||||
Database: configs.Database,
|
|
||||||
Host: host,
|
|
||||||
User: configs.User,
|
|
||||||
Password: configs.Password,
|
|
||||||
}
|
|
||||||
dbSession, err = postgresql.Open(postgresSettings)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//dbSession.SetLogging(true)
|
|
||||||
dbServer = dbType
|
|
||||||
OnLoad(dbSession)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DatabaseMaintence() {
|
|
||||||
defer DatabaseMaintence()
|
|
||||||
since := time.Now().AddDate(0, 0, -7)
|
|
||||||
DeleteAllSince("failures", since)
|
|
||||||
DeleteAllSince("hits", since)
|
|
||||||
time.Sleep(60 * time.Minute)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteAllSince(table string, date time.Time) {
|
|
||||||
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
|
|
||||||
_, err := dbSession.Exec(db.Raw(sql))
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Backup() {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
statup.io
|
|
116
docs/base.css
116
docs/base.css
|
@ -1,116 +0,0 @@
|
||||||
HTML,BODY {
|
|
||||||
background-color: #efefef;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
padding-top: 20px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
max-width: 860px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lg_number {
|
|
||||||
font-size: 26pt;
|
|
||||||
font-weight: bold;
|
|
||||||
display: block;
|
|
||||||
color: #3e3e3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text_perfect {
|
|
||||||
color: #33b418;
|
|
||||||
text-shadow: 0px 1px 0 #0e6702;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text_good {
|
|
||||||
color: #33b418;
|
|
||||||
text-shadow: 0px 1px 0 #0e6702;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text_ok {
|
|
||||||
color: #33b418;
|
|
||||||
text-shadow: 0px 1px 0 #0e6702;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text_bad {
|
|
||||||
color: #33b418;
|
|
||||||
text-shadow: 0px 1px 0 #0e6702;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats_area {
|
|
||||||
text-align: center;
|
|
||||||
color: #a5a5a5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.offline_bg {
|
|
||||||
background-color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.online_list {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-decoration: none;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer A {
|
|
||||||
color: #cccccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.online_badge {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #35b317;
|
|
||||||
}
|
|
||||||
|
|
||||||
.offline_badge {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #c51919;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress {
|
|
||||||
margin-top: -20px;
|
|
||||||
margin-left: -20px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
width: calc(100% + 40px);
|
|
||||||
height: 3px;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body H4 A {
|
|
||||||
color: #239e07;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
|
|
||||||
.container {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
margin-left: 0px;
|
|
||||||
margin-top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
font-size: 6pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lg_number {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
ok
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
VERSION=v0.22
|
|
||||||
OS=osx
|
|
||||||
ARCH=x64
|
|
||||||
if [ `getconf LONG_BIT` = "64" ]
|
|
||||||
then
|
|
||||||
ARCH=x64
|
|
||||||
else
|
|
||||||
ARCH=x32
|
|
||||||
fi
|
|
||||||
unameOut="$(uname -s)"
|
|
||||||
case "${unameOut}" in
|
|
||||||
Linux*) OS=linux;;
|
|
||||||
Darwin*) OS=osx;;
|
|
||||||
CYGWIN*) OS=windows;;
|
|
||||||
MINGW*) OS=windows;;
|
|
||||||
*) OS="UNKNOWN:${unameOut}"
|
|
||||||
esac
|
|
||||||
FILE="https://github.com/hunterlong/statup/releases/download/$VERSION/statup-$OS-$ARCH"
|
|
||||||
curl -sS $FILE -o statup
|
|
||||||
chmod +x statup
|
|
||||||
mv statup /usr/local/bin/
|
|
||||||
statup version
|
|
35
emailer.go
35
emailer.go
|
@ -4,8 +4,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/log"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"gopkg.in/gomail.v2"
|
"gopkg.in/gomail.v2"
|
||||||
"html/template"
|
"html/template"
|
||||||
"time"
|
"time"
|
||||||
|
@ -15,14 +16,16 @@ var (
|
||||||
emailQue *Que
|
emailQue *Que
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Email types.Email
|
||||||
|
|
||||||
type Que struct {
|
type Que struct {
|
||||||
Mailer *gomail.Dialer
|
Mailer *gomail.Dialer
|
||||||
Outgoing []*types.Email
|
Outgoing []*Email
|
||||||
LastSent int
|
LastSent int
|
||||||
LastSentTime time.Time
|
LastSentTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddEmail(email *types.Email) {
|
func AddEmail(email *Email) {
|
||||||
if emailQue == nil {
|
if emailQue == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -33,12 +36,12 @@ func EmailerQueue() {
|
||||||
if emailQue == nil {
|
if emailQue == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uniques := []*types.Email{}
|
uniques := []*Email{}
|
||||||
for _, out := range emailQue.Outgoing {
|
for _, out := range emailQue.Outgoing {
|
||||||
if isUnique(uniques, out) {
|
if isUnique(uniques, out) {
|
||||||
msg := fmt.Sprintf("sending email to: %v \n", out.To)
|
msg := fmt.Sprintf("sending email to: %v \n", out.To)
|
||||||
Send(out)
|
Send(out)
|
||||||
log.Send(0, msg)
|
utils.Log(0, msg)
|
||||||
uniques = append(uniques, out)
|
uniques = append(uniques, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +51,7 @@ func EmailerQueue() {
|
||||||
EmailerQueue()
|
EmailerQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
func isUnique(arr []*types.Email, obj *types.Email) bool {
|
func isUnique(arr []*Email, obj *Email) bool {
|
||||||
for _, v := range arr {
|
for _, v := range arr {
|
||||||
if v.To == obj.To && v.Subject == obj.Subject {
|
if v.To == obj.To && v.Subject == obj.Subject {
|
||||||
return false
|
return false
|
||||||
|
@ -57,7 +60,7 @@ func isUnique(arr []*types.Email, obj *types.Email) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Send(em *types.Email) {
|
func Send(em *Email) {
|
||||||
source := EmailTemplate(em.Template, em.Data)
|
source := EmailTemplate(em.Template, em.Data)
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
m.SetHeader("From", "info@betatude.com")
|
m.SetHeader("From", "info@betatude.com")
|
||||||
|
@ -65,14 +68,14 @@ func Send(em *types.Email) {
|
||||||
m.SetHeader("Subject", em.Subject)
|
m.SetHeader("Subject", em.Subject)
|
||||||
m.SetBody("text/html", source)
|
m.SetBody("text/html", source)
|
||||||
if err := emailQue.Mailer.DialAndSend(m); err != nil {
|
if err := emailQue.Mailer.DialAndSend(m); err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
}
|
}
|
||||||
emailQue.LastSent++
|
emailQue.LastSent++
|
||||||
emailQue.LastSentTime = time.Now()
|
emailQue.LastSentTime = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendFailureEmail(service *Service) {
|
func SendFailureEmail(service *core.Service) {
|
||||||
email := &types.Email{
|
email := &Email{
|
||||||
To: "info@socialeck.com",
|
To: "info@socialeck.com",
|
||||||
Subject: fmt.Sprintf("Service %v is Failing", service.Name),
|
Subject: fmt.Sprintf("Service %v is Failing", service.Name),
|
||||||
Template: "failure.html",
|
Template: "failure.html",
|
||||||
|
@ -81,30 +84,30 @@ func SendFailureEmail(service *Service) {
|
||||||
AddEmail(email)
|
AddEmail(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadMailer(config *types.Communication) *gomail.Dialer {
|
func LoadMailer(config *core.Communication) *gomail.Dialer {
|
||||||
if config.Host == "" || config.Username == "" || config.Password == "" {
|
if config.Host == "" || config.Username == "" || config.Password == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
emailQue = new(Que)
|
emailQue = new(Que)
|
||||||
emailQue.Outgoing = []*types.Email{}
|
emailQue.Outgoing = []*Email{}
|
||||||
emailQue.Mailer = gomail.NewDialer(config.Host, config.Port, config.Username, config.Password)
|
emailQue.Mailer = gomail.NewDialer(config.Host, config.Port, config.Username, config.Password)
|
||||||
emailQue.Mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
emailQue.Mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
return emailQue.Mailer
|
return emailQue.Mailer
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmailTemplate(tmpl string, data interface{}) string {
|
func EmailTemplate(tmpl string, data interface{}) string {
|
||||||
emailTpl, err := emailBox.String(tmpl)
|
emailTpl, err := core.EmailBox.String(tmpl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(3, err)
|
utils.Log(3, err)
|
||||||
}
|
}
|
||||||
t := template.New("email")
|
t := template.New("email")
|
||||||
t, err = t.Parse(emailTpl)
|
t, err = t.Parse(emailTpl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(3, err)
|
utils.Log(3, err)
|
||||||
}
|
}
|
||||||
var tpl bytes.Buffer
|
var tpl bytes.Buffer
|
||||||
if err := t.Execute(&tpl, data); err != nil {
|
if err := t.Execute(&tpl, data); err != nil {
|
||||||
log.Send(2, err)
|
utils.Log(2, err)
|
||||||
}
|
}
|
||||||
result := tpl.String()
|
result := tpl.String()
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
package main
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"math/rand"
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
json.NewEncoder(w).Encode(core)
|
json.NewEncoder(w).Encode(core.CoreApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
checkin := FindCheckin(vars["api"])
|
checkin := core.FindCheckin(vars["api"])
|
||||||
checkin.Receivehit()
|
checkin.Receivehit()
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(checkin)
|
json.NewEncoder(w).Encode(checkin)
|
||||||
|
@ -23,54 +22,31 @@ func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func ApiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := SelectService(StringInt(vars["id"]))
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
json.NewEncoder(w).Encode(service)
|
json.NewEncoder(w).Encode(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := SelectService(StringInt(vars["id"]))
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
var s Service
|
var s core.Service
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
decoder.Decode(&s)
|
decoder.Decode(&s)
|
||||||
json.NewEncoder(w).Encode(service)
|
json.NewEncoder(w).Encode(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
services, _ := SelectAllServices()
|
services, _ := core.SelectAllServices()
|
||||||
json.NewEncoder(w).Encode(services)
|
json.NewEncoder(w).Encode(services)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiUserHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
user, _ := SelectUser(StringInt(vars["id"]))
|
user, _ := core.SelectUser(utils.StringInt(vars["id"]))
|
||||||
json.NewEncoder(w).Encode(user)
|
json.NewEncoder(w).Encode(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
func ApiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
users, _ := SelectAllUsers()
|
users, _ := core.SelectAllUsers()
|
||||||
json.NewEncoder(w).Encode(users)
|
json.NewEncoder(w).Encode(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSHA1Hash(n ...int) string {
|
|
||||||
noRandomCharacters := 32
|
|
||||||
if len(n) > 0 {
|
|
||||||
noRandomCharacters = n[0]
|
|
||||||
}
|
|
||||||
randString := RandomString(noRandomCharacters)
|
|
||||||
hash := sha1.New()
|
|
||||||
hash.Write([]byte(randString))
|
|
||||||
bs := hash.Sum(nil)
|
|
||||||
return fmt.Sprintf("%x", bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
var characterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
|
||||||
|
|
||||||
// RandomString generates a random string of n length
|
|
||||||
func RandomString(n int) string {
|
|
||||||
b := make([]rune, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = characterRunes[rand.Intn(len(characterRunes))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dashboard struct {
|
||||||
|
Services []*core.Service
|
||||||
|
Core *core.Core
|
||||||
|
CountOnline int
|
||||||
|
CountServices int
|
||||||
|
Count24Failures uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
err := core.ErrorResponse{}
|
||||||
|
ExecuteResponse(w, r, "login.html", err)
|
||||||
|
} else {
|
||||||
|
fails := core.CountFailures()
|
||||||
|
out := dashboard{core.CoreApp.Services, core.CoreApp, core.CountOnline(), len(core.CoreApp.Services), fails}
|
||||||
|
ExecuteResponse(w, r, "dashboard.html", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session, _ := Store.Get(r, COOKIE_KEY)
|
||||||
|
r.ParseForm()
|
||||||
|
username := r.PostForm.Get("username")
|
||||||
|
password := r.PostForm.Get("password")
|
||||||
|
user, auth := core.AuthUser(username, password)
|
||||||
|
if auth {
|
||||||
|
session.Values["authenticated"] = true
|
||||||
|
session.Values["user_id"] = user.Id
|
||||||
|
session.Save(r, w)
|
||||||
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||||
|
} else {
|
||||||
|
err := core.ErrorResponse{Error: "Incorrect login information submitted, try again."}
|
||||||
|
ExecuteResponse(w, r, "login.html", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
session, _ := Store.Get(r, COOKIE_KEY)
|
||||||
|
session.Values["authenticated"] = false
|
||||||
|
session.Save(r, w)
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ExecuteResponse(w, r, "help.html", nil)
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
COOKIE_KEY = "statup_auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Store *sessions.CookieStore
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunHTTPServer() {
|
||||||
|
utils.Log(1, "Statup HTTP Server running on http://localhost:8080")
|
||||||
|
r := Router()
|
||||||
|
//for _, p := range allPlugins {
|
||||||
|
// info := p.GetInfo()
|
||||||
|
// for _, route := range p.Routes() {
|
||||||
|
// path := fmt.Sprintf("/plugins/%v/%v", info.Name, route.URL)
|
||||||
|
// r.Handle(path, http.HandlerFunc(route.Handler)).Methods(route.Method)
|
||||||
|
// fmt.Printf("Added Route %v for plugin %v\n", path, info.Name)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: "0.0.0.0:8080",
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
Handler: r,
|
||||||
|
}
|
||||||
|
err := srv.ListenAndServe()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAuthenticated(r *http.Request) bool {
|
||||||
|
if os.Getenv("GO_ENV") == "test" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if core.CoreApp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if Store == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
session, err := Store.Get(r, COOKIE_KEY)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if session.Values["authenticated"] == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return session.Values["authenticated"].(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
||||||
|
utils.Http(r)
|
||||||
|
nav, _ := core.TmplBox.String("nav.html")
|
||||||
|
footer, _ := core.TmplBox.String("footer.html")
|
||||||
|
render, err := core.TmplBox.String(file)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, err)
|
||||||
|
}
|
||||||
|
t := template.New("message")
|
||||||
|
t.Funcs(template.FuncMap{
|
||||||
|
"js": func(html string) template.JS {
|
||||||
|
return template.JS(html)
|
||||||
|
},
|
||||||
|
"safe": func(html string) template.HTML {
|
||||||
|
return template.HTML(html)
|
||||||
|
},
|
||||||
|
"Auth": func() bool {
|
||||||
|
return IsAuthenticated(r)
|
||||||
|
},
|
||||||
|
"VERSION": func() string {
|
||||||
|
return "Version here"
|
||||||
|
},
|
||||||
|
"underscore": func(html string) string {
|
||||||
|
return utils.UnderScoreString(html)
|
||||||
|
},
|
||||||
|
"User": func() *types.User {
|
||||||
|
return SessionUser(r)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
t, _ = t.Parse(nav)
|
||||||
|
t, _ = t.Parse(footer)
|
||||||
|
t.Parse(render)
|
||||||
|
t.Execute(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DbConfig types.DbConfig
|
|
@ -0,0 +1,23 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type index struct {
|
||||||
|
Core core.Core
|
||||||
|
Services []*core.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if core.CoreApp == nil {
|
||||||
|
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := index{*core.CoreApp, core.CoreApp.Services}
|
||||||
|
first, _ := out.Services[0].LimitedHits()
|
||||||
|
fmt.Println(out.Services[0].Name, "start:", first[0].Id, "last:", first[len(first)-1].Id)
|
||||||
|
ExecuteResponse(w, r, "index.html", out)
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RobotsTxtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
robots := []byte(`User-agent: *
|
||||||
|
Disallow: /login
|
||||||
|
Disallow: /dashboard
|
||||||
|
|
||||||
|
Host: ` + core.CoreApp.Domain)
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(robots))
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginSelect struct {
|
||||||
|
Plugin string
|
||||||
|
Form string
|
||||||
|
Params map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PluginSavedHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
//vars := mux.Vars(r)
|
||||||
|
//plug := SelectPlugin(vars["name"])
|
||||||
|
data := make(map[string]string)
|
||||||
|
for k, v := range r.PostForm {
|
||||||
|
data[k] = strings.Join(v, "")
|
||||||
|
}
|
||||||
|
//plug.OnSave(structs.Map(data))
|
||||||
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//vars := mux.Vars(r)
|
||||||
|
//name := vars["name"]
|
||||||
|
//DownloadPlugin(name)
|
||||||
|
core.LoadConfig()
|
||||||
|
http.Redirect(w, r, "/plugins", http.StatusSeeOther)
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Printf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr)
|
||||||
|
metrics := []string{}
|
||||||
|
system := fmt.Sprintf("statup_total_failures %v\n", core.CountFailures())
|
||||||
|
system += fmt.Sprintf("statup_total_services %v", len(core.CoreApp.Services))
|
||||||
|
metrics = append(metrics, system)
|
||||||
|
|
||||||
|
for _, v := range core.CoreApp.Services {
|
||||||
|
online := 1
|
||||||
|
if !v.Online {
|
||||||
|
online = 0
|
||||||
|
}
|
||||||
|
met := fmt.Sprintf("statup_service_failures{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, len(v.Failures))
|
||||||
|
met += fmt.Sprintf("statup_service_latency{id=\"%v\" name=\"%v\"} %0.0f\n", v.Id, v.Name, (v.Latency * 100))
|
||||||
|
met += fmt.Sprintf("statup_service_online{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, online)
|
||||||
|
met += fmt.Sprintf("statup_service_status_code{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, v.LastStatusCode)
|
||||||
|
met += fmt.Sprintf("statup_service_response_length{id=\"%v\" name=\"%v\"} %v", v.Id, v.Name, len([]byte(v.LastResponse)))
|
||||||
|
metrics = append(metrics, met)
|
||||||
|
}
|
||||||
|
output := strings.Join(metrics, "\n")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(output))
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router() *mux.Router {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||||
|
if core.UsingAssets {
|
||||||
|
cssHandler := http.FileServer(http.Dir("./assets/css"))
|
||||||
|
jsHandler := http.FileServer(http.Dir("./assets/js"))
|
||||||
|
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cssHandler))
|
||||||
|
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", jsHandler))
|
||||||
|
} else {
|
||||||
|
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(core.CssBox.HTTPBox())))
|
||||||
|
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(core.JsBox.HTTPBox())))
|
||||||
|
}
|
||||||
|
r.Handle("/robots.txt", http.HandlerFunc(RobotsTxtHandler)).Methods("GET")
|
||||||
|
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||||
|
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||||
|
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
||||||
|
r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
|
||||||
|
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
||||||
|
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")
|
||||||
|
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
||||||
|
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler)).Methods("GET")
|
||||||
|
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
||||||
|
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
||||||
|
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
||||||
|
r.Handle("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler))
|
||||||
|
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
|
||||||
|
r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST")
|
||||||
|
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
|
||||||
|
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
||||||
|
r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
|
||||||
|
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET")
|
||||||
|
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
|
||||||
|
r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST")
|
||||||
|
r.Handle("/settings/build", http.HandlerFunc(SaveAssetsHandler)).Methods("GET")
|
||||||
|
r.Handle("/settings/email", http.HandlerFunc(SaveEmailSettingsHandler)).Methods("POST")
|
||||||
|
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
||||||
|
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
|
||||||
|
r.Handle("/help", http.HandlerFunc(HelpHandler))
|
||||||
|
r.Handle("/api", http.HandlerFunc(ApiIndexHandler))
|
||||||
|
r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler))
|
||||||
|
r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler))
|
||||||
|
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET")
|
||||||
|
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
|
||||||
|
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
||||||
|
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
||||||
|
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler)).Methods("GET")
|
||||||
|
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
||||||
|
return r
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ExecuteResponse(w, r, "services.html", core.CoreApp.Services)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("service adding")
|
||||||
|
r.ParseForm()
|
||||||
|
name := r.PostForm.Get("name")
|
||||||
|
domain := r.PostForm.Get("domain")
|
||||||
|
method := r.PostForm.Get("method")
|
||||||
|
expected := r.PostForm.Get("expected")
|
||||||
|
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
||||||
|
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
||||||
|
port, _ := strconv.Atoi(r.PostForm.Get("port"))
|
||||||
|
checkType := r.PostForm.Get("check_type")
|
||||||
|
|
||||||
|
service := &core.Service{
|
||||||
|
Name: name,
|
||||||
|
Domain: domain,
|
||||||
|
Method: method,
|
||||||
|
Expected: expected,
|
||||||
|
ExpectedStatus: status,
|
||||||
|
Interval: interval,
|
||||||
|
Type: checkType,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
_, err := service.Create()
|
||||||
|
if err != nil {
|
||||||
|
go service.CheckQueue()
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
service.Delete()
|
||||||
|
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
ExecuteResponse(w, r, "service.html", service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
|
||||||
|
var badge []byte
|
||||||
|
if service.Online {
|
||||||
|
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">` + service.Name + `</text><text x="28" y="14">` + service.Name + `</text><text x="78" y="15" fill="#010101" fill-opacity=".3">online</text><text x="78" y="14">online</text></g></svg>`)
|
||||||
|
} else {
|
||||||
|
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">` + service.Name + `</text><text x="28" y="14">` + service.Name + `</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">offline</text><text x="75.5" y="14">offline</text></g></svg>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "image/svg+xml")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
|
||||||
|
w.Write(badge)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
r.ParseForm()
|
||||||
|
name := r.PostForm.Get("name")
|
||||||
|
domain := r.PostForm.Get("domain")
|
||||||
|
method := r.PostForm.Get("method")
|
||||||
|
expected := r.PostForm.Get("expected")
|
||||||
|
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
||||||
|
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
||||||
|
port, _ := strconv.Atoi(r.PostForm.Get("port"))
|
||||||
|
checkType := r.PostForm.Get("check_type")
|
||||||
|
service = &core.Service{
|
||||||
|
Name: name,
|
||||||
|
Domain: domain,
|
||||||
|
Method: method,
|
||||||
|
Expected: expected,
|
||||||
|
ExpectedStatus: status,
|
||||||
|
Interval: interval,
|
||||||
|
Type: checkType,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
service.Update()
|
||||||
|
ExecuteResponse(w, r, "service.html", service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
|
||||||
|
service.DeleteFailures()
|
||||||
|
core.CoreApp.Services, _ = core.SelectAllServices()
|
||||||
|
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
interval := utils.StringInt(r.PostForm.Get("interval"))
|
||||||
|
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||||
|
checkin := &core.Checkin{
|
||||||
|
Service: service.Id,
|
||||||
|
Interval: interval,
|
||||||
|
Api: utils.NewSHA1Hash(18),
|
||||||
|
}
|
||||||
|
checkin.Create()
|
||||||
|
fmt.Println(checkin.Create())
|
||||||
|
ExecuteResponse(w, r, "service.html", service)
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PluginsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//CoreApp.FetchPluginRepo()
|
||||||
|
|
||||||
|
//var pluginFields []PluginSelect
|
||||||
|
//
|
||||||
|
//for _, p := range allPlugins {
|
||||||
|
// fields := structs.Map(p.GetInfo())
|
||||||
|
//
|
||||||
|
// pluginFields = append(pluginFields, PluginSelect{p.GetInfo().Name, p.GetForm(), fields})
|
||||||
|
//}
|
||||||
|
|
||||||
|
//CoreApp.PluginFields = pluginFields
|
||||||
|
fmt.Println(core.CoreApp.Communications)
|
||||||
|
|
||||||
|
ExecuteResponse(w, r, "plugins.html", core.CoreApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
name := r.PostForm.Get("project")
|
||||||
|
if name != "" {
|
||||||
|
core.CoreApp.Name = name
|
||||||
|
}
|
||||||
|
description := r.PostForm.Get("description")
|
||||||
|
if description != core.CoreApp.Description {
|
||||||
|
core.CoreApp.Description = description
|
||||||
|
}
|
||||||
|
style := r.PostForm.Get("style")
|
||||||
|
if style != core.CoreApp.Style {
|
||||||
|
core.CoreApp.Style = style
|
||||||
|
}
|
||||||
|
footer := r.PostForm.Get("footer")
|
||||||
|
if footer != core.CoreApp.Footer {
|
||||||
|
core.CoreApp.Footer = footer
|
||||||
|
}
|
||||||
|
domain := r.PostForm.Get("domain")
|
||||||
|
if domain != core.CoreApp.Domain {
|
||||||
|
core.CoreApp.Domain = domain
|
||||||
|
}
|
||||||
|
core.CoreApp.Update()
|
||||||
|
core.OnSettingsSaved(core.CoreApp)
|
||||||
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveSASSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
theme := r.PostForm.Get("theme")
|
||||||
|
variables := r.PostForm.Get("variables")
|
||||||
|
core.SaveAsset(theme, "scss/base.scss")
|
||||||
|
core.SaveAsset(variables, "scss/variables.scss")
|
||||||
|
core.CompileSASS()
|
||||||
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
core.CreateAllAssets()
|
||||||
|
core.UsingAssets = true
|
||||||
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveEmailSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emailer := core.SelectCommunication(1)
|
||||||
|
|
||||||
|
r.ParseForm()
|
||||||
|
emailer.Host = r.PostForm.Get("host")
|
||||||
|
emailer.Username = r.PostForm.Get("username")
|
||||||
|
emailer.Password = r.PostForm.Get("password")
|
||||||
|
emailer.Port = int(utils.StringInt(r.PostForm.Get("port")))
|
||||||
|
emailer.Var1 = r.PostForm.Get("address")
|
||||||
|
core.Update(emailer)
|
||||||
|
|
||||||
|
//sample := &Email{
|
||||||
|
// To: SessionUser(r).Email,
|
||||||
|
// Subject: "Sample Email",
|
||||||
|
// Template: "error.html",
|
||||||
|
//}
|
||||||
|
//AddEmail(sample)
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if core.CoreApp != nil {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
port := 5432
|
||||||
|
if os.Getenv("DB_CONN") == "mysql" {
|
||||||
|
port = 3306
|
||||||
|
}
|
||||||
|
var data interface{}
|
||||||
|
if os.Getenv("DB_CONN") != "" {
|
||||||
|
data = &types.DbConfig{
|
||||||
|
DbConn: os.Getenv("DB_CONN"),
|
||||||
|
DbHost: os.Getenv("DB_HOST"),
|
||||||
|
DbUser: os.Getenv("DB_USER"),
|
||||||
|
DbPass: os.Getenv("DB_PASS"),
|
||||||
|
DbData: os.Getenv("DB_DATABASE"),
|
||||||
|
DbPort: port,
|
||||||
|
Project: os.Getenv("NAME"),
|
||||||
|
Description: os.Getenv("DESCRIPTION"),
|
||||||
|
Email: "",
|
||||||
|
Username: "admin",
|
||||||
|
Password: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecuteResponse(w, r, "setup.html", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if core.CoreApp != nil {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
dbHost := r.PostForm.Get("db_host")
|
||||||
|
dbUser := r.PostForm.Get("db_user")
|
||||||
|
dbPass := r.PostForm.Get("db_password")
|
||||||
|
dbDatabase := r.PostForm.Get("db_database")
|
||||||
|
dbConn := r.PostForm.Get("db_connection")
|
||||||
|
dbPort, _ := strconv.Atoi(r.PostForm.Get("db_port"))
|
||||||
|
project := r.PostForm.Get("project")
|
||||||
|
username := r.PostForm.Get("username")
|
||||||
|
password := r.PostForm.Get("password")
|
||||||
|
sample := r.PostForm.Get("sample_data")
|
||||||
|
description := r.PostForm.Get("description")
|
||||||
|
domain := r.PostForm.Get("domain")
|
||||||
|
email := r.PostForm.Get("email")
|
||||||
|
|
||||||
|
config := &core.DbConfig{
|
||||||
|
dbConn,
|
||||||
|
dbHost,
|
||||||
|
dbUser,
|
||||||
|
dbPass,
|
||||||
|
dbDatabase,
|
||||||
|
dbPort,
|
||||||
|
project,
|
||||||
|
description,
|
||||||
|
domain,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
email,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
err := config.Save()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
config.Error = err
|
||||||
|
SetupResponseError(w, r, config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.Configs, err = core.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
config.Error = err
|
||||||
|
SetupResponseError(w, r, config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
core.DeleteConfig()
|
||||||
|
config.Error = err
|
||||||
|
SetupResponseError(w, r, config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
admin := &core.User{
|
||||||
|
Username: config.Username,
|
||||||
|
Password: config.Password,
|
||||||
|
Email: email,
|
||||||
|
Admin: true,
|
||||||
|
}
|
||||||
|
admin.Create()
|
||||||
|
|
||||||
|
core.InsertDefaultComms()
|
||||||
|
|
||||||
|
if sample == "on" {
|
||||||
|
go core.LoadSampleData()
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
//mainProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) {
|
||||||
|
ExecuteResponse(w, r, "setup.html", a)
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/types"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SessionUser(r *http.Request) *types.User {
|
||||||
|
session, _ := Store.Get(r, COOKIE_KEY)
|
||||||
|
if session == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
uuid := session.Values["user_id"]
|
||||||
|
var user *types.User
|
||||||
|
col := core.DbSession.Collection("users")
|
||||||
|
res := col.Find("id", uuid)
|
||||||
|
res.One(&user)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
users, _ := core.SelectAllUsers()
|
||||||
|
ExecuteResponse(w, r, "users.html", users)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("creating user")
|
||||||
|
r.ParseForm()
|
||||||
|
username := r.PostForm.Get("username")
|
||||||
|
password := r.PostForm.Get("password")
|
||||||
|
email := r.PostForm.Get("email")
|
||||||
|
user := &core.User{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
Email: email,
|
||||||
|
}
|
||||||
|
_, err := user.Create()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := IsAuthenticated(r)
|
||||||
|
if !auth {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id, _ := strconv.Atoi(vars["id"])
|
||||||
|
user, _ := core.SelectUser(int64(id))
|
||||||
|
|
||||||
|
users, _ := core.SelectAllUsers()
|
||||||
|
if len(users) == 1 {
|
||||||
|
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.Delete()
|
||||||
|
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||||
|
}
|
57
log/log.go
57
log/log.go
|
@ -1,57 +0,0 @@
|
||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fatih/color"
|
|
||||||
lg "log"
|
|
||||||
"os"
|
|
||||||
//"github.com/mkideal/log/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
logFile *os.File
|
|
||||||
logLevel int
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var err error
|
|
||||||
logFile, err = os.OpenFile("statup.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
lg.Fatalf("error opening file: %v", err)
|
|
||||||
}
|
|
||||||
lg.SetOutput(logFile)
|
|
||||||
|
|
||||||
logEnv := os.Getenv("LOG")
|
|
||||||
if logEnv == "fatal" {
|
|
||||||
logLevel = 3
|
|
||||||
} else if logEnv == "debug" {
|
|
||||||
logLevel = 2
|
|
||||||
} else if logEnv == "info" {
|
|
||||||
logLevel = 1
|
|
||||||
} else {
|
|
||||||
logLevel = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Panic(err interface{}) {
|
|
||||||
lg.Printf("PANIC: %v\n", err)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Send(level int, err interface{}) {
|
|
||||||
switch level {
|
|
||||||
case 3:
|
|
||||||
lg.Printf("ERROR: %v\n", err)
|
|
||||||
color.Red("ERROR: %v\n", err)
|
|
||||||
os.Exit(2)
|
|
||||||
case 2:
|
|
||||||
lg.Printf("WARNING: %v\n", err)
|
|
||||||
color.Yellow("WARNING: %v\n", err)
|
|
||||||
case 1:
|
|
||||||
lg.Printf("INFO: %v\n", err)
|
|
||||||
color.Blue("INFO: %v\n", err)
|
|
||||||
case 0:
|
|
||||||
lg.Printf("%v\n", err)
|
|
||||||
color.White("%v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
199
main.go
199
main.go
|
@ -1,169 +1,92 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/GeertJohan/go.rice"
|
"github.com/GeertJohan/go.rice"
|
||||||
"github.com/go-yaml/yaml"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/hunterlong/statup/handlers"
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"github.com/hunterlong/statup/plugin"
|
"github.com/hunterlong/statup/plugin"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
plg "plugin"
|
plg "plugin"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configs *Config
|
VERSION string
|
||||||
core *Core
|
|
||||||
store *sessions.CookieStore
|
|
||||||
VERSION string
|
|
||||||
sqlBox *rice.Box
|
|
||||||
cssBox *rice.Box
|
|
||||||
scssBox *rice.Box
|
|
||||||
jsBox *rice.Box
|
|
||||||
tmplBox *rice.Box
|
|
||||||
emailBox *rice.Box
|
|
||||||
setupMode bool
|
|
||||||
allPlugins []plugin.PluginActions
|
|
||||||
logFile *os.File
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
pluginsRepo = "https://raw.githubusercontent.com/hunterlong/statup/master/plugins.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Connection string `yaml:"connection"`
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Database string `yaml:"database"`
|
|
||||||
User string `yaml:"user"`
|
|
||||||
Password string `yaml:"password"`
|
|
||||||
Port string `yaml:"port"`
|
|
||||||
Secret string `yaml:"secret"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginRepos struct {
|
|
||||||
Plugins []PluginJSON
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginJSON struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Repo string `json:"repo"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) FetchPluginRepo() []PluginJSON {
|
|
||||||
resp, err := http.Get(pluginsRepo)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
var pk []PluginJSON
|
|
||||||
json.Unmarshal(body, &pk)
|
|
||||||
c.Repos = pk
|
|
||||||
return pk
|
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadFile(filepath string, url string) error {
|
|
||||||
out, err := os.Create(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(out, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
LoadDotEnvs()
|
LoadDotEnvs()
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadDotEnvs() {
|
|
||||||
err := godotenv.Load()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error loading .env file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defer logFile.Close()
|
var err error
|
||||||
if len(os.Args) >= 2 {
|
if len(os.Args) >= 2 {
|
||||||
CatchCLI(os.Args)
|
CatchCLI(os.Args)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
utils.Log(1, fmt.Sprintf("Starting Statup v%v\n", VERSION))
|
||||||
var err error
|
|
||||||
fmt.Printf("Starting Statup v%v\n", VERSION)
|
|
||||||
RenderBoxes()
|
RenderBoxes()
|
||||||
hasAssets()
|
core.HasAssets()
|
||||||
|
|
||||||
configs, err = LoadConfig()
|
core.Configs, err = core.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(1, "config.yml file not found - starting in setup mode")
|
utils.Log(2, "config.yml file not found - starting in setup mode")
|
||||||
setupMode = true
|
core.SetupMode = true
|
||||||
RunHTTPServer()
|
handlers.RunHTTPServer()
|
||||||
}
|
}
|
||||||
mainProcess()
|
mainProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
func StringInt(s string) int64 {
|
func RenderBoxes() {
|
||||||
num, _ := strconv.Atoi(s)
|
core.SqlBox = rice.MustFindBox("source/sql")
|
||||||
return int64(num)
|
core.CssBox = rice.MustFindBox("source/css")
|
||||||
|
core.ScssBox = rice.MustFindBox("source/scss")
|
||||||
|
core.JsBox = rice.MustFindBox("source/js")
|
||||||
|
core.TmplBox = rice.MustFindBox("source/tmpl")
|
||||||
|
core.EmailBox = rice.MustFindBox("source/emails")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadDotEnvs() {
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err == nil {
|
||||||
|
utils.Log(1, "Environment file '.env' Loaded")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mainProcess() {
|
func mainProcess() {
|
||||||
var err error
|
var err error
|
||||||
err = DbConnection(configs.Connection)
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
throw(err)
|
utils.Log(3, err)
|
||||||
}
|
}
|
||||||
RunDatabaseUpgrades()
|
core.RunDatabaseUpgrades()
|
||||||
core, err = SelectCore()
|
core.CoreApp, err = core.SelectCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(1, "Core database was not found, Statup is not setup yet.")
|
utils.Log(2, "Core database was not found, Statup is not setup yet.")
|
||||||
RunHTTPServer()
|
handlers.RunHTTPServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckServices()
|
core.CheckServices()
|
||||||
core.Communications, _ = SelectAllCommunications()
|
core.CoreApp.Communications, err = core.SelectAllCommunications()
|
||||||
LoadDefaultCommunications()
|
if err != nil {
|
||||||
|
utils.Log(2, err)
|
||||||
|
}
|
||||||
|
core.LoadDefaultCommunications()
|
||||||
|
|
||||||
go DatabaseMaintence()
|
go core.DatabaseMaintence()
|
||||||
|
|
||||||
if !setupMode {
|
if !core.SetupMode {
|
||||||
LoadPlugins()
|
LoadPlugins()
|
||||||
RunHTTPServer()
|
handlers.RunHTTPServer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func throw(err error) {
|
|
||||||
fmt.Println("ERROR: ", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ForEachPlugin() {
|
func ForEachPlugin() {
|
||||||
if len(core.Plugins) > 0 {
|
if len(core.CoreApp.Plugins) > 0 {
|
||||||
//for _, p := range core.Plugins {
|
//for _, p := range core.Plugins {
|
||||||
// p.OnShutdown()
|
// p.OnShutdown()
|
||||||
//}
|
//}
|
||||||
|
@ -179,7 +102,7 @@ func LoadPlugins() {
|
||||||
|
|
||||||
files, err := ioutil.ReadDir("./plugins")
|
files, err := ioutil.ReadDir("./plugins")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(1, fmt.Sprintf("Plugins directory was not found. Error: %v\n", err))
|
utils.Log(2, fmt.Sprintf("Plugins directory was not found. Error: %v\n", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
@ -192,7 +115,7 @@ func LoadPlugins() {
|
||||||
}
|
}
|
||||||
plug, err := plg.Open("plugins/" + f.Name())
|
plug, err := plg.Open("plugins/" + f.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Send(2, fmt.Sprintf("Plugin '%v' could not load correctly.\n", f.Name()))
|
utils.Log(2, fmt.Sprintf("Plugin '%v' could not load correctly.\n", f.Name()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
symPlugin, err := plug.Lookup("Plugin")
|
symPlugin, err := plug.Lookup("Plugin")
|
||||||
|
@ -200,42 +123,16 @@ func LoadPlugins() {
|
||||||
var plugActions plugin.PluginActions
|
var plugActions plugin.PluginActions
|
||||||
plugActions, ok := symPlugin.(plugin.PluginActions)
|
plugActions, ok := symPlugin.(plugin.PluginActions)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Send(2, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v\n", f.Name(), "unexpected type from module symbol"))
|
utils.Log(2, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v\n", f.Name(), "unexpected type from module symbol"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
allPlugins = append(allPlugins, plugActions)
|
//allPlugins = append(allPlugins, plugActions)
|
||||||
core.Plugins = append(core.Plugins, plugActions.GetInfo())
|
core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
OnLoad(dbSession)
|
core.OnLoad(core.DbSession)
|
||||||
|
|
||||||
fmt.Printf("Loaded %v Plugins\n", len(allPlugins))
|
|
||||||
|
|
||||||
|
//utils.Log(1, fmt.Sprintf("Loaded %v Plugins\n", len(allPlugins)))
|
||||||
ForEachPlugin()
|
ForEachPlugin()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderBoxes() {
|
|
||||||
sqlBox = rice.MustFindBox("sql")
|
|
||||||
cssBox = rice.MustFindBox("html/css")
|
|
||||||
scssBox = rice.MustFindBox("html/scss")
|
|
||||||
jsBox = rice.MustFindBox("html/js")
|
|
||||||
tmplBox = rice.MustFindBox("html/tmpl")
|
|
||||||
emailBox = rice.MustFindBox("html/emails")
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
|
||||||
var config Config
|
|
||||||
file, err := ioutil.ReadFile("config.yml")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = yaml.Unmarshal(file, &config)
|
|
||||||
configs = &config
|
|
||||||
return &config, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func HashPassword(password string) string {
|
|
||||||
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
|
|
||||||
return string(bytes)
|
|
||||||
}
|
|
||||||
|
|
94
main_test.go
94
main_test.go
|
@ -3,6 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/handlers"
|
||||||
"github.com/rendon/testcli"
|
"github.com/rendon/testcli"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -19,19 +21,19 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
route = Router()
|
route = handlers.Router()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
RenderBoxes()
|
RenderBoxes()
|
||||||
os.Remove("./statup.db")
|
os.Remove("./statup.db")
|
||||||
Router()
|
handlers.Router()
|
||||||
LoadDotEnvs()
|
LoadDotEnvs()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMySQLMakeConfig(t *testing.T) {
|
func TestMySQLMakeConfig(t *testing.T) {
|
||||||
config := &DbConfig{
|
config := &core.DbConfig{
|
||||||
"mysql",
|
"mysql",
|
||||||
os.Getenv("DB_HOST"),
|
os.Getenv("DB_HOST"),
|
||||||
os.Getenv("DB_USER"),
|
os.Getenv("DB_USER"),
|
||||||
|
@ -49,30 +51,30 @@ func TestMySQLMakeConfig(t *testing.T) {
|
||||||
err := config.Save()
|
err := config.Save()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, err = LoadConfig()
|
_, err = core.LoadConfig()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "mysql", configs.Connection)
|
assert.Equal(t, "mysql", core.Configs.Connection)
|
||||||
|
|
||||||
err = DbConnection(configs.Connection)
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
InsertDefaultComms()
|
core.InsertDefaultComms()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertMysqlSample(t *testing.T) {
|
func TestInsertMysqlSample(t *testing.T) {
|
||||||
err := LoadSampleData()
|
err := core.LoadSampleData()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectCoreMYQL(t *testing.T) {
|
func TestSelectCoreMYQL(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
core, err = SelectCore()
|
core.CoreApp, err = core.SelectCore()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "Testing MYSQL", core.Name)
|
assert.Equal(t, "Testing MYSQL", core.CoreApp.Name)
|
||||||
assert.Equal(t, VERSION, core.Version)
|
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqliteMakeConfig(t *testing.T) {
|
func TestSqliteMakeConfig(t *testing.T) {
|
||||||
config := &DbConfig{
|
config := &core.DbConfig{
|
||||||
"sqlite",
|
"sqlite",
|
||||||
os.Getenv("DB_HOST"),
|
os.Getenv("DB_HOST"),
|
||||||
os.Getenv("DB_USER"),
|
os.Getenv("DB_USER"),
|
||||||
|
@ -90,22 +92,22 @@ func TestSqliteMakeConfig(t *testing.T) {
|
||||||
err := config.Save()
|
err := config.Save()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, err = LoadConfig()
|
_, err = core.LoadConfig()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "sqlite", configs.Connection)
|
assert.Equal(t, "sqlite", core.Configs.Connection)
|
||||||
|
|
||||||
err = DbConnection(configs.Connection)
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
InsertDefaultComms()
|
core.InsertDefaultComms()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertSqliteSample(t *testing.T) {
|
func TestInsertSqliteSample(t *testing.T) {
|
||||||
err := LoadSampleData()
|
err := core.LoadSampleData()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostgresMakeConfig(t *testing.T) {
|
func TestPostgresMakeConfig(t *testing.T) {
|
||||||
config := &DbConfig{
|
config := &core.DbConfig{
|
||||||
"postgres",
|
"postgres",
|
||||||
os.Getenv("DB_HOST"),
|
os.Getenv("DB_HOST"),
|
||||||
os.Getenv("DB_USER"),
|
os.Getenv("DB_USER"),
|
||||||
|
@ -123,38 +125,38 @@ func TestPostgresMakeConfig(t *testing.T) {
|
||||||
err := config.Save()
|
err := config.Save()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, err = LoadConfig()
|
_, err = core.LoadConfig()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "postgres", configs.Connection)
|
assert.Equal(t, "postgres", core.Configs.Connection)
|
||||||
|
|
||||||
err = DbConnection(configs.Connection)
|
err = core.DbConnection(core.Configs.Connection)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
InsertDefaultComms()
|
core.InsertDefaultComms()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertPostgresSample(t *testing.T) {
|
func TestInsertPostgresSample(t *testing.T) {
|
||||||
err := LoadSampleData()
|
err := core.LoadSampleData()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectCorePostgres(t *testing.T) {
|
func TestSelectCorePostgres(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
core, err = SelectCore()
|
core.CoreApp, err = core.SelectCore()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "Testing POSTGRES", core.Name)
|
assert.Equal(t, "Testing POSTGRES", core.CoreApp.Name)
|
||||||
assert.Equal(t, VERSION, core.Version)
|
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectCore(t *testing.T) {
|
func TestSelectCore(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
core, err = SelectCore()
|
core.CoreApp, err = core.SelectCore()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "Testing POSTGRES", core.Name)
|
assert.Equal(t, "Testing POSTGRES", core.CoreApp.Name)
|
||||||
assert.Equal(t, VERSION, core.Version)
|
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_Create(t *testing.T) {
|
func TestUser_Create(t *testing.T) {
|
||||||
user := &User{
|
user := &core.User{
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
Password: "admin",
|
Password: "admin",
|
||||||
Email: "info@testuser.com",
|
Email: "info@testuser.com",
|
||||||
|
@ -164,14 +166,22 @@ func TestUser_Create(t *testing.T) {
|
||||||
assert.NotZero(t, id)
|
assert.NotZero(t, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectAllServices(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
services, err := core.SelectAllServices()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 4, len(services))
|
||||||
|
}
|
||||||
|
|
||||||
func TestOneService_Check(t *testing.T) {
|
func TestOneService_Check(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
|
t.Log(service)
|
||||||
assert.Equal(t, "Google", service.Name)
|
assert.Equal(t, "Google", service.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_Create(t *testing.T) {
|
func TestService_Create(t *testing.T) {
|
||||||
service := &Service{
|
service := &core.Service{
|
||||||
Name: "test service",
|
Name: "test service",
|
||||||
Domain: "https://google.com",
|
Domain: "https://google.com",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
@ -186,7 +196,7 @@ func TestService_Create(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_Check(t *testing.T) {
|
func TestService_Check(t *testing.T) {
|
||||||
service := SelectService(2)
|
service := core.SelectService(2)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
assert.Equal(t, "Statup.io", service.Name)
|
assert.Equal(t, "Statup.io", service.Name)
|
||||||
out := service.Check()
|
out := service.Check()
|
||||||
|
@ -194,28 +204,28 @@ func TestService_Check(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_AvgTime(t *testing.T) {
|
func TestService_AvgTime(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
avg := service.AvgUptime()
|
avg := service.AvgUptime()
|
||||||
assert.Equal(t, "100", avg)
|
assert.Equal(t, "100", avg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_Online24(t *testing.T) {
|
func TestService_Online24(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
online := service.Online24()
|
online := service.Online24()
|
||||||
assert.Equal(t, float32(100), online)
|
assert.Equal(t, float32(100), online)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_GraphData(t *testing.T) {
|
func TestService_GraphData(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
data := service.GraphData()
|
data := service.GraphData()
|
||||||
assert.NotEmpty(t, data)
|
assert.NotEmpty(t, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadService_Create(t *testing.T) {
|
func TestBadService_Create(t *testing.T) {
|
||||||
service := &Service{
|
service := &core.Service{
|
||||||
Name: "bad service",
|
Name: "bad service",
|
||||||
Domain: "https://9839f83h72gey2g29278hd2od2d.com",
|
Domain: "https://9839f83h72gey2g29278hd2od2d.com",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
@ -230,13 +240,13 @@ func TestBadService_Create(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadService_Check(t *testing.T) {
|
func TestBadService_Check(t *testing.T) {
|
||||||
service := SelectService(4)
|
service := core.SelectService(4)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
assert.Equal(t, "Github Failing Check", service.Name)
|
assert.Equal(t, "Github Failing Check", service.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_Hits(t *testing.T) {
|
func TestService_Hits(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
hits, err := service.Hits()
|
hits, err := service.Hits()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -244,7 +254,7 @@ func TestService_Hits(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_LimitedHits(t *testing.T) {
|
func TestService_LimitedHits(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := core.SelectService(1)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
hits, err := service.LimitedHits()
|
hits, err := service.LimitedHits()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -273,7 +283,7 @@ func TestPrometheusHandler(t *testing.T) {
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
route.ServeHTTP(rr, req)
|
route.ServeHTTP(rr, req)
|
||||||
t.Log(rr.Body.String())
|
t.Log(rr.Body.String())
|
||||||
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 14"))
|
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 6"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoginHandler(t *testing.T) {
|
func TestLoginHandler(t *testing.T) {
|
||||||
|
@ -348,7 +358,7 @@ func TestExportCommand(t *testing.T) {
|
||||||
c := testcli.Command("statup", "export")
|
c := testcli.Command("statup", "export")
|
||||||
c.Run()
|
c.Run()
|
||||||
t.Log(c.Stdout())
|
t.Log(c.Stdout())
|
||||||
assert.True(t, c.StdoutContains("Exported Statup index page"))
|
assert.True(t, c.StdoutContains("Exporting Static 'index.html' page"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetsCommand(t *testing.T) {
|
func TestAssetsCommand(t *testing.T) {
|
||||||
|
|
300
setup.go
300
setup.go
|
@ -1,300 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/go-yaml/yaml"
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"github.com/hunterlong/statup/plugin"
|
|
||||||
"github.com/hunterlong/statup/types"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"upper.io/db.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DbConfig struct {
|
|
||||||
DbConn string `yaml:"connection"`
|
|
||||||
DbHost string `yaml:"host"`
|
|
||||||
DbUser string `yaml:"user"`
|
|
||||||
DbPass string `yaml:"password"`
|
|
||||||
DbData string `yaml:"database"`
|
|
||||||
DbPort int `yaml:"port"`
|
|
||||||
Project string `yaml:"-"`
|
|
||||||
Description string `yaml:"-"`
|
|
||||||
Domain string `yaml:"-"`
|
|
||||||
Username string `yaml:"-"`
|
|
||||||
Password string `yaml:"-"`
|
|
||||||
Email string `yaml:"-"`
|
|
||||||
Error error `yaml:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunDatabaseUpgrades() {
|
|
||||||
fmt.Println("Upgrading Tables...")
|
|
||||||
upgrade, _ := sqlBox.String("upgrade.sql")
|
|
||||||
requests := strings.Split(upgrade, ";")
|
|
||||||
for _, request := range requests {
|
|
||||||
_, err := dbSession.Exec(db.Raw(request + ";"))
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("Database Upgraded")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if core != nil {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.ParseForm()
|
|
||||||
dbHost := r.PostForm.Get("db_host")
|
|
||||||
dbUser := r.PostForm.Get("db_user")
|
|
||||||
dbPass := r.PostForm.Get("db_password")
|
|
||||||
dbDatabase := r.PostForm.Get("db_database")
|
|
||||||
dbConn := r.PostForm.Get("db_connection")
|
|
||||||
dbPort, _ := strconv.Atoi(r.PostForm.Get("db_port"))
|
|
||||||
project := r.PostForm.Get("project")
|
|
||||||
username := r.PostForm.Get("username")
|
|
||||||
password := r.PostForm.Get("password")
|
|
||||||
sample := r.PostForm.Get("sample_data")
|
|
||||||
description := r.PostForm.Get("description")
|
|
||||||
domain := r.PostForm.Get("domain")
|
|
||||||
email := r.PostForm.Get("email")
|
|
||||||
|
|
||||||
config := &DbConfig{
|
|
||||||
dbConn,
|
|
||||||
dbHost,
|
|
||||||
dbUser,
|
|
||||||
dbPass,
|
|
||||||
dbDatabase,
|
|
||||||
dbPort,
|
|
||||||
project,
|
|
||||||
description,
|
|
||||||
domain,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
email,
|
|
||||||
nil,
|
|
||||||
}
|
|
||||||
err := config.Save()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
config.Error = err
|
|
||||||
SetupResponseError(w, r, config)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
configs, err = LoadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
config.Error = err
|
|
||||||
SetupResponseError(w, r, config)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = DbConnection(configs.Connection)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
DeleteConfig()
|
|
||||||
config.Error = err
|
|
||||||
SetupResponseError(w, r, config)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
admin := &User{
|
|
||||||
Username: config.Username,
|
|
||||||
Password: config.Password,
|
|
||||||
Email: email,
|
|
||||||
Admin: true,
|
|
||||||
}
|
|
||||||
admin.Create()
|
|
||||||
|
|
||||||
InsertDefaultComms()
|
|
||||||
|
|
||||||
if sample == "on" {
|
|
||||||
go LoadSampleData()
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
mainProcess()
|
|
||||||
}
|
|
||||||
|
|
||||||
func InsertDefaultComms() {
|
|
||||||
emailer := &types.Communication{
|
|
||||||
Method: "email",
|
|
||||||
Removable: false,
|
|
||||||
Enabled: false,
|
|
||||||
}
|
|
||||||
Create(emailer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteConfig() {
|
|
||||||
err := os.Remove("./config.yml")
|
|
||||||
if err != nil {
|
|
||||||
log.Send(3, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorResponse struct {
|
|
||||||
Error string
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) {
|
|
||||||
ExecuteResponse(w, r, "setup.html", a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *DbConfig) Clean() *DbConfig {
|
|
||||||
if os.Getenv("DB_PORT") != "" {
|
|
||||||
if c.DbConn == "postgres" {
|
|
||||||
c.DbHost = c.DbHost + ":" + os.Getenv("DB_PORT")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *DbConfig) Save() error {
|
|
||||||
var err error
|
|
||||||
config, err := os.Create("config.yml")
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := yaml.Marshal(c)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
config.WriteString(string(data))
|
|
||||||
config.Close()
|
|
||||||
|
|
||||||
configs, err = LoadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = DbConnection(configs.Connection)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
DropDatabase()
|
|
||||||
CreateDatabase()
|
|
||||||
|
|
||||||
newCore := Core{
|
|
||||||
c.Project,
|
|
||||||
c.Description,
|
|
||||||
"config.yml",
|
|
||||||
NewSHA1Hash(5),
|
|
||||||
NewSHA1Hash(10),
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
c.Domain,
|
|
||||||
VERSION,
|
|
||||||
[]plugin.Info{},
|
|
||||||
[]PluginJSON{},
|
|
||||||
[]PluginSelect{},
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
}
|
|
||||||
|
|
||||||
col := dbSession.Collection("core")
|
|
||||||
_, err = col.Insert(newCore)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DropDatabase() {
|
|
||||||
fmt.Println("Dropping Tables...")
|
|
||||||
down, _ := sqlBox.String("down.sql")
|
|
||||||
requests := strings.Split(down, ";")
|
|
||||||
for _, request := range requests {
|
|
||||||
_, err := dbSession.Exec(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateDatabase() {
|
|
||||||
fmt.Println("Creating Tables...")
|
|
||||||
sql := "postgres_up.sql"
|
|
||||||
if dbServer == "mysql" {
|
|
||||||
sql = "mysql_up.sql"
|
|
||||||
} else if dbServer == "sqlite3" {
|
|
||||||
sql = "sqlite_up.sql"
|
|
||||||
}
|
|
||||||
up, _ := sqlBox.String(sql)
|
|
||||||
requests := strings.Split(up, ";")
|
|
||||||
for _, request := range requests {
|
|
||||||
_, err := dbSession.Exec(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//secret := NewSHA1Hash()
|
|
||||||
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan()
|
|
||||||
fmt.Println("Database Created")
|
|
||||||
//SampleData()
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadSampleData() error {
|
|
||||||
fmt.Println("Inserting Sample Data...")
|
|
||||||
s1 := &Service{
|
|
||||||
Name: "Google",
|
|
||||||
Domain: "https://google.com",
|
|
||||||
ExpectedStatus: 200,
|
|
||||||
Interval: 10,
|
|
||||||
Port: 0,
|
|
||||||
Type: "https",
|
|
||||||
Method: "GET",
|
|
||||||
}
|
|
||||||
s2 := &Service{
|
|
||||||
Name: "Statup.io",
|
|
||||||
Domain: "https://statup.io",
|
|
||||||
ExpectedStatus: 200,
|
|
||||||
Interval: 15,
|
|
||||||
Port: 0,
|
|
||||||
Type: "https",
|
|
||||||
Method: "GET",
|
|
||||||
}
|
|
||||||
s3 := &Service{
|
|
||||||
Name: "Statup.io SSL Check",
|
|
||||||
Domain: "https://statup.io",
|
|
||||||
ExpectedStatus: 200,
|
|
||||||
Interval: 15,
|
|
||||||
Port: 443,
|
|
||||||
Type: "tcp",
|
|
||||||
}
|
|
||||||
s4 := &Service{
|
|
||||||
Name: "Github Failing Check",
|
|
||||||
Domain: "https://github.com/thisisnotausernamemaybeitis",
|
|
||||||
ExpectedStatus: 200,
|
|
||||||
Interval: 15,
|
|
||||||
Port: 0,
|
|
||||||
Type: "https",
|
|
||||||
Method: "GET",
|
|
||||||
}
|
|
||||||
s1.Create()
|
|
||||||
s2.Create()
|
|
||||||
s3.Create()
|
|
||||||
s4.Create()
|
|
||||||
|
|
||||||
checkin := &Checkin{
|
|
||||||
Service: s2.Id,
|
|
||||||
Interval: 30,
|
|
||||||
Api: NewSHA1Hash(18),
|
|
||||||
}
|
|
||||||
checkin.Create()
|
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
|
||||||
s1.Check()
|
|
||||||
s2.Check()
|
|
||||||
s3.Check()
|
|
||||||
s4.Check()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,70 +1,86 @@
|
||||||
HTML, BODY {
|
HTML, BODY {
|
||||||
background-color: #fcfcfc; }
|
background-color: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
max-width: 860px; }
|
max-width: 860px;
|
||||||
|
}
|
||||||
|
|
||||||
.online_list .badge {
|
.online_list .badge {
|
||||||
margin-top: 0.2rem; }
|
margin-top: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
margin-bottom: 30px; }
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
font-size: 0.75rem; }
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.view_service_btn {
|
.view_service_btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40px;
|
bottom: -40px;
|
||||||
right: 40px; }
|
right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.service_lower_info {
|
.service_lower_info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40px;
|
bottom: -40px;
|
||||||
left: 40px;
|
left: 40px;
|
||||||
color: #d1ffca;
|
color: #d1ffca;
|
||||||
font-size: 0.85rem; }
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
.lg_number {
|
.lg_number {
|
||||||
font-size: 26pt;
|
font-size: 26pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: block;
|
display: block;
|
||||||
color: #474747; }
|
color: #474747;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area {
|
.stats_area {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #a5a5a5; }
|
color: #a5a5a5;
|
||||||
|
}
|
||||||
|
|
||||||
.lower_canvas {
|
.lower_canvas {
|
||||||
height: 55px;
|
height: 55px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #48d338;
|
background-color: #48d338;
|
||||||
padding: 17px 10px; }
|
padding: 17px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.lower_canvas SPAN {
|
.lower_canvas SPAN {
|
||||||
font-size: 1rem; }
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin-top: 20px; }
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer A {
|
.footer A {
|
||||||
color: #aaaaaa;
|
color: #aaaaaa;
|
||||||
text-decoration: none; }
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.footer A:HOVER {
|
.footer A:HOVER {
|
||||||
color: #6d6d6d; }
|
color: #6d6d6d;
|
||||||
|
}
|
||||||
|
|
||||||
.online_badge {
|
.online_badge {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #35b317; }
|
background-color: #35b317;
|
||||||
|
}
|
||||||
|
|
||||||
.offline_badge {
|
.offline_badge {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #c51919; }
|
background-color: #c51919;
|
||||||
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
margin-top: -20px;
|
margin-top: -20px;
|
||||||
|
@ -72,22 +88,27 @@ HTML, BODY {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
width: calc(100% + 40px);
|
width: calc(100% + 40px);
|
||||||
height: 3px;
|
height: 3px;
|
||||||
border-radius: 0; }
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: #fff; }
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
overflow: hidden; }
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body H4 A {
|
.card-body H4 A {
|
||||||
color: #239e07;
|
color: #239e07;
|
||||||
text-decoration: none; }
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
width: 100%; }
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
/* Bootstrap Settings */
|
/* Bootstrap Settings */
|
||||||
|
@ -107,81 +128,100 @@ HTML, BODY {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
/* Code Mirror Settings */
|
/* Code Mirror Settings */
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 60vh; }
|
height: 60vh;
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror-focused {
|
.CodeMirror-focused {
|
||||||
/* Bootstrap Settings */
|
/* Bootstrap Settings */
|
||||||
border-color: #66afe9;
|
border-color: #66afe9;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; }
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.sm-container {
|
.sm-container {
|
||||||
margin-top: 40px !important;
|
margin-top: 40px !important;
|
||||||
padding: 0 !important; }
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item H5 {
|
.list-group-item H5 {
|
||||||
font-size: 0.9rem; }
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0 !important; }
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0; }
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
font-size: 6pt;
|
font-size: 6pt;
|
||||||
padding: 5px 5px; }
|
padding: 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.lg_number {
|
.lg_number {
|
||||||
font-size: 1.5rem; }
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area {
|
.stats_area {
|
||||||
margin-top: 35px !important;
|
margin-top: 35px !important;
|
||||||
margin-bottom: 35px !important; }
|
margin-bottom: 35px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area .col-4 {
|
.stats_area .col-4 {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0; }
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.lower_canvas SPAN {
|
.lower_canvas SPAN {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
float: left; }
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
line-height: 0.9rem;
|
line-height: 0.9rem;
|
||||||
font-size: 0.65rem; }
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
.full-col-12 {
|
.full-col-12 {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-right: 0px; }
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0; }
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item {
|
.list-group-item {
|
||||||
border-top: 1px solid #e4e4e4;
|
border-top: 1px solid #e4e4e4;
|
||||||
border: 0px; }
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item:first-child {
|
.list-group-item:first-child {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0; }
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item:last-child {
|
.list-group-item:last-child {
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
border-bottom-left-radius: 0; }
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item P {
|
.list-group-item P {
|
||||||
font-size: 0.7rem; } }
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=base.css.map */
|
/*# sourceMappingURL=base.css.map */
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":["../scss/base.scss","../scss/variables.scss"],"names":[],"mappings":"AAGA;EACI,kBCJe;;;ADOnB;EACI;EACA;EACA,WCTQ;;;ADYZ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI,WClCc;EDmCd;EACA;EACA,OCtCe;;;ADyCnB;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI,kBC5Fc;;;AD+FlB;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;EACA;;;AASJ;AACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGF;AACE;EACA;EACA;EACA;EACA;;;AAIF;EAEI;IACI;IACA;;;EAGJ;IACI;;;EAGJ;IACI;;;EAGJ;IACI;IACA;IACA;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI;IACA;;;EAGJ;IACI","file":"base.css"}
|
|
@ -6,6 +6,13 @@ $(".service_li").on('click', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$(".disable_click").on('click', function() {
|
||||||
|
$(this).prop("disabled", true);
|
||||||
|
$(this).text("Loading...");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var ranVar = false;
|
var ranVar = false;
|
||||||
var ranTheme = false;
|
var ranTheme = false;
|
||||||
$('a[data-toggle="pill"]').on('shown.bs.tab', function (e) {
|
$('a[data-toggle="pill"]').on('shown.bs.tab', function (e) {
|
|
@ -1,10 +1,6 @@
|
||||||
var currentLocation = window.location;
|
var currentLocation = window.location;
|
||||||
$("#domain_input").val(currentLocation.origin);
|
$("#domain_input").val(currentLocation.origin);
|
||||||
|
|
||||||
function forceLower(strInput) {
|
|
||||||
strInput.value=strInput.value.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
$('select#database_type').on('change', function(){
|
$('select#database_type').on('change', function(){
|
||||||
var selected = $('#database_type option:selected').val();
|
var selected = $('#database_type option:selected').val();
|
||||||
if (selected=="sqlite") {
|
if (selected=="sqlite") {
|
|
@ -5,14 +5,12 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
||||||
|
|
||||||
{{if .Core.OfflineAssets}}
|
{{if .Core.OfflineAssets}}
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://assets.statup.io/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="https://statup.io/base.css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
|
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="/css/base.css">
|
|
||||||
<script src="/js/Chart.bundle.min.js"></script>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
<link rel="stylesheet" href="/css/base.css">
|
||||||
|
|
||||||
<title>{{.Core.Name}} Status</title>
|
<title>{{.Core.Name}} Status</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -96,6 +94,17 @@
|
||||||
|
|
||||||
{{template "footer"}}
|
{{template "footer"}}
|
||||||
|
|
||||||
|
|
||||||
|
{{if .Core.OfflineAssets}}
|
||||||
|
<script src="https://assets.statup.io/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="https://assets.statup.io/bootstrap.min.js"></script>
|
||||||
|
<script src="https://assets.statup.io/Chart.bundle.min.js"></script>
|
||||||
|
{{ else }}
|
||||||
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/js/Chart.bundle.min.js"></script>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
{{ range .Services }}
|
{{ range .Services }}
|
||||||
{{ if .AvgTime }}
|
{{ if .AvgTime }}
|
||||||
|
@ -247,6 +256,7 @@ var chartdata = new Chart(ctx, {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script src="/js/main.js"></script>
|
||||||
|
|
||||||
{{ if .Core.Style }}
|
{{ if .Core.Style }}
|
||||||
<style>
|
<style>
|
||||||
|
@ -255,14 +265,5 @@ var chartdata = new Chart(ctx, {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
{{if .Core.OfflineAssets}}
|
|
||||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
|
||||||
{{ else }}
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
|
||||||
<script src="/js/bootstrap.min.js"></script>
|
|
||||||
<script src="/js/main.js"></script>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -5,7 +5,6 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
||||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="/css/base.css">
|
<link rel="stylesheet" href="/css/base.css">
|
||||||
<script src="/js/Chart.bundle.min.js"></script>
|
|
||||||
|
|
||||||
<title>Statup | Setup</title>
|
<title>Statup | Setup</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -77,7 +76,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="formGroupExampleInput">Admin Username</label>
|
<label for="formGroupExampleInput">Admin Username</label>
|
||||||
<input type="text" name="username" class="form-control" value="{{.Username}}" id="formGroupExampleInput" value="admin" placeholder="admin" onkeyup="return forceLower(this);" required>
|
<input type="text" name="username" class="form-control" value="{{.Username}}" id="formGroupExampleInput" value="admin" placeholder="admin" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
|
@ -1,6 +1,44 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id int64 `db:"id,omitempty" json:"id"`
|
||||||
|
Username string `db:"username" json:"username"`
|
||||||
|
Password string `db:"password" json:"-"`
|
||||||
|
Email string `db:"email" json:"-"`
|
||||||
|
ApiKey string `db:"api_key" json:"api_key"`
|
||||||
|
ApiSecret string `db:"api_secret" json:"-"`
|
||||||
|
Admin bool `db:"administrator" json:"admin"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hit struct {
|
||||||
|
Id int `db:"id,omitempty"`
|
||||||
|
Service int64 `db:"service"`
|
||||||
|
Latency float64 `db:"latency"`
|
||||||
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Failure struct {
|
||||||
|
Id int `db:"id,omitempty"`
|
||||||
|
Issue string `db:"issue"`
|
||||||
|
Method string `db:"method"`
|
||||||
|
Service int64 `db:"service"`
|
||||||
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Checkin struct {
|
||||||
|
Id int `db:"id,omitempty"`
|
||||||
|
Service int64 `db:"service"`
|
||||||
|
Interval int64 `db:"check_interval"`
|
||||||
|
Api string `db:"api"`
|
||||||
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
Hits int64 `json:"hits"`
|
||||||
|
Last time.Time `json:"last"`
|
||||||
|
}
|
||||||
|
|
||||||
type Communication struct {
|
type Communication struct {
|
||||||
Id int64 `db:"id,omitempty" json:"id"`
|
Id int64 `db:"id,omitempty" json:"id"`
|
||||||
|
@ -25,3 +63,45 @@ type Email struct {
|
||||||
Template string
|
Template string
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Connection string `yaml:"connection"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Database string `yaml:"database"`
|
||||||
|
User string `yaml:"user"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DbConfig struct {
|
||||||
|
DbConn string `yaml:"connection"`
|
||||||
|
DbHost string `yaml:"host"`
|
||||||
|
DbUser string `yaml:"user"`
|
||||||
|
DbPass string `yaml:"password"`
|
||||||
|
DbData string `yaml:"database"`
|
||||||
|
DbPort int `yaml:"port"`
|
||||||
|
Project string `yaml:"-"`
|
||||||
|
Description string `yaml:"-"`
|
||||||
|
Domain string `yaml:"-"`
|
||||||
|
Username string `yaml:"-"`
|
||||||
|
Password string `yaml:"-"`
|
||||||
|
Email string `yaml:"-"`
|
||||||
|
Error error `yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginRepos struct {
|
||||||
|
Plugins []PluginJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginJSON struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Repo string `json:"repo"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FailureData struct {
|
||||||
|
Issue string
|
||||||
|
}
|
||||||
|
|
94
users.go
94
users.go
|
@ -1,94 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Id int64 `db:"id,omitempty" json:"id"`
|
|
||||||
Username string `db:"username" json:"username"`
|
|
||||||
Password string `db:"password" json:"-"`
|
|
||||||
Email string `db:"email" json:"-"`
|
|
||||||
ApiKey string `db:"api_key" json:"api_key"`
|
|
||||||
ApiSecret string `db:"api_secret" json:"-"`
|
|
||||||
Admin bool `db:"administrator" json:"admin"`
|
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func SessionUser(r *http.Request) *User {
|
|
||||||
session, _ := store.Get(r, cookieKey)
|
|
||||||
if session == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
uuid := session.Values["user_id"]
|
|
||||||
var user *User
|
|
||||||
col := dbSession.Collection("users")
|
|
||||||
res := col.Find("id", uuid)
|
|
||||||
res.One(&user)
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectUser(id int64) (*User, error) {
|
|
||||||
var user User
|
|
||||||
col := dbSession.Collection("users")
|
|
||||||
res := col.Find("id", id)
|
|
||||||
err := res.One(&user)
|
|
||||||
return &user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectUsername(username string) (*User, error) {
|
|
||||||
var user User
|
|
||||||
col := dbSession.Collection("users")
|
|
||||||
res := col.Find("username", username)
|
|
||||||
err := res.One(&user)
|
|
||||||
return &user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) Delete() error {
|
|
||||||
col := dbSession.Collection("users")
|
|
||||||
user := col.Find("id", u.Id)
|
|
||||||
return user.Delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) Create() (int64, error) {
|
|
||||||
u.CreatedAt = time.Now()
|
|
||||||
u.Password = HashPassword(u.Password)
|
|
||||||
u.ApiKey = NewSHA1Hash(5)
|
|
||||||
u.ApiSecret = NewSHA1Hash(10)
|
|
||||||
col := dbSession.Collection("users")
|
|
||||||
uuid, err := col.Insert(u)
|
|
||||||
if uuid == nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
OnNewUser(u)
|
|
||||||
return uuid.(int64), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectAllUsers() ([]User, error) {
|
|
||||||
var users []User
|
|
||||||
col := dbSession.Collection("users").Find()
|
|
||||||
err := col.All(&users)
|
|
||||||
return users, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func AuthUser(username, password string) (*User, bool) {
|
|
||||||
var auth bool
|
|
||||||
user, err := SelectUsername(username)
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
if CheckHash(password, user.Password) {
|
|
||||||
auth = true
|
|
||||||
}
|
|
||||||
return user, auth
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckHash(password, hash string) bool {
|
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
||||||
return err == nil
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HashPassword(password string) string {
|
||||||
|
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSHA1Hash(n ...int) string {
|
||||||
|
noRandomCharacters := 32
|
||||||
|
if len(n) > 0 {
|
||||||
|
noRandomCharacters = n[0]
|
||||||
|
}
|
||||||
|
randString := RandomString(noRandomCharacters)
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write([]byte(randString))
|
||||||
|
bs := hash.Sum(nil)
|
||||||
|
return fmt.Sprintf("%x", bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
var characterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
|
|
||||||
|
// RandomString generates a random string of n length
|
||||||
|
func RandomString(n int) string {
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = characterRunes[rand.Intn(len(characterRunes))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
lg "log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logFile *os.File
|
||||||
|
logLevel int
|
||||||
|
fmtLogs *lg.Logger
|
||||||
|
ljLogger *lumberjack.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
logFile, err = os.OpenFile("./statup.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
lg.Fatalf("error opening file: %v", err)
|
||||||
|
}
|
||||||
|
ljLogger = &lumberjack.Logger{
|
||||||
|
Filename: "./statup.log",
|
||||||
|
MaxSize: 16,
|
||||||
|
MaxBackups: 3,
|
||||||
|
MaxAge: 28,
|
||||||
|
}
|
||||||
|
fmtLogs = lg.New(logFile, "", lg.Ldate|lg.Ltime)
|
||||||
|
fmtLogs.SetOutput(ljLogger)
|
||||||
|
|
||||||
|
logEnv := os.Getenv("LOG")
|
||||||
|
|
||||||
|
if logEnv == "fatal" {
|
||||||
|
logLevel = 3
|
||||||
|
} else if logEnv == "debug" {
|
||||||
|
logLevel = 2
|
||||||
|
} else if logEnv == "info" {
|
||||||
|
logLevel = 1
|
||||||
|
} else {
|
||||||
|
logLevel = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotate() {
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, syscall.SIGHUP)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-c
|
||||||
|
ljLogger.Rotate()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Panic(err interface{}) {
|
||||||
|
lg.Printf("PANIC: %v\n", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Log(level int, err interface{}) {
|
||||||
|
switch level {
|
||||||
|
case 5:
|
||||||
|
lg.Fatalf("PANIC: %v\n", err)
|
||||||
|
case 4:
|
||||||
|
lg.Printf("FATAL: %v\n", err)
|
||||||
|
//color.Red("ERROR: %v\n", err)
|
||||||
|
//os.Exit(2)
|
||||||
|
case 3:
|
||||||
|
lg.Printf("ERROR: %v\n", err)
|
||||||
|
//color.Red("ERROR: %v\n", err)
|
||||||
|
case 2:
|
||||||
|
lg.Printf("WARNING: %v\n", err)
|
||||||
|
//color.Yellow("WARNING: %v\n", err)
|
||||||
|
case 1:
|
||||||
|
lg.Printf("INFO: %v\n", err)
|
||||||
|
//color.Blue("INFO: %v\n", err)
|
||||||
|
case 0:
|
||||||
|
lg.Printf("%v\n", err)
|
||||||
|
color.White("%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Http(r *http.Request) {
|
||||||
|
msg := fmt.Sprintf("%v (%v) | IP: %v", r.RequestURI, r.Method, r.Host)
|
||||||
|
lg.Printf("WEB: %v\n", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReportLog() {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StringInt(s string) int64 {
|
||||||
|
num, _ := strconv.Atoi(s)
|
||||||
|
return int64(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnderScoreString(str string) string {
|
||||||
|
|
||||||
|
// convert every letter to lower case
|
||||||
|
newStr := strings.ToLower(str)
|
||||||
|
|
||||||
|
// convert all spaces/tab to underscore
|
||||||
|
regExp := regexp.MustCompile("[[:space:][:blank:]]")
|
||||||
|
newStrByte := regExp.ReplaceAll([]byte(newStr), []byte("_"))
|
||||||
|
|
||||||
|
regExp = regexp.MustCompile("`[^a-z0-9]`i")
|
||||||
|
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
||||||
|
|
||||||
|
regExp = regexp.MustCompile("[!/']")
|
||||||
|
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
||||||
|
|
||||||
|
// and remove underscore from beginning and ending
|
||||||
|
|
||||||
|
newStr = strings.TrimPrefix(string(newStrByte), "_")
|
||||||
|
newStr = strings.TrimSuffix(newStr, "_")
|
||||||
|
|
||||||
|
return newStr
|
||||||
|
}
|
650
web.go
650
web.go
|
@ -1,650 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/fatih/structs"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
"github.com/hunterlong/statup/log"
|
|
||||||
"github.com/hunterlong/statup/types"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
cookieKey = "statup_auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Router() *mux.Router {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
|
||||||
|
|
||||||
if useAssets {
|
|
||||||
cssHandler := http.FileServer(http.Dir("./assets/css"))
|
|
||||||
jsHandler := http.FileServer(http.Dir("./assets/js"))
|
|
||||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cssHandler))
|
|
||||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", jsHandler))
|
|
||||||
} else {
|
|
||||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox())))
|
|
||||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(jsBox.HTTPBox())))
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Handle("/robots.txt", http.HandlerFunc(RobotsTxtHandler)).Methods("GET")
|
|
||||||
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
|
||||||
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
|
||||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
|
||||||
r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
|
|
||||||
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
|
||||||
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")
|
|
||||||
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler)).Methods("GET")
|
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
|
||||||
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
|
||||||
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
|
||||||
r.Handle("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler))
|
|
||||||
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
|
|
||||||
r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST")
|
|
||||||
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
|
|
||||||
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
|
||||||
r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
|
|
||||||
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET")
|
|
||||||
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
|
|
||||||
r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST")
|
|
||||||
r.Handle("/settings/build", http.HandlerFunc(SaveAssetsHandler)).Methods("GET")
|
|
||||||
r.Handle("/settings/email", http.HandlerFunc(SaveEmailSettingsHandler)).Methods("POST")
|
|
||||||
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
|
||||||
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
|
|
||||||
r.Handle("/help", http.HandlerFunc(HelpHandler))
|
|
||||||
r.Handle("/api", http.HandlerFunc(ApiIndexHandler))
|
|
||||||
r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler))
|
|
||||||
r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler))
|
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET")
|
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
|
|
||||||
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
|
||||||
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
|
||||||
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler)).Methods("GET")
|
|
||||||
store = sessions.NewCookieStore([]byte("secretinfo"))
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunHTTPServer() {
|
|
||||||
fmt.Println("Statup HTTP Server running on http://localhost:8080")
|
|
||||||
r := Router()
|
|
||||||
for _, p := range allPlugins {
|
|
||||||
info := p.GetInfo()
|
|
||||||
for _, route := range p.Routes() {
|
|
||||||
path := fmt.Sprintf("/plugins/%v/%v", info.Name, route.URL)
|
|
||||||
r.Handle(path, http.HandlerFunc(route.Handler)).Methods(route.Method)
|
|
||||||
fmt.Printf("Added Route %v for plugin %v\n", path, info.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: "0.0.0.0:8080",
|
|
||||||
WriteTimeout: time.Second * 15,
|
|
||||||
ReadTimeout: time.Second * 15,
|
|
||||||
IdleTimeout: time.Second * 60,
|
|
||||||
Handler: r,
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.ListenAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session, _ := store.Get(r, cookieKey)
|
|
||||||
session.Values["authenticated"] = false
|
|
||||||
session.Save(r, w)
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session, _ := store.Get(r, cookieKey)
|
|
||||||
r.ParseForm()
|
|
||||||
username := r.PostForm.Get("username")
|
|
||||||
password := r.PostForm.Get("password")
|
|
||||||
user, auth := AuthUser(username, password)
|
|
||||||
if auth {
|
|
||||||
session.Values["authenticated"] = true
|
|
||||||
session.Values["user_id"] = user.Id
|
|
||||||
session.Save(r, w)
|
|
||||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
||||||
} else {
|
|
||||||
err := ErrorResponse{Error: "Incorrect login information submitted, try again."}
|
|
||||||
ExecuteResponse(w, r, "login.html", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("creating user")
|
|
||||||
r.ParseForm()
|
|
||||||
username := r.PostForm.Get("username")
|
|
||||||
password := r.PostForm.Get("password")
|
|
||||||
email := r.PostForm.Get("email")
|
|
||||||
user := &User{
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
Email: email,
|
|
||||||
}
|
|
||||||
_, err := user.Create()
|
|
||||||
if err != nil {
|
|
||||||
log.Send(2, err)
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("service adding")
|
|
||||||
r.ParseForm()
|
|
||||||
name := r.PostForm.Get("name")
|
|
||||||
domain := r.PostForm.Get("domain")
|
|
||||||
method := r.PostForm.Get("method")
|
|
||||||
expected := r.PostForm.Get("expected")
|
|
||||||
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
|
||||||
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
|
||||||
port, _ := strconv.Atoi(r.PostForm.Get("port"))
|
|
||||||
checkType := r.PostForm.Get("check_type")
|
|
||||||
|
|
||||||
service := &Service{
|
|
||||||
Name: name,
|
|
||||||
Domain: domain,
|
|
||||||
Method: method,
|
|
||||||
Expected: expected,
|
|
||||||
ExpectedStatus: status,
|
|
||||||
Interval: interval,
|
|
||||||
Type: checkType,
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
_, err := service.Create()
|
|
||||||
if err != nil {
|
|
||||||
go service.CheckQueue()
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Printf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr)
|
|
||||||
metrics := []string{}
|
|
||||||
system := fmt.Sprintf("statup_total_failures %v\n", CountFailures())
|
|
||||||
system += fmt.Sprintf("statup_total_services %v", len(services))
|
|
||||||
metrics = append(metrics, system)
|
|
||||||
|
|
||||||
for _, v := range services {
|
|
||||||
online := 1
|
|
||||||
if !v.Online {
|
|
||||||
online = 0
|
|
||||||
}
|
|
||||||
met := fmt.Sprintf("statup_service_failures{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, len(v.Failures))
|
|
||||||
met += fmt.Sprintf("statup_service_latency{id=\"%v\" name=\"%v\"} %0.0f\n", v.Id, v.Name, (v.Latency * 100))
|
|
||||||
met += fmt.Sprintf("statup_service_online{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, online)
|
|
||||||
met += fmt.Sprintf("statup_service_status_code{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, v.LastStatusCode)
|
|
||||||
met += fmt.Sprintf("statup_service_response_length{id=\"%v\" name=\"%v\"} %v", v.Id, v.Name, len([]byte(v.LastResponse)))
|
|
||||||
metrics = append(metrics, met)
|
|
||||||
}
|
|
||||||
output := strings.Join(metrics, "\n")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
func RobotsTxtHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
robots := []byte(`User-agent: *
|
|
||||||
Disallow: /login
|
|
||||||
Disallow: /dashboard
|
|
||||||
|
|
||||||
Host: ` + core.Domain)
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(robots))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if core != nil {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
port := 5432
|
|
||||||
if os.Getenv("DB_CONN") == "mysql" {
|
|
||||||
port = 3306
|
|
||||||
}
|
|
||||||
var data interface{}
|
|
||||||
if os.Getenv("DB_CONN") != "" {
|
|
||||||
data = &DbConfig{
|
|
||||||
DbConn: os.Getenv("DB_CONN"),
|
|
||||||
DbHost: os.Getenv("DB_HOST"),
|
|
||||||
DbUser: os.Getenv("DB_USER"),
|
|
||||||
DbPass: os.Getenv("DB_PASS"),
|
|
||||||
DbData: os.Getenv("DB_DATABASE"),
|
|
||||||
DbPort: port,
|
|
||||||
Project: os.Getenv("NAME"),
|
|
||||||
Description: os.Getenv("DESCRIPTION"),
|
|
||||||
Email: "",
|
|
||||||
Username: "admin",
|
|
||||||
Password: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExecuteResponse(w, r, "setup.html", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
type index struct {
|
|
||||||
Core Core
|
|
||||||
Services []*Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if core == nil {
|
|
||||||
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
out := index{*core, services}
|
|
||||||
|
|
||||||
first, _ := out.Services[0].LimitedHits()
|
|
||||||
|
|
||||||
fmt.Println(out.Services[0].Name, "start:", first[0].Id, "last:", first[len(first)-1].Id)
|
|
||||||
|
|
||||||
ExecuteResponse(w, r, "index.html", out)
|
|
||||||
}
|
|
||||||
|
|
||||||
type dashboard struct {
|
|
||||||
Services []*Service
|
|
||||||
Core *Core
|
|
||||||
CountOnline int
|
|
||||||
CountServices int
|
|
||||||
Count24Failures uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
err := ErrorResponse{}
|
|
||||||
ExecuteResponse(w, r, "login.html", err)
|
|
||||||
} else {
|
|
||||||
fails := CountFailures()
|
|
||||||
out := dashboard{services, core, CountOnline(), len(services), fails}
|
|
||||||
ExecuteResponse(w, r, "dashboard.html", out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type serviceHandler struct {
|
|
||||||
Service Service
|
|
||||||
Auth bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ExecuteResponse(w, r, "services.html", services)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
service.Delete()
|
|
||||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
|
|
||||||
service.DeleteFailures()
|
|
||||||
services, _ = SelectAllServices()
|
|
||||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsAuthenticated(r *http.Request) bool {
|
|
||||||
if os.Getenv("GO_ENV") == "test" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if core == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if store == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
session, err := store.Get(r, cookieKey)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if session.Values["authenticated"] == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return session.Values["authenticated"].(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveEmailSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
emailer := SelectCommunication(1)
|
|
||||||
|
|
||||||
r.ParseForm()
|
|
||||||
emailer.Host = r.PostForm.Get("host")
|
|
||||||
emailer.Username = r.PostForm.Get("username")
|
|
||||||
emailer.Password = r.PostForm.Get("password")
|
|
||||||
emailer.Port = int(StringInt(r.PostForm.Get("port")))
|
|
||||||
emailer.Var1 = r.PostForm.Get("address")
|
|
||||||
Update(emailer)
|
|
||||||
|
|
||||||
sample := &types.Email{
|
|
||||||
To: SessionUser(r).Email,
|
|
||||||
Subject: "Sample Email",
|
|
||||||
Template: "error.html",
|
|
||||||
}
|
|
||||||
AddEmail(sample)
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
CreateAllAssets()
|
|
||||||
useAssets = true
|
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveSASSHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.ParseForm()
|
|
||||||
theme := r.PostForm.Get("theme")
|
|
||||||
variables := r.PostForm.Get("variables")
|
|
||||||
SaveAsset(theme, "scss/base.scss")
|
|
||||||
SaveAsset(variables, "scss/variables.scss")
|
|
||||||
CompileSASS()
|
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.ParseForm()
|
|
||||||
name := r.PostForm.Get("project")
|
|
||||||
if name != "" {
|
|
||||||
core.Name = name
|
|
||||||
}
|
|
||||||
description := r.PostForm.Get("description")
|
|
||||||
if description != core.Description {
|
|
||||||
core.Description = description
|
|
||||||
}
|
|
||||||
style := r.PostForm.Get("style")
|
|
||||||
if style != core.Style {
|
|
||||||
core.Style = style
|
|
||||||
}
|
|
||||||
footer := r.PostForm.Get("footer")
|
|
||||||
if footer != core.Footer {
|
|
||||||
core.Footer = footer
|
|
||||||
}
|
|
||||||
domain := r.PostForm.Get("domain")
|
|
||||||
if domain != core.Domain {
|
|
||||||
core.Domain = domain
|
|
||||||
}
|
|
||||||
core.Update()
|
|
||||||
OnSettingsSaved(core)
|
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PluginsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
core.FetchPluginRepo()
|
|
||||||
|
|
||||||
var pluginFields []PluginSelect
|
|
||||||
|
|
||||||
for _, p := range allPlugins {
|
|
||||||
fields := structs.Map(p.GetInfo())
|
|
||||||
|
|
||||||
pluginFields = append(pluginFields, PluginSelect{p.GetInfo().Name, p.GetForm(), fields})
|
|
||||||
}
|
|
||||||
|
|
||||||
core.PluginFields = pluginFields
|
|
||||||
fmt.Println(core.Communications)
|
|
||||||
|
|
||||||
ExecuteResponse(w, r, "plugins.html", core)
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginSelect struct {
|
|
||||||
Plugin string
|
|
||||||
Form string
|
|
||||||
Params map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PluginSavedHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.ParseForm()
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
plug := SelectPlugin(vars["name"])
|
|
||||||
data := make(map[string]string)
|
|
||||||
for k, v := range r.PostForm {
|
|
||||||
data[k] = strings.Join(v, "")
|
|
||||||
}
|
|
||||||
plug.OnSave(structs.Map(data))
|
|
||||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//vars := mux.Vars(r)
|
|
||||||
//name := vars["name"]
|
|
||||||
//DownloadPlugin(name)
|
|
||||||
LoadConfig()
|
|
||||||
http.Redirect(w, r, "/plugins", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HelpHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ExecuteResponse(w, r, "help.html", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
interval := StringInt(r.PostForm.Get("interval"))
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
checkin := &Checkin{
|
|
||||||
Service: service.Id,
|
|
||||||
Interval: interval,
|
|
||||||
Api: NewSHA1Hash(18),
|
|
||||||
}
|
|
||||||
checkin.Create()
|
|
||||||
fmt.Println(checkin.Create())
|
|
||||||
ExecuteResponse(w, r, "service.html", service)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
r.ParseForm()
|
|
||||||
name := r.PostForm.Get("name")
|
|
||||||
domain := r.PostForm.Get("domain")
|
|
||||||
method := r.PostForm.Get("method")
|
|
||||||
expected := r.PostForm.Get("expected")
|
|
||||||
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
|
||||||
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
|
||||||
port, _ := strconv.Atoi(r.PostForm.Get("port"))
|
|
||||||
checkType := r.PostForm.Get("check_type")
|
|
||||||
service = &Service{
|
|
||||||
Name: name,
|
|
||||||
Domain: domain,
|
|
||||||
Method: method,
|
|
||||||
Expected: expected,
|
|
||||||
ExpectedStatus: status,
|
|
||||||
Interval: interval,
|
|
||||||
Type: checkType,
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
service.Update()
|
|
||||||
ExecuteResponse(w, r, "service.html", service)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
|
|
||||||
var badge []byte
|
|
||||||
if service.Online {
|
|
||||||
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">` + service.Name + `</text><text x="28" y="14">` + service.Name + `</text><text x="78" y="15" fill="#010101" fill-opacity=".3">online</text><text x="78" y="14">online</text></g></svg>`)
|
|
||||||
} else {
|
|
||||||
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">` + service.Name + `</text><text x="28" y="14">` + service.Name + `</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">offline</text><text x="75.5" y="14">offline</text></g></svg>`)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "image/svg+xml")
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
w.Write(badge)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
service := SelectService(StringInt(vars["id"]))
|
|
||||||
ExecuteResponse(w, r, "service.html", service)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
|
||||||
nav, _ := tmplBox.String("nav.html")
|
|
||||||
footer, _ := tmplBox.String("footer.html")
|
|
||||||
render, err := tmplBox.String(file)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
t := template.New("message")
|
|
||||||
t.Funcs(template.FuncMap{
|
|
||||||
"js": func(html string) template.JS {
|
|
||||||
return template.JS(html)
|
|
||||||
},
|
|
||||||
"safe": func(html string) template.HTML {
|
|
||||||
return template.HTML(html)
|
|
||||||
},
|
|
||||||
"Auth": func() bool {
|
|
||||||
return IsAuthenticated(r)
|
|
||||||
},
|
|
||||||
"VERSION": func() string {
|
|
||||||
return VERSION
|
|
||||||
},
|
|
||||||
"underscore": func(html string) string {
|
|
||||||
return UnderScoreString(html)
|
|
||||||
},
|
|
||||||
"User": func() *User {
|
|
||||||
return SessionUser(r)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
t, _ = t.Parse(nav)
|
|
||||||
t, _ = t.Parse(footer)
|
|
||||||
t.Parse(render)
|
|
||||||
t.Execute(w, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
users, _ := SelectAllUsers()
|
|
||||||
ExecuteResponse(w, r, "users.html", users)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
auth := IsAuthenticated(r)
|
|
||||||
if !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
id, _ := strconv.Atoi(vars["id"])
|
|
||||||
user, _ := SelectUser(int64(id))
|
|
||||||
|
|
||||||
users, _ := SelectAllUsers()
|
|
||||||
if len(users) == 1 {
|
|
||||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user.Delete()
|
|
||||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnderScoreString(str string) string {
|
|
||||||
|
|
||||||
// convert every letter to lower case
|
|
||||||
newStr := strings.ToLower(str)
|
|
||||||
|
|
||||||
// convert all spaces/tab to underscore
|
|
||||||
regExp := regexp.MustCompile("[[:space:][:blank:]]")
|
|
||||||
newStrByte := regExp.ReplaceAll([]byte(newStr), []byte("_"))
|
|
||||||
|
|
||||||
regExp = regexp.MustCompile("`[^a-z0-9]`i")
|
|
||||||
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
|
||||||
|
|
||||||
regExp = regexp.MustCompile("[!/']")
|
|
||||||
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
|
||||||
|
|
||||||
// and remove underscore from beginning and ending
|
|
||||||
|
|
||||||
newStr = strings.TrimPrefix(string(newStrByte), "_")
|
|
||||||
newStr = strings.TrimSuffix(newStr, "_")
|
|
||||||
|
|
||||||
return newStr
|
|
||||||
}
|
|
75
web_test.go
75
web_test.go
|
@ -1,75 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestServiceUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
req, err := http.NewRequest("GET", "/service/1", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
assert.Equal(t, 28, len(rr.Body.Bytes()), "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiAllServiceUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
req, err := http.NewRequest("GET", "/api/services", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
var data []Service
|
|
||||||
json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
assert.Equal(t, "Google", data[0].Name, "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiServiceUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
req, err := http.NewRequest("GET", "/api/services/1", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
var data Service
|
|
||||||
json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
assert.Equal(t, "Google", data.Name, "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiServiceUpdateUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
payload := []byte(`{"name":"test product - updated name","price":11.22}`)
|
|
||||||
req, err := http.NewRequest("POST", "/api/services/1", bytes.NewBuffer(payload))
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
var data Service
|
|
||||||
json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
assert.Equal(t, "Google", data.Name, "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiUserUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
req, err := http.NewRequest("GET", "/api/users/1", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
var data User
|
|
||||||
json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
assert.Equal(t, "testuserhere", data.Username, "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiAllUsersUrl(t *testing.T) {
|
|
||||||
t.SkipNow()
|
|
||||||
req, err := http.NewRequest("GET", "/api/users", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
var data []User
|
|
||||||
json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
assert.Equal(t, "testuserhere", data[0].Username, "should be balance")
|
|
||||||
}
|
|
Loading…
Reference in New Issue