mirror of
https://github.com/mentos1386/zdravko.git
synced 2025-04-02 03:07:54 +00:00
refactor: schema changes etc.
This commit is contained in:
parent
5223aef1ca
commit
07c7c716c5
26 changed files with 495 additions and 375 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -6,9 +6,8 @@ package.json
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
zdravko.db
|
zdravko.db*
|
||||||
temporal.db
|
temporal.db*
|
||||||
temporal.db-journal
|
|
||||||
|
|
||||||
# Keys
|
# Keys
|
||||||
*.pem
|
*.pem
|
||||||
|
|
|
@ -2,6 +2,7 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
@ -13,7 +14,7 @@ import (
|
||||||
var sqliteMigrations embed.FS
|
var sqliteMigrations embed.FS
|
||||||
|
|
||||||
func ConnectToDatabase(logger *slog.Logger, path string) (*sqlx.DB, error) {
|
func ConnectToDatabase(logger *slog.Logger, path string) (*sqlx.DB, error) {
|
||||||
db, err := sqlx.Connect("sqlite3", path)
|
db, err := sqlx.Connect("sqlite3", fmt.Sprintf("%s?_journal=WAL&_timeout=5000&_fk=true", path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,64 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OAuth2State struct {
|
type OAuth2State struct {
|
||||||
State string `db:"state"`
|
State string `db:"state"`
|
||||||
ExpiresAt time.Time `db:"expires_at"`
|
ExpiresAt Time `db:"expires_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MonitorStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MonitorSuccess string = "SUCCESS"
|
MonitorSuccess MonitorStatus = "SUCCESS"
|
||||||
MonitorFailure string = "FAILURE"
|
MonitorFailure MonitorStatus = "FAILURE"
|
||||||
MonitorError string = "ERROR"
|
MonitorError MonitorStatus = "ERROR"
|
||||||
MonitorUnknown string = "UNKNOWN"
|
MonitorUnknown MonitorStatus = "UNKNOWN"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Monitor struct {
|
type Time struct {
|
||||||
CreatedAt time.Time `db:"created_at"`
|
Time time.Time
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
}
|
||||||
DeletedAt *time.Time `db:"deleted_at"`
|
|
||||||
|
|
||||||
Slug string `db:"slug"`
|
// rfc3339Milli is like time.RFC3339Nano, but with millisecond precision, and fractional seconds do not have trailing
|
||||||
|
// zeros removed.
|
||||||
|
const rfc3339Milli = "2006-01-02T15:04:05.000Z07:00"
|
||||||
|
|
||||||
|
// Value satisfies driver.Valuer interface.
|
||||||
|
func (t *Time) Value() (driver.Value, error) {
|
||||||
|
return t.Time.UTC().Format(rfc3339Milli), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan satisfies sql.Scanner interface.
|
||||||
|
func (t *Time) Scan(src any) error {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := src.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("error scanning time, got %+v", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedT, err := time.Parse(rfc3339Milli, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Time = parsedT.UTC()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Monitor struct {
|
||||||
|
CreatedAt Time `db:"created_at"`
|
||||||
|
UpdatedAt Time `db:"updated_at"`
|
||||||
|
|
||||||
|
Id string `db:"id"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
|
|
||||||
Schedule string `db:"schedule"`
|
Schedule string `db:"schedule"`
|
||||||
|
@ -36,19 +73,21 @@ type MonitorWithWorkerGroups struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorHistory struct {
|
type MonitorHistory struct {
|
||||||
CreatedAt time.Time `db:"created_at"`
|
CreatedAt Time `db:"created_at"`
|
||||||
|
|
||||||
MonitorSlug string `db:"monitor_slug"`
|
MonitorId string `db:"monitor_id"`
|
||||||
Status string `db:"status"`
|
Status MonitorStatus `db:"status"`
|
||||||
Note string `db:"note"`
|
Note string `db:"note"`
|
||||||
|
|
||||||
|
WorkerGroupId string `db:"worker_group_id"`
|
||||||
|
WorkerGroupName string `db:"worker_group_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerGroup struct {
|
type WorkerGroup struct {
|
||||||
CreatedAt time.Time `db:"created_at"`
|
CreatedAt Time `db:"created_at"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
UpdatedAt Time `db:"updated_at"`
|
||||||
DeletedAt *time.Time `db:"deleted_at"`
|
|
||||||
|
|
||||||
Slug string `db:"slug"`
|
Id string `db:"id"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,56 +1,66 @@
|
||||||
-- +migrate Up
|
-- +migrate Up
|
||||||
CREATE TABLE oauth2_states (
|
CREATE TABLE oauth2_states (
|
||||||
state TEXT,
|
state TEXT NOT NULL,
|
||||||
expires_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
expires_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
|
|
||||||
PRIMARY KEY (state)
|
PRIMARY KEY (state)
|
||||||
);
|
) STRICT;
|
||||||
|
|
||||||
CREATE TABLE monitors (
|
CREATE TABLE monitors (
|
||||||
slug TEXT,
|
id TEXT NOT NULL,
|
||||||
name TEXT,
|
name TEXT NOT NULL,
|
||||||
schedule TEXT,
|
schedule TEXT NOT NULL,
|
||||||
script TEXT,
|
script TEXT NOT NULL,
|
||||||
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
deleted_at DATETIME,
|
|
||||||
|
|
||||||
PRIMARY KEY (slug),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT unique_monitors_name UNIQUE (name)
|
CONSTRAINT unique_monitors_name UNIQUE (name)
|
||||||
);
|
) STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
--CREATE TRIGGER monitors_updated_timestamp AFTER UPDATE ON monitors BEGIN
|
||||||
|
-- update monitors set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
||||||
|
--END;
|
||||||
|
|
||||||
CREATE TABLE worker_groups (
|
CREATE TABLE worker_groups (
|
||||||
slug TEXT,
|
id TEXT NOT NULL,
|
||||||
name TEXT,
|
name TEXT NOT NULL,
|
||||||
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
deleted_at DATETIME,
|
|
||||||
|
|
||||||
PRIMARY KEY (slug),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT unique_worker_groups_name UNIQUE (name)
|
CONSTRAINT unique_worker_groups_name UNIQUE (name)
|
||||||
);
|
) STRICT;
|
||||||
|
|
||||||
|
--CREATE TRIGGER worker_groups_updated_timestamp AFTER UPDATE ON worker_groups BEGIN
|
||||||
|
-- update worker_groups set updated_at = strftime('%Y-%m-%dT%H:%M:%fZ') where id = new.id;
|
||||||
|
--END;
|
||||||
|
|
||||||
CREATE TABLE monitor_worker_groups (
|
CREATE TABLE monitor_worker_groups (
|
||||||
worker_group_slug TEXT,
|
worker_group_id TEXT NOT NULL,
|
||||||
monitor_slug TEXT,
|
monitor_id TEXT NOT NULL,
|
||||||
|
|
||||||
PRIMARY KEY (worker_group_slug,monitor_slug),
|
PRIMARY KEY (worker_group_id,monitor_id),
|
||||||
CONSTRAINT fk_monitor_worker_groups_worker_group FOREIGN KEY (worker_group_slug) REFERENCES worker_groups(slug),
|
CONSTRAINT fk_monitor_worker_groups_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id),
|
||||||
CONSTRAINT fk_monitor_worker_groups_monitor FOREIGN KEY (monitor_slug) REFERENCES monitors(slug)
|
CONSTRAINT fk_monitor_worker_groups_monitor FOREIGN KEY (monitor_id) REFERENCES monitors(id)
|
||||||
);
|
) STRICT;
|
||||||
|
|
||||||
CREATE TABLE monitor_histories (
|
CREATE TABLE monitor_histories (
|
||||||
monitor_slug TEXT,
|
monitor_id TEXT NOT NULL,
|
||||||
status TEXT,
|
worker_group_id TEXT NOT NULL,
|
||||||
note TEXT,
|
|
||||||
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
status TEXT NOT NULL,
|
||||||
|
note TEXT NOT NULL,
|
||||||
|
|
||||||
PRIMARY KEY (monitor_slug, created_at),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||||
CONSTRAINT fk_monitors_history FOREIGN KEY (monitor_slug) REFERENCES monitors(slug)
|
|
||||||
);
|
PRIMARY KEY (monitor_id, worker_group_id, created_at),
|
||||||
|
CONSTRAINT fk_monitor_histories_monitor FOREIGN KEY (monitor_id) REFERENCES monitors(id),
|
||||||
|
CONSTRAINT fk_monitor_histories_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id)
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
-- +migrate Down
|
-- +migrate Down
|
||||||
DROP TABLE oauth2_states;
|
DROP TABLE oauth2_states;
|
||||||
|
|
|
@ -17,8 +17,8 @@ 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-6.db'
|
TEMPORAL_DATABASE_PATH = '/data/temporal-7.db'
|
||||||
DATABASE_PATH = '/data/zdravko-6.db'
|
DATABASE_PATH = '/data/zdravko-7.db'
|
||||||
|
|
||||||
[processes]
|
[processes]
|
||||||
server = '--temporal --server'
|
server = '--temporal --server'
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
"code.tjo.space/mentos1386/zdravko/pkg/api"
|
"code.tjo.space/mentos1386/zdravko/pkg/api"
|
||||||
"code.tjo.space/mentos1386/zdravko/pkg/k6"
|
"code.tjo.space/mentos1386/zdravko/pkg/k6"
|
||||||
)
|
)
|
||||||
|
@ -33,22 +34,22 @@ func (a *Activities) Monitor(ctx context.Context, param HealtcheckParam) (*Monit
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealtcheckAddToHistoryParam struct {
|
type HealtcheckAddToHistoryParam struct {
|
||||||
Slug string
|
MonitorId string
|
||||||
Status string
|
Status models.MonitorStatus
|
||||||
Note string
|
Note string
|
||||||
WorkerGroup string
|
WorkerGroupId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorAddToHistoryResult struct {
|
type MonitorAddToHistoryResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Activities) MonitorAddToHistory(ctx context.Context, param HealtcheckAddToHistoryParam) (*MonitorAddToHistoryResult, error) {
|
func (a *Activities) MonitorAddToHistory(ctx context.Context, param HealtcheckAddToHistoryParam) (*MonitorAddToHistoryResult, error) {
|
||||||
url := fmt.Sprintf("%s/api/v1/monitors/%s/history", a.config.ApiUrl, param.Slug)
|
url := fmt.Sprintf("%s/api/v1/monitors/%s/history", a.config.ApiUrl, param.MonitorId)
|
||||||
|
|
||||||
body := api.ApiV1MonitorsHistoryPOSTBody{
|
body := api.ApiV1MonitorsHistoryPOSTBody{
|
||||||
Status: param.Status,
|
Status: param.Status,
|
||||||
Note: param.Note,
|
Note: param.Note,
|
||||||
WorkerGroup: param.WorkerGroup,
|
WorkerGroupId: param.WorkerGroupId,
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonBody, err := json.Marshal(body)
|
jsonBody, err := json.Marshal(body)
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (h *BaseHandler) ApiV1WorkersConnectGET(c echo.Context) error {
|
||||||
|
|
||||||
response := ApiV1WorkersConnectGETResponse{
|
response := ApiV1WorkersConnectGETResponse{
|
||||||
Endpoint: h.config.Temporal.ServerHost,
|
Endpoint: h.config.Temporal.ServerHost,
|
||||||
Group: workerGroup.Slug,
|
Group: workerGroup.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, response)
|
return c.JSON(http.StatusOK, response)
|
||||||
|
@ -42,8 +42,7 @@ func (h *BaseHandler) ApiV1WorkersConnectGET(c echo.Context) error {
|
||||||
// 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) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
id := c.Param("id")
|
||||||
slug := c.Param("slug")
|
|
||||||
|
|
||||||
var body api.ApiV1MonitorsHistoryPOSTBody
|
var body api.ApiV1MonitorsHistoryPOSTBody
|
||||||
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
||||||
|
@ -51,7 +50,7 @@ func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = services.GetMonitor(ctx, h.db, slug)
|
_, err = services.GetMonitor(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, "Monitor not found")
|
||||||
|
@ -60,7 +59,8 @@ func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.AddHistoryForMonitor(ctx, h.db, &models.MonitorHistory{
|
err = services.AddHistoryForMonitor(ctx, h.db, &models.MonitorHistory{
|
||||||
MonitorSlug: slug,
|
MonitorId: id,
|
||||||
|
WorkerGroupId: body.WorkerGroupId,
|
||||||
Status: body.Status,
|
Status: body.Status,
|
||||||
Note: body.Note,
|
Note: body.Note,
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,45 +16,39 @@ type IndexData struct {
|
||||||
HealthChecks []*HealthCheck
|
HealthChecks []*HealthCheck
|
||||||
MonitorsLength int
|
MonitorsLength int
|
||||||
TimeRange string
|
TimeRange string
|
||||||
Status string
|
Status models.MonitorStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthCheck struct {
|
type HealthCheck struct {
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status models.MonitorStatus
|
||||||
HistoryDaily *History
|
History *History
|
||||||
HistoryHourly *History
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type History struct {
|
type History struct {
|
||||||
History []string
|
List []models.MonitorStatus
|
||||||
Uptime int
|
Uptime int
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDay(date time.Time) string {
|
|
||||||
return date.Format("2006-01-02")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHour(date time.Time) string {
|
func getHour(date time.Time) string {
|
||||||
return date.Format("2006-01-02T15:04")
|
return date.UTC().Format("2006-01-02T15:04")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDailyHistory(history []*models.MonitorHistory) *History {
|
func getHistory(history []*models.MonitorHistory, period time.Duration, buckets int) *History {
|
||||||
numDays := 90
|
historyMap := map[string]models.MonitorStatus{}
|
||||||
historyDailyMap := map[string]string{}
|
|
||||||
numOfSuccess := 0
|
numOfSuccess := 0
|
||||||
numTotal := 0
|
numTotal := 0
|
||||||
|
|
||||||
for i := 0; i < numDays; i++ {
|
for i := 0; i < buckets; i++ {
|
||||||
day := getDay(time.Now().AddDate(0, 0, -i).Truncate(time.Hour * 24))
|
datetime := getHour(time.Now().Add(period * time.Duration(-i)).Truncate(period))
|
||||||
historyDailyMap[day] = models.MonitorUnknown
|
historyMap[datetime] = models.MonitorUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, _history := range history {
|
for _, _history := range history {
|
||||||
day := getDay(_history.CreatedAt.Truncate(time.Hour * 24))
|
hour := getHour(_history.CreatedAt.Time.Truncate(time.Hour))
|
||||||
|
|
||||||
// skip if day is not in the last 90 days
|
// Skip if not part of the "buckets"
|
||||||
if _, ok := historyDailyMap[day]; !ok {
|
if _, ok := historyMap[hour]; !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,18 +57,18 @@ func getDailyHistory(history []*models.MonitorHistory) *History {
|
||||||
numOfSuccess++
|
numOfSuccess++
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip if day is already set to failure
|
// skip if it is already set to failure
|
||||||
if historyDailyMap[day] == models.MonitorFailure {
|
if historyMap[hour] == models.MonitorFailure {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
historyDailyMap[day] = _history.Status
|
historyMap[hour] = _history.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
historyDaily := make([]string, numDays)
|
historyHourly := make([]models.MonitorStatus, buckets)
|
||||||
for i := 0; i < numDays; i++ {
|
for i := 0; i < buckets; i++ {
|
||||||
day := getDay(time.Now().AddDate(0, 0, -numDays+i+1).Truncate(time.Hour * 24))
|
datetime := getHour(time.Now().Add(period * time.Duration(-buckets+i+1)).Truncate(period))
|
||||||
historyDaily[i] = historyDailyMap[day]
|
historyHourly[i] = historyMap[datetime]
|
||||||
}
|
}
|
||||||
|
|
||||||
uptime := 0
|
uptime := 0
|
||||||
|
@ -83,56 +77,7 @@ func getDailyHistory(history []*models.MonitorHistory) *History {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &History{
|
return &History{
|
||||||
History: historyDaily,
|
List: historyHourly,
|
||||||
Uptime: uptime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHourlyHistory(history []*models.MonitorHistory) *History {
|
|
||||||
numHours := 48
|
|
||||||
historyHourlyMap := map[string]string{}
|
|
||||||
numOfSuccess := 0
|
|
||||||
numTotal := 0
|
|
||||||
|
|
||||||
for i := 0; i < numHours; i++ {
|
|
||||||
hour := getHour(time.Now().Add(time.Hour * time.Duration(-i)).Truncate(time.Hour))
|
|
||||||
historyHourlyMap[hour] = models.MonitorUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, _history := range history {
|
|
||||||
hour := getHour(_history.CreatedAt.Truncate(time.Hour))
|
|
||||||
|
|
||||||
// skip if day is not in the last 90 days
|
|
||||||
if _, ok := historyHourlyMap[hour]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
numTotal++
|
|
||||||
if _history.Status == models.MonitorSuccess {
|
|
||||||
numOfSuccess++
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip if day is already set to failure
|
|
||||||
if historyHourlyMap[hour] == models.MonitorFailure {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
historyHourlyMap[hour] = _history.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
historyHourly := make([]string, numHours)
|
|
||||||
for i := 0; i < numHours; i++ {
|
|
||||||
hour := getHour(time.Now().Add(time.Hour * time.Duration(-numHours+i+1)).Truncate(time.Hour))
|
|
||||||
historyHourly[i] = historyHourlyMap[hour]
|
|
||||||
}
|
|
||||||
|
|
||||||
uptime := 0
|
|
||||||
if numTotal > 0 {
|
|
||||||
uptime = 100 * numOfSuccess / numTotal
|
|
||||||
}
|
|
||||||
|
|
||||||
return &History{
|
|
||||||
History: historyHourly,
|
|
||||||
Uptime: uptime,
|
Uptime: uptime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,23 +90,30 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
timeRange := c.QueryParam("time-range")
|
timeRange := c.QueryParam("time-range")
|
||||||
if timeRange != "48hours" && timeRange != "90days" {
|
if timeRange != "48hours" && timeRange != "90days" && timeRange != "90minutes" {
|
||||||
timeRange = "90days"
|
timeRange = "90days"
|
||||||
}
|
}
|
||||||
|
|
||||||
overallStatus := "SUCCESS"
|
overallStatus := models.MonitorSuccess
|
||||||
|
|
||||||
monitorsWithHistory := make([]*HealthCheck, len(monitors))
|
monitorsWithHistory := make([]*HealthCheck, len(monitors))
|
||||||
for i, monitor := range monitors {
|
for i, monitor := range monitors {
|
||||||
history, err := services.GetMonitorHistoryForMonitor(ctx, h.db, monitor.Slug)
|
history, err := services.GetMonitorHistoryForMonitor(ctx, h.db, monitor.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
historyDaily := getDailyHistory(history)
|
var historyResult *History
|
||||||
historyHourly := getHourlyHistory(history)
|
switch timeRange {
|
||||||
|
case "48hours":
|
||||||
|
historyResult = getHistory(history, time.Hour, 48)
|
||||||
|
case "90days":
|
||||||
|
historyResult = getHistory(history, time.Hour*24, 90)
|
||||||
|
case "90minutes":
|
||||||
|
historyResult = getHistory(history, time.Minute, 90)
|
||||||
|
}
|
||||||
|
|
||||||
status := historyDaily.History[89]
|
status := historyResult.List[len(historyResult.List)-1]
|
||||||
if status != models.MonitorSuccess {
|
if status != models.MonitorSuccess {
|
||||||
overallStatus = status
|
overallStatus = status
|
||||||
}
|
}
|
||||||
|
@ -169,8 +121,7 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
||||||
monitorsWithHistory[i] = &HealthCheck{
|
monitorsWithHistory[i] = &HealthCheck{
|
||||||
Name: monitor.Name,
|
Name: monitor.Name,
|
||||||
Status: status,
|
Status: status,
|
||||||
HistoryDaily: historyDaily,
|
History: historyResult,
|
||||||
HistoryHourly: historyHourly,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,10 @@ func (h *BaseHandler) OAuth2LoginGET(c echo.Context) error {
|
||||||
conf := newOAuth2(h.config)
|
conf := newOAuth2(h.config)
|
||||||
|
|
||||||
state := newRandomState()
|
state := newRandomState()
|
||||||
err := services.CreateOAuth2State(ctx, h.db, &models.OAuth2State{State: state, ExpiresAt: time.Now().Add(5 * time.Minute)})
|
err := services.CreateOAuth2State(ctx, h.db, &models.OAuth2State{
|
||||||
|
State: state,
|
||||||
|
ExpiresAt: models.Time{Time: time.Now().Add(5 * time.Minute)},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -108,7 +111,7 @@ func (h *BaseHandler) OAuth2CallbackGET(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if deleted == false {
|
if !deleted {
|
||||||
return errors.New("invalid state")
|
return errors.New("invalid state")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/services"
|
||||||
"code.tjo.space/mentos1386/zdravko/web/templates/components"
|
"code.tjo.space/mentos1386/zdravko/web/templates/components"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
@ -48,12 +49,42 @@ var SettingsNavbar = []*components.Page{
|
||||||
GetPageByTitle(SettingsPages, "Logout"),
|
GetPageByTitle(SettingsPages, "Logout"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SettingsOverview struct {
|
||||||
|
*Settings
|
||||||
|
WorkerGroupsCount int
|
||||||
|
MonitorsCount int
|
||||||
|
NotificationsCount int
|
||||||
|
History []*services.MonitorHistoryWithMonitor
|
||||||
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
ctx := c.Request().Context()
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "settings_overview.tmpl", NewSettings(
|
workerGroups, err := services.CountWorkerGroups(ctx, h.db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
monitors, err := services.CountMonitors(ctx, h.db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
history, err := services.GetLastNMonitorHistory(ctx, h.db, 10)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Render(http.StatusOK, "settings_overview.tmpl", SettingsOverview{
|
||||||
|
Settings: NewSettings(
|
||||||
cc.Principal.User,
|
cc.Principal.User,
|
||||||
GetPageByTitle(SettingsPages, "Overview"),
|
GetPageByTitle(SettingsPages, "Overview"),
|
||||||
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")},
|
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")},
|
||||||
))
|
),
|
||||||
|
WorkerGroupsCount: workerGroups,
|
||||||
|
MonitorsCount: monitors,
|
||||||
|
NotificationsCount: 42,
|
||||||
|
History: history,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (h *BaseHandler) SettingsMonitorsGET(c echo.Context) error {
|
||||||
|
|
||||||
monitorsWithStatus := make([]*MonitorWithWorkerGroupsAndStatus, len(monitors))
|
monitorsWithStatus := make([]*MonitorWithWorkerGroupsAndStatus, len(monitors))
|
||||||
for i, monitor := range monitors {
|
for i, monitor := range monitors {
|
||||||
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Slug)
|
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -79,14 +79,14 @@ func (h *BaseHandler) SettingsMonitorsGET(c echo.Context) error {
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
|
||||||
slug := c.Param("slug")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitorWithWorkerGroups(context.Background(), h.db, slug)
|
monitor, err := services.GetMonitorWithWorkerGroups(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Slug)
|
status, err := services.GetMonitorStatus(context.Background(), h.temporal, monitor.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribeDELETE(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsDescribeDELETE(c echo.Context) error {
|
||||||
slug := c.Param("slug")
|
slug := c.Param("id")
|
||||||
|
|
||||||
err := services.DeleteMonitor(context.Background(), h.db, slug)
|
err := services.DeleteMonitor(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -140,14 +140,14 @@ func (h *BaseHandler) SettingsMonitorsDescribeDELETE(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDisableGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsDisableGET(c echo.Context) error {
|
||||||
slug := c.Param("slug")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Slug, services.MonitorStatusPaused)
|
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Id, services.MonitorStatusPaused)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -156,14 +156,14 @@ func (h *BaseHandler) SettingsMonitorsDisableGET(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsEnableGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsEnableGET(c echo.Context) error {
|
||||||
slug := c.Param("slug")
|
slug := c.Param("id")
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
monitor, err := services.GetMonitor(context.Background(), h.db, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Slug, services.MonitorStatusActive)
|
err = services.SetMonitorStatus(context.Background(), h.temporal, monitor.Id, services.MonitorStatusActive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ func (h *BaseHandler) SettingsMonitorsEnableGET(c echo.Context) error {
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
monitorSlug := c.Param("slug")
|
monitorId := c.Param("id")
|
||||||
|
|
||||||
update := UpdateMonitor{
|
update := UpdateMonitor{
|
||||||
WorkerGroups: strings.TrimSpace(c.FormValue("workergroups")),
|
WorkerGroups: strings.TrimSpace(c.FormValue("workergroups")),
|
||||||
|
@ -185,7 +185,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor, err := services.GetMonitor(ctx, h.db, monitorSlug)
|
monitor, err := services.GetMonitor(ctx, h.db, monitorId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
workerGroup, err := services.GetWorkerGroup(ctx, h.db, slug.Make(group))
|
workerGroup, err := services.GetWorkerGroup(ctx, h.db, slug.Make(group))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
workerGroup = &models.WorkerGroup{Name: group, Slug: slug.Make(group)}
|
workerGroup = &models.WorkerGroup{Name: group, Id: slug.Make(group)}
|
||||||
err = services.CreateWorkerGroup(ctx, h.db, workerGroup)
|
err = services.CreateWorkerGroup(ctx, h.db, workerGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -231,7 +231,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/monitors/%s", monitorSlug))
|
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/monitors/%s", monitorId))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
||||||
|
@ -249,7 +249,7 @@ func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
monitorSlug := slug.Make(c.FormValue("name"))
|
monitorId := slug.Make(c.FormValue("name"))
|
||||||
|
|
||||||
create := CreateMonitor{
|
create := CreateMonitor{
|
||||||
Name: c.FormValue("name"),
|
Name: c.FormValue("name"),
|
||||||
|
@ -270,7 +270,7 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
||||||
workerGroup, err := services.GetWorkerGroup(ctx, h.db, slug.Make(group))
|
workerGroup, err := services.GetWorkerGroup(ctx, h.db, slug.Make(group))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
workerGroup = &models.WorkerGroup{Name: group, Slug: slug.Make(group)}
|
workerGroup = &models.WorkerGroup{Name: group, Id: slug.Make(group)}
|
||||||
err = services.CreateWorkerGroup(ctx, h.db, workerGroup)
|
err = services.CreateWorkerGroup(ctx, h.db, workerGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -284,7 +284,7 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
||||||
|
|
||||||
monitor := &models.Monitor{
|
monitor := &models.Monitor{
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
Slug: monitorSlug,
|
Id: monitorId,
|
||||||
Schedule: create.Schedule,
|
Schedule: create.Schedule,
|
||||||
Script: create.Script,
|
Script: create.Script,
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
||||||
|
|
||||||
workerGroupsWithActiveWorkers := make([]*WorkerGroupWithActiveWorkers, len(workerGroups))
|
workerGroupsWithActiveWorkers := make([]*WorkerGroupWithActiveWorkers, len(workerGroups))
|
||||||
for i, workerGroup := range workerGroups {
|
for i, workerGroup := range workerGroups {
|
||||||
activeWorkers, err := services.GetActiveWorkers(context.Background(), workerGroup.Slug, h.temporal)
|
activeWorkers, err := services.GetActiveWorkers(context.Background(), workerGroup.Id, h.temporal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -69,10 +69,9 @@ func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
||||||
cc := c.(AuthenticatedContext)
|
cc := c.(AuthenticatedContext)
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
slug := c.Param("slug")
|
worker, err := services.GetWorkerGroup(context.Background(), h.db, id)
|
||||||
|
|
||||||
worker, err := services.GetWorkerGroup(context.Background(), h.db, slug)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +82,7 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
activeWorkers, err := services.GetActiveWorkers(context.Background(), worker.Slug, h.temporal)
|
activeWorkers, err := services.GetActiveWorkers(context.Background(), worker.Id, h.temporal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -95,7 +94,7 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
||||||
[]*components.Page{
|
[]*components.Page{
|
||||||
GetPageByTitle(SettingsPages, "Worker Groups"),
|
GetPageByTitle(SettingsPages, "Worker Groups"),
|
||||||
{
|
{
|
||||||
Path: fmt.Sprintf("/settings/worker-groups/%s", slug),
|
Path: fmt.Sprintf("/settings/worker-groups/%s", id),
|
||||||
Title: "Describe",
|
Title: "Describe",
|
||||||
Breadcrumb: worker.Name,
|
Breadcrumb: worker.Name,
|
||||||
},
|
},
|
||||||
|
@ -109,9 +108,9 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsWorkerGroupsDescribeDELETE(c echo.Context) error {
|
func (h *BaseHandler) SettingsWorkerGroupsDescribeDELETE(c echo.Context) error {
|
||||||
slug := c.Param("slug")
|
id := c.Param("id")
|
||||||
|
|
||||||
err := services.DeleteWorkerGroup(context.Background(), h.db, slug)
|
err := services.DeleteWorkerGroup(context.Background(), h.db, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -134,11 +133,11 @@ func (h *BaseHandler) SettingsWorkerGroupsCreateGET(c echo.Context) error {
|
||||||
|
|
||||||
func (h *BaseHandler) SettingsWorkerGroupsCreatePOST(c echo.Context) error {
|
func (h *BaseHandler) SettingsWorkerGroupsCreatePOST(c echo.Context) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
slug := slug.Make(c.FormValue("name"))
|
id := slug.Make(c.FormValue("name"))
|
||||||
|
|
||||||
workerGroup := &models.WorkerGroup{
|
workerGroup := &models.WorkerGroup{
|
||||||
Name: c.FormValue("name"),
|
Name: c.FormValue("name"),
|
||||||
Slug: slug,
|
Id: id,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := validator.New(validator.WithRequiredStructEnabled()).Struct(workerGroup)
|
err := validator.New(validator.WithRequiredStructEnabled()).Struct(workerGroup)
|
||||||
|
@ -155,5 +154,5 @@ func (h *BaseHandler) SettingsWorkerGroupsCreatePOST(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/worker-groups/%s", slug))
|
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/worker-groups/%s", id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func NewTokenForWorker(privateKey string, publicKey string, workerGroup *models.
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||||
Issuer: "zdravko",
|
Issuer: "zdravko",
|
||||||
Subject: "worker-group:" + workerGroup.Slug,
|
Subject: "worker-group:" + workerGroup.Id,
|
||||||
},
|
},
|
||||||
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
||||||
[]string{"default:read", "default:write", "default:worker"},
|
[]string{"default:read", "default:write", "default:worker"},
|
||||||
|
|
|
@ -21,12 +21,18 @@ const (
|
||||||
MonitorStatusActive MonitorStatus = "ACTIVE"
|
MonitorStatusActive MonitorStatus = "ACTIVE"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getScheduleId(slug string) string {
|
func getScheduleId(id string) string {
|
||||||
return "monitor-" + slug
|
return "monitor-" + id
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMonitorStatus(ctx context.Context, temporal client.Client, slug string) (MonitorStatus, error) {
|
func CountMonitors(ctx context.Context, db *sqlx.DB) (int, error) {
|
||||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
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)
|
description, err := schedule.Describe(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,8 +46,8 @@ func GetMonitorStatus(ctx context.Context, temporal client.Client, slug string)
|
||||||
return MonitorStatusActive, nil
|
return MonitorStatusActive, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetMonitorStatus(ctx context.Context, temporal client.Client, slug string, status MonitorStatus) error {
|
func SetMonitorStatus(ctx context.Context, temporal client.Client, id string, status MonitorStatus) error {
|
||||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||||
|
|
||||||
if status == MonitorStatusActive {
|
if status == MonitorStatusActive {
|
||||||
return schedule.Unpause(ctx, client.ScheduleUnpauseOptions{Note: "Unpaused by user"})
|
return schedule.Unpause(ctx, client.ScheduleUnpauseOptions{Note: "Unpaused by user"})
|
||||||
|
@ -56,7 +62,7 @@ func SetMonitorStatus(ctx context.Context, temporal client.Client, slug string,
|
||||||
|
|
||||||
func CreateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
func CreateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
||||||
_, err := db.NamedExecContext(ctx,
|
_, err := db.NamedExecContext(ctx,
|
||||||
"INSERT INTO monitors (slug, name, script, schedule) VALUES (:slug, :name, :script, :schedule)",
|
"INSERT INTO monitors (id, name, script, schedule) VALUES (:id, :name, :script, :schedule)",
|
||||||
monitor,
|
monitor,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
|
@ -64,16 +70,16 @@ func CreateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) er
|
||||||
|
|
||||||
func UpdateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
func UpdateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error {
|
||||||
_, err := db.NamedExecContext(ctx,
|
_, err := db.NamedExecContext(ctx,
|
||||||
"UPDATE monitors SET name=:name, script=:script, schedule=:schedule WHERE slug=:slug",
|
"UPDATE monitors SET name=:name, script=:script, schedule=:schedule WHERE id=:id",
|
||||||
monitor,
|
monitor,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteMonitor(ctx context.Context, db *sqlx.DB, slug string) error {
|
func DeleteMonitor(ctx context.Context, db *sqlx.DB, id string) error {
|
||||||
_, err := db.ExecContext(ctx,
|
_, err := db.ExecContext(ctx,
|
||||||
"UPDATE monitors SET deleted_at = datetime('now') WHERE slug=$1",
|
"DELETE FROM monitors WHERE id=$1",
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -84,8 +90,8 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(ctx,
|
_, err = tx.ExecContext(ctx,
|
||||||
"DELETE FROM monitor_worker_groups WHERE monitor_slug=$1",
|
"DELETE FROM monitor_worker_groups WHERE monitor_id=$1",
|
||||||
monitor.Slug,
|
monitor.Id,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -93,9 +99,9 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
||||||
}
|
}
|
||||||
for _, group := range workerGroups {
|
for _, group := range workerGroups {
|
||||||
_, err = tx.ExecContext(ctx,
|
_, err = tx.ExecContext(ctx,
|
||||||
"INSERT INTO monitor_worker_groups (monitor_slug, worker_group_slug) VALUES ($1, $2)",
|
"INSERT INTO monitor_worker_groups (monitor_id, worker_group_id) VALUES ($1, $2)",
|
||||||
monitor.Slug,
|
monitor.Id,
|
||||||
group.Slug,
|
group.Id,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -105,33 +111,32 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMonitor(ctx context.Context, db *sqlx.DB, slug string) (*models.Monitor, error) {
|
func GetMonitor(ctx context.Context, db *sqlx.DB, id string) (*models.Monitor, error) {
|
||||||
monitor := &models.Monitor{}
|
monitor := &models.Monitor{}
|
||||||
err := db.GetContext(ctx, monitor,
|
err := db.GetContext(ctx, monitor,
|
||||||
"SELECT * FROM monitors WHERE slug=$1 AND deleted_at IS NULL",
|
"SELECT * FROM monitors WHERE id=$1",
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
return monitor, err
|
return monitor, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMonitorWithWorkerGroups(ctx context.Context, db *sqlx.DB, slug string) (*models.MonitorWithWorkerGroups, error) {
|
func GetMonitorWithWorkerGroups(ctx context.Context, db *sqlx.DB, id string) (*models.MonitorWithWorkerGroups, error) {
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
monitors.slug,
|
monitors.id,
|
||||||
monitors.name,
|
monitors.name,
|
||||||
monitors.script,
|
monitors.script,
|
||||||
monitors.schedule,
|
monitors.schedule,
|
||||||
monitors.created_at,
|
monitors.created_at,
|
||||||
monitors.updated_at,
|
monitors.updated_at,
|
||||||
monitors.deleted_at,
|
|
||||||
worker_groups.name as worker_group_name
|
worker_groups.name as worker_group_name
|
||||||
FROM monitors
|
FROM monitors
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.slug = monitor_worker_groups.monitor_slug
|
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_slug = worker_groups.slug
|
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_id = worker_groups.id
|
||||||
WHERE monitors.slug=$1 AND monitors.deleted_at IS NULL
|
WHERE monitors.id=$1
|
||||||
`,
|
`,
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -143,13 +148,12 @@ WHERE monitors.slug=$1 AND monitors.deleted_at IS NULL
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var workerGroupName *string
|
var workerGroupName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&monitor.Slug,
|
&monitor.Id,
|
||||||
&monitor.Name,
|
&monitor.Name,
|
||||||
&monitor.Script,
|
&monitor.Script,
|
||||||
&monitor.Schedule,
|
&monitor.Schedule,
|
||||||
&monitor.CreatedAt,
|
&monitor.CreatedAt,
|
||||||
&monitor.UpdatedAt,
|
&monitor.UpdatedAt,
|
||||||
&monitor.DeletedAt,
|
|
||||||
&workerGroupName,
|
&workerGroupName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,7 +170,7 @@ WHERE monitors.slug=$1 AND monitors.deleted_at IS NULL
|
||||||
func GetMonitors(ctx context.Context, db *sqlx.DB) ([]*models.Monitor, error) {
|
func GetMonitors(ctx context.Context, db *sqlx.DB) ([]*models.Monitor, error) {
|
||||||
monitors := []*models.Monitor{}
|
monitors := []*models.Monitor{}
|
||||||
err := db.SelectContext(ctx, &monitors,
|
err := db.SelectContext(ctx, &monitors,
|
||||||
"SELECT * FROM monitors WHERE deleted_at IS NULL ORDER BY name",
|
"SELECT * FROM monitors ORDER BY name",
|
||||||
)
|
)
|
||||||
return monitors, err
|
return monitors, err
|
||||||
}
|
}
|
||||||
|
@ -175,18 +179,16 @@ func GetMonitorsWithWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.Mo
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
monitors.slug,
|
monitors.id,
|
||||||
monitors.name,
|
monitors.name,
|
||||||
monitors.script,
|
monitors.script,
|
||||||
monitors.schedule,
|
monitors.schedule,
|
||||||
monitors.created_at,
|
monitors.created_at,
|
||||||
monitors.updated_at,
|
monitors.updated_at,
|
||||||
monitors.deleted_at,
|
|
||||||
worker_groups.name as worker_group_name
|
worker_groups.name as worker_group_name
|
||||||
FROM monitors
|
FROM monitors
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.slug = monitor_worker_groups.monitor_slug
|
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_slug = worker_groups.slug
|
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_id = worker_groups.id
|
||||||
WHERE monitors.deleted_at IS NULL
|
|
||||||
ORDER BY monitors.name
|
ORDER BY monitors.name
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -201,13 +203,12 @@ ORDER BY monitors.name
|
||||||
|
|
||||||
var workerGroupName *string
|
var workerGroupName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&monitor.Slug,
|
&monitor.Id,
|
||||||
&monitor.Name,
|
&monitor.Name,
|
||||||
&monitor.Script,
|
&monitor.Script,
|
||||||
&monitor.Schedule,
|
&monitor.Schedule,
|
||||||
&monitor.CreatedAt,
|
&monitor.CreatedAt,
|
||||||
&monitor.UpdatedAt,
|
&monitor.UpdatedAt,
|
||||||
&monitor.DeletedAt,
|
|
||||||
&workerGroupName,
|
&workerGroupName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -215,19 +216,19 @@ ORDER BY monitors.name
|
||||||
}
|
}
|
||||||
if workerGroupName != nil {
|
if workerGroupName != nil {
|
||||||
workerGroups := []string{}
|
workerGroups := []string{}
|
||||||
if monitors[monitor.Slug] != nil {
|
if monitors[monitor.Id] != nil {
|
||||||
workerGroups = monitors[monitor.Slug].WorkerGroups
|
workerGroups = monitors[monitor.Id].WorkerGroups
|
||||||
}
|
}
|
||||||
monitor.WorkerGroups = append(workerGroups, *workerGroupName)
|
monitor.WorkerGroups = append(workerGroups, *workerGroupName)
|
||||||
}
|
}
|
||||||
monitors[monitor.Slug] = monitor
|
monitors[monitor.Id] = monitor
|
||||||
}
|
}
|
||||||
|
|
||||||
return maps.Values(monitors), err
|
return maps.Values(monitors), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteMonitorSchedule(ctx context.Context, t client.Client, slug string) error {
|
func DeleteMonitorSchedule(ctx context.Context, t client.Client, id string) error {
|
||||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||||
return schedule.Delete(ctx)
|
return schedule.Delete(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,24 +242,24 @@ func CreateOrUpdateMonitorSchedule(
|
||||||
|
|
||||||
workerGroupStrings := make([]string, len(workerGroups))
|
workerGroupStrings := make([]string, len(workerGroups))
|
||||||
for i, group := range workerGroups {
|
for i, group := range workerGroups {
|
||||||
workerGroupStrings[i] = group.Slug
|
workerGroupStrings[i] = group.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]interface{}, 1)
|
args := make([]interface{}, 1)
|
||||||
args[0] = workflows.MonitorWorkflowParam{
|
args[0] = workflows.MonitorWorkflowParam{
|
||||||
Script: monitor.Script,
|
Script: monitor.Script,
|
||||||
Slug: monitor.Slug,
|
MonitorId: monitor.Id,
|
||||||
WorkerGroups: workerGroupStrings,
|
WorkerGroupIds: workerGroupStrings,
|
||||||
}
|
}
|
||||||
|
|
||||||
options := client.ScheduleOptions{
|
options := client.ScheduleOptions{
|
||||||
ID: getScheduleId(monitor.Slug),
|
ID: getScheduleId(monitor.Id),
|
||||||
Spec: client.ScheduleSpec{
|
Spec: client.ScheduleSpec{
|
||||||
CronExpressions: []string{monitor.Schedule},
|
CronExpressions: []string{monitor.Schedule},
|
||||||
Jitter: time.Second * 10,
|
Jitter: time.Second * 10,
|
||||||
},
|
},
|
||||||
Action: &client.ScheduleWorkflowAction{
|
Action: &client.ScheduleWorkflowAction{
|
||||||
ID: getScheduleId(monitor.Slug),
|
ID: getScheduleId(monitor.Id),
|
||||||
Workflow: workflows.NewWorkflows(nil).MonitorWorkflowDefinition,
|
Workflow: workflows.NewWorkflows(nil).MonitorWorkflowDefinition,
|
||||||
Args: args,
|
Args: args,
|
||||||
TaskQueue: "default",
|
TaskQueue: "default",
|
||||||
|
@ -268,7 +269,7 @@ func CreateOrUpdateMonitorSchedule(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(monitor.Slug))
|
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(monitor.Id))
|
||||||
|
|
||||||
// If exists, we update
|
// If exists, we update
|
||||||
_, err := schedule.Describe(ctx)
|
_, err := schedule.Describe(ctx)
|
||||||
|
|
|
@ -7,18 +7,60 @@ import (
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetMonitorHistoryForMonitor(ctx context.Context, db *sqlx.DB, monitorSlug string) ([]*models.MonitorHistory, error) {
|
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
|
var monitorHistory []*models.MonitorHistory
|
||||||
err := db.SelectContext(ctx, &monitorHistory,
|
err := db.SelectContext(ctx, &monitorHistory, `
|
||||||
"SELECT * FROM monitor_histories WHERE monitor_slug = $1 ORDER BY created_at DESC",
|
SELECT
|
||||||
monitorSlug,
|
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
|
return monitorHistory, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddHistoryForMonitor(ctx context.Context, db *sqlx.DB, history *models.MonitorHistory) error {
|
func AddHistoryForMonitor(ctx context.Context, db *sqlx.DB, history *models.MonitorHistory) error {
|
||||||
_, err := db.NamedExecContext(ctx,
|
_, err := db.NamedExecContext(ctx,
|
||||||
"INSERT INTO monitor_histories (monitor_slug, status, note) VALUES (:monitor_slug, :status, :note)",
|
`
|
||||||
|
INSERT INTO monitor_histories (
|
||||||
|
monitor_id,
|
||||||
|
worker_group_id,
|
||||||
|
status,
|
||||||
|
note
|
||||||
|
) VALUES (
|
||||||
|
:monitor_id,
|
||||||
|
:worker_group_id,
|
||||||
|
:status,
|
||||||
|
:note
|
||||||
|
)`,
|
||||||
history,
|
history,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -10,8 +10,14 @@ import (
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetActiveWorkers(ctx context.Context, workerGroupSlug string, temporal client.Client) ([]string, error) {
|
func CountWorkerGroups(ctx context.Context, db *sqlx.DB) (int, error) {
|
||||||
response, err := temporal.DescribeTaskQueue(ctx, workerGroupSlug, enums.TASK_QUEUE_TYPE_ACTIVITY)
|
var count int
|
||||||
|
err := db.GetContext(ctx, &count, "SELECT COUNT(*) FROM worker_groups")
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetActiveWorkers(ctx context.Context, workerGroupId string, temporal client.Client) ([]string, error) {
|
||||||
|
response, err := temporal.DescribeTaskQueue(ctx, workerGroupId, enums.TASK_QUEUE_TYPE_ACTIVITY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return make([]string, 0), err
|
return make([]string, 0), err
|
||||||
}
|
}
|
||||||
|
@ -26,16 +32,16 @@ func GetActiveWorkers(ctx context.Context, workerGroupSlug string, temporal clie
|
||||||
|
|
||||||
func CreateWorkerGroup(ctx context.Context, db *sqlx.DB, workerGroup *models.WorkerGroup) error {
|
func CreateWorkerGroup(ctx context.Context, db *sqlx.DB, workerGroup *models.WorkerGroup) error {
|
||||||
_, err := db.NamedExecContext(ctx,
|
_, err := db.NamedExecContext(ctx,
|
||||||
"INSERT INTO worker_groups (slug, name) VALUES (:slug, :name)",
|
"INSERT INTO worker_groups (id, name) VALUES (:id, :name)",
|
||||||
workerGroup,
|
workerGroup,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteWorkerGroup(ctx context.Context, db *sqlx.DB, slug string) error {
|
func DeleteWorkerGroup(ctx context.Context, db *sqlx.DB, id string) error {
|
||||||
_, err := db.ExecContext(ctx,
|
_, err := db.ExecContext(ctx,
|
||||||
"UPDATE worker_groups SET deleted_at = datetime('now') WHERE slug = $1",
|
"DELETE FROM worker_groups WHERE id = $1",
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +49,7 @@ func DeleteWorkerGroup(ctx context.Context, db *sqlx.DB, slug string) error {
|
||||||
func GetWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.WorkerGroup, error) {
|
func GetWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.WorkerGroup, error) {
|
||||||
var workerGroups []*models.WorkerGroup
|
var workerGroups []*models.WorkerGroup
|
||||||
err := db.SelectContext(ctx, &workerGroups,
|
err := db.SelectContext(ctx, &workerGroups,
|
||||||
"SELECT * FROM worker_groups WHERE deleted_at IS NULL ORDER BY name",
|
"SELECT * FROM worker_groups ORDER BY name",
|
||||||
)
|
)
|
||||||
return workerGroups, err
|
return workerGroups, err
|
||||||
}
|
}
|
||||||
|
@ -52,16 +58,14 @@ func GetWorkerGroupsWithMonitors(ctx context.Context, db *sqlx.DB) ([]*models.Wo
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
worker_groups.slug,
|
worker_groups.id,
|
||||||
worker_groups.name,
|
worker_groups.name,
|
||||||
worker_groups.created_at,
|
worker_groups.created_at,
|
||||||
worker_groups.updated_at,
|
worker_groups.updated_at,
|
||||||
worker_groups.deleted_at,
|
|
||||||
monitors.name as monitor_name
|
monitors.name as monitor_name
|
||||||
FROM worker_groups
|
FROM worker_groups
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.slug = monitor_worker_groups.worker_group_slug
|
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.id = monitor_worker_groups.worker_group_id
|
||||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_slug = monitors.slug
|
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_id = monitors.id
|
||||||
WHERE worker_groups.deleted_at IS NULL AND monitors.deleted_at IS NULL
|
|
||||||
ORDER BY worker_groups.name
|
ORDER BY worker_groups.name
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,11 +80,10 @@ ORDER BY worker_groups.name
|
||||||
|
|
||||||
var monitorName *string
|
var monitorName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&workerGroup.Slug,
|
&workerGroup.Id,
|
||||||
&workerGroup.Name,
|
&workerGroup.Name,
|
||||||
&workerGroup.CreatedAt,
|
&workerGroup.CreatedAt,
|
||||||
&workerGroup.UpdatedAt,
|
&workerGroup.UpdatedAt,
|
||||||
&workerGroup.DeletedAt,
|
|
||||||
&monitorName,
|
&monitorName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -89,52 +92,51 @@ ORDER BY worker_groups.name
|
||||||
|
|
||||||
if monitorName != nil {
|
if monitorName != nil {
|
||||||
monitors := []string{}
|
monitors := []string{}
|
||||||
if workerGroups[workerGroup.Slug] != nil {
|
if workerGroups[workerGroup.Id] != nil {
|
||||||
monitors = workerGroups[workerGroup.Slug].Monitors
|
monitors = workerGroups[workerGroup.Id].Monitors
|
||||||
}
|
}
|
||||||
workerGroup.Monitors = append(monitors, *monitorName)
|
workerGroup.Monitors = append(monitors, *monitorName)
|
||||||
}
|
}
|
||||||
|
|
||||||
workerGroups[workerGroup.Slug] = workerGroup
|
workerGroups[workerGroup.Id] = workerGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
return maps.Values(workerGroups), err
|
return maps.Values(workerGroups), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkerGroupsBySlug(ctx context.Context, db *sqlx.DB, slugs []string) ([]*models.WorkerGroup, error) {
|
func GetWorkerGroupsById(ctx context.Context, db *sqlx.DB, ids []string) ([]*models.WorkerGroup, error) {
|
||||||
var workerGroups []*models.WorkerGroup
|
var workerGroups []*models.WorkerGroup
|
||||||
err := db.SelectContext(ctx, &workerGroups,
|
err := db.SelectContext(ctx, &workerGroups,
|
||||||
"SELECT * FROM worker_groups WHERE slug = ANY($1) AND deleted_at IS NULL",
|
"SELECT * FROM worker_groups WHERE id = ANY($1)",
|
||||||
slugs,
|
ids,
|
||||||
)
|
)
|
||||||
return workerGroups, err
|
return workerGroups, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkerGroup(ctx context.Context, db *sqlx.DB, slug string) (*models.WorkerGroup, error) {
|
func GetWorkerGroup(ctx context.Context, db *sqlx.DB, id string) (*models.WorkerGroup, error) {
|
||||||
var workerGroup models.WorkerGroup
|
var workerGroup models.WorkerGroup
|
||||||
err := db.GetContext(ctx, &workerGroup,
|
err := db.GetContext(ctx, &workerGroup,
|
||||||
"SELECT * FROM worker_groups WHERE slug = $1 AND deleted_at IS NULL",
|
"SELECT * FROM worker_groups WHERE id = $1",
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
return &workerGroup, err
|
return &workerGroup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkerGroupWithMonitors(ctx context.Context, db *sqlx.DB, slug string) (*models.WorkerGroupWithMonitors, error) {
|
func GetWorkerGroupWithMonitors(ctx context.Context, db *sqlx.DB, id string) (*models.WorkerGroupWithMonitors, error) {
|
||||||
rows, err := db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
`
|
`
|
||||||
SELECT
|
SELECT
|
||||||
worker_groups.slug,
|
worker_groups.id,
|
||||||
worker_groups.name,
|
worker_groups.name,
|
||||||
worker_groups.created_at,
|
worker_groups.created_at,
|
||||||
worker_groups.updated_at,
|
worker_groups.updated_at,
|
||||||
worker_groups.deleted_at,
|
|
||||||
monitors.name as monitor_name
|
monitors.name as monitor_name
|
||||||
FROM worker_groups
|
FROM worker_groups
|
||||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.slug = monitor_worker_groups.worker_group_slug
|
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.id = monitor_worker_groups.worker_group_id
|
||||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_slug = monitors.slug
|
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_id = monitors.id
|
||||||
WHERE worker_groups.slug=$1 AND worker_groups.deleted_at IS NULL AND monitors.deleted_at IS NULL
|
WHERE worker_groups.id=$1
|
||||||
`,
|
`,
|
||||||
slug,
|
id,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -146,11 +148,10 @@ WHERE worker_groups.slug=$1 AND worker_groups.deleted_at IS NULL AND monitors.de
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var monitorName *string
|
var monitorName *string
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
&workerGroup.Slug,
|
&workerGroup.Id,
|
||||||
&workerGroup.Name,
|
&workerGroup.Name,
|
||||||
&workerGroup.CreatedAt,
|
&workerGroup.CreatedAt,
|
||||||
&workerGroup.UpdatedAt,
|
&workerGroup.UpdatedAt,
|
||||||
&workerGroup.DeletedAt,
|
|
||||||
&monitorName,
|
&monitorName,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,18 +11,18 @@ import (
|
||||||
|
|
||||||
type MonitorWorkflowParam struct {
|
type MonitorWorkflowParam struct {
|
||||||
Script string
|
Script string
|
||||||
Slug string
|
MonitorId string
|
||||||
WorkerGroups []string
|
WorkerGroupIds []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param MonitorWorkflowParam) error {
|
func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param MonitorWorkflowParam) (models.MonitorStatus, error) {
|
||||||
workerGroups := param.WorkerGroups
|
workerGroupIds := param.WorkerGroupIds
|
||||||
sort.Strings(workerGroups)
|
sort.Strings(workerGroupIds)
|
||||||
|
|
||||||
for _, workerGroup := range workerGroups {
|
for _, workerGroupId := range workerGroupIds {
|
||||||
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
||||||
StartToCloseTimeout: 60 * time.Second,
|
StartToCloseTimeout: 60 * time.Second,
|
||||||
TaskQueue: workerGroup,
|
TaskQueue: workerGroupId,
|
||||||
})
|
})
|
||||||
|
|
||||||
heatlcheckParam := activities.HealtcheckParam{
|
heatlcheckParam := activities.HealtcheckParam{
|
||||||
|
@ -32,7 +32,7 @@ func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param Monito
|
||||||
var monitorResult *activities.MonitorResult
|
var monitorResult *activities.MonitorResult
|
||||||
err := workflow.ExecuteActivity(ctx, w.activities.Monitor, heatlcheckParam).Get(ctx, &monitorResult)
|
err := workflow.ExecuteActivity(ctx, w.activities.Monitor, heatlcheckParam).Get(ctx, &monitorResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return models.MonitorUnknown, err
|
||||||
}
|
}
|
||||||
|
|
||||||
status := models.MonitorFailure
|
status := models.MonitorFailure
|
||||||
|
@ -41,18 +41,18 @@ func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param Monito
|
||||||
}
|
}
|
||||||
|
|
||||||
historyParam := activities.HealtcheckAddToHistoryParam{
|
historyParam := activities.HealtcheckAddToHistoryParam{
|
||||||
Slug: param.Slug,
|
MonitorId: param.MonitorId,
|
||||||
Status: status,
|
Status: status,
|
||||||
Note: monitorResult.Note,
|
Note: monitorResult.Note,
|
||||||
WorkerGroup: workerGroup,
|
WorkerGroupId: workerGroupId,
|
||||||
}
|
}
|
||||||
|
|
||||||
var historyResult *activities.MonitorAddToHistoryResult
|
var historyResult *activities.MonitorAddToHistoryResult
|
||||||
err = workflow.ExecuteActivity(ctx, w.activities.MonitorAddToHistory, historyParam).Get(ctx, &historyResult)
|
err = workflow.ExecuteActivity(ctx, w.activities.MonitorAddToHistory, historyParam).Get(ctx, &historyResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return models.MonitorUnknown, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return models.MonitorSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
|
import "code.tjo.space/mentos1386/zdravko/database/models"
|
||||||
|
|
||||||
type ApiV1MonitorsHistoryPOSTBody struct {
|
type ApiV1MonitorsHistoryPOSTBody struct {
|
||||||
Status string `json:"status"`
|
Status models.MonitorStatus `json:"status"`
|
||||||
Note string `json:"note"`
|
Note string `json:"note"`
|
||||||
WorkerGroup string `json:"worker_group"`
|
WorkerGroupId string `json:"worker_group"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,16 +48,16 @@ func Routes(
|
||||||
settings.GET("/monitors", h.SettingsMonitorsGET)
|
settings.GET("/monitors", h.SettingsMonitorsGET)
|
||||||
settings.GET("/monitors/create", h.SettingsMonitorsCreateGET)
|
settings.GET("/monitors/create", h.SettingsMonitorsCreateGET)
|
||||||
settings.POST("/monitors/create", h.SettingsMonitorsCreatePOST)
|
settings.POST("/monitors/create", h.SettingsMonitorsCreatePOST)
|
||||||
settings.GET("/monitors/:slug", h.SettingsMonitorsDescribeGET)
|
settings.GET("/monitors/:id", h.SettingsMonitorsDescribeGET)
|
||||||
settings.POST("/monitors/:slug", h.SettingsMonitorsDescribePOST)
|
settings.POST("/monitors/:id", h.SettingsMonitorsDescribePOST)
|
||||||
settings.GET("/monitors/:slug/delete", h.SettingsMonitorsDescribeDELETE)
|
settings.GET("/monitors/:id/delete", h.SettingsMonitorsDescribeDELETE)
|
||||||
settings.GET("/monitors/:slug/disable", h.SettingsMonitorsDisableGET)
|
settings.GET("/monitors/:id/disable", h.SettingsMonitorsDisableGET)
|
||||||
settings.GET("/monitors/:slug/enable", h.SettingsMonitorsEnableGET)
|
settings.GET("/monitors/:id/enable", h.SettingsMonitorsEnableGET)
|
||||||
settings.GET("/worker-groups", h.SettingsWorkerGroupsGET)
|
settings.GET("/worker-groups", h.SettingsWorkerGroupsGET)
|
||||||
settings.GET("/worker-groups/create", h.SettingsWorkerGroupsCreateGET)
|
settings.GET("/worker-groups/create", h.SettingsWorkerGroupsCreateGET)
|
||||||
settings.POST("/worker-groups/create", h.SettingsWorkerGroupsCreatePOST)
|
settings.POST("/worker-groups/create", h.SettingsWorkerGroupsCreatePOST)
|
||||||
settings.GET("/worker-groups/:slug", h.SettingsWorkerGroupsDescribeGET)
|
settings.GET("/worker-groups/:id", h.SettingsWorkerGroupsDescribeGET)
|
||||||
settings.GET("/worker-groups/:slug/delete", h.SettingsWorkerGroupsDescribeDELETE)
|
settings.GET("/worker-groups/:id/delete", h.SettingsWorkerGroupsDescribeDELETE)
|
||||||
|
|
||||||
settings.Match([]string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"}, "/temporal*", h.Temporal)
|
settings.Match([]string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"}, "/temporal*", h.Temporal)
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ 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/:slug/history", h.ApiV1MonitorsHistoryPOST)
|
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) {
|
||||||
|
|
|
@ -1677,14 +1677,6 @@ code {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.sm\:ml-2 {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sm\:mt-0 {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sm\:w-auto {
|
.sm\:w-auto {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,3 @@
|
||||||
{{ define "daily" }}
|
|
||||||
<div class="justify-self-end text-sm">{{ .HistoryDaily.Uptime }}% uptime</div>
|
|
||||||
<div class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden">
|
|
||||||
{{ range .HistoryDaily.History }}
|
|
||||||
{{ if eq . "SUCCESS" }}
|
|
||||||
<div class="bg-green-400 hover:bg-green-500 flex-auto"></div>
|
|
||||||
{{ else if eq . "FAILURE" }}
|
|
||||||
<div class="bg-red-400 hover:bg-red-500 flex-auto"></div>
|
|
||||||
{{ else }}
|
|
||||||
<div class="bg-gray-400 hover:bg-gray-500 flex-auto"></div>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<div class="text-slate-500 justify-self-start text-sm">90 days ago</div>
|
|
||||||
<div class="text-slate-500 justify-self-end text-sm">Today</div>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "hourly" }}
|
|
||||||
<div class="justify-self-end text-sm">
|
|
||||||
{{ .HistoryHourly.Uptime }}% uptime
|
|
||||||
</div>
|
|
||||||
<div class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden">
|
|
||||||
{{ range .HistoryHourly.History }}
|
|
||||||
{{ if eq . "SUCCESS" }}
|
|
||||||
<div class="bg-green-400 hover:bg-green-500 flex-auto"></div>
|
|
||||||
{{ else if eq . "FAILURE" }}
|
|
||||||
<div class="bg-red-400 hover:bg-red-500 flex-auto"></div>
|
|
||||||
{{ else }}
|
|
||||||
<div class="bg-gray-400 hover:bg-gray-500 flex-auto"></div>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<div class="text-slate-500 justify-self-start text-sm">48 hours ago</div>
|
|
||||||
<div class="text-slate-500 justify-self-end text-sm">Now</div>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<div class="container max-w-screen-md flex flex-col mt-20">
|
<div class="container max-w-screen-md flex flex-col mt-20">
|
||||||
{{ if eq .MonitorsLength 0 }}
|
{{ if eq .MonitorsLength 0 }}
|
||||||
|
@ -136,11 +100,32 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<p>{{ .Name }}</p>
|
<p>{{ .Name }}</p>
|
||||||
</div>
|
</div>
|
||||||
{{ if eq $.TimeRange "90days" }}
|
<div class="justify-self-end text-sm">
|
||||||
{{ template "daily" . }}
|
{{ .History.Uptime }}% uptime
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden"
|
||||||
|
>
|
||||||
|
{{ range .History.List }}
|
||||||
|
{{ if eq . "SUCCESS" }}
|
||||||
|
<div class="bg-green-400 hover:bg-green-500 flex-auto"></div>
|
||||||
|
{{ else if eq . "FAILURE" }}
|
||||||
|
<div class="bg-red-400 hover:bg-red-500 flex-auto"></div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
{{ template "hourly" . }}
|
<div class="bg-gray-400 hover:bg-gray-500 flex-auto"></div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="text-slate-500 justify-self-start text-sm">
|
||||||
|
{{ if eq $.TimeRange "90days" }}
|
||||||
|
90 days ago
|
||||||
|
{{ else if eq $.TimeRange "48hours" }}
|
||||||
|
48 hours ago
|
||||||
|
{{ else if eq $.TimeRange "90minutes" }}
|
||||||
|
90 minutes ago
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="text-slate-500 justify-self-end text-sm">Now</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -96,9 +96,7 @@
|
||||||
{{ .Schedule }}
|
{{ .Schedule }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/settings/monitors/{{ .Slug }}" class="link"
|
<a href="/settings/monitors/{{ .Id }}" class="link">Details</a>
|
||||||
>Details</a
|
|
||||||
>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{ define "settings" }}
|
{{ define "settings" }}
|
||||||
<section class="p-5">
|
<section class="p-5">
|
||||||
<form action="/settings/monitors/{{ .Monitor.Slug }}" method="post">
|
<form action="/settings/monitors/{{ .Monitor.Id }}" method="post">
|
||||||
<h2>Configuration</h2>
|
<h2>Configuration</h2>
|
||||||
<label for="workergroups">Worker Groups</label>
|
<label for="workergroups">Worker Groups</label>
|
||||||
<input
|
<input
|
||||||
|
@ -70,13 +70,13 @@
|
||||||
{{ if eq .Monitor.Status "ACTIVE" }}
|
{{ if eq .Monitor.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.Slug }}/disable"
|
href="/settings/monitors/{{ .Monitor.Id }}/disable"
|
||||||
>Pause</a
|
>Pause</a
|
||||||
>
|
>
|
||||||
{{ else if eq .Monitor.Status "PAUSED" }}
|
{{ else if eq .Monitor.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.Slug }}/enable"
|
href="/settings/monitors/{{ .Monitor.Id }}/enable"
|
||||||
>Resume</a
|
>Resume</a
|
||||||
>
|
>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
<p class="text-sm mb-2">Permanently delete this monitor.</p>
|
<p class="text-sm mb-2">Permanently delete this monitor.</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.Slug }}/delete"
|
href="/settings/monitors/{{ .Monitor.Id }}/delete"
|
||||||
>Delete</a
|
>Delete</a
|
||||||
>
|
>
|
||||||
</section>
|
</section>
|
||||||
|
@ -102,6 +102,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
|
<th>Worker Group</th>
|
||||||
<th>Created At</th>
|
<th>Created At</th>
|
||||||
<th>Duration</th>
|
<th>Duration</th>
|
||||||
<th>Note</th>
|
<th>Note</th>
|
||||||
|
@ -122,7 +123,14 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ .CreatedAt.Format "2006-01-02 15:04:05" }}
|
<span
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800"
|
||||||
|
>
|
||||||
|
{{ .WorkerGroupName }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ .CreatedAt.Time.Format "2006-01-02 15:04:05" }}
|
||||||
</td>
|
</td>
|
||||||
<td>{ .Duration }</td>
|
<td>{ .Duration }</td>
|
||||||
<td class="whitespace-normal">
|
<td class="whitespace-normal">
|
||||||
|
|
|
@ -6,33 +6,90 @@
|
||||||
Hi there, {{ .User.Email }}.
|
Hi there, {{ .User.Email }}.
|
||||||
</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">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
Welcome to the settings page. Here you can manage your worker groups,
|
||||||
tempor incididunt ut labore et dolore magna aliqua.
|
monitors, and notifications.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-screen-xl flex flex-col sm:flex-row gap-4">
|
<div class="mx-auto max-w-screen-xl flex flex-col sm:flex-row gap-4">
|
||||||
<div
|
<div
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:mt-0 sm:ml-2 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 Workers</h3>
|
<h3 class="text-sm leading-6 font-medium text-gray-400">
|
||||||
<p class="text-3xl font-bold text-black">42</p>
|
Total Worker Groups
|
||||||
|
</h3>
|
||||||
|
<p class="text-3xl font-bold text-black">{{ .WorkerGroupsCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:mt-0 sm:ml-2 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 Monitors
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-3xl font-bold text-black">42</p>
|
<p class="text-3xl font-bold text-black">{{ .MonitorsCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="inline-block bg-white rounded-lg shadow p-5 text-center sm:mt-0 sm:ml-2 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 Notifications
|
Total Notifications
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-3xl font-bold text-black">42</p>
|
<p class="text-3xl font-bold text-black">{{ .NotificationsCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="mt-4">
|
||||||
|
<table>
|
||||||
|
<caption>
|
||||||
|
Execution History
|
||||||
|
<p>Last 10 executions for all monitors and worker groups.</p>
|
||||||
|
</caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Monitor</th>
|
||||||
|
<th>Worker Group</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Executed At</th>
|
||||||
|
<th>Note</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .History }}
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<a
|
||||||
|
class="underline hover:text-blue-600"
|
||||||
|
href="/settings/monitors/{{ .MonitorId }}"
|
||||||
|
>{{ .MonitorName }}</a
|
||||||
|
>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800"
|
||||||
|
>
|
||||||
|
{{ .WorkerGroupName }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ if eq .Status "SUCCESS" }}
|
||||||
|
bg-green-100 text-green-800
|
||||||
|
{{ else }}
|
||||||
|
bg-red-100 text-red-800
|
||||||
|
{{ end }}"
|
||||||
|
>
|
||||||
|
{{ .Status }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ .CreatedAt.Time.Format "2006-01-02 15:04:05" }}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-normal">
|
||||||
|
{{ .Note }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
{{ len .Monitors }}
|
{{ len .Monitors }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/settings/worker-groups/{{ .Slug }}" class="link"
|
<a href="/settings/worker-groups/{{ .Id }}" class="link"
|
||||||
>Details</a
|
>Details</a
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
</p>
|
</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/worker-groups/{{ .Worker.Slug }}/delete"
|
href="/settings/worker-groups/{{ .Worker.Id }}/delete"
|
||||||
>Delete</a
|
>Delete</a
|
||||||
>
|
>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Reference in a new issue