mirror of
https://github.com/mentos1386/zdravko.git
synced 2024-11-21 23:33:34 +00:00
feat: renaming things and thus breaking existing database schema
This commit is contained in:
parent
4603f7a79a
commit
1afde077a6
39 changed files with 890 additions and 786 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -6,9 +6,7 @@ package.json
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
zdravko.db*
|
store/
|
||||||
zdravko_kv.db*
|
|
||||||
temporal.db*
|
|
||||||
|
|
||||||
# Keys
|
# Keys
|
||||||
*.pem
|
*.pem
|
||||||
|
|
|
@ -45,16 +45,16 @@ type OAuth2State struct {
|
||||||
ExpiresAt *Time `db:"expires_at"`
|
ExpiresAt *Time `db:"expires_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorStatus string
|
type CheckStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MonitorSuccess MonitorStatus = "SUCCESS"
|
CheckSuccess CheckStatus = "SUCCESS"
|
||||||
MonitorFailure MonitorStatus = "FAILURE"
|
CheckFailure CheckStatus = "FAILURE"
|
||||||
MonitorError MonitorStatus = "ERROR"
|
CheckError CheckStatus = "ERROR"
|
||||||
MonitorUnknown MonitorStatus = "UNKNOWN"
|
CheckUnknown CheckStatus = "UNKNOWN"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Monitor struct {
|
type Check struct {
|
||||||
CreatedAt *Time `db:"created_at"`
|
CreatedAt *Time `db:"created_at"`
|
||||||
UpdatedAt *Time `db:"updated_at"`
|
UpdatedAt *Time `db:"updated_at"`
|
||||||
|
|
||||||
|
@ -66,18 +66,18 @@ type Monitor struct {
|
||||||
Script string `db:"script"`
|
Script string `db:"script"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorWithWorkerGroups struct {
|
type CheckWithWorkerGroups struct {
|
||||||
Monitor
|
Check
|
||||||
|
|
||||||
// List of worker group names
|
// List of worker group names
|
||||||
WorkerGroups []string
|
WorkerGroups []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorHistory struct {
|
type CheckHistory struct {
|
||||||
CreatedAt *Time `db:"created_at"`
|
CreatedAt *Time `db:"created_at"`
|
||||||
|
|
||||||
MonitorId string `db:"monitor_id"`
|
CheckId string `db:"check_id"`
|
||||||
Status MonitorStatus `db:"status"`
|
Status CheckStatus `db:"status"`
|
||||||
Note string `db:"note"`
|
Note string `db:"note"`
|
||||||
|
|
||||||
WorkerGroupId string `db:"worker_group_id"`
|
WorkerGroupId string `db:"worker_group_id"`
|
||||||
|
@ -92,11 +92,11 @@ type WorkerGroup struct {
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerGroupWithMonitors struct {
|
type WorkerGroupWithChecks struct {
|
||||||
WorkerGroup
|
WorkerGroup
|
||||||
|
|
||||||
// List of worker group names
|
// List of worker group names
|
||||||
Monitors []string
|
Checks []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TriggerStatus string
|
type TriggerStatus string
|
||||||
|
|
|
@ -6,7 +6,7 @@ CREATE TABLE oauth2_states (
|
||||||
PRIMARY KEY (state)
|
PRIMARY KEY (state)
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
CREATE TABLE monitors (
|
CREATE TABLE checks (
|
||||||
id TEXT NOT NULL,
|
id TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
"group" TEXT NOT NULL DEFAULT 'default',
|
"group" TEXT NOT NULL DEFAULT 'default',
|
||||||
|
@ -17,12 +17,12 @@ CREATE TABLE monitors (
|
||||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT unique_monitors_name UNIQUE (name)
|
CONSTRAINT unique_checks_name UNIQUE (name)
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
|
|
||||||
--CREATE TRIGGER monitors_updated_timestamp AFTER UPDATE ON monitors BEGIN
|
--CREATE TRIGGER checks_updated_timestamp AFTER UPDATE ON checks BEGIN
|
||||||
-- update monitors set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
-- update checks set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
||||||
--END;
|
--END;
|
||||||
|
|
||||||
CREATE TABLE worker_groups (
|
CREATE TABLE worker_groups (
|
||||||
|
@ -40,17 +40,17 @@ CREATE TABLE worker_groups (
|
||||||
-- update worker_groups set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
-- update worker_groups set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
||||||
--END;
|
--END;
|
||||||
|
|
||||||
CREATE TABLE monitor_worker_groups (
|
CREATE TABLE check_worker_groups (
|
||||||
worker_group_id TEXT NOT NULL,
|
worker_group_id TEXT NOT NULL,
|
||||||
monitor_id TEXT NOT NULL,
|
check_id TEXT NOT NULL,
|
||||||
|
|
||||||
PRIMARY KEY (worker_group_id,monitor_id),
|
PRIMARY KEY (worker_group_id,check_id),
|
||||||
CONSTRAINT fk_monitor_worker_groups_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id) ON DELETE CASCADE,
|
CONSTRAINT fk_check_worker_groups_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id) ON DELETE CASCADE,
|
||||||
CONSTRAINT fk_monitor_worker_groups_monitor FOREIGN KEY (monitor_id) REFERENCES monitors(id) ON DELETE CASCADE
|
CONSTRAINT fk_check_worker_groups_check FOREIGN KEY (check_id) REFERENCES checks(id) ON DELETE CASCADE
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
CREATE TABLE monitor_histories (
|
CREATE TABLE check_histories (
|
||||||
monitor_id TEXT NOT NULL,
|
check_id TEXT NOT NULL,
|
||||||
worker_group_id TEXT NOT NULL,
|
worker_group_id TEXT NOT NULL,
|
||||||
|
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
|
@ -58,14 +58,41 @@ CREATE TABLE monitor_histories (
|
||||||
|
|
||||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
|
||||||
PRIMARY KEY (monitor_id, worker_group_id, created_at),
|
PRIMARY KEY (check_id, worker_group_id, created_at),
|
||||||
CONSTRAINT fk_monitor_histories_monitor FOREIGN KEY (monitor_id) REFERENCES monitors(id) ON DELETE CASCADE,
|
CONSTRAINT fk_check_histories_check FOREIGN KEY (check_id) REFERENCES checks(id) ON DELETE CASCADE,
|
||||||
CONSTRAINT fk_monitor_histories_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id) ON DELETE CASCADE
|
CONSTRAINT fk_check_histories_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id) ON DELETE CASCADE
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
CREATE TABLE triggers (
|
||||||
|
id TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
script TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CONSTRAINT unique_triggers_name UNIQUE (name)
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
CREATE TABLE trigger_histories (
|
||||||
|
trigger_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
note TEXT NOT NULL,
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
|
||||||
|
PRIMARY KEY (trigger_id, created_at),
|
||||||
|
CONSTRAINT fk_trigger_histories_trigger FOREIGN KEY (trigger_id) REFERENCES triggers(id) ON DELETE CASCADE
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
-- +migrate Down
|
-- +migrate Down
|
||||||
DROP TABLE oauth2_states;
|
DROP TABLE oauth2_states;
|
||||||
DROP TABLE monitor_worker_groups;
|
DROP TABLE check_worker_groups;
|
||||||
DROP TABLE worker_groups;
|
DROP TABLE worker_groups;
|
||||||
DROP TABLE monitor_histories;
|
DROP TABLE check_histories;
|
||||||
DROP TABLE monitors;
|
DROP TABLE checks;
|
||||||
|
DROP TABLE triggers;
|
||||||
|
DROP TABLE trigger_histories;
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
-- +migrate Up
|
|
||||||
CREATE TABLE triggers (
|
|
||||||
id TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
script TEXT NOT NULL,
|
|
||||||
status TEXT NOT NULL,
|
|
||||||
|
|
||||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
|
||||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
|
||||||
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
CONSTRAINT unique_triggers_name UNIQUE (name)
|
|
||||||
) STRICT;
|
|
||||||
|
|
||||||
CREATE TABLE trigger_histories (
|
|
||||||
trigger_id TEXT NOT NULL,
|
|
||||||
|
|
||||||
status TEXT NOT NULL,
|
|
||||||
note TEXT NOT NULL,
|
|
||||||
|
|
||||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
|
||||||
|
|
||||||
PRIMARY KEY (trigger_id, created_at),
|
|
||||||
CONSTRAINT fk_trigger_histories_trigger FOREIGN KEY (trigger_id) REFERENCES triggers(id) ON DELETE CASCADE
|
|
||||||
) STRICT;
|
|
||||||
|
|
||||||
-- +migrate Down
|
|
||||||
DROP TABLE triggers;
|
|
||||||
DROP TABLE trigger_histories;
|
|
|
@ -17,9 +17,9 @@ primary_region = 'waw'
|
||||||
ROOT_URL = 'https://zdravko.mnts.dev'
|
ROOT_URL = 'https://zdravko.mnts.dev'
|
||||||
TEMPORAL_SERVER_HOST = 'server.process.zdravko.internal:7233'
|
TEMPORAL_SERVER_HOST = 'server.process.zdravko.internal:7233'
|
||||||
|
|
||||||
TEMPORAL_DATABASE_PATH = '/data/temporal-9.db'
|
TEMPORAL_DATABASE_PATH = '/data/temporal-10.db'
|
||||||
SQLITE_DATABASE_PATH = '/data/zdravko-9.db'
|
SQLITE_DATABASE_PATH = '/data/zdravko-10.db'
|
||||||
KEYVALUE_DATABASE_PATH = '/data/zdravko_kv.db'
|
KEYVALUE_DATABASE_PATH = '/data/zdravko_kv-10.db'
|
||||||
|
|
||||||
[processes]
|
[processes]
|
||||||
server = '--temporal --server'
|
server = '--temporal --server'
|
||||||
|
|
|
@ -18,12 +18,12 @@ type HealtcheckParam struct {
|
||||||
Script string
|
Script string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorResult struct {
|
type CheckResult struct {
|
||||||
Success bool
|
Success bool
|
||||||
Note string
|
Note string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Activities) Monitor(ctx context.Context, param HealtcheckParam) (*MonitorResult, error) {
|
func (a *Activities) Check(ctx context.Context, param HealtcheckParam) (*CheckResult, error) {
|
||||||
execution := k6.NewExecution(slog.Default(), script.UnescapeString(param.Script))
|
execution := k6.NewExecution(slog.Default(), script.UnescapeString(param.Script))
|
||||||
|
|
||||||
result, err := execution.Run(ctx)
|
result, err := execution.Run(ctx)
|
||||||
|
@ -31,23 +31,23 @@ func (a *Activities) Monitor(ctx context.Context, param HealtcheckParam) (*Monit
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MonitorResult{Success: result.Success, Note: result.Note}, nil
|
return &CheckResult{Success: result.Success, Note: result.Note}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealtcheckAddToHistoryParam struct {
|
type HealtcheckAddToHistoryParam struct {
|
||||||
MonitorId string
|
CheckId string
|
||||||
Status models.MonitorStatus
|
Status models.CheckStatus
|
||||||
Note string
|
Note string
|
||||||
WorkerGroupId string
|
WorkerGroupId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorAddToHistoryResult struct {
|
type CheckAddToHistoryResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Activities) MonitorAddToHistory(ctx context.Context, param HealtcheckAddToHistoryParam) (*MonitorAddToHistoryResult, error) {
|
func (a *Activities) CheckAddToHistory(ctx context.Context, param HealtcheckAddToHistoryParam) (*CheckAddToHistoryResult, error) {
|
||||||
url := fmt.Sprintf("%s/api/v1/monitors/%s/history", a.config.ApiUrl, param.MonitorId)
|
url := fmt.Sprintf("%s/api/v1/checks/%s/history", a.config.ApiUrl, param.CheckId)
|
||||||
|
|
||||||
body := api.ApiV1MonitorsHistoryPOSTBody{
|
body := api.ApiV1ChecksHistoryPOSTBody{
|
||||||
Status: param.Status,
|
Status: param.Status,
|
||||||
Note: param.Note,
|
Note: param.Note,
|
||||||
WorkerGroupId: param.WorkerGroupId,
|
WorkerGroupId: param.WorkerGroupId,
|
||||||
|
@ -73,5 +73,5 @@ func (a *Activities) MonitorAddToHistory(ctx context.Context, param HealtcheckAd
|
||||||
return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode)
|
return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MonitorAddToHistoryResult{}, nil
|
return &CheckAddToHistoryResult{}, nil
|
||||||
}
|
}
|
|
@ -48,8 +48,8 @@ func NewServerConfig() *ServerConfig {
|
||||||
// Set defaults
|
// Set defaults
|
||||||
v.SetDefault("port", GetEnvOrDefault("PORT", "8000"))
|
v.SetDefault("port", GetEnvOrDefault("PORT", "8000"))
|
||||||
v.SetDefault("rooturl", GetEnvOrDefault("ROOT_URL", "http://localhost:8000"))
|
v.SetDefault("rooturl", GetEnvOrDefault("ROOT_URL", "http://localhost:8000"))
|
||||||
v.SetDefault("sqlitedatabasepath", GetEnvOrDefault("SQLITE_DATABASE_PATH", "zdravko.db"))
|
v.SetDefault("sqlitedatabasepath", GetEnvOrDefault("SQLITE_DATABASE_PATH", "store/zdravko.db"))
|
||||||
v.SetDefault("keyvaluedatabasepath", GetEnvOrDefault("KEYVALUE_DATABASE_PATH", "zdravko_kv.db"))
|
v.SetDefault("keyvaluedatabasepath", GetEnvOrDefault("KEYVALUE_DATABASE_PATH", "store/zdravko_kv.db"))
|
||||||
v.SetDefault("sessionsecret", os.Getenv("SESSION_SECRET"))
|
v.SetDefault("sessionsecret", os.Getenv("SESSION_SECRET"))
|
||||||
v.SetDefault("temporal.uihost", GetEnvOrDefault("TEMPORAL_UI_HOST", "127.0.0.1:8223"))
|
v.SetDefault("temporal.uihost", GetEnvOrDefault("TEMPORAL_UI_HOST", "127.0.0.1:8223"))
|
||||||
v.SetDefault("temporal.serverhost", GetEnvOrDefault("TEMPORAL_SERVER_HOST", "127.0.0.1:7233"))
|
v.SetDefault("temporal.serverhost", GetEnvOrDefault("TEMPORAL_SERVER_HOST", "127.0.0.1:7233"))
|
||||||
|
|
|
@ -23,7 +23,7 @@ func NewTemporalConfig() *TemporalConfig {
|
||||||
v := newViper()
|
v := newViper()
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
v.SetDefault("databasepath", GetEnvOrDefault("TEMPORAL_DATABASE_PATH", "temporal.db"))
|
v.SetDefault("databasepath", GetEnvOrDefault("TEMPORAL_DATABASE_PATH", "store/temporal.db"))
|
||||||
v.SetDefault("listenaddress", GetEnvOrDefault("TEMPORAL_LISTEN_ADDRESS", "0.0.0.0"))
|
v.SetDefault("listenaddress", GetEnvOrDefault("TEMPORAL_LISTEN_ADDRESS", "0.0.0.0"))
|
||||||
v.SetDefault("jwt.publickey", os.Getenv("JWT_PUBLIC_KEY"))
|
v.SetDefault("jwt.publickey", os.Getenv("JWT_PUBLIC_KEY"))
|
||||||
|
|
||||||
|
|
|
@ -40,26 +40,26 @@ func (h *BaseHandler) ApiV1WorkersConnectGET(c echo.Context) error {
|
||||||
// TODO: Can we instead get this from the Workflow outcome?
|
// TODO: Can we instead get this from the Workflow outcome?
|
||||||
//
|
//
|
||||||
// To somehow listen for the outcomes and then store them automatically.
|
// To somehow listen for the outcomes and then store them automatically.
|
||||||
func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
func (h *BaseHandler) ApiV1ChecksHistoryPOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var body api.ApiV1MonitorsHistoryPOSTBody
|
var body api.ApiV1ChecksHistoryPOSTBody
|
||||||
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = services.GetMonitor(ctx, h.db, id)
|
_, err = services.GetCheck(ctx, h.db, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return echo.NewHTTPError(http.StatusNotFound, "Monitor not found")
|
return echo.NewHTTPError(http.StatusNotFound, "Check not found")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.AddHistoryForMonitor(ctx, h.db, &models.MonitorHistory{
|
err = services.AddHistoryForCheck(ctx, h.db, &models.CheckHistory{
|
||||||
MonitorId: id,
|
CheckId: id,
|
||||||
WorkerGroupId: body.WorkerGroupId,
|
WorkerGroupId: body.WorkerGroupId,
|
||||||
Status: body.Status,
|
Status: body.Status,
|
||||||
Note: body.Note,
|
Note: body.Note,
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
var examplesYaml embed.FS
|
var examplesYaml embed.FS
|
||||||
|
|
||||||
type examples struct {
|
type examples struct {
|
||||||
Monitor string `yaml:"monitor"`
|
Check string `yaml:"check"`
|
||||||
Trigger string `yaml:"trigger"`
|
Trigger string `yaml:"trigger"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func NewBaseHandler(db *sqlx.DB, kvStore kv.KeyValueStore, temporal client.Clien
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
examples.Monitor = script.EscapeString(examples.Monitor)
|
examples.Check = script.EscapeString(examples.Check)
|
||||||
examples.Trigger = script.EscapeString(examples.Trigger)
|
examples.Trigger = script.EscapeString(examples.Trigger)
|
||||||
|
|
||||||
return &BaseHandler{
|
return &BaseHandler{
|
||||||
|
|
|
@ -13,21 +13,21 @@ import (
|
||||||
|
|
||||||
type IndexData struct {
|
type IndexData struct {
|
||||||
*components.Base
|
*components.Base
|
||||||
Monitors map[string]MonitorsAndStatus
|
Checks map[string]ChecksAndStatus
|
||||||
MonitorsLength int
|
ChecksLength int
|
||||||
TimeRange string
|
TimeRange string
|
||||||
Status models.MonitorStatus
|
Status models.CheckStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type Monitor struct {
|
type Check struct {
|
||||||
Name string
|
Name string
|
||||||
Group string
|
Group string
|
||||||
Status models.MonitorStatus
|
Status models.CheckStatus
|
||||||
History *History
|
History *History
|
||||||
}
|
}
|
||||||
|
|
||||||
type HistoryItem struct {
|
type HistoryItem struct {
|
||||||
Status models.MonitorStatus
|
Status models.CheckStatus
|
||||||
Date time.Time
|
Date time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,23 +36,23 @@ type History struct {
|
||||||
Uptime int
|
Uptime int
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorsAndStatus struct {
|
type ChecksAndStatus struct {
|
||||||
Status models.MonitorStatus
|
Status models.CheckStatus
|
||||||
Monitors []*Monitor
|
Checks []*Check
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDateString(date time.Time) string {
|
func getDateString(date time.Time) string {
|
||||||
return date.UTC().Format("2006-01-02T15:04:05")
|
return date.UTC().Format("2006-01-02T15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHistory(history []*models.MonitorHistory, period time.Duration, buckets int) *History {
|
func getHistory(history []*models.CheckHistory, period time.Duration, buckets int) *History {
|
||||||
historyMap := map[string]models.MonitorStatus{}
|
historyMap := map[string]models.CheckStatus{}
|
||||||
numOfSuccess := 0
|
numOfSuccess := 0
|
||||||
numTotal := 0
|
numTotal := 0
|
||||||
|
|
||||||
for i := 0; i < buckets; i++ {
|
for i := 0; i < buckets; i++ {
|
||||||
dateString := getDateString(time.Now().Add(period * time.Duration(-i)).Truncate(period))
|
dateString := getDateString(time.Now().Add(period * time.Duration(-i)).Truncate(period))
|
||||||
historyMap[dateString] = models.MonitorUnknown
|
historyMap[dateString] = models.CheckUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, _history := range history {
|
for _, _history := range history {
|
||||||
|
@ -64,12 +64,12 @@ func getHistory(history []*models.MonitorHistory, period time.Duration, buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
numTotal++
|
numTotal++
|
||||||
if _history.Status == models.MonitorSuccess {
|
if _history.Status == models.CheckSuccess {
|
||||||
numOfSuccess++
|
numOfSuccess++
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip if it is already set to failure
|
// skip if it is already set to failure
|
||||||
if historyMap[dateString] == models.MonitorFailure {
|
if historyMap[dateString] == models.CheckFailure {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func getHistory(history []*models.MonitorHistory, period time.Duration, buckets
|
||||||
|
|
||||||
func (h *BaseHandler) Index(c echo.Context) error {
|
func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
monitors, err := services.GetMonitors(ctx, h.db)
|
checks, err := services.GetChecks(ctx, h.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -111,12 +111,12 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
timeRange = "90days"
|
timeRange = "90days"
|
||||||
}
|
}
|
||||||
|
|
||||||
overallStatus := models.MonitorUnknown
|
overallStatus := models.CheckUnknown
|
||||||
statusByGroup := make(map[string]models.MonitorStatus)
|
statusByGroup := make(map[string]models.CheckStatus)
|
||||||
|
|
||||||
monitorsWithHistory := make([]*Monitor, len(monitors))
|
checksWithHistory := make([]*Check, len(checks))
|
||||||
for i, monitor := range monitors {
|
for i, check := range checks {
|
||||||
history, err := services.GetMonitorHistoryForMonitor(ctx, h.db, monitor.Id)
|
history, err := services.GetCheckHistoryForCheck(ctx, h.db, check.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -131,37 +131,37 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
historyResult = getHistory(history, time.Minute, 90)
|
historyResult = getHistory(history, time.Minute, 90)
|
||||||
}
|
}
|
||||||
|
|
||||||
if statusByGroup[monitor.Group] == "" {
|
if statusByGroup[check.Group] == "" {
|
||||||
statusByGroup[monitor.Group] = models.MonitorUnknown
|
statusByGroup[check.Group] = models.CheckUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
status := historyResult.List[len(historyResult.List)-1]
|
status := historyResult.List[len(historyResult.List)-1]
|
||||||
if status.Status == models.MonitorSuccess {
|
if status.Status == models.CheckSuccess {
|
||||||
if overallStatus == models.MonitorUnknown {
|
if overallStatus == models.CheckUnknown {
|
||||||
overallStatus = status.Status
|
overallStatus = status.Status
|
||||||
}
|
}
|
||||||
if statusByGroup[monitor.Group] == models.MonitorUnknown {
|
if statusByGroup[check.Group] == models.CheckUnknown {
|
||||||
statusByGroup[monitor.Group] = status.Status
|
statusByGroup[check.Group] = status.Status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if status.Status != models.MonitorSuccess && status.Status != models.MonitorUnknown {
|
if status.Status != models.CheckSuccess && status.Status != models.CheckUnknown {
|
||||||
overallStatus = status.Status
|
overallStatus = status.Status
|
||||||
statusByGroup[monitor.Group] = status.Status
|
statusByGroup[check.Group] = status.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorsWithHistory[i] = &Monitor{
|
checksWithHistory[i] = &Check{
|
||||||
Name: monitor.Name,
|
Name: check.Name,
|
||||||
Group: monitor.Group,
|
Group: check.Group,
|
||||||
Status: status.Status,
|
Status: status.Status,
|
||||||
History: historyResult,
|
History: historyResult,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorsByGroup := map[string]MonitorsAndStatus{}
|
checksByGroup := map[string]ChecksAndStatus{}
|
||||||
for _, monitor := range monitorsWithHistory {
|
for _, check := range checksWithHistory {
|
||||||
monitorsByGroup[monitor.Group] = MonitorsAndStatus{
|
checksByGroup[check.Group] = ChecksAndStatus{
|
||||||
Status: statusByGroup[monitor.Group],
|
Status: statusByGroup[check.Group],
|
||||||
Monitors: append(monitorsByGroup[monitor.Group].Monitors, monitor),
|
Checks: append(checksByGroup[check.Group].Checks, check),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
NavbarActive: GetPageByTitle(Pages, "Status"),
|
NavbarActive: GetPageByTitle(Pages, "Status"),
|
||||||
Navbar: Pages,
|
Navbar: Pages,
|
||||||
},
|
},
|
||||||
Monitors: monitorsByGroup,
|
Checks: checksByGroup,
|
||||||
TimeRange: timeRange,
|
TimeRange: timeRange,
|
||||||
Status: overallStatus,
|
Status: overallStatus,
|
||||||
})
|
})
|
||||||
|
|
|
@ -36,11 +36,13 @@ func NewSettings(user *AuthenticatedUser, page *components.Page, breadCrumbs []*
|
||||||
|
|
||||||
var SettingsPages = []*components.Page{
|
var SettingsPages = []*components.Page{
|
||||||
{Path: "/settings", Title: "Overview", Breadcrumb: "Overview"},
|
{Path: "/settings", Title: "Overview", Breadcrumb: "Overview"},
|
||||||
{Path: "/settings/targets", Title: "Incidents", Breadcrumb: "Incidents"},
|
{Path: "/settings/incidents", Title: "Incidents", Breadcrumb: "Incidents"},
|
||||||
{Path: "/settings/targets", Title: "Targets", Breadcrumb: "Targets"},
|
{Path: "/settings/targets", Title: "Targets", Breadcrumb: "Targets"},
|
||||||
{Path: "/settings/targets/create", Title: "Targets Create", Breadcrumb: "Create"},
|
{Path: "/settings/targets/create", Title: "Targets Create", Breadcrumb: "Create"},
|
||||||
{Path: "/settings/monitors", Title: "Checks", Breadcrumb: "Checks"},
|
{Path: "/settings/hooks", Title: "Hooks", Breadcrumb: "Hooks"},
|
||||||
{Path: "/settings/monitors/create", Title: "Checks Create", Breadcrumb: "Create"},
|
{Path: "/settings/hooks/create", Title: "Hooks Create", Breadcrumb: "Create"},
|
||||||
|
{Path: "/settings/checks", Title: "Checks", Breadcrumb: "Checks"},
|
||||||
|
{Path: "/settings/checks/create", Title: "Checks Create", Breadcrumb: "Create"},
|
||||||
{Path: "/settings/worker-groups", Title: "Worker Groups", Breadcrumb: "Worker Groups"},
|
{Path: "/settings/worker-groups", Title: "Worker Groups", Breadcrumb: "Worker Groups"},
|
||||||
{Path: "/settings/worker-groups/create", Title: "Worker Groups Create", Breadcrumb: "Create"},
|
{Path: "/settings/worker-groups/create", Title: "Worker Groups Create", Breadcrumb: "Create"},
|
||||||
{Path: "/settings/notifications", Title: "Notifications", Breadcrumb: "Notifications"},
|
{Path: "/settings/notifications", Title: "Notifications", Breadcrumb: "Notifications"},
|
||||||
|
@ -63,10 +65,11 @@ var SettingsSidebar = []SettingsSidebarGroup{
|
||||||
Pages: []*components.Page{
|
Pages: []*components.Page{
|
||||||
GetPageByTitle(SettingsPages, "Targets"),
|
GetPageByTitle(SettingsPages, "Targets"),
|
||||||
GetPageByTitle(SettingsPages, "Checks"),
|
GetPageByTitle(SettingsPages, "Checks"),
|
||||||
|
GetPageByTitle(SettingsPages, "Hooks"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Group: "Decide",
|
Group: "Alert",
|
||||||
Pages: []*components.Page{
|
Pages: []*components.Page{
|
||||||
GetPageByTitle(SettingsPages, "Triggers"),
|
GetPageByTitle(SettingsPages, "Triggers"),
|
||||||
},
|
},
|
||||||
|
@ -91,9 +94,9 @@ var SettingsSidebar = []SettingsSidebarGroup{
|
||||||
type SettingsOverview struct {
|
type SettingsOverview struct {
|
||||||
*Settings
|
*Settings
|
||||||
WorkerGroupsCount int
|
WorkerGroupsCount int
|
||||||
MonitorsCount int
|
ChecksCount int
|
||||||
NotificationsCount int
|
NotificationsCount int
|
||||||
History []*services.MonitorHistoryWithMonitor
|
History []*services.CheckHistoryWithCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
||||||
|
@ -105,12 +108,12 @@ func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monitors, err := services.CountMonitors(ctx, h.db)
|
checks, err := services.CountChecks(ctx, h.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
history, err := services.GetLastNMonitorHistory(ctx, h.db, 10)
|
history, err := services.GetLastNCheckHistory(ctx, h.db, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -122,7 +125,7 @@ func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
||||||
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")},
|
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")},
|
||||||
),
|
),
|
||||||
WorkerGroupsCount: workerGroups,
|
WorkerGroupsCount: workerGroups,
|
||||||
MonitorsCount: monitors,
|
ChecksCount: checks,
|
||||||
NotificationsCount: 42,
|
NotificationsCount: 42,
|
||||||
History: history,
|
History: history,
|
||||||
})
|
})
|
||||||
|
|
30
internal/handlers/settings_incidents.go
Normal file
30
internal/handlers/settings_incidents.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/web/templates/components"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Incident struct{}
|
||||||
|
|
||||||
|
type SettingsIncidents struct {
|
||||||
|
*Settings
|
||||||
|
Incidents []*Incident
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BaseHandler) SettingsIncidentsGET(c echo.Context) error {
|
||||||
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
|
incidents := make([]*Incident, 0)
|
||||||
|
|
||||||
|
return c.Render(http.StatusOK, "settings_incidents.tmpl", &SettingsIncidents{
|
||||||
|
Settings: NewSettings(
|
||||||
|
cc.Principal.User,
|
||||||
|
GetPageByTitle(SettingsPages, "Incidents"),
|
||||||
|
[]*components.Page{GetPageByTitle(SettingsPages, "Incidents")},
|
||||||
|
),
|
||||||
|
Incidents: incidents,
|
||||||
|
})
|
||||||
|
}
|
|
@ -14,13 +14,6 @@ import (
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Incident struct{}
|
|
||||||
|
|
||||||
type SettingsIncidents struct {
|
|
||||||
*Settings
|
|
||||||
Incidents []*Incident
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateTrigger struct {
|
type CreateTrigger struct {
|
||||||
Name string `validate:"required"`
|
Name string `validate:"required"`
|
||||||
Script string `validate:"required"`
|
Script string `validate:"required"`
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateMonitor struct {
|
type CreateCheck struct {
|
||||||
Name string `validate:"required"`
|
Name string `validate:"required"`
|
||||||
Group string `validate:"required"`
|
Group string `validate:"required"`
|
||||||
WorkerGroups string `validate:"required"`
|
WorkerGroups string `validate:"required"`
|
||||||
|
@ -25,96 +25,96 @@ type CreateMonitor struct {
|
||||||
Script string `validate:"required"`
|
Script string `validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateMonitor struct {
|
type UpdateCheck struct {
|
||||||
Group string `validate:"required"`
|
Group string `validate:"required"`
|
||||||
WorkerGroups string `validate:"required"`
|
WorkerGroups string `validate:"required"`
|
||||||
Schedule string `validate:"required,cron"`
|
Schedule string `validate:"required,cron"`
|
||||||
Script string `validate:"required"`
|
Script string `validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorWithWorkerGroupsAndStatus struct {
|
type CheckWithWorkerGroupsAndStatus struct {
|
||||||
*models.MonitorWithWorkerGroups
|
*models.CheckWithWorkerGroups
|
||||||
Status services.MonitorStatus
|
Status services.CheckStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingsMonitors struct {
|
type SettingsChecks struct {
|
||||||
*Settings
|
*Settings
|
||||||
Monitors map[string][]*MonitorWithWorkerGroupsAndStatus
|
Checks map[string][]*CheckWithWorkerGroupsAndStatus
|
||||||
MonitorGroups []string
|
CheckGroups []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingsMonitor struct {
|
type SettingsCheck struct {
|
||||||
*Settings
|
*Settings
|
||||||
Monitor *MonitorWithWorkerGroupsAndStatus
|
Check *CheckWithWorkerGroupsAndStatus
|
||||||
History []*models.MonitorHistory
|
History []*models.CheckHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingsMonitorCreate struct {
|
type SettingsCheckCreate struct {
|
||||||
*Settings
|
*Settings
|
||||||
Example string
|
Example string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
monitors, err := services.GetMonitorsWithWorkerGroups(context.Background(), h.db)
|
checks, err := services.GetChecksWithWorkerGroups(context.Background(), h.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorsWithStatus := make([]*MonitorWithWorkerGroupsAndStatus, len(monitors))
|
checksWithStatus := make([]*CheckWithWorkerGroupsAndStatus, len(checks))
|
||||||
for i, monitor := range monitors {
|
for i, check := range checks {
|
||||||
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Id)
|
status, err := services.GetCheckStatus(context.Background(), h.temporal, check.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
monitorsWithStatus[i] = &MonitorWithWorkerGroupsAndStatus{
|
checksWithStatus[i] = &CheckWithWorkerGroupsAndStatus{
|
||||||
MonitorWithWorkerGroups: monitor,
|
CheckWithWorkerGroups: check,
|
||||||
Status: status,
|
Status: status,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorGroups := []string{}
|
checkGroups := []string{}
|
||||||
monitorsByGroup := map[string][]*MonitorWithWorkerGroupsAndStatus{}
|
checksByGroup := map[string][]*CheckWithWorkerGroupsAndStatus{}
|
||||||
for _, monitor := range monitorsWithStatus {
|
for _, check := range checksWithStatus {
|
||||||
monitorsByGroup[monitor.Group] = append(monitorsByGroup[monitor.Group], monitor)
|
checksByGroup[check.Group] = append(checksByGroup[check.Group], check)
|
||||||
if slices.Contains(monitorGroups, monitor.Group) == false {
|
if slices.Contains(checkGroups, check.Group) == false {
|
||||||
monitorGroups = append(monitorGroups, monitor.Group)
|
checkGroups = append(checkGroups, check.Group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "settings_monitors.tmpl", &SettingsMonitors{
|
return c.Render(http.StatusOK, "settings_checks.tmpl", &SettingsChecks{
|
||||||
Settings: NewSettings(
|
Settings: NewSettings(
|
||||||
cc.Principal.User,
|
cc.Principal.User,
|
||||||
GetPageByTitle(SettingsPages, "Checks"),
|
GetPageByTitle(SettingsPages, "Checks"),
|
||||||
[]*components.Page{GetPageByTitle(SettingsPages, "Checks")},
|
[]*components.Page{GetPageByTitle(SettingsPages, "Checks")},
|
||||||
),
|
),
|
||||||
Monitors: monitorsByGroup,
|
Checks: checksByGroup,
|
||||||
MonitorGroups: monitorGroups,
|
CheckGroups: checkGroups,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksDescribeGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
slug := c.Param("id")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitorWithWorkerGroups(context.Background(), h.db, slug)
|
check, err := services.GetCheckWithWorkerGroups(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Id)
|
status, err := services.GetCheckStatus(context.Background(), h.temporal, check.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorWithStatus := &MonitorWithWorkerGroupsAndStatus{
|
checkWithStatus := &CheckWithWorkerGroupsAndStatus{
|
||||||
MonitorWithWorkerGroups: monitor,
|
CheckWithWorkerGroups: check,
|
||||||
Status: status,
|
Status: status,
|
||||||
}
|
}
|
||||||
|
|
||||||
history, err := services.GetMonitorHistoryForMonitor(context.Background(), h.db, slug)
|
history, err := services.GetCheckHistoryForCheck(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,76 +124,76 @@ func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
||||||
maxElements = len(history)
|
maxElements = len(history)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "settings_monitors_describe.tmpl", &SettingsMonitor{
|
return c.Render(http.StatusOK, "settings_checks_describe.tmpl", &SettingsCheck{
|
||||||
Settings: NewSettings(
|
Settings: NewSettings(
|
||||||
cc.Principal.User,
|
cc.Principal.User,
|
||||||
GetPageByTitle(SettingsPages, "Checks"),
|
GetPageByTitle(SettingsPages, "Checks"),
|
||||||
[]*components.Page{
|
[]*components.Page{
|
||||||
GetPageByTitle(SettingsPages, "Checks"),
|
GetPageByTitle(SettingsPages, "Checks"),
|
||||||
{
|
{
|
||||||
Path: fmt.Sprintf("/settings/monitors/%s", slug),
|
Path: fmt.Sprintf("/settings/checks/%s", slug),
|
||||||
Title: "Describe",
|
Title: "Describe",
|
||||||
Breadcrumb: monitor.Name,
|
Breadcrumb: check.Name,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Monitor: monitorWithStatus,
|
Check: checkWithStatus,
|
||||||
History: history[:maxElements],
|
History: history[:maxElements],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribeDELETE(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksDescribeDELETE(c echo.Context) error {
|
||||||
slug := c.Param("id")
|
slug := c.Param("id")
|
||||||
|
|
||||||
err := services.DeleteMonitor(context.Background(), h.db, slug)
|
err := services.DeleteCheck(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.DeleteMonitorSchedule(context.Background(), h.temporal, slug)
|
err = services.DeleteCheckSchedule(context.Background(), h.temporal, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, "/settings/monitors")
|
return c.Redirect(http.StatusSeeOther, "/settings/checks")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDisableGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksDisableGET(c echo.Context) error {
|
||||||
slug := c.Param("id")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
check, err := services.GetCheck(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Id, services.MonitorStatusPaused)
|
err = services.SetCheckStatus(context.Background(), h.temporal, check.Id, services.CheckStatusPaused)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/monitors/%s", slug))
|
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/checks/%s", slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsEnableGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksEnableGET(c echo.Context) error {
|
||||||
slug := c.Param("id")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
check, err := services.GetCheck(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Id, services.MonitorStatusActive)
|
err = services.SetCheckStatus(context.Background(), h.temporal, check.Id, services.CheckStatusActive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/monitors/%s", slug))
|
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/checks/%s", slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksDescribePOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
monitorId := c.Param("id")
|
checkId := c.Param("id")
|
||||||
|
|
||||||
update := UpdateMonitor{
|
update := UpdateCheck{
|
||||||
Group: strings.ToLower(c.FormValue("group")),
|
Group: strings.ToLower(c.FormValue("group")),
|
||||||
WorkerGroups: strings.ToLower(strings.TrimSpace(c.FormValue("workergroups"))),
|
WorkerGroups: strings.ToLower(strings.TrimSpace(c.FormValue("workergroups"))),
|
||||||
Schedule: c.FormValue("schedule"),
|
Schedule: c.FormValue("schedule"),
|
||||||
|
@ -204,18 +204,18 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(ctx, h.db, monitorId)
|
check, err := services.GetCheck(ctx, h.db, checkId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
monitor.Group = update.Group
|
check.Group = update.Group
|
||||||
monitor.Schedule = update.Schedule
|
check.Schedule = update.Schedule
|
||||||
monitor.Script = update.Script
|
check.Script = update.Script
|
||||||
|
|
||||||
err = services.UpdateMonitor(
|
err = services.UpdateCheck(
|
||||||
ctx,
|
ctx,
|
||||||
h.db,
|
h.db,
|
||||||
monitor,
|
check,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -241,23 +241,23 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
workerGroups = append(workerGroups, workerGroup)
|
workerGroups = append(workerGroups, workerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.UpdateMonitorWorkerGroups(ctx, h.db, monitor, workerGroups)
|
err = services.UpdateCheckWorkerGroups(ctx, h.db, check, workerGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.CreateOrUpdateMonitorSchedule(ctx, h.temporal, monitor, workerGroups)
|
err = services.CreateOrUpdateCheckSchedule(ctx, h.temporal, check, workerGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/monitors/%s", monitorId))
|
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/checks/%s", checkId))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksCreateGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "settings_monitors_create.tmpl", &SettingsMonitorCreate{
|
return c.Render(http.StatusOK, "settings_checks_create.tmpl", &SettingsCheckCreate{
|
||||||
Settings: NewSettings(
|
Settings: NewSettings(
|
||||||
cc.Principal.User,
|
cc.Principal.User,
|
||||||
GetPageByTitle(SettingsPages, "Checks"),
|
GetPageByTitle(SettingsPages, "Checks"),
|
||||||
|
@ -266,15 +266,15 @@ func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
||||||
GetPageByTitle(SettingsPages, "Checks Create"),
|
GetPageByTitle(SettingsPages, "Checks Create"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Example: h.examples.Monitor,
|
Example: h.examples.Check,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
func (h *BaseHandler) SettingsChecksCreatePOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
monitorId := slug.Make(c.FormValue("name"))
|
checkId := slug.Make(c.FormValue("name"))
|
||||||
|
|
||||||
create := CreateMonitor{
|
create := CreateCheck{
|
||||||
Name: c.FormValue("name"),
|
Name: c.FormValue("name"),
|
||||||
Group: strings.ToLower(c.FormValue("group")),
|
Group: strings.ToLower(c.FormValue("group")),
|
||||||
WorkerGroups: strings.ToLower(strings.TrimSpace(c.FormValue("workergroups"))),
|
WorkerGroups: strings.ToLower(strings.TrimSpace(c.FormValue("workergroups"))),
|
||||||
|
@ -306,32 +306,32 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
||||||
workerGroups = append(workerGroups, workerGroup)
|
workerGroups = append(workerGroups, workerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor := &models.Monitor{
|
check := &models.Check{
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
Group: create.Group,
|
Group: create.Group,
|
||||||
Id: monitorId,
|
Id: checkId,
|
||||||
Schedule: create.Schedule,
|
Schedule: create.Schedule,
|
||||||
Script: create.Script,
|
Script: create.Script,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.CreateMonitor(
|
err = services.CreateCheck(
|
||||||
ctx,
|
ctx,
|
||||||
h.db,
|
h.db,
|
||||||
monitor,
|
check,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.UpdateMonitorWorkerGroups(ctx, h.db, monitor, workerGroups)
|
err = services.UpdateCheckWorkerGroups(ctx, h.db, check, workerGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.CreateOrUpdateMonitorSchedule(ctx, h.temporal, monitor, workerGroups)
|
err = services.CreateOrUpdateCheckSchedule(ctx, h.temporal, check, workerGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, "/settings/monitors")
|
return c.Redirect(http.StatusSeeOther, "/settings/checks")
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ type WorkerWithTokenAndActiveWorkers struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerGroupWithActiveWorkers struct {
|
type WorkerGroupWithActiveWorkers struct {
|
||||||
*models.WorkerGroupWithMonitors
|
*models.WorkerGroupWithChecks
|
||||||
ActiveWorkers []string
|
ActiveWorkers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ type SettingsWorker struct {
|
||||||
func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
workerGroups, err := services.GetWorkerGroupsWithMonitors(context.Background(), h.db)
|
workerGroups, err := services.GetWorkerGroupsWithChecks(context.Background(), h.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
workerGroupsWithActiveWorkers[i] = &WorkerGroupWithActiveWorkers{
|
workerGroupsWithActiveWorkers[i] = &WorkerGroupWithActiveWorkers{
|
||||||
WorkerGroupWithMonitors: workerGroup,
|
WorkerGroupWithChecks: workerGroup,
|
||||||
ActiveWorkers: activeWorkers,
|
ActiveWorkers: activeWorkers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
316
internal/services/check.go
Normal file
316
internal/services/check.go
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/workflows"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"go.temporal.io/sdk/client"
|
||||||
|
"go.temporal.io/sdk/temporal"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CheckStatusUnknown CheckStatus = "UNKNOWN"
|
||||||
|
CheckStatusPaused CheckStatus = "PAUSED"
|
||||||
|
CheckStatusActive CheckStatus = "ACTIVE"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getScheduleId(id string) string {
|
||||||
|
return "check-" + id
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountChecks(ctx context.Context, db *sqlx.DB) (int, error) {
|
||||||
|
var count int
|
||||||
|
err := db.GetContext(ctx, &count, "SELECT COUNT(*) FROM checks")
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckStatus(ctx context.Context, temporal client.Client, id string) (CheckStatus, error) {
|
||||||
|
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||||
|
|
||||||
|
description, err := schedule.Describe(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return CheckStatusUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if description.Schedule.State.Paused {
|
||||||
|
return CheckStatusPaused, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return CheckStatusActive, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCheckStatus(ctx context.Context, temporal client.Client, id string, status CheckStatus) error {
|
||||||
|
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||||
|
|
||||||
|
if status == CheckStatusActive {
|
||||||
|
return schedule.Unpause(ctx, client.ScheduleUnpauseOptions{Note: "Unpaused by user"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == CheckStatusPaused {
|
||||||
|
return schedule.Pause(ctx, client.SchedulePauseOptions{Note: "Paused by user"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateCheck(ctx context.Context, db *sqlx.DB, check *models.Check) error {
|
||||||
|
_, err := db.NamedExecContext(ctx,
|
||||||
|
`INSERT INTO checks (id, name, "group", script, schedule) VALUES (:id, :name, :group, :script, :schedule)`,
|
||||||
|
check,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCheck(ctx context.Context, db *sqlx.DB, check *models.Check) error {
|
||||||
|
_, err := db.NamedExecContext(ctx,
|
||||||
|
`UPDATE checks SET "group"=:group, script=:script, schedule=:schedule WHERE id=:id`,
|
||||||
|
check,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteCheck(ctx context.Context, db *sqlx.DB, id string) error {
|
||||||
|
_, err := db.ExecContext(ctx,
|
||||||
|
"DELETE FROM checks WHERE id=$1",
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCheckWorkerGroups(ctx context.Context, db *sqlx.DB, check *models.Check, workerGroups []*models.WorkerGroup) error {
|
||||||
|
tx, err := db.BeginTxx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tx.ExecContext(ctx,
|
||||||
|
"DELETE FROM check_worker_groups WHERE check_id=$1",
|
||||||
|
check.Id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, group := range workerGroups {
|
||||||
|
_, err = tx.ExecContext(ctx,
|
||||||
|
"INSERT INTO check_worker_groups (check_id, worker_group_id) VALUES ($1, $2)",
|
||||||
|
check.Id,
|
||||||
|
group.Id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheck(ctx context.Context, db *sqlx.DB, id string) (*models.Check, error) {
|
||||||
|
check := &models.Check{}
|
||||||
|
err := db.GetContext(ctx, check,
|
||||||
|
"SELECT * FROM checks WHERE id=$1",
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
return check, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckWithWorkerGroups(ctx context.Context, db *sqlx.DB, id string) (*models.CheckWithWorkerGroups, error) {
|
||||||
|
rows, err := db.QueryContext(ctx,
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
checks.id,
|
||||||
|
checks.name,
|
||||||
|
checks."group",
|
||||||
|
checks.script,
|
||||||
|
checks.schedule,
|
||||||
|
checks.created_at,
|
||||||
|
checks.updated_at,
|
||||||
|
worker_groups.name as worker_group_name
|
||||||
|
FROM checks
|
||||||
|
LEFT OUTER JOIN check_worker_groups ON checks.id = check_worker_groups.check_id
|
||||||
|
LEFT OUTER JOIN worker_groups ON check_worker_groups.worker_group_id = worker_groups.id
|
||||||
|
WHERE checks.id=$1
|
||||||
|
ORDER BY checks.name
|
||||||
|
`,
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
check := &models.CheckWithWorkerGroups{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var workerGroupName *string
|
||||||
|
err = rows.Scan(
|
||||||
|
&check.Id,
|
||||||
|
&check.Name,
|
||||||
|
&check.Group,
|
||||||
|
&check.Script,
|
||||||
|
&check.Schedule,
|
||||||
|
&check.CreatedAt,
|
||||||
|
&check.UpdatedAt,
|
||||||
|
&workerGroupName,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if workerGroupName != nil {
|
||||||
|
check.WorkerGroups = append(check.WorkerGroups, *workerGroupName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return check, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetChecks(ctx context.Context, db *sqlx.DB) ([]*models.Check, error) {
|
||||||
|
checks := []*models.Check{}
|
||||||
|
err := db.SelectContext(ctx, &checks,
|
||||||
|
"SELECT * FROM checks ORDER BY name",
|
||||||
|
)
|
||||||
|
return checks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetChecksWithWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.CheckWithWorkerGroups, error) {
|
||||||
|
rows, err := db.QueryContext(ctx,
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
checks.id,
|
||||||
|
checks.name,
|
||||||
|
checks."group",
|
||||||
|
checks.script,
|
||||||
|
checks.schedule,
|
||||||
|
checks.created_at,
|
||||||
|
checks.updated_at,
|
||||||
|
worker_groups.name as worker_group_name
|
||||||
|
FROM checks
|
||||||
|
LEFT OUTER JOIN check_worker_groups ON checks.id = check_worker_groups.check_id
|
||||||
|
LEFT OUTER JOIN worker_groups ON check_worker_groups.worker_group_id = worker_groups.id
|
||||||
|
ORDER BY checks.name
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
checks := map[string]*models.CheckWithWorkerGroups{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
check := &models.CheckWithWorkerGroups{}
|
||||||
|
|
||||||
|
var workerGroupName *string
|
||||||
|
err = rows.Scan(
|
||||||
|
&check.Id,
|
||||||
|
&check.Name,
|
||||||
|
&check.Group,
|
||||||
|
&check.Script,
|
||||||
|
&check.Schedule,
|
||||||
|
&check.CreatedAt,
|
||||||
|
&check.UpdatedAt,
|
||||||
|
&workerGroupName,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if workerGroupName != nil {
|
||||||
|
workerGroups := []string{}
|
||||||
|
if checks[check.Id] != nil {
|
||||||
|
workerGroups = checks[check.Id].WorkerGroups
|
||||||
|
}
|
||||||
|
check.WorkerGroups = append(workerGroups, *workerGroupName)
|
||||||
|
}
|
||||||
|
checks[check.Id] = check
|
||||||
|
}
|
||||||
|
|
||||||
|
checksWithWorkerGroups := maps.Values(checks)
|
||||||
|
sort.SliceStable(checksWithWorkerGroups, func(i, j int) bool {
|
||||||
|
return checksWithWorkerGroups[i].Name < checksWithWorkerGroups[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
return checksWithWorkerGroups, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteCheckSchedule(ctx context.Context, t client.Client, id string) error {
|
||||||
|
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||||
|
return schedule.Delete(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateOrUpdateCheckSchedule(
|
||||||
|
ctx context.Context,
|
||||||
|
t client.Client,
|
||||||
|
check *models.Check,
|
||||||
|
workerGroups []*models.WorkerGroup,
|
||||||
|
) error {
|
||||||
|
log.Println("Creating or Updating Check Schedule")
|
||||||
|
|
||||||
|
workerGroupStrings := make([]string, len(workerGroups))
|
||||||
|
for i, group := range workerGroups {
|
||||||
|
workerGroupStrings[i] = group.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, 1)
|
||||||
|
args[0] = workflows.CheckWorkflowParam{
|
||||||
|
Script: check.Script,
|
||||||
|
CheckId: check.Id,
|
||||||
|
WorkerGroupIds: workerGroupStrings,
|
||||||
|
}
|
||||||
|
|
||||||
|
options := client.ScheduleOptions{
|
||||||
|
ID: getScheduleId(check.Id),
|
||||||
|
Spec: client.ScheduleSpec{
|
||||||
|
CronExpressions: []string{check.Schedule},
|
||||||
|
Jitter: time.Second * 10,
|
||||||
|
},
|
||||||
|
Action: &client.ScheduleWorkflowAction{
|
||||||
|
ID: getScheduleId(check.Id),
|
||||||
|
Workflow: workflows.NewWorkflows(nil).CheckWorkflowDefinition,
|
||||||
|
Args: args,
|
||||||
|
TaskQueue: "default",
|
||||||
|
RetryPolicy: &temporal.RetryPolicy{
|
||||||
|
MaximumAttempts: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(check.Id))
|
||||||
|
|
||||||
|
// If exists, we update
|
||||||
|
_, err := schedule.Describe(ctx)
|
||||||
|
if err == nil {
|
||||||
|
err = schedule.Update(ctx, client.ScheduleUpdateOptions{
|
||||||
|
DoUpdate: func(input client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) {
|
||||||
|
return &client.ScheduleUpdate{
|
||||||
|
Schedule: &client.Schedule{
|
||||||
|
Spec: &options.Spec,
|
||||||
|
Action: options.Action,
|
||||||
|
Policy: input.Description.Schedule.Policy,
|
||||||
|
State: input.Description.Schedule.State,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schedule, err = t.ScheduleClient().Create(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = schedule.Trigger(ctx, client.ScheduleTriggerOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
67
internal/services/check_history.go
Normal file
67
internal/services/check_history.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckHistoryWithCheck struct {
|
||||||
|
*models.CheckHistory
|
||||||
|
CheckName string `db:"check_name"`
|
||||||
|
CheckId string `db:"check_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLastNCheckHistory(ctx context.Context, db *sqlx.DB, n int) ([]*CheckHistoryWithCheck, error) {
|
||||||
|
var checkHistory []*CheckHistoryWithCheck
|
||||||
|
err := db.SelectContext(ctx, &checkHistory, `
|
||||||
|
SELECT
|
||||||
|
mh.*,
|
||||||
|
wg.name AS worker_group_name,
|
||||||
|
m.name AS check_name,
|
||||||
|
m.id AS check_id
|
||||||
|
FROM check_histories mh
|
||||||
|
LEFT JOIN worker_groups wg ON mh.worker_group_id = wg.id
|
||||||
|
LEFT JOIN check_worker_groups mwg ON mh.check_id = mwg.check_id
|
||||||
|
LEFT JOIN checks m ON mwg.check_id = m.id
|
||||||
|
ORDER BY mh.created_at DESC
|
||||||
|
LIMIT $1
|
||||||
|
`, n)
|
||||||
|
return checkHistory, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckHistoryForCheck(ctx context.Context, db *sqlx.DB, checkId string) ([]*models.CheckHistory, error) {
|
||||||
|
var checkHistory []*models.CheckHistory
|
||||||
|
err := db.SelectContext(ctx, &checkHistory, `
|
||||||
|
SELECT
|
||||||
|
mh.*,
|
||||||
|
wg.name AS worker_group_name,
|
||||||
|
wg.id AS worker_group_id
|
||||||
|
FROM check_histories as mh
|
||||||
|
LEFT JOIN worker_groups wg ON mh.worker_group_id = wg.id
|
||||||
|
LEFT JOIN check_worker_groups mwg ON mh.check_id = mwg.check_id
|
||||||
|
WHERE mh.check_id = $1
|
||||||
|
ORDER BY mh.created_at DESC
|
||||||
|
`, checkId)
|
||||||
|
return checkHistory, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddHistoryForCheck(ctx context.Context, db *sqlx.DB, history *models.CheckHistory) error {
|
||||||
|
_, err := db.NamedExecContext(ctx,
|
||||||
|
`
|
||||||
|
INSERT INTO check_histories (
|
||||||
|
check_id,
|
||||||
|
worker_group_id,
|
||||||
|
status,
|
||||||
|
note
|
||||||
|
) VALUES (
|
||||||
|
:check_id,
|
||||||
|
:worker_group_id,
|
||||||
|
:status,
|
||||||
|
:note
|
||||||
|
)`,
|
||||||
|
history,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -1,316 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/database/models"
|
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/workflows"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.temporal.io/sdk/client"
|
|
||||||
"go.temporal.io/sdk/temporal"
|
|
||||||
"golang.org/x/exp/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MonitorStatus string
|
|
||||||
|
|
||||||
const (
|
|
||||||
MonitorStatusUnknown MonitorStatus = "UNKNOWN"
|
|
||||||
MonitorStatusPaused MonitorStatus = "PAUSED"
|
|
||||||
MonitorStatusActive MonitorStatus = "ACTIVE"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getScheduleId(id string) string {
|
|
||||||
return "monitor-" + id
|
|
||||||
}
|
|
||||||
|
|
||||||
func CountMonitors(ctx context.Context, db *sqlx.DB) (int, error) {
|
|
||||||
var count int
|
|
||||||
err := db.GetContext(ctx, &count, "SELECT COUNT(*) FROM monitors")
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitorStatus(ctx context.Context, temporal client.Client, id string) (MonitorStatus, error) {
|
|
||||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
|
||||||
|
|
||||||
description, err := schedule.Describe(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return MonitorStatusUnknown, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if description.Schedule.State.Paused {
|
|
||||||
return MonitorStatusPaused, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return MonitorStatusActive, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetMonitorStatus(ctx context.Context, temporal client.Client, id string, status MonitorStatus) error {
|
|
||||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
|
||||||
|
|
||||||
if status == MonitorStatusActive {
|
|
||||||
return schedule.Unpause(ctx, client.ScheduleUnpauseOptions{Note: "Unpaused by user"})
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == MonitorStatusPaused {
|
|
||||||
return schedule.Pause(ctx, client.SchedulePauseOptions{Note: "Paused by user"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
|
||||||
_, err := db.NamedExecContext(ctx,
|
|
||||||
`INSERT INTO monitors (id, name, "group", script, schedule) VALUES (:id, :name, :group, :script, :schedule)`,
|
|
||||||
monitor,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
|
||||||
_, err := db.NamedExecContext(ctx,
|
|
||||||
`UPDATE monitors SET "group"=:group, script=:script, schedule=:schedule WHERE id=:id`,
|
|
||||||
monitor,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteMonitor(ctx context.Context, db *sqlx.DB, id string) error {
|
|
||||||
_, err := db.ExecContext(ctx,
|
|
||||||
"DELETE FROM monitors WHERE id=$1",
|
|
||||||
id,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models.Monitor, workerGroups []*models.WorkerGroup) error {
|
|
||||||
tx, err := db.BeginTxx(ctx, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = tx.ExecContext(ctx,
|
|
||||||
"DELETE FROM monitor_worker_groups WHERE monitor_id=$1",
|
|
||||||
monitor.Id,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, group := range workerGroups {
|
|
||||||
_, err = tx.ExecContext(ctx,
|
|
||||||
"INSERT INTO monitor_worker_groups (monitor_id, worker_group_id) VALUES ($1, $2)",
|
|
||||||
monitor.Id,
|
|
||||||
group.Id,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tx.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitor(ctx context.Context, db *sqlx.DB, id string) (*models.Monitor, error) {
|
|
||||||
monitor := &models.Monitor{}
|
|
||||||
err := db.GetContext(ctx, monitor,
|
|
||||||
"SELECT * FROM monitors WHERE id=$1",
|
|
||||||
id,
|
|
||||||
)
|
|
||||||
return monitor, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitorWithWorkerGroups(ctx context.Context, db *sqlx.DB, id string) (*models.MonitorWithWorkerGroups, error) {
|
|
||||||
rows, err := db.QueryContext(ctx,
|
|
||||||
`
|
|
||||||
SELECT
|
|
||||||
monitors.id,
|
|
||||||
monitors.name,
|
|
||||||
monitors."group",
|
|
||||||
monitors.script,
|
|
||||||
monitors.schedule,
|
|
||||||
monitors.created_at,
|
|
||||||
monitors.updated_at,
|
|
||||||
worker_groups.name as worker_group_name
|
|
||||||
FROM monitors
|
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.id = monitor_worker_groups.monitor_id
|
|
||||||
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_id = worker_groups.id
|
|
||||||
WHERE monitors.id=$1
|
|
||||||
ORDER BY monitors.name
|
|
||||||
`,
|
|
||||||
id,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
monitor := &models.MonitorWithWorkerGroups{}
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var workerGroupName *string
|
|
||||||
err = rows.Scan(
|
|
||||||
&monitor.Id,
|
|
||||||
&monitor.Name,
|
|
||||||
&monitor.Group,
|
|
||||||
&monitor.Script,
|
|
||||||
&monitor.Schedule,
|
|
||||||
&monitor.CreatedAt,
|
|
||||||
&monitor.UpdatedAt,
|
|
||||||
&workerGroupName,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if workerGroupName != nil {
|
|
||||||
monitor.WorkerGroups = append(monitor.WorkerGroups, *workerGroupName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return monitor, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitors(ctx context.Context, db *sqlx.DB) ([]*models.Monitor, error) {
|
|
||||||
monitors := []*models.Monitor{}
|
|
||||||
err := db.SelectContext(ctx, &monitors,
|
|
||||||
"SELECT * FROM monitors ORDER BY name",
|
|
||||||
)
|
|
||||||
return monitors, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitorsWithWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.MonitorWithWorkerGroups, error) {
|
|
||||||
rows, err := db.QueryContext(ctx,
|
|
||||||
`
|
|
||||||
SELECT
|
|
||||||
monitors.id,
|
|
||||||
monitors.name,
|
|
||||||
monitors."group",
|
|
||||||
monitors.script,
|
|
||||||
monitors.schedule,
|
|
||||||
monitors.created_at,
|
|
||||||
monitors.updated_at,
|
|
||||||
worker_groups.name as worker_group_name
|
|
||||||
FROM monitors
|
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.id = monitor_worker_groups.monitor_id
|
|
||||||
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_id = worker_groups.id
|
|
||||||
ORDER BY monitors.name
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
monitors := map[string]*models.MonitorWithWorkerGroups{}
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
monitor := &models.MonitorWithWorkerGroups{}
|
|
||||||
|
|
||||||
var workerGroupName *string
|
|
||||||
err = rows.Scan(
|
|
||||||
&monitor.Id,
|
|
||||||
&monitor.Name,
|
|
||||||
&monitor.Group,
|
|
||||||
&monitor.Script,
|
|
||||||
&monitor.Schedule,
|
|
||||||
&monitor.CreatedAt,
|
|
||||||
&monitor.UpdatedAt,
|
|
||||||
&workerGroupName,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if workerGroupName != nil {
|
|
||||||
workerGroups := []string{}
|
|
||||||
if monitors[monitor.Id] != nil {
|
|
||||||
workerGroups = monitors[monitor.Id].WorkerGroups
|
|
||||||
}
|
|
||||||
monitor.WorkerGroups = append(workerGroups, *workerGroupName)
|
|
||||||
}
|
|
||||||
monitors[monitor.Id] = monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
monitorsWithWorkerGroups := maps.Values(monitors)
|
|
||||||
sort.SliceStable(monitorsWithWorkerGroups, func(i, j int) bool {
|
|
||||||
return monitorsWithWorkerGroups[i].Name < monitorsWithWorkerGroups[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
return monitorsWithWorkerGroups, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteMonitorSchedule(ctx context.Context, t client.Client, id string) error {
|
|
||||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
|
||||||
return schedule.Delete(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateOrUpdateMonitorSchedule(
|
|
||||||
ctx context.Context,
|
|
||||||
t client.Client,
|
|
||||||
monitor *models.Monitor,
|
|
||||||
workerGroups []*models.WorkerGroup,
|
|
||||||
) error {
|
|
||||||
log.Println("Creating or Updating Monitor Schedule")
|
|
||||||
|
|
||||||
workerGroupStrings := make([]string, len(workerGroups))
|
|
||||||
for i, group := range workerGroups {
|
|
||||||
workerGroupStrings[i] = group.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
args := make([]interface{}, 1)
|
|
||||||
args[0] = workflows.MonitorWorkflowParam{
|
|
||||||
Script: monitor.Script,
|
|
||||||
MonitorId: monitor.Id,
|
|
||||||
WorkerGroupIds: workerGroupStrings,
|
|
||||||
}
|
|
||||||
|
|
||||||
options := client.ScheduleOptions{
|
|
||||||
ID: getScheduleId(monitor.Id),
|
|
||||||
Spec: client.ScheduleSpec{
|
|
||||||
CronExpressions: []string{monitor.Schedule},
|
|
||||||
Jitter: time.Second * 10,
|
|
||||||
},
|
|
||||||
Action: &client.ScheduleWorkflowAction{
|
|
||||||
ID: getScheduleId(monitor.Id),
|
|
||||||
Workflow: workflows.NewWorkflows(nil).MonitorWorkflowDefinition,
|
|
||||||
Args: args,
|
|
||||||
TaskQueue: "default",
|
|
||||||
RetryPolicy: &temporal.RetryPolicy{
|
|
||||||
MaximumAttempts: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(monitor.Id))
|
|
||||||
|
|
||||||
// If exists, we update
|
|
||||||
_, err := schedule.Describe(ctx)
|
|
||||||
if err == nil {
|
|
||||||
err = schedule.Update(ctx, client.ScheduleUpdateOptions{
|
|
||||||
DoUpdate: func(input client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) {
|
|
||||||
return &client.ScheduleUpdate{
|
|
||||||
Schedule: &client.Schedule{
|
|
||||||
Spec: &options.Spec,
|
|
||||||
Action: options.Action,
|
|
||||||
Policy: input.Description.Schedule.Policy,
|
|
||||||
State: input.Description.Schedule.State,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
schedule, err = t.ScheduleClient().Create(ctx, options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = schedule.Trigger(ctx, client.ScheduleTriggerOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/database/models"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MonitorHistoryWithMonitor struct {
|
|
||||||
*models.MonitorHistory
|
|
||||||
MonitorName string `db:"monitor_name"`
|
|
||||||
MonitorId string `db:"monitor_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLastNMonitorHistory(ctx context.Context, db *sqlx.DB, n int) ([]*MonitorHistoryWithMonitor, error) {
|
|
||||||
var monitorHistory []*MonitorHistoryWithMonitor
|
|
||||||
err := db.SelectContext(ctx, &monitorHistory, `
|
|
||||||
SELECT
|
|
||||||
mh.*,
|
|
||||||
wg.name AS worker_group_name,
|
|
||||||
m.name AS monitor_name,
|
|
||||||
m.id AS monitor_id
|
|
||||||
FROM monitor_histories mh
|
|
||||||
LEFT JOIN worker_groups wg ON mh.worker_group_id = wg.id
|
|
||||||
LEFT JOIN monitor_worker_groups mwg ON mh.monitor_id = mwg.monitor_id
|
|
||||||
LEFT JOIN monitors m ON mwg.monitor_id = m.id
|
|
||||||
ORDER BY mh.created_at DESC
|
|
||||||
LIMIT $1
|
|
||||||
`, n)
|
|
||||||
return monitorHistory, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitorHistoryForMonitor(ctx context.Context, db *sqlx.DB, monitorId string) ([]*models.MonitorHistory, error) {
|
|
||||||
var monitorHistory []*models.MonitorHistory
|
|
||||||
err := db.SelectContext(ctx, &monitorHistory, `
|
|
||||||
SELECT
|
|
||||||
mh.*,
|
|
||||||
wg.name AS worker_group_name,
|
|
||||||
wg.id AS worker_group_id
|
|
||||||
FROM monitor_histories as mh
|
|
||||||
LEFT JOIN worker_groups wg ON mh.worker_group_id = wg.id
|
|
||||||
LEFT JOIN monitor_worker_groups mwg ON mh.monitor_id = mwg.monitor_id
|
|
||||||
WHERE mh.monitor_id = $1
|
|
||||||
ORDER BY mh.created_at DESC
|
|
||||||
`, monitorId)
|
|
||||||
return monitorHistory, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddHistoryForMonitor(ctx context.Context, db *sqlx.DB, history *models.MonitorHistory) error {
|
|
||||||
_, err := db.NamedExecContext(ctx,
|
|
||||||
`
|
|
||||||
INSERT INTO monitor_histories (
|
|
||||||
monitor_id,
|
|
||||||
worker_group_id,
|
|
||||||
status,
|
|
||||||
note
|
|
||||||
) VALUES (
|
|
||||||
:monitor_id,
|
|
||||||
:worker_group_id,
|
|
||||||
:status,
|
|
||||||
:note
|
|
||||||
)`,
|
|
||||||
history,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -54,7 +54,7 @@ func GetWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.WorkerGroup, e
|
||||||
return workerGroups, err
|
return workerGroups, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkerGroupsWithMonitors(ctx context.Context, db *sqlx.DB) ([]*models.WorkerGroupWithMonitors, error) {
|
func GetWorkerGroupsWithChecks(ctx context.Context, db *sqlx.DB) ([]*models.WorkerGroupWithChecks, error) {
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -62,10 +62,10 @@ SELECT
|
||||||
worker_groups.name,
|
worker_groups.name,
|
||||||
worker_groups.created_at,
|
worker_groups.created_at,
|
||||||
worker_groups.updated_at,
|
worker_groups.updated_at,
|
||||||
monitors.name as monitor_name
|
checks.name as check_name
|
||||||
FROM worker_groups
|
FROM worker_groups
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.id = monitor_worker_groups.worker_group_id
|
LEFT OUTER JOIN check_worker_groups ON worker_groups.id = check_worker_groups.worker_group_id
|
||||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_id = monitors.id
|
LEFT OUTER JOIN checks ON check_worker_groups.check_id = checks.id
|
||||||
ORDER BY worker_groups.name
|
ORDER BY worker_groups.name
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,29 +73,29 @@ ORDER BY worker_groups.name
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
workerGroups := map[string]*models.WorkerGroupWithMonitors{}
|
workerGroups := map[string]*models.WorkerGroupWithChecks{}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
workerGroup := &models.WorkerGroupWithMonitors{}
|
workerGroup := &models.WorkerGroupWithChecks{}
|
||||||
|
|
||||||
var monitorName *string
|
var checkName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&workerGroup.Id,
|
&workerGroup.Id,
|
||||||
&workerGroup.Name,
|
&workerGroup.Name,
|
||||||
&workerGroup.CreatedAt,
|
&workerGroup.CreatedAt,
|
||||||
&workerGroup.UpdatedAt,
|
&workerGroup.UpdatedAt,
|
||||||
&monitorName,
|
&checkName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if monitorName != nil {
|
if checkName != nil {
|
||||||
monitors := []string{}
|
checks := []string{}
|
||||||
if workerGroups[workerGroup.Id] != nil {
|
if workerGroups[workerGroup.Id] != nil {
|
||||||
monitors = workerGroups[workerGroup.Id].Monitors
|
checks = workerGroups[workerGroup.Id].Checks
|
||||||
}
|
}
|
||||||
workerGroup.Monitors = append(monitors, *monitorName)
|
workerGroup.Checks = append(checks, *checkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
workerGroups[workerGroup.Id] = workerGroup
|
workerGroups[workerGroup.Id] = workerGroup
|
||||||
|
@ -122,7 +122,7 @@ func GetWorkerGroup(ctx context.Context, db *sqlx.DB, id string) (*models.Worker
|
||||||
return &workerGroup, err
|
return &workerGroup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkerGroupWithMonitors(ctx context.Context, db *sqlx.DB, id string) (*models.WorkerGroupWithMonitors, error) {
|
func GetWorkerGroupWithChecks(ctx context.Context, db *sqlx.DB, id string) (*models.WorkerGroupWithChecks, error) {
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -130,10 +130,10 @@ SELECT
|
||||||
worker_groups.name,
|
worker_groups.name,
|
||||||
worker_groups.created_at,
|
worker_groups.created_at,
|
||||||
worker_groups.updated_at,
|
worker_groups.updated_at,
|
||||||
monitors.name as monitor_name
|
checks.name as check_name
|
||||||
FROM worker_groups
|
FROM worker_groups
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.id = monitor_worker_groups.worker_group_id
|
LEFT OUTER JOIN check_worker_groups ON worker_groups.id = check_worker_groups.worker_group_id
|
||||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_id = monitors.id
|
LEFT OUTER JOIN checks ON check_worker_groups.check_id = checks.id
|
||||||
WHERE worker_groups.id=$1
|
WHERE worker_groups.id=$1
|
||||||
`,
|
`,
|
||||||
id,
|
id,
|
||||||
|
@ -143,22 +143,22 @@ WHERE worker_groups.id=$1
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
workerGroup := &models.WorkerGroupWithMonitors{}
|
workerGroup := &models.WorkerGroupWithChecks{}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var monitorName *string
|
var checkName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&workerGroup.Id,
|
&workerGroup.Id,
|
||||||
&workerGroup.Name,
|
&workerGroup.Name,
|
||||||
&workerGroup.CreatedAt,
|
&workerGroup.CreatedAt,
|
||||||
&workerGroup.UpdatedAt,
|
&workerGroup.UpdatedAt,
|
||||||
&monitorName,
|
&checkName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if monitorName != nil {
|
if checkName != nil {
|
||||||
workerGroup.Monitors = append(workerGroup.Monitors, *monitorName)
|
workerGroup.Checks = append(workerGroup.Checks, *checkName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
internal/workflows/check.go
Normal file
58
internal/workflows/check.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package workflows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/activities"
|
||||||
|
"go.temporal.io/sdk/workflow"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckWorkflowParam struct {
|
||||||
|
Script string
|
||||||
|
CheckId string
|
||||||
|
WorkerGroupIds []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param CheckWorkflowParam) (models.CheckStatus, error) {
|
||||||
|
workerGroupIds := param.WorkerGroupIds
|
||||||
|
sort.Strings(workerGroupIds)
|
||||||
|
|
||||||
|
for _, workerGroupId := range workerGroupIds {
|
||||||
|
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
||||||
|
StartToCloseTimeout: 60 * time.Second,
|
||||||
|
TaskQueue: workerGroupId,
|
||||||
|
})
|
||||||
|
|
||||||
|
heatlcheckParam := activities.HealtcheckParam{
|
||||||
|
Script: param.Script,
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkResult *activities.CheckResult
|
||||||
|
err := workflow.ExecuteActivity(ctx, w.activities.Check, heatlcheckParam).Get(ctx, &checkResult)
|
||||||
|
if err != nil {
|
||||||
|
return models.CheckUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
status := models.CheckFailure
|
||||||
|
if checkResult.Success {
|
||||||
|
status = models.CheckSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
historyParam := activities.HealtcheckAddToHistoryParam{
|
||||||
|
CheckId: param.CheckId,
|
||||||
|
Status: status,
|
||||||
|
Note: checkResult.Note,
|
||||||
|
WorkerGroupId: workerGroupId,
|
||||||
|
}
|
||||||
|
|
||||||
|
var historyResult *activities.CheckAddToHistoryResult
|
||||||
|
err = workflow.ExecuteActivity(ctx, w.activities.CheckAddToHistory, historyParam).Get(ctx, &historyResult)
|
||||||
|
if err != nil {
|
||||||
|
return models.CheckUnknown, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.CheckSuccess, nil
|
||||||
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
package workflows
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/database/models"
|
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/activities"
|
|
||||||
"go.temporal.io/sdk/workflow"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MonitorWorkflowParam struct {
|
|
||||||
Script string
|
|
||||||
MonitorId string
|
|
||||||
WorkerGroupIds []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param MonitorWorkflowParam) (models.MonitorStatus, error) {
|
|
||||||
workerGroupIds := param.WorkerGroupIds
|
|
||||||
sort.Strings(workerGroupIds)
|
|
||||||
|
|
||||||
for _, workerGroupId := range workerGroupIds {
|
|
||||||
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
|
||||||
StartToCloseTimeout: 60 * time.Second,
|
|
||||||
TaskQueue: workerGroupId,
|
|
||||||
})
|
|
||||||
|
|
||||||
heatlcheckParam := activities.HealtcheckParam{
|
|
||||||
Script: param.Script,
|
|
||||||
}
|
|
||||||
|
|
||||||
var monitorResult *activities.MonitorResult
|
|
||||||
err := workflow.ExecuteActivity(ctx, w.activities.Monitor, heatlcheckParam).Get(ctx, &monitorResult)
|
|
||||||
if err != nil {
|
|
||||||
return models.MonitorUnknown, err
|
|
||||||
}
|
|
||||||
|
|
||||||
status := models.MonitorFailure
|
|
||||||
if monitorResult.Success {
|
|
||||||
status = models.MonitorSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
historyParam := activities.HealtcheckAddToHistoryParam{
|
|
||||||
MonitorId: param.MonitorId,
|
|
||||||
Status: status,
|
|
||||||
Note: monitorResult.Note,
|
|
||||||
WorkerGroupId: workerGroupId,
|
|
||||||
}
|
|
||||||
|
|
||||||
var historyResult *activities.MonitorAddToHistoryResult
|
|
||||||
err = workflow.ExecuteActivity(ctx, w.activities.MonitorAddToHistory, historyParam).Get(ctx, &historyResult)
|
|
||||||
if err != nil {
|
|
||||||
return models.MonitorUnknown, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return models.MonitorSuccess, nil
|
|
||||||
}
|
|
|
@ -2,8 +2,8 @@ package api
|
||||||
|
|
||||||
import "code.tjo.space/mentos1386/zdravko/database/models"
|
import "code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
|
|
||||||
type ApiV1MonitorsHistoryPOSTBody struct {
|
type ApiV1ChecksHistoryPOSTBody struct {
|
||||||
Status models.MonitorStatus `json:"status"`
|
Status models.CheckStatus `json:"status"`
|
||||||
Note string `json:"note"`
|
Note string `json:"note"`
|
||||||
WorkerGroupId string `json:"worker_group"`
|
WorkerGroupId string `json:"worker_group"`
|
||||||
}
|
}
|
|
@ -72,14 +72,16 @@ func Routes(
|
||||||
//settings.GET("/targets/:id/disable", h.SettingsTargetsDisableGET)
|
//settings.GET("/targets/:id/disable", h.SettingsTargetsDisableGET)
|
||||||
//settings.GET("/targets/:id/enable", h.SettingsTargetsEnableGET)
|
//settings.GET("/targets/:id/enable", h.SettingsTargetsEnableGET)
|
||||||
|
|
||||||
settings.GET("/monitors", h.SettingsMonitorsGET)
|
settings.GET("/incidents", h.SettingsIncidentsGET)
|
||||||
settings.GET("/monitors/create", h.SettingsMonitorsCreateGET)
|
|
||||||
settings.POST("/monitors/create", h.SettingsMonitorsCreatePOST)
|
settings.GET("/checks", h.SettingsChecksGET)
|
||||||
settings.GET("/monitors/:id", h.SettingsMonitorsDescribeGET)
|
settings.GET("/checks/create", h.SettingsChecksCreateGET)
|
||||||
settings.POST("/monitors/:id", h.SettingsMonitorsDescribePOST)
|
settings.POST("/checks/create", h.SettingsChecksCreatePOST)
|
||||||
settings.GET("/monitors/:id/delete", h.SettingsMonitorsDescribeDELETE)
|
settings.GET("/checks/:id", h.SettingsChecksDescribeGET)
|
||||||
settings.GET("/monitors/:id/disable", h.SettingsMonitorsDisableGET)
|
settings.POST("/checks/:id", h.SettingsChecksDescribePOST)
|
||||||
settings.GET("/monitors/:id/enable", h.SettingsMonitorsEnableGET)
|
settings.GET("/checks/:id/delete", h.SettingsChecksDescribeDELETE)
|
||||||
|
settings.GET("/checks/:id/disable", h.SettingsChecksDisableGET)
|
||||||
|
settings.GET("/checks/:id/enable", h.SettingsChecksEnableGET)
|
||||||
|
|
||||||
settings.GET("/notifications", h.SettingsNotificationsGET)
|
settings.GET("/notifications", h.SettingsNotificationsGET)
|
||||||
|
|
||||||
|
@ -101,7 +103,6 @@ func Routes(
|
||||||
apiv1 := e.Group("/api/v1")
|
apiv1 := e.Group("/api/v1")
|
||||||
apiv1.Use(h.Authenticated)
|
apiv1.Use(h.Authenticated)
|
||||||
apiv1.GET("/workers/connect", h.ApiV1WorkersConnectGET)
|
apiv1.GET("/workers/connect", h.ApiV1WorkersConnectGET)
|
||||||
apiv1.POST("/monitors/:id/history", h.ApiV1MonitorsHistoryPOST)
|
|
||||||
|
|
||||||
// Error handler
|
// Error handler
|
||||||
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ func NewWorker(temporalClient client.Client, cfg *config.ServerConfig) *Worker {
|
||||||
workerWorkflows := workflows.NewWorkflows(workerActivities)
|
workerWorkflows := workflows.NewWorkflows(workerActivities)
|
||||||
|
|
||||||
// Register Workflows
|
// Register Workflows
|
||||||
w.RegisterWorkflow(workerWorkflows.MonitorWorkflowDefinition)
|
w.RegisterWorkflow(workerWorkflows.CheckWorkflowDefinition)
|
||||||
|
|
||||||
return &Worker{
|
return &Worker{
|
||||||
worker: w,
|
worker: w,
|
||||||
|
|
|
@ -92,11 +92,11 @@ func (w *Worker) Start() error {
|
||||||
workerWorkflows := workflows.NewWorkflows(workerActivities)
|
workerWorkflows := workflows.NewWorkflows(workerActivities)
|
||||||
|
|
||||||
// Register Workflows
|
// Register Workflows
|
||||||
w.worker.RegisterWorkflow(workerWorkflows.MonitorWorkflowDefinition)
|
w.worker.RegisterWorkflow(workerWorkflows.CheckWorkflowDefinition)
|
||||||
|
|
||||||
// Register Activities
|
// Register Activities
|
||||||
w.worker.RegisterActivity(workerActivities.Monitor)
|
w.worker.RegisterActivity(workerActivities.Check)
|
||||||
w.worker.RegisterActivity(workerActivities.MonitorAddToHistory)
|
w.worker.RegisterActivity(workerActivities.CheckAddToHistory)
|
||||||
|
|
||||||
return w.worker.Run(worker.InterruptCh())
|
return w.worker.Run(worker.InterruptCh())
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,6 @@ code {
|
||||||
@apply shadow;
|
@apply shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
@apply flex flex-row flex-wrap justify-center lg:flex-col lg:w-48 gap-2 h-fit text-sm font-medium text-gray-900;
|
|
||||||
}
|
|
||||||
.sidebar a {
|
.sidebar a {
|
||||||
@apply w-full block rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-700 focus:text-blue-700;
|
@apply w-full block rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-700 focus:text-blue-700;
|
||||||
}
|
}
|
||||||
|
|
|
@ -825,6 +825,10 @@ video {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.items-center {
|
.items-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
@ -1361,28 +1365,6 @@ code {
|
||||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
display: flex;
|
|
||||||
height: -moz-fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
font-weight: 500;
|
|
||||||
--tw-text-opacity: 1;
|
|
||||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.sidebar {
|
|
||||||
width: 12rem;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar a {
|
.sidebar a {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1856,10 +1838,22 @@ code {
|
||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg\:w-48 {
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
.lg\:grid-cols-\[min-content_minmax\(0\2c 1fr\)\] {
|
.lg\:grid-cols-\[min-content_minmax\(0\2c 1fr\)\] {
|
||||||
grid-template-columns: min-content minmax(0,1fr);
|
grid-template-columns: min-content minmax(0,1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg\:flex-col {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg\:items-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.lg\:justify-start {
|
.lg\:justify-start {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,17 @@
|
||||||
<div
|
<div
|
||||||
class="md:px-4 lg:px-8 mx-auto mt-8 w-full max-w-screen-xl lg:mt-20 grid grid-cols-1 lg:grid-cols-[min-content_minmax(0,1fr)] gap-8"
|
class="md:px-4 lg:px-8 mx-auto mt-8 w-full max-w-screen-xl lg:mt-20 grid grid-cols-1 lg:grid-cols-[min-content_minmax(0,1fr)] gap-8"
|
||||||
>
|
>
|
||||||
<ul class="sidebar gap-4">
|
<ul
|
||||||
|
class="sidebar gap-2 flex flex-row flex-wrap justify-center lg:flex-col lg:w-48 h-fit text-sm font-medium text-gray-900"
|
||||||
|
>
|
||||||
{{ range .SettingsSidebar }}
|
{{ range .SettingsSidebar }}
|
||||||
<li>
|
<li class="flex items-center gap-1 lg:flex-col lg:items-start">
|
||||||
<p
|
<p
|
||||||
class="mb-2 text-xs font-semibold text-gray-600 uppercase tracking-wider"
|
class="text-xs font-semibold text-gray-600 uppercase tracking-wider"
|
||||||
>
|
>
|
||||||
{{ .Group }}
|
{{ .Group }}
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul class="flex flex-row flex-wrap gap-1 lg:flex-col">
|
||||||
{{ range .Pages }}
|
{{ range .Pages }}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
|
@ -40,6 +42,9 @@
|
||||||
{{ if eq .Title "Checks" }}
|
{{ if eq .Title "Checks" }}
|
||||||
<span class="text-slate-400">(3)</span>
|
<span class="text-slate-400">(3)</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if eq .Title "Hooks" }}
|
||||||
|
<span class="text-slate-400">(3)</span>
|
||||||
|
{{ end }}
|
||||||
{{ if eq .Title "Triggers" }}
|
{{ if eq .Title "Triggers" }}
|
||||||
<span class="text-slate-400">(3)</span>
|
<span class="text-slate-400">(3)</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<div class="container max-w-screen-md flex flex-col mt-20 gap-20">
|
<div class="container max-w-screen-md flex flex-col mt-20 gap-20">
|
||||||
{{ $length := len .Monitors }}
|
{{ $length := len .Checks }}
|
||||||
{{ if eq $length 0 }}
|
{{ if eq $length 0 }}
|
||||||
<section>
|
<section>
|
||||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||||
<h1
|
<h1
|
||||||
class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl"
|
class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl"
|
||||||
>
|
>
|
||||||
There are no monitors yet.
|
There are no checks yet.
|
||||||
</h1>
|
</h1>
|
||||||
<p
|
<p
|
||||||
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
|
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
|
||||||
>
|
>
|
||||||
Create a monitor to monitor your services and get notified when they
|
Create a check to check your services and get notified when they
|
||||||
are down.
|
are down.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
|
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
|
||||||
<a
|
<a
|
||||||
href="/settings/monitors/create"
|
href="/settings/checks/create"
|
||||||
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
>
|
>
|
||||||
Create First Monitor
|
Create First Check
|
||||||
<svg class="feather ml-1 h-5 w-5 overflow-visible">
|
<svg class="feather ml-1 h-5 w-5 overflow-visible">
|
||||||
<use href="/static/icons/feather-sprite.svg#plus" />
|
<use href="/static/icons/feather-sprite.svg#plus" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="monitors flex flex-col gap-4">
|
<div class="checks flex flex-col gap-4">
|
||||||
<div
|
<div
|
||||||
class="inline-flex gap-1 justify-center md:justify-end time-range"
|
class="inline-flex gap-1 justify-center md:justify-end time-range"
|
||||||
role="group"
|
role="group"
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
>90 Minutes</a
|
>90 Minutes</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{{ range $group, $monitorsAndStatus := .Monitors }}
|
{{ range $group, $checksAndStatus := .Checks }}
|
||||||
<details
|
<details
|
||||||
open
|
open
|
||||||
class="bg-white shadow-md rounded-lg p-6 py-4 gap-2 [&_svg]:open:rotate-90"
|
class="bg-white shadow-md rounded-lg p-6 py-4 gap-2 [&_svg]:open:rotate-90"
|
||||||
|
@ -101,11 +101,11 @@
|
||||||
<summary
|
<summary
|
||||||
class="flex flex-row gap-2 p-3 py-2 -mx-3 cursor-pointer hover:bg-blue-50 rounded-lg"
|
class="flex flex-row gap-2 p-3 py-2 -mx-3 cursor-pointer hover:bg-blue-50 rounded-lg"
|
||||||
>
|
>
|
||||||
{{ if eq $monitorsAndStatus.Status "SUCCESS" }}
|
{{ if eq $checksAndStatus.Status "SUCCESS" }}
|
||||||
<span
|
<span
|
||||||
class="flex w-3 h-3 bg-green-400 rounded-full self-center"
|
class="flex w-3 h-3 bg-green-400 rounded-full self-center"
|
||||||
></span>
|
></span>
|
||||||
{{ else if eq $monitorsAndStatus.Status "FAILURE" }}
|
{{ else if eq $checksAndStatus.Status "FAILURE" }}
|
||||||
<span
|
<span
|
||||||
class="flex w-3 h-3 bg-red-400 rounded-full self-center"
|
class="flex w-3 h-3 bg-red-400 rounded-full self-center"
|
||||||
></span>
|
></span>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
<use href="/static/icons/feather-sprite.svg#chevron-right" />
|
<use href="/static/icons/feather-sprite.svg#chevron-right" />
|
||||||
</svg>
|
</svg>
|
||||||
</summary>
|
</summary>
|
||||||
{{ range $monitorsAndStatus.Monitors }}
|
{{ range $checksAndStatus.Checks }}
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-2 pb-2 border-b last-of-type:pb-0 last-of-type:border-0 border-gray-100"
|
class="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-2 pb-2 border-b last-of-type:pb-0 last-of-type:border-0 border-gray-100"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{{ define "settings" }}
|
{{ define "settings" }}
|
||||||
{{ $description := "Monitors are constantly determining if targets are healthy or not." }}
|
{{ $description := "Checks are constantly determining if targets are healthy or not." }}
|
||||||
|
|
||||||
{{ $length := len .Monitors }}
|
{{ $length := len .Checks }}
|
||||||
{{ if eq $length 0 }}
|
{{ if eq $length 0 }}
|
||||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||||
<h1
|
<h1
|
||||||
class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl"
|
class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl"
|
||||||
>
|
>
|
||||||
There are no monitors yet.
|
There are no checks yet.
|
||||||
</h1>
|
</h1>
|
||||||
<p
|
<p
|
||||||
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
|
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
|
||||||
|
@ -16,10 +16,10 @@
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
|
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
|
||||||
<a
|
<a
|
||||||
href="/settings/monitors/create"
|
href="/settings/checks/create"
|
||||||
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
>
|
>
|
||||||
Create First Monitor
|
Create First Check
|
||||||
<svg class="feather ml-1 h-5 w-5 overflow-visible">
|
<svg class="feather ml-1 h-5 w-5 overflow-visible">
|
||||||
<use href="/static/icons/feather-sprite.svg#plus" />
|
<use href="/static/icons/feather-sprite.svg#plus" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -30,13 +30,13 @@
|
||||||
<section>
|
<section>
|
||||||
<table>
|
<table>
|
||||||
<caption>
|
<caption>
|
||||||
List of Monitors
|
List of Checks
|
||||||
<div class="mt-1 gap-4 grid grid-cols-1 md:grid-cols-[1fr,20%]">
|
<div class="mt-1 gap-4 grid grid-cols-1 md:grid-cols-[1fr,20%]">
|
||||||
<p>
|
<p>
|
||||||
{{ $description }}
|
{{ $description }}
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="/settings/monitors/create"
|
href="/settings/checks/create"
|
||||||
class="h-min inline-flex justify-center items-center py-2 px-4 text-sm font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
class="h-min inline-flex justify-center items-center py-2 px-4 text-sm font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
>
|
>
|
||||||
Create New
|
Create New
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</caption>
|
</caption>
|
||||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Monitor Group</th>
|
<th scope="col">Check Group</th>
|
||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
<th scope="col">Visibility</th>
|
<th scope="col">Visibility</th>
|
||||||
<th scope="col">Worker Groups</th>
|
<th scope="col">Worker Groups</th>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{ range .MonitorGroups }}
|
{{ range .CheckGroups }}
|
||||||
{{ $currentGroup := . }}
|
{{ $currentGroup := . }}
|
||||||
<tr class="row-special">
|
<tr class="row-special">
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
|
@ -71,9 +71,9 @@
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ range $group, $monitors := $.Monitors }}
|
{{ range $group, $checks := $.Checks }}
|
||||||
{{ if eq $group $currentGroup }}
|
{{ if eq $group $currentGroup }}
|
||||||
{{ range $monitors }}
|
{{ range $checks }}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">└─</th>
|
<th scope="row">└─</th>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
{{ .Schedule }}
|
{{ .Schedule }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/settings/monitors/{{ .Id }}" class="link"
|
<a href="/settings/checks/{{ .Id }}" class="link"
|
||||||
>Details</a
|
>Details</a
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
|
@ -1,10 +1,10 @@
|
||||||
{{ define "settings" }}
|
{{ define "settings" }}
|
||||||
<section class="p-5">
|
<section class="p-5">
|
||||||
<form action="/settings/monitors/create" method="post">
|
<form action="/settings/checks/create" method="post">
|
||||||
<label for="name">Name</label>
|
<label for="name">Name</label>
|
||||||
<input type="text" name="name" id="name" placeholder="Github.com" />
|
<input type="text" name="name" id="name" placeholder="Github.com" />
|
||||||
<p>Name of the monitor can be anything.</p>
|
<p>Name of the check can be anything.</p>
|
||||||
<label list="existing-groups" for="group">Monitor Group</label>
|
<label list="existing-groups" for="group">Check Group</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="group"
|
name="group"
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
<option value="default"></option>
|
<option value="default"></option>
|
||||||
</datalist>
|
</datalist>
|
||||||
<p>
|
<p>
|
||||||
Group monitors together. This affects how they are presented on the
|
Group checks together. This affects how they are presented on the
|
||||||
homepage.
|
homepage.
|
||||||
</p>
|
</p>
|
||||||
<label for="workergroups">Worker Groups</label>
|
<label for="workergroups">Worker Groups</label>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Worker groups are used to distribute the monitor to specific workers.
|
Worker groups are used to distribute the check to specific workers.
|
||||||
</p>
|
</p>
|
||||||
<label for="schedule">Schedule</label>
|
<label for="schedule">Schedule</label>
|
||||||
<input
|
<input
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Schedule is a cron expression that defines when the monitor should be
|
Schedule is a cron expression that defines when the check should be
|
||||||
executed.
|
executed.
|
||||||
<br />
|
<br />
|
||||||
You can also use <code>@every [interval]</code> where interval is a
|
You can also use <code>@every [interval]</code> where interval is a
|
|
@ -1,17 +1,17 @@
|
||||||
{{ define "settings" }}
|
{{ define "settings" }}
|
||||||
<section class="p-5">
|
<section class="p-5">
|
||||||
<form action="/settings/monitors/{{ .Monitor.Id }}" method="post">
|
<form action="/settings/checks/{{ .Check.Id }}" method="post">
|
||||||
<h2>Configuration</h2>
|
<h2>Configuration</h2>
|
||||||
<label for="group">Monitor Group</label>
|
<label for="group">Check Group</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="group"
|
name="group"
|
||||||
id="group"
|
id="group"
|
||||||
value="{{ .Monitor.Group }}"
|
value="{{ .Check.Group }}"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Group monitors together. This affects how they are presented on the
|
Group checks together. This affects how they are presented on the
|
||||||
homepage.
|
homepage.
|
||||||
</p>
|
</p>
|
||||||
<label for="workergroups">Worker Groups</label>
|
<label for="workergroups">Worker Groups</label>
|
||||||
|
@ -19,22 +19,22 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="workergroups"
|
name="workergroups"
|
||||||
id="workergroups"
|
id="workergroups"
|
||||||
value="{{ range .Monitor.WorkerGroups }}{{ . }}{{ end }}"
|
value="{{ range .Check.WorkerGroups }}{{ . }}{{ end }}"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Worker groups are used to distribute the monitor to specific workers.
|
Worker groups are used to distribute the check to specific workers.
|
||||||
</p>
|
</p>
|
||||||
<label for="schedule">Schedule</label>
|
<label for="schedule">Schedule</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="schedule"
|
name="schedule"
|
||||||
id="schedule"
|
id="schedule"
|
||||||
value="{{ .Monitor.Schedule }}"
|
value="{{ .Check.Schedule }}"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
Schedule is a cron expression that defines when the monitor should be
|
Schedule is a cron expression that defines when the check should be
|
||||||
executed.
|
executed.
|
||||||
<br />
|
<br />
|
||||||
You can also use <code>@every [interval]</code> where interval is a
|
You can also use <code>@every [interval]</code> where interval is a
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
</p>
|
</p>
|
||||||
<label for="script">Script</label>
|
<label for="script">Script</label>
|
||||||
<textarea required id="script" name="script" class="h-96">
|
<textarea required id="script" name="script" class="h-96">
|
||||||
{{ ScriptUnescapeString .Monitor.Script }}</textarea
|
{{ ScriptUnescapeString .Check.Script }}</textarea
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="editor"
|
id="editor"
|
||||||
|
@ -65,13 +65,13 @@
|
||||||
<section class="p-5 flex-1">
|
<section class="p-5 flex-1">
|
||||||
<h2 class="mb-2 flex flex-row gap-2">
|
<h2 class="mb-2 flex flex-row gap-2">
|
||||||
Status
|
Status
|
||||||
{{ if eq .Monitor.Status "ACTIVE" }}
|
{{ if eq .Check.Status "ACTIVE" }}
|
||||||
<span
|
<span
|
||||||
class="self-center h-fit w-fit px-2 text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
|
class="self-center h-fit w-fit px-2 text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
|
||||||
>
|
>
|
||||||
ACTIVE
|
ACTIVE
|
||||||
</span>
|
</span>
|
||||||
{{ else if eq .Monitor.Status "PAUSED" }}
|
{{ else if eq .Check.Status "PAUSED" }}
|
||||||
<span
|
<span
|
||||||
class="self-center h-fit w-fit px-2 text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800"
|
class="self-center h-fit w-fit px-2 text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800"
|
||||||
>
|
>
|
||||||
|
@ -80,19 +80,19 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-sm mb-2">
|
<p class="text-sm mb-2">
|
||||||
Pausing the monitor will stop it from executing. This can be useful in
|
Pausing the check will stop it from executing. This can be useful in
|
||||||
cases of expected downtime. Or when the monitor is not needed anymore.
|
cases of expected downtime. Or when the check is not needed anymore.
|
||||||
</p>
|
</p>
|
||||||
{{ if eq .Monitor.Status "ACTIVE" }}
|
{{ if eq .Check.Status "ACTIVE" }}
|
||||||
<a
|
<a
|
||||||
class="block text-center py-2.5 px-5 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100"
|
class="block text-center py-2.5 px-5 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100"
|
||||||
href="/settings/monitors/{{ .Monitor.Id }}/disable"
|
href="/settings/checks/{{ .Check.Id }}/disable"
|
||||||
>Pause</a
|
>Pause</a
|
||||||
>
|
>
|
||||||
{{ else if eq .Monitor.Status "PAUSED" }}
|
{{ else if eq .Check.Status "PAUSED" }}
|
||||||
<a
|
<a
|
||||||
class="block text-center py-2.5 px-5 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100"
|
class="block text-center py-2.5 px-5 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100"
|
||||||
href="/settings/monitors/{{ .Monitor.Id }}/enable"
|
href="/settings/checks/{{ .Check.Id }}/enable"
|
||||||
>Resume</a
|
>Resume</a
|
||||||
>
|
>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -100,10 +100,10 @@
|
||||||
|
|
||||||
<section class="p-2 flex-1 border-4 border-red-300">
|
<section class="p-2 flex-1 border-4 border-red-300">
|
||||||
<h2 class="mb-2">Danger Zone</h2>
|
<h2 class="mb-2">Danger Zone</h2>
|
||||||
<p class="text-sm mb-2">Permanently delete this monitor.</p>
|
<p class="text-sm mb-2">Permanently delete this check.</p>
|
||||||
<a
|
<a
|
||||||
class="block text-center focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
|
class="block text-center focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
|
||||||
href="/settings/monitors/{{ .Monitor.Id }}/delete"
|
href="/settings/checks/{{ .Check.Id }}/delete"
|
||||||
>Delete</a
|
>Delete</a
|
||||||
>
|
>
|
||||||
</section>
|
</section>
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
<table>
|
<table>
|
||||||
<caption>
|
<caption>
|
||||||
History
|
History
|
||||||
<p>Last 10 executions of monitor script.</p>
|
<p>Last 10 executions of check script.</p>
|
||||||
</caption>
|
</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
var doc = new DOMParser().parseFromString(input, "text/html");
|
var doc = new DOMParser().parseFromString(input, "text/html");
|
||||||
return doc.documentElement.textContent;
|
return doc.documentElement.textContent;
|
||||||
}
|
}
|
||||||
script = htmlDecode("{{ .Monitor.Script }}")
|
script = htmlDecode("{{ .Check.Script }}")
|
||||||
|
|
||||||
require.config({ paths: { vs: '/static/monaco/vs' } });
|
require.config({ paths: { vs: '/static/monaco/vs' } });
|
||||||
require(['vs/editor/editor.main'], function () {
|
require(['vs/editor/editor.main'], function () {
|
84
web/templates/pages/settings_incidents.tmpl
Normal file
84
web/templates/pages/settings_incidents.tmpl
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{{ define "settings" }}
|
||||||
|
{{ $description := "Incidents represent an event where some services were unavailable. Incidents can either be created automatically via the Triggers or manually. Incidents can be resolved manually or via triggers as well, once services are healthy again." }}
|
||||||
|
|
||||||
|
{{ $length := len .Incidents }}
|
||||||
|
{{ if eq $length 0 }}
|
||||||
|
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||||
|
<h1
|
||||||
|
class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl"
|
||||||
|
>
|
||||||
|
There are no incidents yet.
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
|
||||||
|
>
|
||||||
|
{{ $description }}
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
|
||||||
|
<a
|
||||||
|
href="/settings/incidents/create"
|
||||||
|
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
|
>
|
||||||
|
Add Manual Incident
|
||||||
|
<svg class="feather ml-1 h-5 w-5 overflow-visible">
|
||||||
|
<use href="/static/icons/feather-sprite.svg#plus" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/settings/triggers"
|
||||||
|
class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
|
>
|
||||||
|
Go to Triggers
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<section>
|
||||||
|
<table>
|
||||||
|
<caption>
|
||||||
|
List of Incidents
|
||||||
|
<div class="mt-1 gap-4 grid grid-cols-1 md:grid-cols-[1fr,20%]">
|
||||||
|
<p>
|
||||||
|
{{ $description }}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="/settings/incidents/create"
|
||||||
|
class="h-min inline-flex justify-center items-center py-2 px-4 text-sm font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300"
|
||||||
|
>
|
||||||
|
Create New
|
||||||
|
<svg class="feather h-5 w-5 overflow-visible">
|
||||||
|
<use href="/static/icons/feather-sprite.svg#plus" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{{ range .Incidents }}
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{{ .Name }}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800"
|
||||||
|
>
|
||||||
|
{{ .Type }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="/settings/incidents/{{ .Id }}" class="link">Details</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
|
@ -7,7 +7,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 md:px-40">
|
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 md:px-40">
|
||||||
Welcome to the settings page. Here you can manage your worker groups,
|
Welcome to the settings page. Here you can manage your worker groups,
|
||||||
monitors, and notifications.
|
checks, and notifications.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
||||||
>
|
>
|
||||||
<h3 class="text-sm leading-6 font-medium text-gray-400">Total Targets</h3>
|
<h3 class="text-sm leading-6 font-medium text-gray-400">Total Targets</h3>
|
||||||
<p class="text-3xl font-bold text-black">{{ .MonitorsCount }}</p>
|
<p class="text-3xl font-bold text-black">{{ .ChecksCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
||||||
>
|
>
|
||||||
<h3 class="text-sm leading-6 font-medium text-gray-400">
|
<h3 class="text-sm leading-6 font-medium text-gray-400">
|
||||||
Total Monitors
|
Total Checks
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-3xl font-bold text-black">{{ .MonitorsCount }}</p>
|
<p class="text-3xl font-bold text-black">{{ .ChecksCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:text-left"
|
||||||
|
@ -48,11 +48,11 @@
|
||||||
<table>
|
<table>
|
||||||
<caption>
|
<caption>
|
||||||
Execution History
|
Execution History
|
||||||
<p>Last 10 executions for all monitors and worker groups.</p>
|
<p>Last 10 executions for all checks and worker groups.</p>
|
||||||
</caption>
|
</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Monitor</th>
|
<th>Check</th>
|
||||||
<th>Worker Group</th>
|
<th>Worker Group</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Executed At</th>
|
<th>Executed At</th>
|
||||||
|
@ -65,8 +65,8 @@
|
||||||
<th>
|
<th>
|
||||||
<a
|
<a
|
||||||
class="underline hover:text-blue-600"
|
class="underline hover:text-blue-600"
|
||||||
href="/settings/monitors/{{ .MonitorId }}"
|
href="/settings/checks/{{ .CheckId }}"
|
||||||
>{{ .MonitorName }}</a
|
>{{ .CheckName }}</a
|
||||||
>
|
>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{ define "settings" }}
|
{{ define "settings" }}
|
||||||
{{ $description := "Triggers process monitor outcomes and determine if an incident should be created, updated or closed." }}
|
{{ $description := "Triggers process check outcomes and determine if an incident should be created, updated or closed." }}
|
||||||
|
|
||||||
{{ $length := len .Triggers }}
|
{{ $length := len .Triggers }}
|
||||||
{{ if eq $length 0 }}
|
{{ if eq $length 0 }}
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Workers</th>
|
<th>Workers</th>
|
||||||
<th>Monitors</th>
|
<th>Checks</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ len .Monitors }}
|
{{ len .Checks }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/settings/worker-groups/{{ .Id }}" class="link"
|
<a href="/settings/worker-groups/{{ .Id }}" class="link"
|
||||||
|
|
|
@ -51,13 +51,14 @@ func NewTemplates() *Templates {
|
||||||
"settings_triggers_create.tmpl": loadSettings("pages/settings_triggers_create.tmpl"),
|
"settings_triggers_create.tmpl": loadSettings("pages/settings_triggers_create.tmpl"),
|
||||||
"settings_triggers_describe.tmpl": loadSettings("pages/settings_triggers_describe.tmpl"),
|
"settings_triggers_describe.tmpl": loadSettings("pages/settings_triggers_describe.tmpl"),
|
||||||
"settings_targets.tmpl": loadSettings("pages/settings_targets.tmpl"),
|
"settings_targets.tmpl": loadSettings("pages/settings_targets.tmpl"),
|
||||||
|
"settings_incidents.tmpl": loadSettings("pages/settings_incidents.tmpl"),
|
||||||
"settings_notifications.tmpl": loadSettings("pages/settings_notifications.tmpl"),
|
"settings_notifications.tmpl": loadSettings("pages/settings_notifications.tmpl"),
|
||||||
"settings_worker_groups.tmpl": loadSettings("pages/settings_worker_groups.tmpl"),
|
"settings_worker_groups.tmpl": loadSettings("pages/settings_worker_groups.tmpl"),
|
||||||
"settings_worker_groups_create.tmpl": loadSettings("pages/settings_worker_groups_create.tmpl"),
|
"settings_worker_groups_create.tmpl": loadSettings("pages/settings_worker_groups_create.tmpl"),
|
||||||
"settings_worker_groups_describe.tmpl": loadSettings("pages/settings_worker_groups_describe.tmpl"),
|
"settings_worker_groups_describe.tmpl": loadSettings("pages/settings_worker_groups_describe.tmpl"),
|
||||||
"settings_monitors.tmpl": loadSettings("pages/settings_monitors.tmpl"),
|
"settings_checks.tmpl": loadSettings("pages/settings_checks.tmpl"),
|
||||||
"settings_monitors_create.tmpl": loadSettings("pages/settings_monitors_create.tmpl"),
|
"settings_checks_create.tmpl": loadSettings("pages/settings_checks_create.tmpl"),
|
||||||
"settings_monitors_describe.tmpl": loadSettings("pages/settings_monitors_describe.tmpl"),
|
"settings_checks_describe.tmpl": loadSettings("pages/settings_checks_describe.tmpl"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue