pull/99/head
Hunter Long 2018-11-25 04:56:09 +01:00
parent eac1b0568c
commit 439ffc293e
22 changed files with 226 additions and 230 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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)
}()

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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(),

View File

@ -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))

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -43,7 +43,7 @@ var mobile = &mobilePush{&notifier.Notification{
Title: "Device Identifiers",
Placeholder: "A list of your mobile device push notification ID's.",
DbField: "var1",
Hidden: true,
IsHidden: true,
}}},
}

View File

@ -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")
}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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",

View File

@ -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"`
}

View File

@ -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
}