diff --git a/database/models/models.go b/database/models/models.go index 3c5b0d8..99c4f97 100644 --- a/database/models/models.go +++ b/database/models/models.go @@ -58,8 +58,9 @@ type Monitor struct { CreatedAt *Time `db:"created_at"` UpdatedAt *Time `db:"updated_at"` - Id string `db:"id"` - Name string `db:"name"` + Id string `db:"id"` + Name string `db:"name"` + Group string `db:"group"` Schedule string `db:"schedule"` Script string `db:"script"` diff --git a/database/sqlite/migrations/2024-02-27-initial.sql b/database/sqlite/migrations/2024-02-27-initial.sql index 9c9db35..a052158 100644 --- a/database/sqlite/migrations/2024-02-27-initial.sql +++ b/database/sqlite/migrations/2024-02-27-initial.sql @@ -9,6 +9,7 @@ CREATE TABLE oauth2_states ( CREATE TABLE monitors ( id TEXT NOT NULL, name TEXT NOT NULL, + "group" TEXT NOT NULL DEFAULT 'default', schedule TEXT NOT NULL, script TEXT NOT NULL, diff --git a/deploy/fly.toml b/deploy/fly.toml index ea13837..61f0895 100644 --- a/deploy/fly.toml +++ b/deploy/fly.toml @@ -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-7.db' - DATABASE_PATH = '/data/zdravko-7.db' + TEMPORAL_DATABASE_PATH = '/data/temporal-8.db' + DATABASE_PATH = '/data/zdravko-8.db' [processes] server = '--temporal --server' diff --git a/internal/handlers/index.go b/internal/handlers/index.go index a3d9758..0352010 100644 --- a/internal/handlers/index.go +++ b/internal/handlers/index.go @@ -13,14 +13,15 @@ import ( type IndexData struct { *components.Base - HealthChecks []*HealthCheck + Monitors map[string][]*Monitor MonitorsLength int TimeRange string Status models.MonitorStatus } -type HealthCheck struct { +type Monitor struct { Name string + Group string Status models.MonitorStatus History *History } @@ -96,7 +97,7 @@ func (h *BaseHandler) Index(c echo.Context) error { overallStatus := models.MonitorSuccess - monitorsWithHistory := make([]*HealthCheck, len(monitors)) + monitorsWithHistory := make([]*Monitor, len(monitors)) for i, monitor := range monitors { history, err := services.GetMonitorHistoryForMonitor(ctx, h.db, monitor.Id) if err != nil { @@ -118,21 +119,26 @@ func (h *BaseHandler) Index(c echo.Context) error { overallStatus = status } - monitorsWithHistory[i] = &HealthCheck{ + monitorsWithHistory[i] = &Monitor{ Name: monitor.Name, + Group: monitor.Group, Status: status, History: historyResult, } } + monitorsByGroup := map[string][]*Monitor{} + for _, monitor := range monitorsWithHistory { + monitorsByGroup[monitor.Group] = append(monitorsByGroup[monitor.Group], monitor) + } + return c.Render(http.StatusOK, "index.tmpl", &IndexData{ Base: &components.Base{ NavbarActive: GetPageByTitle(Pages, "Status"), Navbar: Pages, }, - HealthChecks: monitorsWithHistory, - MonitorsLength: len(monitors), - TimeRange: timeRange, - Status: overallStatus, + Monitors: monitorsByGroup, + TimeRange: timeRange, + Status: overallStatus, }) } diff --git a/internal/handlers/settingsmonitors.go b/internal/handlers/settingsmonitors.go index 4a50b2c..1977690 100644 --- a/internal/handlers/settingsmonitors.go +++ b/internal/handlers/settingsmonitors.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "net/http" + "slices" "strings" "code.tjo.space/mentos1386/zdravko/database/models" @@ -17,12 +18,14 @@ import ( type CreateMonitor struct { Name string `validate:"required"` + Group string `validate:"required"` WorkerGroups string `validate:"required"` Schedule string `validate:"required,cron"` Script string `validate:"required"` } type UpdateMonitor struct { + Group string `validate:"required"` WorkerGroups string `validate:"required"` Schedule string `validate:"required,cron"` Script string `validate:"required"` @@ -35,7 +38,8 @@ type MonitorWithWorkerGroupsAndStatus struct { type SettingsMonitors struct { *Settings - Monitors []*MonitorWithWorkerGroupsAndStatus + Monitors map[string][]*MonitorWithWorkerGroupsAndStatus + MonitorGroups []string } type SettingsMonitor struct { @@ -64,13 +68,23 @@ func (h *BaseHandler) SettingsMonitorsGET(c echo.Context) error { } } + monitorGroups := []string{} + monitorsByGroup := map[string][]*MonitorWithWorkerGroupsAndStatus{} + for _, monitor := range monitorsWithStatus { + monitorsByGroup[monitor.Group] = append(monitorsByGroup[monitor.Group], monitor) + if slices.Contains(monitorGroups, monitor.Group) == false { + monitorGroups = append(monitorGroups, monitor.Group) + } + } + return c.Render(http.StatusOK, "settings_monitors.tmpl", &SettingsMonitors{ Settings: NewSettings( cc.Principal.User, GetPageByTitle(SettingsPages, "Monitors"), []*components.Page{GetPageByTitle(SettingsPages, "Monitors")}, ), - Monitors: monitorsWithStatus, + Monitors: monitorsByGroup, + MonitorGroups: monitorGroups, }) } @@ -174,6 +188,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error { monitorId := c.Param("id") update := UpdateMonitor{ + Group: c.FormValue("group"), WorkerGroups: strings.TrimSpace(c.FormValue("workergroups")), Schedule: c.FormValue("schedule"), Script: c.FormValue("script"), @@ -187,6 +202,7 @@ func (h *BaseHandler) SettingsMonitorsDescribePOST(c echo.Context) error { if err != nil { return err } + monitor.Group = update.Group monitor.Schedule = update.Schedule monitor.Script = update.Script @@ -251,6 +267,7 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error { create := CreateMonitor{ Name: c.FormValue("name"), + Group: c.FormValue("group"), Schedule: c.FormValue("schedule"), WorkerGroups: c.FormValue("workergroups"), Script: c.FormValue("script"), @@ -282,6 +299,7 @@ func (h *BaseHandler) SettingsMonitorsCreatePOST(c echo.Context) error { monitor := &models.Monitor{ Name: create.Name, + Group: create.Group, Id: monitorId, Schedule: create.Schedule, Script: create.Script, diff --git a/internal/services/monitor.go b/internal/services/monitor.go index 2d41892..9885076 100644 --- a/internal/services/monitor.go +++ b/internal/services/monitor.go @@ -62,7 +62,7 @@ func SetMonitorStatus(ctx context.Context, temporal client.Client, id string, st func CreateMonitor(ctx context.Context, db *sqlx.DB, monitor *models.Monitor) error { _, err := db.NamedExecContext(ctx, - "INSERT INTO monitors (id, name, script, schedule) VALUES (:id, :name, :script, :schedule)", + `INSERT INTO monitors (id, name, "group", script, schedule) VALUES (:id, :name, :group, :script, :schedule)`, monitor, ) return err @@ -70,7 +70,7 @@ 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 id=:id", + `UPDATE monitors SET "group"=:group, script=:script, schedule=:schedule WHERE id=:id`, monitor, ) return err @@ -126,6 +126,7 @@ func GetMonitorWithWorkerGroups(ctx context.Context, db *sqlx.DB, id string) (*m SELECT monitors.id, monitors.name, + monitors."group", monitors.script, monitors.schedule, monitors.created_at, @@ -150,6 +151,7 @@ WHERE monitors.id=$1 err = rows.Scan( &monitor.Id, &monitor.Name, + &monitor.Group, &monitor.Script, &monitor.Schedule, &monitor.CreatedAt, @@ -181,6 +183,7 @@ func GetMonitorsWithWorkerGroups(ctx context.Context, db *sqlx.DB) ([]*models.Mo SELECT monitors.id, monitors.name, + monitors."group", monitors.script, monitors.schedule, monitors.created_at, @@ -205,6 +208,7 @@ ORDER BY monitors.name err = rows.Scan( &monitor.Id, &monitor.Name, + &monitor.Group, &monitor.Script, &monitor.Schedule, &monitor.CreatedAt, diff --git a/web/static/css/main.css b/web/static/css/main.css index 1beb704..2958d7f 100644 --- a/web/static/css/main.css +++ b/web/static/css/main.css @@ -56,10 +56,10 @@ code { @apply bg-blue-700 text-white; } -.monitors { - @apply grid justify-items-stretch justify-stretch items-center mt-20 bg-white shadow-md p-5 rounded-lg; +.monitors .monitors-list { + @apply grid justify-items-stretch justify-stretch items-center bg-white shadow-md p-5 py-2 rounded-lg; } -.monitors > div:not(:last-child) { +.monitors .monitors-list > div:not(:last-child) { @apply mb-3; } .monitors .time-range > a { @@ -113,9 +113,16 @@ code { .settings section table thead th { @apply px-6 py-4 text-center text-xs font-semibold text-gray-600 uppercase tracking-wider; } +.settings section table tbody tr { + @apply odd:bg-white even:bg-gray-50; +} .settings section table tbody tr th { @apply px-6 py-4 font-medium text-gray-900 whitespace-nowrap text-center; } .settings section table tbody tr td { @apply px-6 py-4 text-center whitespace-nowrap; } +.settings section table tbody tr.row-special { + @apply bg-gray-100; + @apply font-semibold text-xs uppercase tracking-wider; +} diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index d318d87..e36ebdb 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -697,6 +697,10 @@ video { height: 1.25rem; } +.h-6 { + height: 1.5rem; +} + .h-8 { height: 2rem; } @@ -731,6 +735,10 @@ video { width: 1.25rem; } +.w-6 { + width: 1.5rem; +} + .w-fit { width: -moz-fit-content; width: fit-content; @@ -796,6 +804,10 @@ video { gap: 0.5rem; } +.gap-20 { + gap: 5rem; +} + .gap-4 { gap: 1rem; } @@ -1039,11 +1051,21 @@ video { line-height: 1.5rem; } +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + .text-sm { font-size: 0.875rem; line-height: 1.25rem; } +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + .text-xs { font-size: 0.75rem; line-height: 1rem; @@ -1322,8 +1344,7 @@ code { color: rgb(255 255 255 / var(--tw-text-opacity)); } -.monitors { - margin-top: 5rem; +.monitors .monitors-list { display: grid; align-items: center; justify-content: stretch; @@ -1332,12 +1353,14 @@ code { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); padding: 1.25rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.monitors > div:not(:last-child) { +.monitors .monitors-list > div:not(:last-child) { margin-bottom: 0.75rem; } @@ -1581,6 +1604,16 @@ code { color: rgb(75 85 99 / var(--tw-text-opacity)); } +.settings section table tbody tr:nth-child(odd) { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.settings section table tbody tr:nth-child(even) { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + .settings section table tbody tr th { white-space: nowrap; padding-left: 1.5rem; @@ -1602,6 +1635,16 @@ code { text-align: center; } +.settings section table tbody tr.row-special { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); + font-size: 0.75rem; + line-height: 1rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + .hover\:bg-blue-800:hover { --tw-bg-opacity: 1; background-color: rgb(30 64 175 / var(--tw-bg-opacity)); @@ -1703,6 +1746,11 @@ code { justify-self: end; } + .sm\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + .sm\:px-8 { padding-left: 2rem; padding-right: 2rem; diff --git a/web/templates/pages/index.tmpl b/web/templates/pages/index.tmpl index 3c83fb9..42954af 100644 --- a/web/templates/pages/index.tmpl +++ b/web/templates/pages/index.tmpl @@ -1,6 +1,7 @@ {{ define "main" }} -
+
{{ .Name }}
-