diff --git a/cmd/server/main.go b/cmd/server/main.go index 754af4f..11db40f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -56,7 +56,7 @@ func main() { r.HandleFunc("/settings/healthchecks", h.Authenticated(h.SettingsHealthchecksGET)).Methods("GET") r.HandleFunc("/settings/healthchecks/create", h.Authenticated(h.SettingsHealthchecksCreateGET)).Methods("GET") r.HandleFunc("/settings/healthchecks/create", h.Authenticated(h.SettingsHealthchecksCreatePOST)).Methods("POST") - r.HandleFunc("/settings/healthchecks/{id}", h.Authenticated(h.SettingsHealthchecksDescribeGET)).Methods("GET") + r.HandleFunc("/settings/healthchecks/{slug}", h.Authenticated(h.SettingsHealthchecksDescribeGET)).Methods("GET") // OAuth2 r.HandleFunc("/oauth2/login", h.OAuth2LoginGET).Methods("GET") diff --git a/go.mod b/go.mod index 142d77b..1837468 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,8 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gosimple/slug v1.13.1 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect diff --git a/go.sum b/go.sum index 010122d..e72e794 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,10 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= diff --git a/internal/activities/healthcheck.go b/internal/activities/healthcheck.go index a81ba7e..e618148 100644 --- a/internal/activities/healthcheck.go +++ b/internal/activities/healthcheck.go @@ -2,6 +2,7 @@ package activities import ( "context" + "log" "net/http" ) @@ -11,7 +12,7 @@ type HealtcheckHttpActivityParam struct { } type HealthcheckHttpActivityResult struct { - Success bool + StatusCode int } func HealthcheckHttpActivityDefinition(ctx context.Context, param HealtcheckHttpActivityParam) (*HealthcheckHttpActivityResult, error) { @@ -35,5 +36,7 @@ func HealthcheckHttpActivityDefinition(ctx context.Context, param HealtcheckHttp return nil, err } - return &HealthcheckHttpActivityResult{Success: response.StatusCode == 200}, nil + log.Printf("HealthcheckHttpActivityDefinition produced statuscode %d for url %s", response.StatusCode, param.Url) + + return &HealthcheckHttpActivityResult{StatusCode: response.StatusCode}, nil } diff --git a/internal/handlers/settingshealthchecks.go b/internal/handlers/settingshealthchecks.go index 0d9e93e..4225a1c 100644 --- a/internal/handlers/settingshealthchecks.go +++ b/internal/handlers/settingshealthchecks.go @@ -3,9 +3,7 @@ package handlers import ( "context" "fmt" - "log" "net/http" - "strconv" "text/template" "code.tjo.space/mentos1386/zdravko/internal/models" @@ -13,6 +11,7 @@ import ( "code.tjo.space/mentos1386/zdravko/web/templates" "code.tjo.space/mentos1386/zdravko/web/templates/components" "github.com/gorilla/mux" + "github.com/gosimple/slug" ) type SettingsHealthchecks struct { @@ -58,10 +57,7 @@ func (h *BaseHandler) SettingsHealthchecksGET(w http.ResponseWriter, r *http.Req func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) { vars := mux.Vars(r) - id, err := strconv.ParseUint(vars["id"], 10, 32) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + slug := vars["slug"] ts, err := template.ParseFS(templates.Templates, "components/base.tmpl", @@ -73,7 +69,7 @@ func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r * return } - healthcheck, err := services.GetHealthcheckHttp(context.Background(), h.query, uint(id)) + healthcheck, err := services.GetHealthcheckHttp(context.Background(), h.query, slug) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } @@ -85,7 +81,7 @@ func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r * []*components.Page{ GetPageByTitle(SettingsPages, "Healthchecks"), { - Path: fmt.Sprintf("/settings/healthchecks/%d", id), + Path: fmt.Sprintf("/settings/healthchecks/%s", slug), Title: "Describe", Breadcrumb: healthcheck.Name, }, @@ -124,24 +120,27 @@ func (h *BaseHandler) SettingsHealthchecksCreateGET(w http.ResponseWriter, r *ht func (h *BaseHandler) SettingsHealthchecksCreatePOST(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) { ctx := context.Background() + healthcheckHttp := &models.HealthcheckHttp{ + Healthcheck: models.Healthcheck{ + Name: r.FormValue("name"), + Slug: slug.Make(r.FormValue("name")), + Schedule: r.FormValue("schedule"), + }, + Url: r.FormValue("url"), + Method: r.FormValue("method"), + } + err := services.CreateHealthcheckHttp( ctx, h.db, - &models.HealthcheckHttp{ - Healthcheck: models.Healthcheck{ - Name: r.FormValue("name"), - Schedule: r.FormValue("schedule"), - }, - URL: r.FormValue("url"), - Method: r.FormValue("method"), - }) + healthcheckHttp, + ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } - err = services.StartHealthcheckHttp(ctx, h.temporal) + err = services.StartHealthcheckHttp(ctx, h.temporal, healthcheckHttp) if err != nil { - log.Println("Error starting healthcheck workflow", err) http.Error(w, err.Error(), http.StatusInternalServerError) } diff --git a/internal/models/models.go b/internal/models/models.go index dbbcfad..1458adb 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -13,6 +13,7 @@ type OAuth2State struct { type Healthcheck struct { gorm.Model + Slug string `gorm:"unique"` Name string `gorm:"unique"` Status string // UP, DOWN UptimePercentage float64 @@ -22,7 +23,7 @@ type Healthcheck struct { type HealthcheckHttp struct { gorm.Model Healthcheck - URL string + Url string Method string } @@ -35,6 +36,7 @@ type HealthcheckTcp struct { type Cronjob struct { gorm.Model + Slug string `gorm:"unique"` Name string `gorm:"unique"` Schedule string Buffer int diff --git a/internal/models/query/cronjobs.gen.go b/internal/models/query/cronjobs.gen.go index fa1732c..27c1dc4 100644 --- a/internal/models/query/cronjobs.gen.go +++ b/internal/models/query/cronjobs.gen.go @@ -31,6 +31,7 @@ func newCronjob(db *gorm.DB, opts ...gen.DOOption) cronjob { _cronjob.CreatedAt = field.NewTime(tableName, "created_at") _cronjob.UpdatedAt = field.NewTime(tableName, "updated_at") _cronjob.DeletedAt = field.NewField(tableName, "deleted_at") + _cronjob.Slug = field.NewString(tableName, "slug") _cronjob.Name = field.NewString(tableName, "name") _cronjob.Schedule = field.NewString(tableName, "schedule") _cronjob.Buffer = field.NewInt(tableName, "buffer") @@ -48,6 +49,7 @@ type cronjob struct { CreatedAt field.Time UpdatedAt field.Time DeletedAt field.Field + Slug field.String Name field.String Schedule field.String Buffer field.Int @@ -71,6 +73,7 @@ func (c *cronjob) updateTableName(table string) *cronjob { c.CreatedAt = field.NewTime(table, "created_at") c.UpdatedAt = field.NewTime(table, "updated_at") c.DeletedAt = field.NewField(table, "deleted_at") + c.Slug = field.NewString(table, "slug") c.Name = field.NewString(table, "name") c.Schedule = field.NewString(table, "schedule") c.Buffer = field.NewInt(table, "buffer") @@ -98,11 +101,12 @@ func (c *cronjob) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (c *cronjob) fillFieldMap() { - c.fieldMap = make(map[string]field.Expr, 7) + c.fieldMap = make(map[string]field.Expr, 8) c.fieldMap["id"] = c.ID c.fieldMap["created_at"] = c.CreatedAt c.fieldMap["updated_at"] = c.UpdatedAt c.fieldMap["deleted_at"] = c.DeletedAt + c.fieldMap["slug"] = c.Slug c.fieldMap["name"] = c.Name c.fieldMap["schedule"] = c.Schedule c.fieldMap["buffer"] = c.Buffer diff --git a/internal/models/query/healthcheck_https.gen.go b/internal/models/query/healthcheck_https.gen.go index d0e9b71..227393f 100644 --- a/internal/models/query/healthcheck_https.gen.go +++ b/internal/models/query/healthcheck_https.gen.go @@ -31,11 +31,12 @@ func newHealthcheckHttp(db *gorm.DB, opts ...gen.DOOption) healthcheckHttp { _healthcheckHttp.CreatedAt = field.NewTime(tableName, "created_at") _healthcheckHttp.UpdatedAt = field.NewTime(tableName, "updated_at") _healthcheckHttp.DeletedAt = field.NewField(tableName, "deleted_at") + _healthcheckHttp.Slug = field.NewString(tableName, "slug") _healthcheckHttp.Name = field.NewString(tableName, "name") _healthcheckHttp.Status = field.NewString(tableName, "status") _healthcheckHttp.UptimePercentage = field.NewFloat64(tableName, "uptime_percentage") _healthcheckHttp.Schedule = field.NewString(tableName, "schedule") - _healthcheckHttp.URL = field.NewString(tableName, "url") + _healthcheckHttp.Url = field.NewString(tableName, "url") _healthcheckHttp.Method = field.NewString(tableName, "method") _healthcheckHttp.fillFieldMap() @@ -51,11 +52,12 @@ type healthcheckHttp struct { CreatedAt field.Time UpdatedAt field.Time DeletedAt field.Field + Slug field.String Name field.String Status field.String UptimePercentage field.Float64 Schedule field.String - URL field.String + Url field.String Method field.String fieldMap map[string]field.Expr @@ -77,11 +79,12 @@ func (h *healthcheckHttp) updateTableName(table string) *healthcheckHttp { h.CreatedAt = field.NewTime(table, "created_at") h.UpdatedAt = field.NewTime(table, "updated_at") h.DeletedAt = field.NewField(table, "deleted_at") + h.Slug = field.NewString(table, "slug") h.Name = field.NewString(table, "name") h.Status = field.NewString(table, "status") h.UptimePercentage = field.NewFloat64(table, "uptime_percentage") h.Schedule = field.NewString(table, "schedule") - h.URL = field.NewString(table, "url") + h.Url = field.NewString(table, "url") h.Method = field.NewString(table, "method") h.fillFieldMap() @@ -111,16 +114,17 @@ func (h *healthcheckHttp) GetFieldByName(fieldName string) (field.OrderExpr, boo } func (h *healthcheckHttp) fillFieldMap() { - h.fieldMap = make(map[string]field.Expr, 10) + h.fieldMap = make(map[string]field.Expr, 11) h.fieldMap["id"] = h.ID h.fieldMap["created_at"] = h.CreatedAt h.fieldMap["updated_at"] = h.UpdatedAt h.fieldMap["deleted_at"] = h.DeletedAt + h.fieldMap["slug"] = h.Slug h.fieldMap["name"] = h.Name h.fieldMap["status"] = h.Status h.fieldMap["uptime_percentage"] = h.UptimePercentage h.fieldMap["schedule"] = h.Schedule - h.fieldMap["url"] = h.URL + h.fieldMap["url"] = h.Url h.fieldMap["method"] = h.Method } diff --git a/internal/models/query/healthcheck_tcps.gen.go b/internal/models/query/healthcheck_tcps.gen.go index 9605a8f..eb7b169 100644 --- a/internal/models/query/healthcheck_tcps.gen.go +++ b/internal/models/query/healthcheck_tcps.gen.go @@ -31,6 +31,7 @@ func newHealthcheckTcp(db *gorm.DB, opts ...gen.DOOption) healthcheckTcp { _healthcheckTcp.CreatedAt = field.NewTime(tableName, "created_at") _healthcheckTcp.UpdatedAt = field.NewTime(tableName, "updated_at") _healthcheckTcp.DeletedAt = field.NewField(tableName, "deleted_at") + _healthcheckTcp.Slug = field.NewString(tableName, "slug") _healthcheckTcp.Name = field.NewString(tableName, "name") _healthcheckTcp.Status = field.NewString(tableName, "status") _healthcheckTcp.UptimePercentage = field.NewFloat64(tableName, "uptime_percentage") @@ -51,6 +52,7 @@ type healthcheckTcp struct { CreatedAt field.Time UpdatedAt field.Time DeletedAt field.Field + Slug field.String Name field.String Status field.String UptimePercentage field.Float64 @@ -77,6 +79,7 @@ func (h *healthcheckTcp) updateTableName(table string) *healthcheckTcp { h.CreatedAt = field.NewTime(table, "created_at") h.UpdatedAt = field.NewTime(table, "updated_at") h.DeletedAt = field.NewField(table, "deleted_at") + h.Slug = field.NewString(table, "slug") h.Name = field.NewString(table, "name") h.Status = field.NewString(table, "status") h.UptimePercentage = field.NewFloat64(table, "uptime_percentage") @@ -111,11 +114,12 @@ func (h *healthcheckTcp) GetFieldByName(fieldName string) (field.OrderExpr, bool } func (h *healthcheckTcp) fillFieldMap() { - h.fieldMap = make(map[string]field.Expr, 10) + h.fieldMap = make(map[string]field.Expr, 11) h.fieldMap["id"] = h.ID h.fieldMap["created_at"] = h.CreatedAt h.fieldMap["updated_at"] = h.UpdatedAt h.fieldMap["deleted_at"] = h.DeletedAt + h.fieldMap["slug"] = h.Slug h.fieldMap["name"] = h.Name h.fieldMap["status"] = h.Status h.fieldMap["uptime_percentage"] = h.UptimePercentage diff --git a/internal/services/healthcheck.go b/internal/services/healthcheck.go index 6ca02bd..3bae49d 100644 --- a/internal/services/healthcheck.go +++ b/internal/services/healthcheck.go @@ -8,6 +8,7 @@ import ( "code.tjo.space/mentos1386/zdravko/internal/models/query" "code.tjo.space/mentos1386/zdravko/internal/workflows" "go.temporal.io/sdk/client" + "go.temporal.io/sdk/temporal" "gorm.io/gorm" ) @@ -15,29 +16,32 @@ func CreateHealthcheckHttp(ctx context.Context, db *gorm.DB, healthcheck *models return db.WithContext(ctx).Create(healthcheck).Error } -func GetHealthcheckHttp(ctx context.Context, q *query.Query, id uint) (*models.HealthcheckHttp, error) { +func GetHealthcheckHttp(ctx context.Context, q *query.Query, slug string) (*models.HealthcheckHttp, error) { log.Println("GetHealthcheckHttp") return q.HealthcheckHttp.WithContext(ctx).Where( - q.HealthcheckHttp.ID.Eq(id), + q.HealthcheckHttp.Slug.Eq(slug), ).First() } -func StartHealthcheckHttp(ctx context.Context, t client.Client) error { +func StartHealthcheckHttp(ctx context.Context, t client.Client, healthcheckHttp *models.HealthcheckHttp) error { log.Println("Starting HealthcheckHttp Workflow") args := make([]interface{}, 0) - args = append(args, workflows.HealthcheckHttpWorkflowParam{Id: 1}) + args = append(args, workflows.HealthcheckHttpWorkflowParam{Url: healthcheckHttp.Url, Method: healthcheckHttp.Method}) _, err := t.ScheduleClient().Create(ctx, client.ScheduleOptions{ - ID: "healthcheck-http-id", + ID: "healthcheck-http-" + healthcheckHttp.Slug, Spec: client.ScheduleSpec{ - CronExpressions: []string{"0 * * * *"}, + CronExpressions: []string{healthcheckHttp.Schedule}, }, Action: &client.ScheduleWorkflowAction{ ID: "healthcheck-http-id-workflow", Workflow: workflows.HealthcheckHttpWorkflowDefinition, Args: args, TaskQueue: "default", + RetryPolicy: &temporal.RetryPolicy{ + MaximumAttempts: 3, + }, }, }) diff --git a/internal/workflows/healthcheck.go b/internal/workflows/healthcheck.go index fac6570..8c25455 100644 --- a/internal/workflows/healthcheck.go +++ b/internal/workflows/healthcheck.go @@ -1,6 +1,7 @@ package workflows import ( + "fmt" "time" "code.tjo.space/mentos1386/zdravko/internal/activities" @@ -8,7 +9,8 @@ import ( ) type HealthcheckHttpWorkflowParam struct { - Id uint + Url string + Method string } func HealthcheckHttpWorkflowDefinition(ctx workflow.Context, param HealthcheckHttpWorkflowParam) error { @@ -18,12 +20,19 @@ func HealthcheckHttpWorkflowDefinition(ctx workflow.Context, param HealthcheckHt ctx = workflow.WithActivityOptions(ctx, options) activityParam := activities.HealtcheckHttpActivityParam{ - Url: "https://google.com", - Method: "GET", + Url: param.Url, + Method: param.Method, } var result *activities.HealthcheckHttpActivityResult err := workflow.ExecuteActivity(ctx, activities.HealthcheckHttpActivityDefinition, activityParam).Get(ctx, &result) + if err != nil { + return err + } - return err + if result.StatusCode != 200 { + return fmt.Errorf("HealthcheckHttpActivityDefinition produced statuscode %d for url %s", result.StatusCode, param.Url) + } + + return nil } diff --git a/web/templates/pages/settings_healthchecks.tmpl b/web/templates/pages/settings_healthchecks.tmpl index 2953ecb..f08ef94 100644 --- a/web/templates/pages/settings_healthchecks.tmpl +++ b/web/templates/pages/settings_healthchecks.tmpl @@ -70,7 +70,7 @@ {{.Schedule}}