mirror of https://github.com/statping/statping
dev
parent
eac1b0568c
commit
439ffc293e
87
cmd/cli.go
87
cmd/cli.go
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/hunterlong/statup/utils"
|
||||
"github.com/joho/godotenv"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"time"
|
||||
)
|
||||
|
@ -35,7 +34,9 @@ import (
|
|||
// catchCLI will run functions based on the commands sent to Statup
|
||||
func catchCLI(args []string) error {
|
||||
dir := utils.Directory
|
||||
utils.InitLogs()
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
source.Assets()
|
||||
loadDotEnvs()
|
||||
|
||||
|
@ -50,22 +51,21 @@ func catchCLI(args []string) error {
|
|||
}
|
||||
return errors.New("end")
|
||||
case "assets":
|
||||
err := source.CreateAllAssets(dir)
|
||||
if err != nil {
|
||||
var err error
|
||||
if err = source.CreateAllAssets(dir); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return errors.New("end")
|
||||
}
|
||||
return errors.New("end")
|
||||
case "sass":
|
||||
err := source.CompileSASS(dir)
|
||||
if err == nil {
|
||||
return errors.New("end")
|
||||
}
|
||||
if err := source.CompileSASS(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("end")
|
||||
case "update":
|
||||
gitCurrent, err := checkGithubUpdates()
|
||||
if err != nil {
|
||||
return nil
|
||||
var err error
|
||||
var gitCurrent githubResponse
|
||||
if gitCurrent, err = checkGithubUpdates(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Statup Version: v%v\nLatest Version: %v\n", VERSION, gitCurrent.TagName)
|
||||
if VERSION != gitCurrent.TagName[1:] {
|
||||
|
@ -73,10 +73,7 @@ func catchCLI(args []string) error {
|
|||
} else {
|
||||
fmt.Printf("You have the latest version of Statup!\n")
|
||||
}
|
||||
if err == nil {
|
||||
return errors.New("end")
|
||||
}
|
||||
return nil
|
||||
case "test":
|
||||
cmd := args[1]
|
||||
switch cmd {
|
||||
|
@ -84,19 +81,17 @@ func catchCLI(args []string) error {
|
|||
plugin.LoadPlugins()
|
||||
}
|
||||
return errors.New("end")
|
||||
case "export":
|
||||
case "static":
|
||||
var err error
|
||||
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
utils.InitLogs()
|
||||
core.Configs, err = core.LoadConfigFile(dir)
|
||||
if err != nil {
|
||||
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
||||
utils.Log(4, "config.yml file not found")
|
||||
return err
|
||||
}
|
||||
indexSource := ExportIndexHTML()
|
||||
core.CloseDB()
|
||||
err = utils.SaveFile(dir+"/index.html", indexSource)
|
||||
if err != nil {
|
||||
if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
|
||||
utils.Log(4, err)
|
||||
return err
|
||||
}
|
||||
|
@ -104,6 +99,40 @@ func catchCLI(args []string) error {
|
|||
case "help":
|
||||
HelpEcho()
|
||||
return errors.New("end")
|
||||
case "export":
|
||||
var err error
|
||||
var data []byte
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = core.Configs.Connect(false, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = core.ExportSettings(); err != nil {
|
||||
return fmt.Errorf("could not export settings: %v", err.Error())
|
||||
}
|
||||
if err = utils.SaveFile(dir+"/statup-export.json", data); err != nil {
|
||||
return fmt.Errorf("could not write file statup-export.json: %v", err.Error())
|
||||
}
|
||||
return errors.New("end")
|
||||
case "import":
|
||||
var err error
|
||||
var data []byte
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("did not include a JSON file to import\nstatup import filename.json")
|
||||
}
|
||||
filename := args[1]
|
||||
if data, err = ioutil.ReadFile(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
var exportData core.ExportData
|
||||
if err = json.Unmarshal(data, &exportData); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("end")
|
||||
case "run":
|
||||
utils.Log(1, "Running 1 time and saving to database...")
|
||||
RunOnce()
|
||||
|
@ -175,11 +204,13 @@ func HelpEcho() {
|
|||
fmt.Println(" statup version - Returns the current version of Statup")
|
||||
fmt.Println(" statup run - Check all services 1 time and then quit")
|
||||
fmt.Println(" statup assets - Dump all assets used locally to be edited.")
|
||||
fmt.Println(" statup export - Exports the index page as a static HTML for pushing")
|
||||
fmt.Println(" statup static - Creates a static HTML file of the index page")
|
||||
fmt.Println(" statup sass - Compile .scss files into the css directory")
|
||||
fmt.Println(" statup test plugins - Test all plugins for required information")
|
||||
fmt.Println(" statup env - Show all environment variables being used for Statup")
|
||||
fmt.Println(" statup update - Attempts to update to the latest version")
|
||||
fmt.Println(" statup export - Exports your Statup settings to a 'statup-export.json' file.")
|
||||
fmt.Println(" statup import <file> - Imports settings from a previously saved JSON file.")
|
||||
fmt.Println(" statup help - Shows the user basic information about Statup")
|
||||
fmt.Printf("Flags:\n")
|
||||
fmt.Println(" -ip 127.0.0.1 - Run HTTP server on specific IP address (default: localhost)")
|
||||
|
@ -190,7 +221,7 @@ func HelpEcho() {
|
|||
fmt.Println(" DB_HOST - Database hostname or IP address")
|
||||
fmt.Println(" DB_USER - Database username")
|
||||
fmt.Println(" DB_PASS - Database password")
|
||||
fmt.Println(" DB_PORT - Database port (5432, 3306, ...")
|
||||
fmt.Println(" DB_PORT - Database port (5432, 3306, ...)")
|
||||
fmt.Println(" DB_DATABASE - Database connection's database name")
|
||||
fmt.Println(" GO_ENV - Run Statup in testmode, will bypass HTTP authentication (if set as 'true')")
|
||||
fmt.Println(" NAME - Set a name for the Statup status page")
|
||||
|
@ -198,19 +229,15 @@ func HelpEcho() {
|
|||
fmt.Println(" DOMAIN - Set a URL for the Statup status page")
|
||||
fmt.Println(" ADMIN_USER - Username for administrator account (default: admin)")
|
||||
fmt.Println(" ADMIN_PASS - Password for administrator account (default: admin)")
|
||||
fmt.Println(" SASS - Set the absolute path to the sass binary location")
|
||||
fmt.Println(" * You can insert environment variables into a '.env' file in root directory.")
|
||||
|
||||
fmt.Println("Give Statup a Star at https://github.com/hunterlong/statup")
|
||||
}
|
||||
|
||||
func checkGithubUpdates() (githubResponse, error) {
|
||||
var gitResp githubResponse
|
||||
response, err := http.Get("https://api.github.com/repos/hunterlong/statup/releases/latest")
|
||||
if err != nil {
|
||||
return githubResponse{}, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
url := "https://api.github.com/repos/hunterlong/statup/releases/latest"
|
||||
contents, err := utils.HttpRequest(url, "GET", nil, nil, nil, time.Duration(10*time.Second))
|
||||
if err != nil {
|
||||
return githubResponse{}, err
|
||||
}
|
||||
|
|
|
@ -61,9 +61,9 @@ func TestHelpCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestExportCommand(t *testing.T) {
|
||||
cmd := helperCommand(nil, "export")
|
||||
cmd := helperCommand(nil, "static")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(4*time.Second), got)
|
||||
commandAndSleep(cmd, time.Duration(10*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "Exporting Static 'index.html' page...")
|
||||
|
@ -72,10 +72,12 @@ func TestExportCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUpdateCommand(t *testing.T) {
|
||||
c := testcli.Command("statup", "update")
|
||||
c.Run()
|
||||
assert.True(t, c.StdoutContains("Statup Version: "+VERSION))
|
||||
assert.True(t, c.StdoutContains("Latest Version:"))
|
||||
cmd := helperCommand(nil, "version")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(10*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "Statup")
|
||||
}
|
||||
|
||||
func TestAssetsCommand(t *testing.T) {
|
||||
|
|
|
@ -74,13 +74,11 @@ func main() {
|
|||
}
|
||||
}
|
||||
utils.Log(1, fmt.Sprintf("Starting Statup v%v", VERSION))
|
||||
defer core.CloseDB()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
core.CloseDB()
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ func RunInit(db string, t *testing.T) {
|
|||
func TestRunAll(t *testing.T) {
|
||||
//t.Parallel()
|
||||
|
||||
databases := []string{"sqlite", "postgres", "mysql"}
|
||||
databases := []string{"postgres", "sqlite", "mysql"}
|
||||
if os.Getenv("ONLY_DB") != "" {
|
||||
databases = []string{os.Getenv("ONLY_DB")}
|
||||
}
|
||||
|
@ -89,6 +89,8 @@ func TestRunAll(t *testing.T) {
|
|||
})
|
||||
t.Run(dbt+" Drop Database", func(t *testing.T) {
|
||||
assert.NotNil(t, core.Configs)
|
||||
assert.NotNil(t, core.DbSession)
|
||||
assert.Nil(t, core.DbSession.DB().Ping())
|
||||
RunDropDatabase(t)
|
||||
})
|
||||
t.Run(dbt+" Connect to Database Again", func(t *testing.T) {
|
||||
|
@ -209,14 +211,13 @@ func TestRunAll(t *testing.T) {
|
|||
RunSettingsHandler(t)
|
||||
})
|
||||
t.Run(dbt+" Cleanup", func(t *testing.T) {
|
||||
core.Configs.Close()
|
||||
core.DbSession = nil
|
||||
//core.CloseDB()
|
||||
if dbt == "mssql" {
|
||||
os.Setenv("DB_DATABASE", "root")
|
||||
os.Setenv("DB_PASS", "password123")
|
||||
os.Setenv("DB_PORT", "1433")
|
||||
}
|
||||
//Clean()
|
||||
Clean()
|
||||
})
|
||||
|
||||
//<-done
|
||||
|
|
|
@ -69,7 +69,7 @@ func usersDB() *gorm.DB {
|
|||
|
||||
// checkinDB returns the Checkin records for a service
|
||||
func checkinDB() *gorm.DB {
|
||||
return DbSession.Table("checkins").Model(&types.Checkin{})
|
||||
return DbSession.Model(&types.Checkin{})
|
||||
}
|
||||
|
||||
// checkinHitsDB returns the Checkin Hits records for a service
|
||||
|
@ -100,11 +100,6 @@ func CloseDB() {
|
|||
}
|
||||
}
|
||||
|
||||
// Close shutsdown the database connection
|
||||
func (db *DbConfig) Close() error {
|
||||
return DbSession.DB().Close()
|
||||
}
|
||||
|
||||
// AfterFind for Core will set the timezone
|
||||
func (c *Core) AfterFind() (err error) {
|
||||
c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone)
|
||||
|
|
|
@ -17,7 +17,9 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/hunterlong/statup/source"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"html/template"
|
||||
)
|
||||
|
@ -42,3 +44,28 @@ func ExportChartsJs() string {
|
|||
result := tpl.String()
|
||||
return result
|
||||
}
|
||||
|
||||
type ExportData struct {
|
||||
Core *types.Core `json:"core"`
|
||||
Services []types.ServiceInterface `json:"services"`
|
||||
Messages []*types.Message `json:"messages"`
|
||||
Checkins []*Checkin `json:"checkins"`
|
||||
Users []*User `json:"users"`
|
||||
Notifiers []types.AllNotifiers `json:"notifiers"`
|
||||
}
|
||||
|
||||
func ExportSettings() ([]byte, error) {
|
||||
users, err := SelectAllUsers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := ExportData{
|
||||
Core: CoreApp.Core,
|
||||
Notifiers: CoreApp.Notifications,
|
||||
Checkins: AllCheckins(),
|
||||
Users: users,
|
||||
Services: CoreApp.Services,
|
||||
}
|
||||
export, err := json.Marshal(data)
|
||||
return export, err
|
||||
}
|
||||
|
|
|
@ -80,7 +80,9 @@ type NotificationForm struct {
|
|||
DbField string `json:"field"` // true variable key for input
|
||||
SmallText string `json:"small_text"` // insert small text under a html input
|
||||
Required bool `json:"required"` // require this input on the html form
|
||||
Hidden bool `json:"hidden"` // hide this form element from end user
|
||||
IsHidden bool `json:"hidden"` // hide this form element from end user
|
||||
IsList bool `json:"list"` // make this form element a comma separated list
|
||||
IsSwitch bool `json:"switch"` // make the notifier a boolean true/false switch
|
||||
}
|
||||
|
||||
// NotificationLog contains the normalized message from previously sent notifications
|
||||
|
|
|
@ -217,7 +217,7 @@ func (s *Service) DowntimeText() string {
|
|||
|
||||
// Dbtimestamp will return a SQL query for grouping by date
|
||||
func Dbtimestamp(group string, column string) string {
|
||||
var seconds int64
|
||||
seconds := 3600
|
||||
switch group {
|
||||
case "minute":
|
||||
seconds = 60
|
||||
|
@ -268,7 +268,10 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
|
|||
return &DateScanObj{[]DateScan{}}
|
||||
}
|
||||
model = model.Order("timeframe asc", false).Group("timeframe")
|
||||
rows, _ := model.Rows()
|
||||
rows, err := model.Debug().Rows()
|
||||
if err != nil {
|
||||
utils.Log(3, fmt.Errorf("issue fetching service chart data: %v", err))
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var gd DateScan
|
||||
|
|
|
@ -18,6 +18,7 @@ package handlers
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
|
@ -62,6 +63,7 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
||||
utils.Log(2, fmt.Errorf("sending error response for %v: %v", r.URL.String(), err.Error()))
|
||||
output := apiResponse{
|
||||
Status: "error",
|
||||
Error: err.Error(),
|
||||
|
|
|
@ -17,11 +17,9 @@ package handlers
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/source"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -99,15 +97,6 @@ func logsLineHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
type exportData struct {
|
||||
Core *types.Core `json:"core"`
|
||||
Services []types.ServiceInterface `json:"services"`
|
||||
Messages []*types.Message `json:"messages"`
|
||||
Checkins []*core.Checkin `json:"checkins"`
|
||||
Users []*core.User `json:"users"`
|
||||
Notifiers []types.AllNotifiers `json:"notifiers"`
|
||||
}
|
||||
|
||||
func exportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !IsAuthenticated(r) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
@ -120,17 +109,7 @@ func exportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
notifiers = append(notifiers, notifier.Select())
|
||||
}
|
||||
|
||||
users, _ := core.SelectAllUsers()
|
||||
|
||||
data := exportData{
|
||||
Core: core.CoreApp.Core,
|
||||
Notifiers: core.CoreApp.Notifications,
|
||||
Checkins: core.AllCheckins(),
|
||||
Users: users,
|
||||
Services: core.CoreApp.Services,
|
||||
}
|
||||
|
||||
export, _ := json.Marshal(data)
|
||||
export, _ := core.ExportSettings()
|
||||
|
||||
mime := http.DetectContentType(export)
|
||||
fileSize := len(string(export))
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -69,59 +65,10 @@ func init() {
|
|||
}
|
||||
|
||||
func runCommand(app, cmd string) (string, string, error) {
|
||||
testCmd := exec.Command(app, "-c", cmd)
|
||||
|
||||
var stdout, stderr []byte
|
||||
var errStdout, errStderr error
|
||||
stdoutIn, _ := testCmd.StdoutPipe()
|
||||
stderrIn, _ := testCmd.StderrPipe()
|
||||
testCmd.Start()
|
||||
|
||||
go func() {
|
||||
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
|
||||
}()
|
||||
|
||||
err := testCmd.Wait()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if errStdout != nil || errStderr != nil {
|
||||
return "", "", errors.New("failed to capture stdout or stderr")
|
||||
}
|
||||
|
||||
outStr, errStr := string(stdout), string(stderr)
|
||||
outStr, errStr, err := utils.Command(cmd)
|
||||
return outStr, errStr, err
|
||||
}
|
||||
|
||||
// copyAndCapture captures the response from a terminal command
|
||||
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
||||
var out []byte
|
||||
buf := make([]byte, 1024, 1024)
|
||||
for {
|
||||
n, err := r.Read(buf[:])
|
||||
if n > 0 {
|
||||
d := buf[:n]
|
||||
out = append(out, d...)
|
||||
_, err := w.Write(d)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Read returns io.EOF at the end of file, which is not an error for us
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *commandLine) Select() *notifier.Notification {
|
||||
return u.Notification
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -59,14 +59,8 @@ func init() {
|
|||
// Send will send a HTTP Post to the discord API. It accepts type: []byte
|
||||
func (u *discord) Send(msg interface{}) error {
|
||||
message := msg.(string)
|
||||
req, _ := http.NewRequest("POST", discorder.GetValue("host"), bytes.NewBuffer([]byte(message)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
_, err := utils.HttpRequest(discorder.GetValue("host"), "POST", "application/json", nil, strings.NewReader(message), time.Duration(10*time.Second))
|
||||
return err
|
||||
}
|
||||
return resp.Body.Close()
|
||||
}
|
||||
|
||||
func (u *discord) Select() *notifier.Notification {
|
||||
|
@ -101,15 +95,7 @@ func (u *discord) OnSave() error {
|
|||
func (u *discord) OnTest() error {
|
||||
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
||||
message := `{"content": "Testing the discord notifier"}`
|
||||
req, _ := http.NewRequest("POST", discorder.Host, bytes.NewBuffer([]byte(message)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
contents, _ := ioutil.ReadAll(resp.Body)
|
||||
contents, err := utils.HttpRequest(discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second))
|
||||
if string(contents) == "" {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -26,12 +26,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
EMAIL_HOST = os.Getenv("EMAIL_HOST")
|
||||
EMAIL_USER = os.Getenv("EMAIL_USER")
|
||||
EMAIL_PASS = os.Getenv("EMAIL_PASS")
|
||||
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
|
||||
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
|
||||
EMAIL_PORT = utils.StringInt(os.Getenv("EMAIL_PORT"))
|
||||
EMAIL_HOST string
|
||||
EMAIL_USER string
|
||||
EMAIL_PASS string
|
||||
EMAIL_OUTGOING string
|
||||
EMAIL_SEND_TO string
|
||||
EMAIL_PORT int64
|
||||
)
|
||||
|
||||
var testEmail *emailOutgoing
|
||||
|
@ -42,7 +42,7 @@ func init() {
|
|||
EMAIL_PASS = os.Getenv("EMAIL_PASS")
|
||||
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
|
||||
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
|
||||
EMAIL_PORT = utils.StringInt(os.Getenv("EMAIL_PORT"))
|
||||
EMAIL_PORT = utils.ToInt(os.Getenv("EMAIL_PORT"))
|
||||
|
||||
emailer.Host = EMAIL_HOST
|
||||
emailer.Username = EMAIL_USER
|
||||
|
|
|
@ -20,9 +20,9 @@ import (
|
|||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -59,21 +59,11 @@ func init() {
|
|||
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
|
||||
func (u *lineNotifier) Send(msg interface{}) error {
|
||||
message := msg.(string)
|
||||
client := new(http.Client)
|
||||
v := url.Values{}
|
||||
v.Set("message", message)
|
||||
req, err := http.NewRequest("POST", "https://notify-api.line.me/api/notify", strings.NewReader(v.Encode()))
|
||||
if err != nil {
|
||||
headers := []string{fmt.Sprintf("Authorization=Bearer %v", u.GetValue("api_secret"))}
|
||||
_, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second))
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", u.GetValue("api_secret")))
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
_, err = client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *lineNotifier) Select() *notifier.Notification {
|
||||
|
|
|
@ -43,7 +43,7 @@ var mobile = &mobilePush{¬ifier.Notification{
|
|||
Title: "Device Identifiers",
|
||||
Placeholder: "A list of your mobile device push notification ID's.",
|
||||
DbField: "var1",
|
||||
Hidden: true,
|
||||
IsHidden: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
@ -85,14 +85,8 @@ func init() {
|
|||
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string
|
||||
func (u *slack) Send(msg interface{}) error {
|
||||
message := msg.(string)
|
||||
client := new(http.Client)
|
||||
res, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(message)))
|
||||
if err != nil {
|
||||
_, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, strings.NewReader(message), time.Duration(10*time.Second))
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
//contents, _ := ioutil.ReadAll(res.Body)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *slack) Select() *notifier.Notification {
|
||||
|
@ -100,13 +94,7 @@ func (u *slack) Select() *notifier.Notification {
|
|||
}
|
||||
|
||||
func (u *slack) OnTest() error {
|
||||
client := new(http.Client)
|
||||
res, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(`{"text":"testing message"}`)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
contents, _ := ioutil.ReadAll(res.Body)
|
||||
contents, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second))
|
||||
if string(contents) != "ok" {
|
||||
return errors.New("The slack response was incorrect, check the URL")
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ import (
|
|||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -84,32 +82,21 @@ func (u *twilio) Select() *notifier.Notification {
|
|||
func (u *twilio) Send(msg interface{}) error {
|
||||
message := msg.(string)
|
||||
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.GetValue("api_key"))
|
||||
client := &http.Client{}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("To", "+"+u.Var1)
|
||||
v.Set("From", "+"+u.Var2)
|
||||
v.Set("Body", message)
|
||||
rb := *strings.NewReader(v.Encode())
|
||||
req, err := http.NewRequest("POST", twilioUrl, &rb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.SetBasicAuth(u.ApiKey, u.ApiSecret)
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
contents, _ := ioutil.ReadAll(res.Body)
|
||||
|
||||
contents, err := utils.HttpRequest(twilioUrl, "POST", "application/x-www-form-urlencoded", nil, &rb, time.Duration(10*time.Second))
|
||||
success, _ := twilioSuccess(contents)
|
||||
if !success {
|
||||
errorOut := twilioError(contents)
|
||||
out := fmt.Sprintf("Error code %v - %v", errorOut.Code, errorOut.Message)
|
||||
return errors.New(out)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
|
|
|
@ -59,7 +59,14 @@
|
|||
<div class="form-group row">
|
||||
<label for="notify_before" class="col-sm-4 col-form-label">Notify Before</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="notify_before" class="form-control" id="notify_before" value="{{.NotifyBefore}}">
|
||||
<div class="form-inline">
|
||||
<input type="number" name="notify_before_scale" class="col-4 form-control" id="notify_before" value="{{.NotifyBefore.Int64}}" >
|
||||
<select class="ml-2 col-7 form-control" name="notify_before_scale" id="notify_before_scale">
|
||||
<option value="minute"{{if ne .Id 0}} selected{{end}}>Minutes</option>
|
||||
<option value="hour">Hours</option>
|
||||
<option value="day">Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<div class="form-group row{{if (eq .Type "tcp") or (eq .Type "udp")}} d-none{{end}}">
|
||||
<label for="service_response_code" class="col-sm-4 col-form-label">Expected Status Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{if ne .ExpectedStatus 0}}{{.ExpectedStatus}}{{end}}" placeholder="200" id="service_response_code">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{if ne .ExpectedStatus 0}}{{.ExpectedStatus}}{{else}}200{{end}}" placeholder="200" id="service_response_code">
|
||||
<small class="form-text text-muted">A status code of 200 is success, or view all the <a target="_blank" href="https://www.restapitutorial.com/httpstatuscodes.html">HTTP Status Codes</a></small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -90,6 +90,15 @@
|
|||
<small class="form-text text-muted">You can also drag and drop services to reorder on the Services tab.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="order" class="col-sm-4 col-form-label">Notifications</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span class="switch float-left">
|
||||
<input type="checkbox" name="allow_notifications" class="switch" id="switch-service" {{if eq .Id 0}}checked{{end}}{{if .AllowNotifications.Bool}}checked{{end}}>
|
||||
<label for="switch-service">Allow notifications to be sent for this service</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="{{if ne .Id 0}}col-6{{else}}col-12{{end}}">
|
||||
<button type="submit" class="btn btn-success btn-block">{{if ne .Id 0}}Update Service{{else}}Create Service{{end}}</button>
|
||||
|
|
|
@ -940,8 +940,12 @@
|
|||
"exec": [
|
||||
"pm.test(\"Create Message\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.output.title).to.eql(\"API Message\");",
|
||||
" pm.expect(jsonData.output.service).to.eql(1);",
|
||||
" var object = jsonData.output;",
|
||||
" pm.expect(object.title).to.eql(\"API Message\");",
|
||||
" pm.expect(object.description).to.eql(\"This is an example a upcoming message for a service!\");",
|
||||
" pm.expect(object.service).to.eql(1);",
|
||||
" pm.expect(object.notify_before).to.eql(6);",
|
||||
" pm.expect(object.notify_before_scale).to.eql(\"hour\");",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -960,7 +964,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"title\": \"API Message\",\n \"description\": \"This is an example a upcoming message for a service!\",\n \"start_on\": \"2022-11-17T03:28:16.323797-08:00\",\n \"end_on\": \"2022-11-17T05:13:16.323798-08:00\",\n \"service\": 1,\n \"notify_users\": null,\n \"notify_method\": \"\",\n \"notify_before\": 0\n}"
|
||||
"raw": "{\n \"title\": \"API Message\",\n \"description\": \"This is an example a upcoming message for a service!\",\n \"start_on\": \"2022-11-17T03:28:16.323797-08:00\",\n \"end_on\": \"2022-11-17T05:13:16.323798-08:00\",\n \"service\": 1,\n \"notify_users\": true,\n \"notify_method\": \"email\",\n \"notify_before\": 6,\n \"notify_before_scale\": \"hour\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/messages",
|
||||
|
@ -981,7 +985,7 @@
|
|||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "abbb5178-9613-418c-b5ee-be2d6b4fdb8f",
|
||||
"id": "c30cc333-53f4-4e9a-9c32-958c905ec163",
|
||||
"exec": [
|
||||
"pm.test(\"View Message\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
|
@ -1020,13 +1024,16 @@
|
|||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "a0403c03-0838-4fd2-9cce-aebaf8a128c3",
|
||||
"id": "e9dd78cc-0f38-4516-bf82-38dd3451b2e7",
|
||||
"exec": [
|
||||
"pm.test(\"Update Message\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.status).to.eql(\"success\");",
|
||||
" pm.expect(jsonData.method).to.eql(\"update\");",
|
||||
" pm.expect(jsonData.id).to.eql(1);",
|
||||
" var object = jsonData.output;",
|
||||
" pm.expect(object.title).to.eql(\"Updated Message\");",
|
||||
" pm.expect(object.description).to.eql(\"This message was updated\");",
|
||||
" pm.expect(object.service).to.eql(1);",
|
||||
" pm.expect(object.notify_before).to.eql(3);",
|
||||
" pm.expect(object.notify_before_scale).to.eql(\"hour\");",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -1045,7 +1052,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"title\": \"Routine Downtime\",\n \"description\": \"This is an example a upcoming message for a service!\",\n \"start_on\": \"2055-11-17T03:28:16.323797-08:00\",\n \"end_on\": \"2055-11-17T05:13:16.323798-08:00\",\n \"service\": 2,\n \"notify_users\": true,\n \"notify_method\": \"email\",\n \"notify_before\": 900\n}"
|
||||
"raw": "{\n \"title\": \"Updated Message\",\n \"description\": \"This message was updated\",\n \"start_on\": \"2022-11-17T03:28:16.323797-08:00\",\n \"end_on\": \"2022-11-17T05:13:16.323798-08:00\",\n \"service\": 1,\n \"notify_users\": true,\n \"notify_method\": \"email\",\n \"notify_before\": 3,\n \"notify_before_scale\": \"hour\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/messages/1",
|
||||
|
|
|
@ -29,7 +29,8 @@ type Message struct {
|
|||
ServiceId int64 `gorm:"index;column:service" json:"service"`
|
||||
NotifyUsers NullBool `gorm:"column:notify_users" json:"notify_users"`
|
||||
NotifyMethod string `gorm:"column:notify_method" json:"notify_method"`
|
||||
NotifyBefore time.Duration `gorm:"column:notify_before" json:"notify_before"`
|
||||
NotifyBefore NullInt64 `gorm:"column:notify_before" json:"notify_before"`
|
||||
NotifyBeforeScale string `gorm:"column:notify_before_scale" json:"notify_before_scale"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
|
@ -221,3 +223,39 @@ func SaveFile(filename string, data []byte) error {
|
|||
err := ioutil.WriteFile(filename, data, 0644)
|
||||
return err
|
||||
}
|
||||
|
||||
// HttpRequest is a global function to send a HTTP request
|
||||
func HttpRequest(url, method string, content interface{}, headers []string, body io.Reader, timeout time.Duration) ([]byte, error) {
|
||||
var err error
|
||||
var contentType string
|
||||
if content != nil {
|
||||
contentType = content.(string)
|
||||
}
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TLSHandshakeTimeout: timeout,
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: timeout,
|
||||
}
|
||||
var response *http.Response
|
||||
response.Header.Set("User-Agent", "Statup")
|
||||
for _, h := range headers {
|
||||
keyVal := strings.Split(h, "=")
|
||||
response.Header.Add(keyVal[0], keyVal[1])
|
||||
}
|
||||
if method == "POST" {
|
||||
response, err = client.Post(url, contentType, body)
|
||||
} else {
|
||||
response, err = client.Get(url)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
return contents, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue