mirror of
https://github.com/mentos1386/zdravko.git
synced 2024-11-21 23:33:34 +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/
|
||||
|
||||
# Database
|
||||
zdravko.db
|
||||
temporal.db
|
||||
temporal.db-journal
|
||||
zdravko.db*
|
||||
temporal.db*
|
||||
|
||||
# Keys
|
||||
*.pem
|
||||
|
|
|
@ -2,6 +2,7 @@ package database
|
|||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
@ -13,7 +14,7 @@ import (
|
|||
var sqliteMigrations embed.FS
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,27 +1,64 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OAuth2State struct {
|
||||
State string `db:"state"`
|
||||
ExpiresAt time.Time `db:"expires_at"`
|
||||
State string `db:"state"`
|
||||
ExpiresAt Time `db:"expires_at"`
|
||||
}
|
||||
|
||||
type MonitorStatus string
|
||||
|
||||
const (
|
||||
MonitorSuccess string = "SUCCESS"
|
||||
MonitorFailure string = "FAILURE"
|
||||
MonitorError string = "ERROR"
|
||||
MonitorUnknown string = "UNKNOWN"
|
||||
MonitorSuccess MonitorStatus = "SUCCESS"
|
||||
MonitorFailure MonitorStatus = "FAILURE"
|
||||
MonitorError MonitorStatus = "ERROR"
|
||||
MonitorUnknown MonitorStatus = "UNKNOWN"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
DeletedAt *time.Time `db:"deleted_at"`
|
||||
type Time struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
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"`
|
||||
|
||||
Schedule string `db:"schedule"`
|
||||
|
@ -36,19 +73,21 @@ type MonitorWithWorkerGroups struct {
|
|||
}
|
||||
|
||||
type MonitorHistory struct {
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
CreatedAt Time `db:"created_at"`
|
||||
|
||||
MonitorSlug string `db:"monitor_slug"`
|
||||
Status string `db:"status"`
|
||||
Note string `db:"note"`
|
||||
MonitorId string `db:"monitor_id"`
|
||||
Status MonitorStatus `db:"status"`
|
||||
Note string `db:"note"`
|
||||
|
||||
WorkerGroupId string `db:"worker_group_id"`
|
||||
WorkerGroupName string `db:"worker_group_name"`
|
||||
}
|
||||
|
||||
type WorkerGroup struct {
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
DeletedAt *time.Time `db:"deleted_at"`
|
||||
CreatedAt Time `db:"created_at"`
|
||||
UpdatedAt Time `db:"updated_at"`
|
||||
|
||||
Slug string `db:"slug"`
|
||||
Id string `db:"id"`
|
||||
Name string `db:"name"`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,56 +1,66 @@
|
|||
-- +migrate Up
|
||||
CREATE TABLE oauth2_states (
|
||||
state TEXT,
|
||||
expires_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
state TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||
|
||||
PRIMARY KEY (state)
|
||||
);
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE monitors (
|
||||
slug TEXT,
|
||||
name TEXT,
|
||||
schedule TEXT,
|
||||
script TEXT,
|
||||
id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
schedule TEXT NOT NULL,
|
||||
script TEXT NOT NULL,
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at DATETIME,
|
||||
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 (slug),
|
||||
PRIMARY KEY (id),
|
||||
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 (
|
||||
slug TEXT,
|
||||
name TEXT,
|
||||
id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at DATETIME,
|
||||
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 (slug),
|
||||
PRIMARY KEY (id),
|
||||
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 (
|
||||
worker_group_slug TEXT,
|
||||
monitor_slug TEXT,
|
||||
worker_group_id TEXT NOT NULL,
|
||||
monitor_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (worker_group_slug,monitor_slug),
|
||||
CONSTRAINT fk_monitor_worker_groups_worker_group FOREIGN KEY (worker_group_slug) REFERENCES worker_groups(slug),
|
||||
CONSTRAINT fk_monitor_worker_groups_monitor FOREIGN KEY (monitor_slug) REFERENCES monitors(slug)
|
||||
);
|
||||
PRIMARY KEY (worker_group_id,monitor_id),
|
||||
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_id) REFERENCES monitors(id)
|
||||
) STRICT;
|
||||
|
||||
CREATE TABLE monitor_histories (
|
||||
monitor_slug TEXT,
|
||||
status TEXT,
|
||||
note TEXT,
|
||||
monitor_id TEXT NOT NULL,
|
||||
worker_group_id TEXT NOT NULL,
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status TEXT NOT NULL,
|
||||
note TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (monitor_slug, created_at),
|
||||
CONSTRAINT fk_monitors_history FOREIGN KEY (monitor_slug) REFERENCES monitors(slug)
|
||||
);
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')),
|
||||
|
||||
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
|
||||
DROP TABLE oauth2_states;
|
||||
|
|
|
@ -17,8 +17,8 @@ primary_region = 'waw'
|
|||
ROOT_URL = 'https://zdravko.mnts.dev'
|
||||
TEMPORAL_SERVER_HOST = 'server.process.zdravko.internal:7233'
|
||||
|
||||
TEMPORAL_DATABASE_PATH = '/data/temporal-6.db'
|
||||
DATABASE_PATH = '/data/zdravko-6.db'
|
||||
TEMPORAL_DATABASE_PATH = '/data/temporal-7.db'
|
||||
DATABASE_PATH = '/data/zdravko-7.db'
|
||||
|
||||
[processes]
|
||||
server = '--temporal --server'
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"code.tjo.space/mentos1386/zdravko/database/models"
|
||||
"code.tjo.space/mentos1386/zdravko/pkg/api"
|
||||
"code.tjo.space/mentos1386/zdravko/pkg/k6"
|
||||
)
|
||||
|
@ -33,22 +34,22 @@ func (a *Activities) Monitor(ctx context.Context, param HealtcheckParam) (*Monit
|
|||
}
|
||||
|
||||
type HealtcheckAddToHistoryParam struct {
|
||||
Slug string
|
||||
Status string
|
||||
Note string
|
||||
WorkerGroup string
|
||||
MonitorId string
|
||||
Status models.MonitorStatus
|
||||
Note string
|
||||
WorkerGroupId string
|
||||
}
|
||||
|
||||
type MonitorAddToHistoryResult struct {
|
||||
}
|
||||
|
||||
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{
|
||||
Status: param.Status,
|
||||
Note: param.Note,
|
||||
WorkerGroup: param.WorkerGroup,
|
||||
Status: param.Status,
|
||||
Note: param.Note,
|
||||
WorkerGroupId: param.WorkerGroupId,
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(body)
|
||||
|
|
|
@ -31,7 +31,7 @@ func (h *BaseHandler) ApiV1WorkersConnectGET(c echo.Context) error {
|
|||
|
||||
response := ApiV1WorkersConnectGETResponse{
|
||||
Endpoint: h.config.Temporal.ServerHost,
|
||||
Group: workerGroup.Slug,
|
||||
Group: workerGroup.Id,
|
||||
}
|
||||
|
||||
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.
|
||||
func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
||||
ctx := context.Background()
|
||||
|
||||
slug := c.Param("slug")
|
||||
id := c.Param("id")
|
||||
|
||||
var body api.ApiV1MonitorsHistoryPOSTBody
|
||||
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
||||
|
@ -51,7 +50,7 @@ func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = services.GetMonitor(ctx, h.db, slug)
|
||||
_, err = services.GetMonitor(ctx, h.db, id)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Monitor not found")
|
||||
|
@ -60,9 +59,10 @@ func (h *BaseHandler) ApiV1MonitorsHistoryPOST(c echo.Context) error {
|
|||
}
|
||||
|
||||
err = services.AddHistoryForMonitor(ctx, h.db, &models.MonitorHistory{
|
||||
MonitorSlug: slug,
|
||||
Status: body.Status,
|
||||
Note: body.Note,
|
||||
MonitorId: id,
|
||||
WorkerGroupId: body.WorkerGroupId,
|
||||
Status: body.Status,
|
||||
Note: body.Note,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -16,45 +16,39 @@ type IndexData struct {
|
|||
HealthChecks []*HealthCheck
|
||||
MonitorsLength int
|
||||
TimeRange string
|
||||
Status string
|
||||
Status models.MonitorStatus
|
||||
}
|
||||
|
||||
type HealthCheck struct {
|
||||
Name string
|
||||
Status string
|
||||
HistoryDaily *History
|
||||
HistoryHourly *History
|
||||
Name string
|
||||
Status models.MonitorStatus
|
||||
History *History
|
||||
}
|
||||
|
||||
type History struct {
|
||||
History []string
|
||||
Uptime int
|
||||
}
|
||||
|
||||
func getDay(date time.Time) string {
|
||||
return date.Format("2006-01-02")
|
||||
List []models.MonitorStatus
|
||||
Uptime int
|
||||
}
|
||||
|
||||
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 {
|
||||
numDays := 90
|
||||
historyDailyMap := map[string]string{}
|
||||
func getHistory(history []*models.MonitorHistory, period time.Duration, buckets int) *History {
|
||||
historyMap := map[string]models.MonitorStatus{}
|
||||
numOfSuccess := 0
|
||||
numTotal := 0
|
||||
|
||||
for i := 0; i < numDays; i++ {
|
||||
day := getDay(time.Now().AddDate(0, 0, -i).Truncate(time.Hour * 24))
|
||||
historyDailyMap[day] = models.MonitorUnknown
|
||||
for i := 0; i < buckets; i++ {
|
||||
datetime := getHour(time.Now().Add(period * time.Duration(-i)).Truncate(period))
|
||||
historyMap[datetime] = models.MonitorUnknown
|
||||
}
|
||||
|
||||
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
|
||||
if _, ok := historyDailyMap[day]; !ok {
|
||||
// Skip if not part of the "buckets"
|
||||
if _, ok := historyMap[hour]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -63,18 +57,18 @@ func getDailyHistory(history []*models.MonitorHistory) *History {
|
|||
numOfSuccess++
|
||||
}
|
||||
|
||||
// skip if day is already set to failure
|
||||
if historyDailyMap[day] == models.MonitorFailure {
|
||||
// skip if it is already set to failure
|
||||
if historyMap[hour] == models.MonitorFailure {
|
||||
continue
|
||||
}
|
||||
|
||||
historyDailyMap[day] = _history.Status
|
||||
historyMap[hour] = _history.Status
|
||||
}
|
||||
|
||||
historyDaily := make([]string, numDays)
|
||||
for i := 0; i < numDays; i++ {
|
||||
day := getDay(time.Now().AddDate(0, 0, -numDays+i+1).Truncate(time.Hour * 24))
|
||||
historyDaily[i] = historyDailyMap[day]
|
||||
historyHourly := make([]models.MonitorStatus, buckets)
|
||||
for i := 0; i < buckets; i++ {
|
||||
datetime := getHour(time.Now().Add(period * time.Duration(-buckets+i+1)).Truncate(period))
|
||||
historyHourly[i] = historyMap[datetime]
|
||||
}
|
||||
|
||||
uptime := 0
|
||||
|
@ -83,57 +77,8 @@ func getDailyHistory(history []*models.MonitorHistory) *History {
|
|||
}
|
||||
|
||||
return &History{
|
||||
History: historyDaily,
|
||||
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,
|
||||
List: historyHourly,
|
||||
Uptime: uptime,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,32 +90,38 @@ func (h *BaseHandler) Index(c echo.Context) error {
|
|||
}
|
||||
|
||||
timeRange := c.QueryParam("time-range")
|
||||
if timeRange != "48hours" && timeRange != "90days" {
|
||||
if timeRange != "48hours" && timeRange != "90days" && timeRange != "90minutes" {
|
||||
timeRange = "90days"
|
||||
}
|
||||
|
||||
overallStatus := "SUCCESS"
|
||||
overallStatus := models.MonitorSuccess
|
||||
|
||||
monitorsWithHistory := make([]*HealthCheck, len(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 {
|
||||
return err
|
||||
}
|
||||
|
||||
historyDaily := getDailyHistory(history)
|
||||
historyHourly := getHourlyHistory(history)
|
||||
var historyResult *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 {
|
||||
overallStatus = status
|
||||
}
|
||||
|
||||
monitorsWithHistory[i] = &HealthCheck{
|
||||
Name: monitor.Name,
|
||||
Status: status,
|
||||
HistoryDaily: historyDaily,
|
||||
HistoryHourly: historyHourly,
|
||||
Name: monitor.Name,
|
||||
Status: status,
|
||||
History: historyResult,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,10 @@ func (h *BaseHandler) OAuth2LoginGET(c echo.Context) error {
|
|||
conf := newOAuth2(h.config)
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -108,7 +111,7 @@ func (h *BaseHandler) OAuth2CallbackGET(c echo.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if deleted == false {
|
||||
if !deleted {
|
||||
return errors.New("invalid state")
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.tjo.space/mentos1386/zdravko/internal/services"
|
||||
"code.tjo.space/mentos1386/zdravko/web/templates/components"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
@ -48,12 +49,42 @@ var SettingsNavbar = []*components.Page{
|
|||
GetPageByTitle(SettingsPages, "Logout"),
|
||||
}
|
||||
|
||||
type SettingsOverview struct {
|
||||
*Settings
|
||||
WorkerGroupsCount int
|
||||
MonitorsCount int
|
||||
NotificationsCount int
|
||||
History []*services.MonitorHistoryWithMonitor
|
||||
}
|
||||
|
||||
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
|
||||
cc := c.(AuthenticatedContext)
|
||||
ctx := c.Request().Context()
|
||||
|
||||
return c.Render(http.StatusOK, "settings_overview.tmpl", NewSettings(
|
||||
cc.Principal.User,
|
||||
GetPageByTitle(SettingsPages, "Overview"),
|
||||
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")},
|
||||
))
|
||||
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,
|
||||
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))
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -79,14 +79,14 @@ func (h *BaseHandler) SettingsMonitorsGET(c echo.Context) error {
|
|||
func (h *BaseHandler) SettingsMonitorsDescribeGET(c echo.Context) error {
|
||||
cc := c.(AuthenticatedContext)
|
||||
|
||||
slug := c.Param("slug")
|
||||
slug := c.Param("id")
|
||||
|
||||
monitor, err := services.GetMonitorWithWorkerGroups(context.Background(), h.db, slug)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ func (h *BaseHandler) SettingsMonitorsDescribeGET(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)
|
||||
if err != nil {
|
||||
|
@ -140,14 +140,14 @@ func (h *BaseHandler) SettingsMonitorsDescribeDELETE(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)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -156,14 +156,14 @@ func (h *BaseHandler) SettingsMonitorsDisableGET(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)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ func (h *BaseHandler) SettingsMonitorsEnableGET(c echo.Context) error {
|
|||
|
||||
func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
||||
ctx := context.Background()
|
||||
monitorSlug := c.Param("slug")
|
||||
monitorId := c.Param("id")
|
||||
|
||||
update := UpdateMonitor{
|
||||
WorkerGroups: strings.TrimSpace(c.FormValue("workergroups")),
|
||||
|
@ -185,7 +185,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
monitor, err := services.GetMonitor(ctx, h.db, monitorSlug)
|
||||
monitor, err := services.GetMonitor(ctx, h.db, monitorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
|||
workerGroup, err := services.GetWorkerGroup(ctx, h.db, slug.Make(group))
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -231,7 +231,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error {
|
|||
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 {
|
||||
|
@ -249,7 +249,7 @@ func (h *BaseHandler) SettingsMonitorsCreateGET(c echo.Context) error {
|
|||
|
||||
func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
||||
ctx := context.Background()
|
||||
monitorSlug := slug.Make(c.FormValue("name"))
|
||||
monitorId := slug.Make(c.FormValue("name"))
|
||||
|
||||
create := CreateMonitor{
|
||||
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))
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -284,7 +284,7 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error {
|
|||
|
||||
monitor := &models.Monitor{
|
||||
Name: create.Name,
|
||||
Slug: monitorSlug,
|
||||
Id: monitorId,
|
||||
Schedule: create.Schedule,
|
||||
Script: create.Script,
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
|||
|
||||
workerGroupsWithActiveWorkers := make([]*WorkerGroupWithActiveWorkers, len(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 {
|
||||
return err
|
||||
}
|
||||
|
@ -69,10 +69,9 @@ func (h *BaseHandler) SettingsWorkerGroupsGET(c echo.Context) error {
|
|||
|
||||
func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
||||
cc := c.(AuthenticatedContext)
|
||||
id := c.Param("id")
|
||||
|
||||
slug := c.Param("slug")
|
||||
|
||||
worker, err := services.GetWorkerGroup(context.Background(), h.db, slug)
|
||||
worker, err := services.GetWorkerGroup(context.Background(), h.db, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -83,7 +82,7 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -95,7 +94,7 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(c echo.Context) error {
|
|||
[]*components.Page{
|
||||
GetPageByTitle(SettingsPages, "Worker Groups"),
|
||||
{
|
||||
Path: fmt.Sprintf("/settings/worker-groups/%s", slug),
|
||||
Path: fmt.Sprintf("/settings/worker-groups/%s", id),
|
||||
Title: "Describe",
|
||||
Breadcrumb: worker.Name,
|
||||
},
|
||||
|
@ -109,9 +108,9 @@ func (h *BaseHandler) SettingsWorkerGroupsDescribeGET(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 {
|
||||
return err
|
||||
}
|
||||
|
@ -134,11 +133,11 @@ func (h *BaseHandler) SettingsWorkerGroupsCreateGET(c echo.Context) error {
|
|||
|
||||
func (h *BaseHandler) SettingsWorkerGroupsCreatePOST(c echo.Context) error {
|
||||
ctx := context.Background()
|
||||
slug := slug.Make(c.FormValue("name"))
|
||||
id := slug.Make(c.FormValue("name"))
|
||||
|
||||
workerGroup := &models.WorkerGroup{
|
||||
Name: c.FormValue("name"),
|
||||
Slug: slug,
|
||||
Id: id,
|
||||
}
|
||||
|
||||
err := validator.New(validator.WithRequiredStructEnabled()).Struct(workerGroup)
|
||||
|
@ -155,5 +154,5 @@ func (h *BaseHandler) SettingsWorkerGroupsCreatePOST(c echo.Context) error {
|
|||
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()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "zdravko",
|
||||
Subject: "worker-group:" + workerGroup.Slug,
|
||||
Subject: "worker-group:" + workerGroup.Id,
|
||||
},
|
||||
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
||||
[]string{"default:read", "default:write", "default:worker"},
|
||||
|
|
|
@ -21,12 +21,18 @@ const (
|
|||
MonitorStatusActive MonitorStatus = "ACTIVE"
|
||||
)
|
||||
|
||||
func getScheduleId(slug string) string {
|
||||
return "monitor-" + slug
|
||||
func getScheduleId(id string) string {
|
||||
return "monitor-" + id
|
||||
}
|
||||
|
||||
func GetMonitorStatus(ctx context.Context, temporal client.Client, slug string) (MonitorStatus, error) {
|
||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
||||
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 {
|
||||
|
@ -40,8 +46,8 @@ func GetMonitorStatus(ctx context.Context, temporal client.Client, slug string)
|
|||
return MonitorStatusActive, nil
|
||||
}
|
||||
|
||||
func SetMonitorStatus(ctx context.Context, temporal client.Client, slug string, status MonitorStatus) error {
|
||||
schedule := temporal.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
||||
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"})
|
||||
|
@ -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 {
|
||||
_, 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,
|
||||
)
|
||||
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 {
|
||||
_, 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,
|
||||
)
|
||||
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,
|
||||
"UPDATE monitors SET deleted_at = datetime('now') WHERE slug=$1",
|
||||
slug,
|
||||
"DELETE FROM monitors WHERE id=$1",
|
||||
id,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
@ -84,8 +90,8 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
|||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx,
|
||||
"DELETE FROM monitor_worker_groups WHERE monitor_slug=$1",
|
||||
monitor.Slug,
|
||||
"DELETE FROM monitor_worker_groups WHERE monitor_id=$1",
|
||||
monitor.Id,
|
||||
)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
|
@ -93,9 +99,9 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
|||
}
|
||||
for _, group := range workerGroups {
|
||||
_, err = tx.ExecContext(ctx,
|
||||
"INSERT INTO monitor_worker_groups (monitor_slug, worker_group_slug) VALUES ($1, $2)",
|
||||
monitor.Slug,
|
||||
group.Slug,
|
||||
"INSERT INTO monitor_worker_groups (monitor_id, worker_group_id) VALUES ($1, $2)",
|
||||
monitor.Id,
|
||||
group.Id,
|
||||
)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
|
@ -105,33 +111,32 @@ func UpdateMonitorWorkerGroups(ctx context.Context, db *sqlx.DB, monitor *models
|
|||
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{}
|
||||
err := db.GetContext(ctx, monitor,
|
||||
"SELECT * FROM monitors WHERE slug=$1 AND deleted_at IS NULL",
|
||||
slug,
|
||||
"SELECT * FROM monitors WHERE id=$1",
|
||||
id,
|
||||
)
|
||||
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,
|
||||
`
|
||||
SELECT
|
||||
monitors.slug,
|
||||
monitors.id,
|
||||
monitors.name,
|
||||
monitors.script,
|
||||
monitors.schedule,
|
||||
monitors.created_at,
|
||||
monitors.updated_at,
|
||||
monitors.deleted_at,
|
||||
worker_groups.name as worker_group_name
|
||||
FROM monitors
|
||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.slug = monitor_worker_groups.monitor_slug
|
||||
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_slug = worker_groups.slug
|
||||
WHERE monitors.slug=$1 AND monitors.deleted_at IS NULL
|
||||
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
|
||||
`,
|
||||
slug,
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -143,13 +148,12 @@ WHERE monitors.slug=$1 AND monitors.deleted_at IS NULL
|
|||
for rows.Next() {
|
||||
var workerGroupName *string
|
||||
err = rows.Scan(
|
||||
&monitor.Slug,
|
||||
&monitor.Id,
|
||||
&monitor.Name,
|
||||
&monitor.Script,
|
||||
&monitor.Schedule,
|
||||
&monitor.CreatedAt,
|
||||
&monitor.UpdatedAt,
|
||||
&monitor.DeletedAt,
|
||||
&workerGroupName,
|
||||
)
|
||||
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) {
|
||||
monitors := []*models.Monitor{}
|
||||
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
|
||||
}
|
||||
|
@ -175,18 +179,16 @@ func GetMonitorsWithWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.Mo
|
|||
rows, err := db.QueryContext(ctx,
|
||||
`
|
||||
SELECT
|
||||
monitors.slug,
|
||||
monitors.id,
|
||||
monitors.name,
|
||||
monitors.script,
|
||||
monitors.schedule,
|
||||
monitors.created_at,
|
||||
monitors.updated_at,
|
||||
monitors.deleted_at,
|
||||
worker_groups.name as worker_group_name
|
||||
FROM monitors
|
||||
LEFT OUTER JOIN monitor_worker_groups ON monitors.slug = monitor_worker_groups.monitor_slug
|
||||
LEFT OUTER JOIN worker_groups ON monitor_worker_groups.worker_group_slug = worker_groups.slug
|
||||
WHERE monitors.deleted_at IS NULL
|
||||
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 {
|
||||
|
@ -201,13 +203,12 @@ ORDER BY monitors.name
|
|||
|
||||
var workerGroupName *string
|
||||
err = rows.Scan(
|
||||
&monitor.Slug,
|
||||
&monitor.Id,
|
||||
&monitor.Name,
|
||||
&monitor.Script,
|
||||
&monitor.Schedule,
|
||||
&monitor.CreatedAt,
|
||||
&monitor.UpdatedAt,
|
||||
&monitor.DeletedAt,
|
||||
&workerGroupName,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -215,19 +216,19 @@ ORDER BY monitors.name
|
|||
}
|
||||
if workerGroupName != nil {
|
||||
workerGroups := []string{}
|
||||
if monitors[monitor.Slug] != nil {
|
||||
workerGroups = monitors[monitor.Slug].WorkerGroups
|
||||
if monitors[monitor.Id] != nil {
|
||||
workerGroups = monitors[monitor.Id].WorkerGroups
|
||||
}
|
||||
monitor.WorkerGroups = append(workerGroups, *workerGroupName)
|
||||
}
|
||||
monitors[monitor.Slug] = monitor
|
||||
monitors[monitor.Id] = monitor
|
||||
}
|
||||
|
||||
return maps.Values(monitors), err
|
||||
}
|
||||
|
||||
func DeleteMonitorSchedule(ctx context.Context, t client.Client, slug string) error {
|
||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(slug))
|
||||
func DeleteMonitorSchedule(ctx context.Context, t client.Client, id string) error {
|
||||
schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(id))
|
||||
return schedule.Delete(ctx)
|
||||
}
|
||||
|
||||
|
@ -241,24 +242,24 @@ func CreateOrUpdateMonitorSchedule(
|
|||
|
||||
workerGroupStrings := make([]string, len(workerGroups))
|
||||
for i, group := range workerGroups {
|
||||
workerGroupStrings[i] = group.Slug
|
||||
workerGroupStrings[i] = group.Id
|
||||
}
|
||||
|
||||
args := make([]interface{}, 1)
|
||||
args[0] = workflows.MonitorWorkflowParam{
|
||||
Script: monitor.Script,
|
||||
Slug: monitor.Slug,
|
||||
WorkerGroups: workerGroupStrings,
|
||||
Script: monitor.Script,
|
||||
MonitorId: monitor.Id,
|
||||
WorkerGroupIds: workerGroupStrings,
|
||||
}
|
||||
|
||||
options := client.ScheduleOptions{
|
||||
ID: getScheduleId(monitor.Slug),
|
||||
ID: getScheduleId(monitor.Id),
|
||||
Spec: client.ScheduleSpec{
|
||||
CronExpressions: []string{monitor.Schedule},
|
||||
Jitter: time.Second * 10,
|
||||
},
|
||||
Action: &client.ScheduleWorkflowAction{
|
||||
ID: getScheduleId(monitor.Slug),
|
||||
ID: getScheduleId(monitor.Id),
|
||||
Workflow: workflows.NewWorkflows(nil).MonitorWorkflowDefinition,
|
||||
Args: args,
|
||||
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
|
||||
_, err := schedule.Describe(ctx)
|
||||
|
|
|
@ -7,18 +7,60 @@ import (
|
|||
"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
|
||||
err := db.SelectContext(ctx, &monitorHistory,
|
||||
"SELECT * FROM monitor_histories WHERE monitor_slug = $1 ORDER BY created_at DESC",
|
||||
monitorSlug,
|
||||
)
|
||||
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_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,
|
||||
)
|
||||
return err
|
||||
|
|
|
@ -10,8 +10,14 @@ import (
|
|||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func GetActiveWorkers(ctx context.Context, workerGroupSlug string, temporal client.Client) ([]string, error) {
|
||||
response, err := temporal.DescribeTaskQueue(ctx, workerGroupSlug, enums.TASK_QUEUE_TYPE_ACTIVITY)
|
||||
func CountWorkerGroups(ctx context.Context, db *sqlx.DB) (int, error) {
|
||||
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 {
|
||||
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 {
|
||||
_, err := db.NamedExecContext(ctx,
|
||||
"INSERT INTO worker_groups (slug, name) VALUES (:slug, :name)",
|
||||
"INSERT INTO worker_groups (id, name) VALUES (:id, :name)",
|
||||
workerGroup,
|
||||
)
|
||||
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,
|
||||
"UPDATE worker_groups SET deleted_at = datetime('now') WHERE slug = $1",
|
||||
slug,
|
||||
"DELETE FROM worker_groups WHERE id = $1",
|
||||
id,
|
||||
)
|
||||
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) {
|
||||
var workerGroups []*models.WorkerGroup
|
||||
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
|
||||
}
|
||||
|
@ -52,16 +58,14 @@ func GetWorkerGroupsWithMonitors(ctx context.Context, db *sqlx.DB) ([]*models.Wo
|
|||
rows, err := db.QueryContext(ctx,
|
||||
`
|
||||
SELECT
|
||||
worker_groups.slug,
|
||||
worker_groups.id,
|
||||
worker_groups.name,
|
||||
worker_groups.created_at,
|
||||
worker_groups.updated_at,
|
||||
worker_groups.deleted_at,
|
||||
monitors.name as monitor_name
|
||||
FROM worker_groups
|
||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.slug = monitor_worker_groups.worker_group_slug
|
||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_slug = monitors.slug
|
||||
WHERE worker_groups.deleted_at IS NULL AND monitors.deleted_at IS NULL
|
||||
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_id = monitors.id
|
||||
ORDER BY worker_groups.name
|
||||
`)
|
||||
if err != nil {
|
||||
|
@ -76,11 +80,10 @@ ORDER BY worker_groups.name
|
|||
|
||||
var monitorName *string
|
||||
err = rows.Scan(
|
||||
&workerGroup.Slug,
|
||||
&workerGroup.Id,
|
||||
&workerGroup.Name,
|
||||
&workerGroup.CreatedAt,
|
||||
&workerGroup.UpdatedAt,
|
||||
&workerGroup.DeletedAt,
|
||||
&monitorName,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -89,52 +92,51 @@ ORDER BY worker_groups.name
|
|||
|
||||
if monitorName != nil {
|
||||
monitors := []string{}
|
||||
if workerGroups[workerGroup.Slug] != nil {
|
||||
monitors = workerGroups[workerGroup.Slug].Monitors
|
||||
if workerGroups[workerGroup.Id] != nil {
|
||||
monitors = workerGroups[workerGroup.Id].Monitors
|
||||
}
|
||||
workerGroup.Monitors = append(monitors, *monitorName)
|
||||
}
|
||||
|
||||
workerGroups[workerGroup.Slug] = workerGroup
|
||||
workerGroups[workerGroup.Id] = workerGroup
|
||||
}
|
||||
|
||||
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
|
||||
err := db.SelectContext(ctx, &workerGroups,
|
||||
"SELECT * FROM worker_groups WHERE slug = ANY($1) AND deleted_at IS NULL",
|
||||
slugs,
|
||||
"SELECT * FROM worker_groups WHERE id = ANY($1)",
|
||||
ids,
|
||||
)
|
||||
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
|
||||
err := db.GetContext(ctx, &workerGroup,
|
||||
"SELECT * FROM worker_groups WHERE slug = $1 AND deleted_at IS NULL",
|
||||
slug,
|
||||
"SELECT * FROM worker_groups WHERE id = $1",
|
||||
id,
|
||||
)
|
||||
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,
|
||||
`
|
||||
SELECT
|
||||
worker_groups.slug,
|
||||
worker_groups.id,
|
||||
worker_groups.name,
|
||||
worker_groups.created_at,
|
||||
worker_groups.updated_at,
|
||||
worker_groups.deleted_at,
|
||||
monitors.name as monitor_name
|
||||
FROM worker_groups
|
||||
LEFT OUTER JOIN monitor_worker_groups ON worker_groups.slug = monitor_worker_groups.worker_group_slug
|
||||
LEFT OUTER JOIN monitors ON monitor_worker_groups.monitor_slug = monitors.slug
|
||||
WHERE worker_groups.slug=$1 AND worker_groups.deleted_at IS NULL AND monitors.deleted_at IS NULL
|
||||
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_id = monitors.id
|
||||
WHERE worker_groups.id=$1
|
||||
`,
|
||||
slug,
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
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() {
|
||||
var monitorName *string
|
||||
err = rows.Scan(
|
||||
&workerGroup.Slug,
|
||||
&workerGroup.Id,
|
||||
&workerGroup.Name,
|
||||
&workerGroup.CreatedAt,
|
||||
&workerGroup.UpdatedAt,
|
||||
&workerGroup.DeletedAt,
|
||||
&monitorName,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,19 +10,19 @@ import (
|
|||
)
|
||||
|
||||
type MonitorWorkflowParam struct {
|
||||
Script string
|
||||
Slug string
|
||||
WorkerGroups []string
|
||||
Script string
|
||||
MonitorId string
|
||||
WorkerGroupIds []string
|
||||
}
|
||||
|
||||
func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param MonitorWorkflowParam) error {
|
||||
workerGroups := param.WorkerGroups
|
||||
sort.Strings(workerGroups)
|
||||
func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param MonitorWorkflowParam) (models.MonitorStatus, error) {
|
||||
workerGroupIds := param.WorkerGroupIds
|
||||
sort.Strings(workerGroupIds)
|
||||
|
||||
for _, workerGroup := range workerGroups {
|
||||
for _, workerGroupId := range workerGroupIds {
|
||||
ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
||||
StartToCloseTimeout: 60 * time.Second,
|
||||
TaskQueue: workerGroup,
|
||||
TaskQueue: workerGroupId,
|
||||
})
|
||||
|
||||
heatlcheckParam := activities.HealtcheckParam{
|
||||
|
@ -32,7 +32,7 @@ func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param Monito
|
|||
var monitorResult *activities.MonitorResult
|
||||
err := workflow.ExecuteActivity(ctx, w.activities.Monitor, heatlcheckParam).Get(ctx, &monitorResult)
|
||||
if err != nil {
|
||||
return err
|
||||
return models.MonitorUnknown, err
|
||||
}
|
||||
|
||||
status := models.MonitorFailure
|
||||
|
@ -41,18 +41,18 @@ func (w *Workflows) MonitorWorkflowDefinition(ctx workflow.Context, param Monito
|
|||
}
|
||||
|
||||
historyParam := activities.HealtcheckAddToHistoryParam{
|
||||
Slug: param.Slug,
|
||||
Status: status,
|
||||
Note: monitorResult.Note,
|
||||
WorkerGroup: workerGroup,
|
||||
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 err
|
||||
return models.MonitorUnknown, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return models.MonitorSuccess, nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package api
|
||||
|
||||
import "code.tjo.space/mentos1386/zdravko/database/models"
|
||||
|
||||
type ApiV1MonitorsHistoryPOSTBody struct {
|
||||
Status string `json:"status"`
|
||||
Note string `json:"note"`
|
||||
WorkerGroup string `json:"worker_group"`
|
||||
Status models.MonitorStatus `json:"status"`
|
||||
Note string `json:"note"`
|
||||
WorkerGroupId string `json:"worker_group"`
|
||||
}
|
||||
|
|
|
@ -48,16 +48,16 @@ func Routes(
|
|||
settings.GET("/monitors", h.SettingsMonitorsGET)
|
||||
settings.GET("/monitors/create", h.SettingsMonitorsCreateGET)
|
||||
settings.POST("/monitors/create", h.SettingsMonitorsCreatePOST)
|
||||
settings.GET("/monitors/:slug", h.SettingsMonitorsDescribeGET)
|
||||
settings.POST("/monitors/:slug", h.SettingsMonitorsDescribePOST)
|
||||
settings.GET("/monitors/:slug/delete", h.SettingsMonitorsDescribeDELETE)
|
||||
settings.GET("/monitors/:slug/disable", h.SettingsMonitorsDisableGET)
|
||||
settings.GET("/monitors/:slug/enable", h.SettingsMonitorsEnableGET)
|
||||
settings.GET("/monitors/:id", h.SettingsMonitorsDescribeGET)
|
||||
settings.POST("/monitors/:id", h.SettingsMonitorsDescribePOST)
|
||||
settings.GET("/monitors/:id/delete", h.SettingsMonitorsDescribeDELETE)
|
||||
settings.GET("/monitors/:id/disable", h.SettingsMonitorsDisableGET)
|
||||
settings.GET("/monitors/:id/enable", h.SettingsMonitorsEnableGET)
|
||||
settings.GET("/worker-groups", h.SettingsWorkerGroupsGET)
|
||||
settings.GET("/worker-groups/create", h.SettingsWorkerGroupsCreateGET)
|
||||
settings.POST("/worker-groups/create", h.SettingsWorkerGroupsCreatePOST)
|
||||
settings.GET("/worker-groups/:slug", h.SettingsWorkerGroupsDescribeGET)
|
||||
settings.GET("/worker-groups/:slug/delete", h.SettingsWorkerGroupsDescribeDELETE)
|
||||
settings.GET("/worker-groups/:id", h.SettingsWorkerGroupsDescribeGET)
|
||||
settings.GET("/worker-groups/:id/delete", h.SettingsWorkerGroupsDescribeDELETE)
|
||||
|
||||
settings.Match([]string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"}, "/temporal*", h.Temporal)
|
||||
|
||||
|
@ -71,7 +71,7 @@ func Routes(
|
|||
apiv1 := e.Group("/api/v1")
|
||||
apiv1.Use(h.Authenticated)
|
||||
apiv1.GET("/workers/connect", h.ApiV1WorkersConnectGET)
|
||||
apiv1.POST("/monitors/:slug/history", h.ApiV1MonitorsHistoryPOST)
|
||||
apiv1.POST("/monitors/:id/history", h.ApiV1MonitorsHistoryPOST)
|
||||
|
||||
// Error handler
|
||||
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
||||
|
|
|
@ -1677,14 +1677,6 @@ code {
|
|||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:ml-2 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.sm\:mt-0 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.sm\:w-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" }}
|
||||
<div class="container max-w-screen-md flex flex-col mt-20">
|
||||
{{ if eq .MonitorsLength 0 }}
|
||||
|
@ -136,11 +100,32 @@
|
|||
{{ end }}
|
||||
<p>{{ .Name }}</p>
|
||||
</div>
|
||||
{{ if eq $.TimeRange "90days" }}
|
||||
{{ template "daily" . }}
|
||||
{{ else }}
|
||||
{{ template "hourly" . }}
|
||||
{{ end }}
|
||||
<div class="justify-self-end text-sm">
|
||||
{{ .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 }}
|
||||
<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">
|
||||
{{ 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>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
|
|
@ -96,9 +96,7 @@
|
|||
{{ .Schedule }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="/settings/monitors/{{ .Slug }}" class="link"
|
||||
>Details</a
|
||||
>
|
||||
<a href="/settings/monitors/{{ .Id }}" class="link">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{ define "settings" }}
|
||||
<section class="p-5">
|
||||
<form action="/settings/monitors/{{ .Monitor.Slug }}" method="post">
|
||||
<form action="/settings/monitors/{{ .Monitor.Id }}" method="post">
|
||||
<h2>Configuration</h2>
|
||||
<label for="workergroups">Worker Groups</label>
|
||||
<input
|
||||
|
@ -70,13 +70,13 @@
|
|||
{{ if eq .Monitor.Status "ACTIVE" }}
|
||||
<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"
|
||||
href="/settings/monitors/{{ .Monitor.Slug }}/disable"
|
||||
href="/settings/monitors/{{ .Monitor.Id }}/disable"
|
||||
>Pause</a
|
||||
>
|
||||
{{ else if eq .Monitor.Status "PAUSED" }}
|
||||
<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"
|
||||
href="/settings/monitors/{{ .Monitor.Slug }}/enable"
|
||||
href="/settings/monitors/{{ .Monitor.Id }}/enable"
|
||||
>Resume</a
|
||||
>
|
||||
{{ end }}
|
||||
|
@ -87,7 +87,7 @@
|
|||
<p class="text-sm mb-2">Permanently delete this monitor.</p>
|
||||
<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"
|
||||
href="/settings/monitors/{{ .Monitor.Slug }}/delete"
|
||||
href="/settings/monitors/{{ .Monitor.Id }}/delete"
|
||||
>Delete</a
|
||||
>
|
||||
</section>
|
||||
|
@ -102,6 +102,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Worker Group</th>
|
||||
<th>Created At</th>
|
||||
<th>Duration</th>
|
||||
<th>Note</th>
|
||||
|
@ -122,7 +123,14 @@
|
|||
</span>
|
||||
</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>{ .Duration }</td>
|
||||
<td class="whitespace-normal">
|
||||
|
|
|
@ -6,33 +6,90 @@
|
|||
Hi there, {{ .User.Email }}.
|
||||
</h1>
|
||||
<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
|
||||
tempor incididunt ut labore et dolore magna aliqua.
|
||||
Welcome to the settings page. Here you can manage your worker groups,
|
||||
monitors, and notifications.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-screen-xl flex flex-col sm:flex-row gap-4">
|
||||
<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>
|
||||
<p class="text-3xl font-bold text-black">42</p>
|
||||
<h3 class="text-sm leading-6 font-medium text-gray-400">
|
||||
Total Worker Groups
|
||||
</h3>
|
||||
<p class="text-3xl font-bold text-black">{{ .WorkerGroupsCount }}</p>
|
||||
</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 Monitors
|
||||
</h3>
|
||||
<p class="text-3xl font-bold text-black">42</p>
|
||||
<p class="text-3xl font-bold text-black">{{ .MonitorsCount }}</p>
|
||||
</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 Notifications
|
||||
</h3>
|
||||
<p class="text-3xl font-bold text-black">42</p>
|
||||
<p class="text-3xl font-bold text-black">{{ .NotificationsCount }}</p>
|
||||
</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 }}
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
{{ len .Monitors }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="/settings/worker-groups/{{ .Slug }}" class="link"
|
||||
<a href="/settings/worker-groups/{{ .Id }}" class="link"
|
||||
>Details</a
|
||||
>
|
||||
</td>
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
</p>
|
||||
<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"
|
||||
href="/settings/worker-groups/{{ .Worker.Slug }}/delete"
|
||||
href="/settings/worker-groups/{{ .Worker.Id }}/delete"
|
||||
>Delete</a
|
||||
>
|
||||
</section>
|
||||
|
|
Loading…
Reference in a new issue