mirror of https://github.com/statping/statping
new 2
parent
4ff0ee5a0d
commit
ae92209808
|
@ -18,7 +18,7 @@ services:
|
|||
|
||||
env:
|
||||
global:
|
||||
- VERSION=0.27.4
|
||||
- VERSION=0.27.6
|
||||
- DB_HOST=localhost
|
||||
- DB_USER=travis
|
||||
- DB_PASS=
|
||||
|
@ -51,8 +51,6 @@ deploy:
|
|||
notifications:
|
||||
email: false
|
||||
|
||||
before_install:
|
||||
|
||||
before_script:
|
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||
- psql -c 'create database test;' -U postgres
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
# RENDERING CSS
|
||||
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
|
||||
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
|
||||
rice embed-go
|
||||
|
|
|
@ -20,7 +20,6 @@ curl -s -X POST \
|
|||
-d "$body" \
|
||||
https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
||||
|
||||
|
||||
if [ "$TRAVIS_BRANCH" == "master" ]
|
||||
then
|
||||
curl -X POST $DOCKER > /dev/null
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM alpine:latest
|
||||
|
||||
ENV VERSION=v0.27.4
|
||||
ENV VERSION=v0.27.6
|
||||
|
||||
RUN apk --no-cache add libstdc++ ca-certificates
|
||||
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 (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"github.com/joho/godotenv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CatchCLI(args []string) {
|
||||
|
@ -13,65 +13,67 @@ func CatchCLI(args []string) {
|
|||
fmt.Printf("Statup v%v\n", VERSION)
|
||||
case "assets":
|
||||
RenderBoxes()
|
||||
CreateAllAssets()
|
||||
core.CreateAllAssets()
|
||||
case "sass":
|
||||
CompileSASS()
|
||||
core.CompileSASS()
|
||||
case "export":
|
||||
var err error
|
||||
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
RenderBoxes()
|
||||
configs, err = LoadConfig()
|
||||
core.Configs, err = core.LoadConfig()
|
||||
if err != nil {
|
||||
log.Send(3, "config.yml file not found")
|
||||
utils.Log(4, "config.yml file not found")
|
||||
}
|
||||
setupMode = true
|
||||
mainProcess()
|
||||
time.Sleep(10 * time.Second)
|
||||
indexSource := ExportIndexHTML()
|
||||
err = SaveFile("./index.html", []byte(indexSource))
|
||||
RunOnce()
|
||||
indexSource := core.ExportIndexHTML()
|
||||
err = core.SaveFile("./index.html", []byte(indexSource))
|
||||
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":
|
||||
HelpEcho()
|
||||
case "update":
|
||||
fmt.Println("Sorry updating isn't available yet!")
|
||||
case "run":
|
||||
log.Send(1, "Running 1 time and saving to database...")
|
||||
var err error
|
||||
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)
|
||||
}
|
||||
utils.Log(1, "Running 1 time and saving to database...")
|
||||
RunOnce()
|
||||
fmt.Println("Check is complete.")
|
||||
case "env":
|
||||
fmt.Println("Statup Environment Variables")
|
||||
envs, err := godotenv.Read(".env")
|
||||
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 {
|
||||
fmt.Printf("%v=%v\n", k, e)
|
||||
}
|
||||
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 (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FailureData types.FailureData
|
||||
|
||||
func CheckServices() {
|
||||
services, _ = SelectAllServices()
|
||||
log.Send(1, fmt.Sprintf("Loaded %v Services", len(services)))
|
||||
for _, v := range services {
|
||||
CoreApp.Services, _ = SelectAllServices()
|
||||
utils.Log(1, fmt.Sprintf("Loaded %v Services", len(CoreApp.Services)))
|
||||
for _, v := range CoreApp.Services {
|
||||
obj := v
|
||||
go obj.StartCheckins()
|
||||
go obj.CheckQueue()
|
||||
|
@ -26,7 +29,7 @@ func (s *Service) CheckQueue() {
|
|||
s.Interval = 1
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -80,17 +83,13 @@ func (s *Service) Record(response *http.Response) {
|
|||
OnSuccess(s)
|
||||
}
|
||||
|
||||
type FailureData struct {
|
||||
Issue string
|
||||
}
|
||||
|
||||
func (s *Service) Failure(issue string) {
|
||||
s.Online = false
|
||||
data := FailureData{
|
||||
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)
|
||||
SendFailureEmail(s)
|
||||
//SendFailureEmail(s)
|
||||
OnFailure(s)
|
||||
}
|
|
@ -1,25 +1,33 @@
|
|||
package main
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 Checkin types.Checkin
|
||||
|
||||
func (c *Checkin) String() string {
|
||||
return c.Api
|
||||
}
|
||||
|
||||
func FindCheckin(api string) *Checkin {
|
||||
for _, s := range CoreApp.Services {
|
||||
for _, c := range s.Checkins {
|
||||
if c.Api == api {
|
||||
return c
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) SelectAllCheckins() []*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)
|
||||
s.Checkins = checkins
|
||||
return checkins
|
||||
|
@ -27,9 +35,9 @@ func (s *Service) SelectAllCheckins() []*Checkin {
|
|||
|
||||
func (u *Checkin) Create() (int64, error) {
|
||||
u.CreatedAt = time.Now()
|
||||
uuid, err := dbSession.Collection("checkins").Insert(u)
|
||||
uuid, err := DbSession.Collection("checkins").Insert(u)
|
||||
if uuid == nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
return 0, err
|
||||
}
|
||||
fmt.Println("new checkin: ", uuid)
|
||||
|
@ -38,7 +46,7 @@ func (u *Checkin) Create() (int64, error) {
|
|||
|
||||
func SelectCheckinApi(api string) *Checkin {
|
||||
var checkin *Checkin
|
||||
dbSession.Collection("checkins").Find("api", api).One(&checkin)
|
||||
DbSession.Collection("checkins").Find("api", api).One(&checkin)
|
||||
return checkin
|
||||
}
|
||||
|
||||
|
@ -70,17 +78,6 @@ func (f *Checkin) Ago() string {
|
|||
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() {
|
||||
if c.Interval == 0 {
|
||||
return
|
||||
|
@ -104,7 +101,7 @@ func (s *Service) StartCheckins() {
|
|||
}
|
||||
|
||||
func CheckinProcess() {
|
||||
for _, s := range services {
|
||||
for _, s := range CoreApp.Services {
|
||||
for _, c := range s.Checkins {
|
||||
checkin := c
|
||||
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 (
|
||||
"github.com/fatih/structs"
|
||||
|
@ -7,55 +7,55 @@ import (
|
|||
)
|
||||
|
||||
func OnLoad(db sqlbuilder.Database) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnLoad(db)
|
||||
}
|
||||
}
|
||||
|
||||
func OnSuccess(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnSuccess(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnFailure(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnFailure(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnSettingsSaved(c *Core) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnSettingsSaved(structs.Map(c))
|
||||
}
|
||||
}
|
||||
|
||||
func OnNewUser(u *User) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnNewUser(structs.Map(u))
|
||||
}
|
||||
}
|
||||
|
||||
func OnNewService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnNewService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnDeletedService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnDeletedService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnUpdateService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
p.OnUpdatedService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func SelectPlugin(name string) plugin.PluginActions {
|
||||
for _, p := range allPlugins {
|
||||
for _, p := range AllPlugins {
|
||||
if p.GetInfo().Name == name {
|
||||
return p
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
package main
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func ExportIndexHTML() string {
|
||||
core.OfflineAssets = true
|
||||
out := index{*core, services}
|
||||
nav, _ := tmplBox.String("nav.html")
|
||||
footer, _ := tmplBox.String("footer.html")
|
||||
render, err := tmplBox.String("index.html")
|
||||
CoreApp.OfflineAssets = true
|
||||
//out := index{*CoreApp, CoreApp.Services}
|
||||
nav, _ := TmplBox.String("nav.html")
|
||||
footer, _ := TmplBox.String("footer.html")
|
||||
render, err := TmplBox.String("index.html")
|
||||
if err != nil {
|
||||
log.Send(3, err)
|
||||
utils.Log(3, err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(template.FuncMap{
|
||||
|
@ -25,18 +25,18 @@ func ExportIndexHTML() string {
|
|||
return template.HTML(html)
|
||||
},
|
||||
"VERSION": func() string {
|
||||
return VERSION
|
||||
return "version here"
|
||||
},
|
||||
"underscore": func(html string) string {
|
||||
return UnderScoreString(html)
|
||||
return utils.UnderScoreString(html)
|
||||
},
|
||||
})
|
||||
t, _ = t.Parse(nav)
|
||||
t, _ = t.Parse(footer)
|
||||
t.Parse(render)
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, out); err != nil {
|
||||
log.Send(3, err)
|
||||
if err := t.Execute(&tpl, nil); err != nil {
|
||||
utils.Log(3, err)
|
||||
}
|
||||
result := tpl.String()
|
||||
return result
|
|
@ -1,21 +1,13 @@
|
|||
package main
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"strings"
|
||||
"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) {
|
||||
fail := &Failure{
|
||||
Issue: data.Issue,
|
||||
|
@ -23,7 +15,7 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
|||
CreatedAt: time.Now(),
|
||||
}
|
||||
s.Failures = append(s.Failures, fail)
|
||||
col := dbSession.Collection("failures")
|
||||
col := DbSession.Collection("failures")
|
||||
uuid, err := col.Insert(fail)
|
||||
if uuid == nil {
|
||||
return 0, err
|
||||
|
@ -33,14 +25,14 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
|||
|
||||
func (s *Service) SelectAllFailures() []*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)
|
||||
return fails
|
||||
}
|
||||
|
||||
func (u *Service) DeleteFailures() {
|
||||
var fails []*Failure
|
||||
col := dbSession.Collection("failures")
|
||||
col := DbSession.Collection("failures")
|
||||
col.Find("service", u.Id).All(&fails)
|
||||
for _, fail := range fails {
|
||||
fail.Delete()
|
||||
|
@ -49,7 +41,7 @@ func (u *Service) DeleteFailures() {
|
|||
|
||||
func (s *Service) LimitedFailures() []*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)
|
||||
return fails
|
||||
}
|
||||
|
@ -67,28 +59,28 @@ func (f *Failure) Ago() string {
|
|||
}
|
||||
|
||||
func (f *Failure) Delete() error {
|
||||
col := dbSession.Collection("failures").Find("id", f.Id)
|
||||
col := DbSession.Collection("failures").Find("id", f.Id)
|
||||
return col.Delete()
|
||||
}
|
||||
|
||||
func CountFailures() uint64 {
|
||||
col := dbSession.Collection("failures").Find()
|
||||
col := DbSession.Collection("failures").Find()
|
||||
amount, err := col.Count()
|
||||
if err != nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
return 0
|
||||
}
|
||||
return amount
|
||||
}
|
||||
|
||||
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()
|
||||
return amount, err
|
||||
}
|
||||
|
||||
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()
|
||||
return amount, err
|
||||
}
|
||||
|
@ -110,5 +102,9 @@ func (f *Failure) ParseError() string {
|
|||
if err {
|
||||
return fmt.Sprintf("Incorrect HTTP Status Code")
|
||||
}
|
||||
err = strings.Contains(f.Issue, "connection refused")
|
||||
if err {
|
||||
return fmt.Sprintf("Connection Failed")
|
||||
}
|
||||
return f.Issue
|
||||
}
|
|
@ -1,20 +1,16 @@
|
|||
package main
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"time"
|
||||
"upper.io/db.v3"
|
||||
)
|
||||
|
||||
type Hit struct {
|
||||
Id int `db:"id,omitempty"`
|
||||
Service int64 `db:"service"`
|
||||
Latency float64 `db:"latency"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
type Hit types.Hit
|
||||
|
||||
func hitCol() db.Collection {
|
||||
return dbSession.Collection("hits")
|
||||
return DbSession.Collection("hits")
|
||||
}
|
||||
|
||||
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)
|
||||
if uuid == nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
return 0, err
|
||||
}
|
||||
return uuid.(int64), err
|
||||
|
@ -68,6 +64,9 @@ func (s *Service) TotalHits() (uint64, error) {
|
|||
func (s *Service) Sum() (float64, error) {
|
||||
var amount float64
|
||||
hits, err := s.Hits()
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
}
|
||||
for _, h := range hits {
|
||||
amount += h.Latency
|
||||
}
|
|
@ -1,17 +1,16 @@
|
|||
package main
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
"upper.io/db.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
services []*Service
|
||||
)
|
||||
type Failure types.Failure
|
||||
|
||||
type Service struct {
|
||||
Id int64 `db:"id,omitempty" json:"id"`
|
||||
|
@ -40,11 +39,11 @@ type Service struct {
|
|||
}
|
||||
|
||||
func serviceCol() db.Collection {
|
||||
return dbSession.Collection("services")
|
||||
return DbSession.Collection("services")
|
||||
}
|
||||
|
||||
func SelectService(id int64) *Service {
|
||||
for _, s := range services {
|
||||
for _, s := range CoreApp.Services {
|
||||
if s.Id == id {
|
||||
return s
|
||||
}
|
||||
|
@ -56,10 +55,14 @@ func SelectAllServices() ([]*Service, error) {
|
|||
var srvcs []*Service
|
||||
col := serviceCol().Find()
|
||||
err := col.All(&srvcs)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
}
|
||||
for _, s := range srvcs {
|
||||
s.Checkins = s.SelectAllCheckins()
|
||||
s.Failures = s.SelectAllFailures()
|
||||
}
|
||||
CoreApp.Services = srvcs
|
||||
return srvcs, err
|
||||
}
|
||||
|
||||
|
@ -106,9 +109,9 @@ func (s *Service) GraphData() string {
|
|||
increment := "minute"
|
||||
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))
|
||||
dated, err := dbSession.Query(db.Raw(sql))
|
||||
dated, err := DbSession.Query(db.Raw(sql))
|
||||
if err != nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
return ""
|
||||
}
|
||||
for dated.Next() {
|
||||
|
@ -120,7 +123,7 @@ func (s *Service) GraphData() string {
|
|||
}
|
||||
data, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
|
@ -151,18 +154,22 @@ func (s *Service) AvgUptime() string {
|
|||
|
||||
func (u *Service) RemoveArray() []*Service {
|
||||
var srvcs []*Service
|
||||
for _, s := range services {
|
||||
for _, s := range CoreApp.Services {
|
||||
if s.Id != u.Id {
|
||||
srvcs = append(srvcs, s)
|
||||
}
|
||||
}
|
||||
services = srvcs
|
||||
CoreApp.Services = srvcs
|
||||
return srvcs
|
||||
}
|
||||
|
||||
func (u *Service) Delete() error {
|
||||
res := serviceCol().Find("id", u.Id)
|
||||
err := res.Delete()
|
||||
if err != nil {
|
||||
utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err))
|
||||
return err
|
||||
}
|
||||
u.RemoveArray()
|
||||
OnDeletedService(u)
|
||||
return err
|
||||
|
@ -176,10 +183,11 @@ func (u *Service) Create() (int64, error) {
|
|||
u.CreatedAt = time.Now()
|
||||
uuid, err := serviceCol().Insert(u)
|
||||
if uuid == nil {
|
||||
utils.Log(3, fmt.Sprintf("Failed to create service %v. %v", u.Name, err))
|
||||
return 0, err
|
||||
}
|
||||
u.Id = uuid.(int64)
|
||||
services = append(services, u)
|
||||
CoreApp.Services = append(CoreApp.Services, u)
|
||||
go u.CheckQueue()
|
||||
OnNewService(u)
|
||||
return uuid.(int64), err
|
||||
|
@ -187,7 +195,7 @@ func (u *Service) Create() (int64, error) {
|
|||
|
||||
func CountOnline() int {
|
||||
amount := 0
|
||||
for _, v := range services {
|
||||
for _, v := range CoreApp.Services {
|
||||
if v.Online {
|
||||
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"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"gopkg.in/gomail.v2"
|
||||
"html/template"
|
||||
"time"
|
||||
|
@ -15,14 +16,16 @@ var (
|
|||
emailQue *Que
|
||||
)
|
||||
|
||||
type Email types.Email
|
||||
|
||||
type Que struct {
|
||||
Mailer *gomail.Dialer
|
||||
Outgoing []*types.Email
|
||||
Outgoing []*Email
|
||||
LastSent int
|
||||
LastSentTime time.Time
|
||||
}
|
||||
|
||||
func AddEmail(email *types.Email) {
|
||||
func AddEmail(email *Email) {
|
||||
if emailQue == nil {
|
||||
return
|
||||
}
|
||||
|
@ -33,12 +36,12 @@ func EmailerQueue() {
|
|||
if emailQue == nil {
|
||||
return
|
||||
}
|
||||
uniques := []*types.Email{}
|
||||
uniques := []*Email{}
|
||||
for _, out := range emailQue.Outgoing {
|
||||
if isUnique(uniques, out) {
|
||||
msg := fmt.Sprintf("sending email to: %v \n", out.To)
|
||||
Send(out)
|
||||
log.Send(0, msg)
|
||||
utils.Log(0, msg)
|
||||
uniques = append(uniques, out)
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +51,7 @@ func EmailerQueue() {
|
|||
EmailerQueue()
|
||||
}
|
||||
|
||||
func isUnique(arr []*types.Email, obj *types.Email) bool {
|
||||
func isUnique(arr []*Email, obj *Email) bool {
|
||||
for _, v := range arr {
|
||||
if v.To == obj.To && v.Subject == obj.Subject {
|
||||
return false
|
||||
|
@ -57,7 +60,7 @@ func isUnique(arr []*types.Email, obj *types.Email) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func Send(em *types.Email) {
|
||||
func Send(em *Email) {
|
||||
source := EmailTemplate(em.Template, em.Data)
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", "info@betatude.com")
|
||||
|
@ -65,14 +68,14 @@ func Send(em *types.Email) {
|
|||
m.SetHeader("Subject", em.Subject)
|
||||
m.SetBody("text/html", source)
|
||||
if err := emailQue.Mailer.DialAndSend(m); err != nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
}
|
||||
emailQue.LastSent++
|
||||
emailQue.LastSentTime = time.Now()
|
||||
}
|
||||
|
||||
func SendFailureEmail(service *Service) {
|
||||
email := &types.Email{
|
||||
func SendFailureEmail(service *core.Service) {
|
||||
email := &Email{
|
||||
To: "info@socialeck.com",
|
||||
Subject: fmt.Sprintf("Service %v is Failing", service.Name),
|
||||
Template: "failure.html",
|
||||
|
@ -81,30 +84,30 @@ func SendFailureEmail(service *Service) {
|
|||
AddEmail(email)
|
||||
}
|
||||
|
||||
func LoadMailer(config *types.Communication) *gomail.Dialer {
|
||||
func LoadMailer(config *core.Communication) *gomail.Dialer {
|
||||
if config.Host == "" || config.Username == "" || config.Password == "" {
|
||||
return nil
|
||||
}
|
||||
emailQue = new(Que)
|
||||
emailQue.Outgoing = []*types.Email{}
|
||||
emailQue.Outgoing = []*Email{}
|
||||
emailQue.Mailer = gomail.NewDialer(config.Host, config.Port, config.Username, config.Password)
|
||||
emailQue.Mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
return emailQue.Mailer
|
||||
}
|
||||
|
||||
func EmailTemplate(tmpl string, data interface{}) string {
|
||||
emailTpl, err := emailBox.String(tmpl)
|
||||
emailTpl, err := core.EmailBox.String(tmpl)
|
||||
if err != nil {
|
||||
log.Send(3, err)
|
||||
utils.Log(3, err)
|
||||
}
|
||||
t := template.New("email")
|
||||
t, err = t.Parse(emailTpl)
|
||||
if err != nil {
|
||||
log.Send(3, err)
|
||||
utils.Log(3, err)
|
||||
}
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, data); err != nil {
|
||||
log.Send(2, err)
|
||||
utils.Log(2, err)
|
||||
}
|
||||
result := tpl.String()
|
||||
return result
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
package main
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"math/rand"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
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) {
|
||||
vars := mux.Vars(r)
|
||||
checkin := FindCheckin(vars["api"])
|
||||
checkin := core.FindCheckin(vars["api"])
|
||||
checkin.Receivehit()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
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) {
|
||||
vars := mux.Vars(r)
|
||||
service := SelectService(StringInt(vars["id"]))
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
json.NewEncoder(w).Encode(service)
|
||||
}
|
||||
|
||||
func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
service := SelectService(StringInt(vars["id"]))
|
||||
var s Service
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
var s core.Service
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.Decode(&s)
|
||||
json.NewEncoder(w).Encode(service)
|
||||
}
|
||||
|
||||
func ApiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
services, _ := SelectAllServices()
|
||||
services, _ := core.SelectAllServices()
|
||||
json.NewEncoder(w).Encode(services)
|
||||
}
|
||||
|
||||
func ApiUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
user, _ := SelectUser(StringInt(vars["id"]))
|
||||
user, _ := core.SelectUser(utils.StringInt(vars["id"]))
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
func ApiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
users, _ := SelectAllUsers()
|
||||
users, _ := core.SelectAllUsers()
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/hunterlong/statup/log"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/handlers"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
plg "plugin"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
configs *Config
|
||||
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
|
||||
VERSION string
|
||||
)
|
||||
|
||||
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() {
|
||||
LoadDotEnvs()
|
||||
}
|
||||
|
||||
func LoadDotEnvs() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
fmt.Println("Error loading .env file")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer logFile.Close()
|
||||
var err error
|
||||
if len(os.Args) >= 2 {
|
||||
CatchCLI(os.Args)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var err error
|
||||
fmt.Printf("Starting Statup v%v\n", VERSION)
|
||||
utils.Log(1, fmt.Sprintf("Starting Statup v%v\n", VERSION))
|
||||
RenderBoxes()
|
||||
hasAssets()
|
||||
core.HasAssets()
|
||||
|
||||
configs, err = LoadConfig()
|
||||
core.Configs, err = core.LoadConfig()
|
||||
if err != nil {
|
||||
log.Send(1, "config.yml file not found - starting in setup mode")
|
||||
setupMode = true
|
||||
RunHTTPServer()
|
||||
utils.Log(2, "config.yml file not found - starting in setup mode")
|
||||
core.SetupMode = true
|
||||
handlers.RunHTTPServer()
|
||||
}
|
||||
mainProcess()
|
||||
}
|
||||
|
||||
func StringInt(s string) int64 {
|
||||
num, _ := strconv.Atoi(s)
|
||||
return int64(num)
|
||||
func RenderBoxes() {
|
||||
core.SqlBox = rice.MustFindBox("source/sql")
|
||||
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() {
|
||||
var err error
|
||||
err = DbConnection(configs.Connection)
|
||||
err = core.DbConnection(core.Configs.Connection)
|
||||
if err != nil {
|
||||
throw(err)
|
||||
utils.Log(3, err)
|
||||
}
|
||||
RunDatabaseUpgrades()
|
||||
core, err = SelectCore()
|
||||
core.RunDatabaseUpgrades()
|
||||
core.CoreApp, err = core.SelectCore()
|
||||
if err != nil {
|
||||
log.Send(1, "Core database was not found, Statup is not setup yet.")
|
||||
RunHTTPServer()
|
||||
utils.Log(2, "Core database was not found, Statup is not setup yet.")
|
||||
handlers.RunHTTPServer()
|
||||
}
|
||||
|
||||
CheckServices()
|
||||
core.Communications, _ = SelectAllCommunications()
|
||||
LoadDefaultCommunications()
|
||||
core.CheckServices()
|
||||
core.CoreApp.Communications, err = core.SelectAllCommunications()
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
}
|
||||
core.LoadDefaultCommunications()
|
||||
|
||||
go DatabaseMaintence()
|
||||
go core.DatabaseMaintence()
|
||||
|
||||
if !setupMode {
|
||||
if !core.SetupMode {
|
||||
LoadPlugins()
|
||||
RunHTTPServer()
|
||||
handlers.RunHTTPServer()
|
||||
}
|
||||
}
|
||||
|
||||
func throw(err error) {
|
||||
fmt.Println("ERROR: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func ForEachPlugin() {
|
||||
if len(core.Plugins) > 0 {
|
||||
if len(core.CoreApp.Plugins) > 0 {
|
||||
//for _, p := range core.Plugins {
|
||||
// p.OnShutdown()
|
||||
//}
|
||||
|
@ -179,7 +102,7 @@ func LoadPlugins() {
|
|||
|
||||
files, err := ioutil.ReadDir("./plugins")
|
||||
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
|
||||
}
|
||||
for _, f := range files {
|
||||
|
@ -192,7 +115,7 @@ func LoadPlugins() {
|
|||
}
|
||||
plug, err := plg.Open("plugins/" + f.Name())
|
||||
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
|
||||
}
|
||||
symPlugin, err := plug.Lookup("Plugin")
|
||||
|
@ -200,42 +123,16 @@ func LoadPlugins() {
|
|||
var plugActions plugin.PluginActions
|
||||
plugActions, ok := symPlugin.(plugin.PluginActions)
|
||||
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
|
||||
}
|
||||
|
||||
allPlugins = append(allPlugins, plugActions)
|
||||
core.Plugins = append(core.Plugins, plugActions.GetInfo())
|
||||
//allPlugins = append(allPlugins, plugActions)
|
||||
core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo())
|
||||
}
|
||||
|
||||
OnLoad(dbSession)
|
||||
|
||||
fmt.Printf("Loaded %v Plugins\n", len(allPlugins))
|
||||
core.OnLoad(core.DbSession)
|
||||
|
||||
//utils.Log(1, fmt.Sprintf("Loaded %v Plugins\n", len(allPlugins)))
|
||||
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 (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/handlers"
|
||||
"github.com/rendon/testcli"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
|
@ -19,19 +21,19 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
route = Router()
|
||||
route = handlers.Router()
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
RenderBoxes()
|
||||
os.Remove("./statup.db")
|
||||
Router()
|
||||
handlers.Router()
|
||||
LoadDotEnvs()
|
||||
|
||||
}
|
||||
|
||||
func TestMySQLMakeConfig(t *testing.T) {
|
||||
config := &DbConfig{
|
||||
config := &core.DbConfig{
|
||||
"mysql",
|
||||
os.Getenv("DB_HOST"),
|
||||
os.Getenv("DB_USER"),
|
||||
|
@ -49,30 +51,30 @@ func TestMySQLMakeConfig(t *testing.T) {
|
|||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = LoadConfig()
|
||||
_, err = core.LoadConfig()
|
||||
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)
|
||||
InsertDefaultComms()
|
||||
core.InsertDefaultComms()
|
||||
}
|
||||
|
||||
func TestInsertMysqlSample(t *testing.T) {
|
||||
err := LoadSampleData()
|
||||
err := core.LoadSampleData()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestSelectCoreMYQL(t *testing.T) {
|
||||
var err error
|
||||
core, err = SelectCore()
|
||||
core.CoreApp, err = core.SelectCore()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Testing MYSQL", core.Name)
|
||||
assert.Equal(t, VERSION, core.Version)
|
||||
assert.Equal(t, "Testing MYSQL", core.CoreApp.Name)
|
||||
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||
}
|
||||
|
||||
func TestSqliteMakeConfig(t *testing.T) {
|
||||
config := &DbConfig{
|
||||
config := &core.DbConfig{
|
||||
"sqlite",
|
||||
os.Getenv("DB_HOST"),
|
||||
os.Getenv("DB_USER"),
|
||||
|
@ -90,22 +92,22 @@ func TestSqliteMakeConfig(t *testing.T) {
|
|||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = LoadConfig()
|
||||
_, err = core.LoadConfig()
|
||||
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)
|
||||
InsertDefaultComms()
|
||||
core.InsertDefaultComms()
|
||||
}
|
||||
|
||||
func TestInsertSqliteSample(t *testing.T) {
|
||||
err := LoadSampleData()
|
||||
err := core.LoadSampleData()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestPostgresMakeConfig(t *testing.T) {
|
||||
config := &DbConfig{
|
||||
config := &core.DbConfig{
|
||||
"postgres",
|
||||
os.Getenv("DB_HOST"),
|
||||
os.Getenv("DB_USER"),
|
||||
|
@ -123,38 +125,38 @@ func TestPostgresMakeConfig(t *testing.T) {
|
|||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = LoadConfig()
|
||||
_, err = core.LoadConfig()
|
||||
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)
|
||||
InsertDefaultComms()
|
||||
core.InsertDefaultComms()
|
||||
}
|
||||
|
||||
func TestInsertPostgresSample(t *testing.T) {
|
||||
err := LoadSampleData()
|
||||
err := core.LoadSampleData()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestSelectCorePostgres(t *testing.T) {
|
||||
var err error
|
||||
core, err = SelectCore()
|
||||
core.CoreApp, err = core.SelectCore()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Testing POSTGRES", core.Name)
|
||||
assert.Equal(t, VERSION, core.Version)
|
||||
assert.Equal(t, "Testing POSTGRES", core.CoreApp.Name)
|
||||
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||
}
|
||||
|
||||
func TestSelectCore(t *testing.T) {
|
||||
var err error
|
||||
core, err = SelectCore()
|
||||
core.CoreApp, err = core.SelectCore()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Testing POSTGRES", core.Name)
|
||||
assert.Equal(t, VERSION, core.Version)
|
||||
assert.Equal(t, "Testing POSTGRES", core.CoreApp.Name)
|
||||
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||
}
|
||||
|
||||
func TestUser_Create(t *testing.T) {
|
||||
user := &User{
|
||||
user := &core.User{
|
||||
Username: "admin",
|
||||
Password: "admin",
|
||||
Email: "info@testuser.com",
|
||||
|
@ -164,14 +166,22 @@ func TestUser_Create(t *testing.T) {
|
|||
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) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
t.Log(service)
|
||||
assert.Equal(t, "Google", service.Name)
|
||||
}
|
||||
|
||||
func TestService_Create(t *testing.T) {
|
||||
service := &Service{
|
||||
service := &core.Service{
|
||||
Name: "test service",
|
||||
Domain: "https://google.com",
|
||||
ExpectedStatus: 200,
|
||||
|
@ -186,7 +196,7 @@ func TestService_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestService_Check(t *testing.T) {
|
||||
service := SelectService(2)
|
||||
service := core.SelectService(2)
|
||||
assert.NotNil(t, service)
|
||||
assert.Equal(t, "Statup.io", service.Name)
|
||||
out := service.Check()
|
||||
|
@ -194,28 +204,28 @@ func TestService_Check(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestService_AvgTime(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
avg := service.AvgUptime()
|
||||
assert.Equal(t, "100", avg)
|
||||
}
|
||||
|
||||
func TestService_Online24(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
online := service.Online24()
|
||||
assert.Equal(t, float32(100), online)
|
||||
}
|
||||
|
||||
func TestService_GraphData(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
data := service.GraphData()
|
||||
assert.NotEmpty(t, data)
|
||||
}
|
||||
|
||||
func TestBadService_Create(t *testing.T) {
|
||||
service := &Service{
|
||||
service := &core.Service{
|
||||
Name: "bad service",
|
||||
Domain: "https://9839f83h72gey2g29278hd2od2d.com",
|
||||
ExpectedStatus: 200,
|
||||
|
@ -230,13 +240,13 @@ func TestBadService_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBadService_Check(t *testing.T) {
|
||||
service := SelectService(4)
|
||||
service := core.SelectService(4)
|
||||
assert.NotNil(t, service)
|
||||
assert.Equal(t, "Github Failing Check", service.Name)
|
||||
}
|
||||
|
||||
func TestService_Hits(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
hits, err := service.Hits()
|
||||
assert.Nil(t, err)
|
||||
|
@ -244,7 +254,7 @@ func TestService_Hits(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestService_LimitedHits(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service := core.SelectService(1)
|
||||
assert.NotNil(t, service)
|
||||
hits, err := service.LimitedHits()
|
||||
assert.Nil(t, err)
|
||||
|
@ -273,7 +283,7 @@ func TestPrometheusHandler(t *testing.T) {
|
|||
rr := httptest.NewRecorder()
|
||||
route.ServeHTTP(rr, req)
|
||||
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) {
|
||||
|
@ -348,7 +358,7 @@ func TestExportCommand(t *testing.T) {
|
|||
c := testcli.Command("statup", "export")
|
||||
c.Run()
|
||||
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) {
|
||||
|
|
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 {
|
||||
background-color: #fcfcfc; }
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
max-width: 860px; }
|
||||
max-width: 860px;
|
||||
}
|
||||
|
||||
.online_list .badge {
|
||||
margin-top: 0.2rem; }
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px; }
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem; }
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.view_service_btn {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 40px; }
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.service_lower_info {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 40px;
|
||||
color: #d1ffca;
|
||||
font-size: 0.85rem; }
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 26pt;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #474747; }
|
||||
color: #474747;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
text-align: center;
|
||||
color: #a5a5a5; }
|
||||
color: #a5a5a5;
|
||||
}
|
||||
|
||||
.lower_canvas {
|
||||
height: 55px;
|
||||
width: 100%;
|
||||
background-color: #48d338;
|
||||
padding: 17px 10px; }
|
||||
padding: 17px 10px;
|
||||
}
|
||||
|
||||
.lower_canvas SPAN {
|
||||
font-size: 1rem; }
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: #aaaaaa;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d; }
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.online_badge {
|
||||
color: #fff;
|
||||
background-color: #35b317; }
|
||||
background-color: #35b317;
|
||||
}
|
||||
|
||||
.offline_badge {
|
||||
color: #fff;
|
||||
background-color: #c51919; }
|
||||
background-color: #c51919;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-top: -20px;
|
||||
|
@ -72,22 +88,27 @@ HTML, BODY {
|
|||
margin-bottom: 15px;
|
||||
width: calc(100% + 40px);
|
||||
height: 3px;
|
||||
border-radius: 0; }
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #fff; }
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: #239e07;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 170px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
|
@ -107,81 +128,100 @@ HTML, BODY {
|
|||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
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 */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 60vh; }
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
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) {
|
||||
.sm-container {
|
||||
margin-top: 40px !important;
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.list-group-item H5 {
|
||||
font-size: 0.9rem; }
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0; }
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 6pt;
|
||||
padding: 5px 5px; }
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 1.5rem; }
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
margin-top: 35px !important;
|
||||
margin-bottom: 35px !important; }
|
||||
margin-bottom: 35px !important;
|
||||
}
|
||||
|
||||
.stats_area .col-4 {
|
||||
padding-left: 0;
|
||||
padding-right: 0; }
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.lower_canvas SPAN {
|
||||
font-size: 0.9rem;
|
||||
float: left; }
|
||||
float: left;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 0.9rem;
|
||||
font-size: 0.65rem; }
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px; }
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0;
|
||||
border-radius: 0; }
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-top: 1px solid #e4e4e4;
|
||||
border: 0px; }
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0; }
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0; }
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item P {
|
||||
font-size: 0.7rem; } }
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*# 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 ranTheme = false;
|
||||
$('a[data-toggle="pill"]').on('shown.bs.tab', function (e) {
|
|
@ -1,10 +1,6 @@
|
|||
var currentLocation = window.location;
|
||||
$("#domain_input").val(currentLocation.origin);
|
||||
|
||||
function forceLower(strInput) {
|
||||
strInput.value=strInput.value.toLowerCase();
|
||||
}
|
||||
|
||||
$('select#database_type').on('change', function(){
|
||||
var selected = $('#database_type option:selected').val();
|
||||
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">
|
||||
|
||||
{{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://statup.io/base.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="https://assets.statup.io/bootstrap.min.css">
|
||||
{{ else }}
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
<script src="/js/Chart.bundle.min.js"></script>
|
||||
{{end}}
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
|
||||
<title>{{.Core.Name}} Status</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -96,6 +94,17 @@
|
|||
|
||||
{{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>
|
||||
{{ range .Services }}
|
||||
{{ if .AvgTime }}
|
||||
|
@ -247,6 +256,7 @@ var chartdata = new Chart(ctx, {
|
|||
{{ end }}
|
||||
</script>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
|
||||
{{ if .Core.Style }}
|
||||
<style>
|
||||
|
@ -255,14 +265,5 @@ var chartdata = new Chart(ctx, {
|
|||
{{ 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>
|
||||
</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">
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
<script src="/js/Chart.bundle.min.js"></script>
|
||||
|
||||
<title>Statup | Setup</title>
|
||||
</head>
|
||||
|
@ -77,7 +76,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<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 class="form-group">
|
|
@ -1,6 +1,44 @@
|
|||
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 {
|
||||
Id int64 `db:"id,omitempty" json:"id"`
|
||||
|
@ -25,3 +63,45 @@ type Email struct {
|
|||
Template string
|
||||
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