From d5cd8695270b67b0ca782a6bd08dcb26b33c3d7e Mon Sep 17 00:00:00 2001 From: Bart6114 Date: Thu, 30 Nov 2023 21:36:20 +0100 Subject: [PATCH 01/22] start refac to switch to sqlite --- go.mod | 1 + go.sum | 2 ++ pkg/db.go | 36 ++++++++++++++++++++++++++++++++++++ pkg/job.go | 14 +++++++++++--- pkg/job_test.go | 2 +- pkg/schedule.go | 9 ++++++--- 6 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 pkg/db.go diff --git a/go.mod b/go.mod index 3e1e8bd..2d4a208 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-sqlite3 v1.14.18 github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect diff --git a/go.sum b/go.sum index f0bae2c..679c053 100644 --- a/go.sum +++ b/go.sum @@ -1093,6 +1093,8 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= +github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= diff --git a/pkg/db.go b/pkg/db.go new file mode 100644 index 0000000..564ea73 --- /dev/null +++ b/pkg/db.go @@ -0,0 +1,36 @@ +package cheek + +import ( + "database/sql" + "fmt" + + _ "github.com/mattn/go-sqlite3" +) + +func OpenDB(dbPath string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return nil, fmt.Errorf("open db: %w", err) + } + + if err := InitDB(db); err != nil { + return nil, fmt.Errorf("init db: %w", err) + } + + return db, nil +} + +// assert that log table exists +func InitDB(db *sql.DB) error { + _, err := db.Exec(`CREATE TABLE IF NOT EXISTS log ( + id INTEGER PRIMARY KEY, + job TEXT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + message TEXT + )`) + if err != nil { + return fmt.Errorf("create log table: %w", err) + } + + return nil +} diff --git a/pkg/job.go b/pkg/job.go index 2a41730..df9aa79 100644 --- a/pkg/job.go +++ b/pkg/job.go @@ -61,6 +61,16 @@ func (jr *JobRun) flushLogBuffer() { jr.Log = jr.logBuf.String() } +func (jr *JobRun) logToDb() { + if jr.jobRef.globalSchedule.db == nil { + return + } + _, err := jr.jobRef.globalSchedule.db.Exec("INSERT INTO log (job, message) VALUES (?, ?)", jr.Name, jr.Log) + if err != nil { + jr.jobRef.log.Warn().Str("job", jr.Name).Err(err).Msg("Couldn't save job log to db.") + } +} + func (j *JobRun) logToDisk() { logFn := path.Join(CheekPath(), fmt.Sprintf("%s.job.jsonl", j.Name)) f, err := os.OpenFile(logFn, @@ -77,10 +87,8 @@ func (j *JobRun) logToDisk() { } func (j *JobSpec) finalize(jr *JobRun) { - // flush logbuf to string - jr.flushLogBuffer() // write logs to disk - jr.logToDisk() + jr.logToDb() // launch on_events j.OnEvent(jr) } diff --git a/pkg/job_test.go b/pkg/job_test.go index 5530bbd..b316674 100644 --- a/pkg/job_test.go +++ b/pkg/job_test.go @@ -27,7 +27,7 @@ func TestLoadLogs(t *testing.T) { } jr := j.execCommandWithRetry("test") - jr.logToDisk() + jr.logToDb() // log loading goes on job name basis // let's recreate diff --git a/pkg/schedule.go b/pkg/schedule.go index e8fbf8b..be32258 100644 --- a/pkg/schedule.go +++ b/pkg/schedule.go @@ -1,6 +1,7 @@ package cheek import ( + "database/sql" "fmt" "os" "os/signal" @@ -21,6 +22,7 @@ type Schedule struct { TZLocation string `yaml:"tz_location,omitempty" json:"tz_location,omitempty"` loc *time.Location log zerolog.Logger + db *sql.DB cfg Config } @@ -143,13 +145,14 @@ func (s *Schedule) now() time.Time { return time.Now().In(s.loc) } -func loadSchedule(log zerolog.Logger, cfg Config, fn string) (Schedule, error) { +func loadSchedule(log zerolog.Logger, db *sql.DB, cfg Config, fn string) (Schedule, error) { s, err := readSpecs(fn) if err != nil { return Schedule{}, err } s.log = log s.cfg = cfg + s.db = db // run validations if err := s.initialize(); err != nil { @@ -160,8 +163,8 @@ func loadSchedule(log zerolog.Logger, cfg Config, fn string) (Schedule, error) { } // RunSchedule is the main entry entrypoint of cheek. -func RunSchedule(log zerolog.Logger, cfg Config, scheduleFn string) error { - s, err := loadSchedule(log, cfg, scheduleFn) +func RunSchedule(log zerolog.Logger, db *sql.DB, cfg Config, scheduleFn string) error { + s, err := loadSchedule(log, db, cfg, scheduleFn) if err != nil { return err } From 4d86bbca7a7dfa1e56ea46deddeacc50a509c7b3 Mon Sep 17 00:00:00 2001 From: Bart6114 Date: Fri, 1 Dec 2023 06:59:10 +0100 Subject: [PATCH 02/22] preliminary log writer --- .gitignore | 3 ++- pkg/db.go | 14 ++++++++------ pkg/job.go | 35 ++++++++++++++--------------------- pkg/job_test.go | 9 ++++++--- pkg/schedule.go | 14 +++++++++++--- pkg/utils.go | 2 ++ 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index b9d6584..3bc8ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -252,4 +252,5 @@ build cheek cover.out butt -*sheep \ No newline at end of file +*sheep +*.sqlite3 \ No newline at end of file diff --git a/pkg/db.go b/pkg/db.go index 564ea73..29ccb8c 100644 --- a/pkg/db.go +++ b/pkg/db.go @@ -20,14 +20,16 @@ func OpenDB(dbPath string) (*sql.DB, error) { return db, nil } -// assert that log table exists func InitDB(db *sql.DB) error { _, err := db.Exec(`CREATE TABLE IF NOT EXISTS log ( - id INTEGER PRIMARY KEY, - job TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - message TEXT - )`) + id INTEGER PRIMARY KEY, + job TEXT, + triggered_at DATETIME, + triggered_by TEXT, + duration INTEGER, + status INTEGER, + message TEXT + )`) if err != nil { return fmt.Errorf("create log table: %w", err) } diff --git a/pkg/job.go b/pkg/job.go index df9aa79..0d83b58 100644 --- a/pkg/job.go +++ b/pkg/job.go @@ -2,7 +2,7 @@ package cheek import ( "bytes" - "encoding/json" + "database/sql" "errors" "fmt" "io" @@ -40,6 +40,7 @@ type JobSpec struct { Runs []JobRun `yaml:"runs,omitempty"` nextTick time.Time + db *sql.DB log zerolog.Logger cfg Config } @@ -62,31 +63,23 @@ func (jr *JobRun) flushLogBuffer() { } func (jr *JobRun) logToDb() { - if jr.jobRef.globalSchedule.db == nil { + if jr.jobRef.db == nil { + jr.jobRef.log.Warn().Str("job", jr.Name).Msg("No db connection, not saving job log to db.") return } - _, err := jr.jobRef.globalSchedule.db.Exec("INSERT INTO log (job, message) VALUES (?, ?)", jr.Name, jr.Log) + _, err := jr.jobRef.db.Exec("INSERT INTO log (job, triggered_at, triggered_by, duration, status, message) VALUES (?, ?, ?, ?, ?, ?)", jr.Name, jr.TriggeredAt, jr.TriggeredBy, jr.Duration, jr.Status, jr.Log) if err != nil { - jr.jobRef.log.Warn().Str("job", jr.Name).Err(err).Msg("Couldn't save job log to db.") - } -} - -func (j *JobRun) logToDisk() { - logFn := path.Join(CheekPath(), fmt.Sprintf("%s.job.jsonl", j.Name)) - f, err := os.OpenFile(logFn, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - j.jobRef.log.Warn().Str("job", j.Name).Err(err).Msgf("Can't open job log '%v' for writing", logFn) - return - } - defer f.Close() - - if err := json.NewEncoder(f).Encode(j); err != nil { - j.jobRef.log.Warn().Str("job", j.Name).Err(err).Msg("Couldn't save job log to disk.") + if jr.jobRef.globalSchedule != nil { + jr.jobRef.globalSchedule.log.Warn().Str("job", jr.Name).Err(err).Msg("Couldn't save job log to db.") + } else { + panic(err) + } } } func (j *JobSpec) finalize(jr *JobRun) { + // flush logbuf to string + jr.flushLogBuffer() // write logs to disk jr.logToDb() // launch on_events @@ -122,7 +115,7 @@ func (j *JobSpec) execCommandWithRetry(trigger string) JobRun { } func (j JobSpec) now() time.Time { - // defer for if schedule doesn't exist, allows fore easy testing + // defer for if schedule doesn't exist, allows for easy testing if j.globalSchedule != nil { return j.globalSchedule.now() } @@ -196,7 +189,7 @@ func (j *JobSpec) execCommand(trigger string) JobRun { return jr } - jr.Duration = time.Since(jr.TriggeredAt) + jr.Duration = time.Duration(time.Since(jr.TriggeredAt).Milliseconds()) jr.Status = 0 j.log.Debug().Str("job", j.Name).Int("exitcode", jr.Status).Msgf("job exited status: %v", jr.Status) diff --git a/pkg/job_test.go b/pkg/job_test.go index b316674..dd30fb2 100644 --- a/pkg/job_test.go +++ b/pkg/job_test.go @@ -14,20 +14,23 @@ import ( ) func TestLoadLogs(t *testing.T) { + db, err := OpenDB("./tmp.sqlite3") + j := &JobSpec{ Cron: "* * * * *", Name: "test", Command: []string{"echo", "bar"}, cfg: NewConfig(), + db: db, + log: NewLogger("debug", os.Stdout, os.Stdout), } - _, err := j.ToYAML(false) + _, err = j.ToYAML(false) if err != nil { t.Fatal(err) } - jr := j.execCommandWithRetry("test") - jr.logToDb() + _ = j.execCommandWithRetry("test") // log loading goes on job name basis // let's recreate diff --git a/pkg/schedule.go b/pkg/schedule.go index be32258..94c90af 100644 --- a/pkg/schedule.go +++ b/pkg/schedule.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/signal" + "path" "strings" "syscall" "time" @@ -125,6 +126,7 @@ func (s *Schedule) initialize() error { v.globalSchedule = s v.log = s.log v.cfg = s.cfg + v.db = s.db // validate cron string if err := v.ValidateCron(); err != nil { @@ -145,7 +147,12 @@ func (s *Schedule) now() time.Time { return time.Now().In(s.loc) } -func loadSchedule(log zerolog.Logger, db *sql.DB, cfg Config, fn string) (Schedule, error) { +func loadSchedule(log zerolog.Logger, cfg Config, fn string) (Schedule, error) { + db, err := sql.Open("sqlite3", path.Join(CheekPath(), cfg.DBName)) + if err != nil { + return Schedule{}, fmt.Errorf("open db: %w", err) + } + s, err := readSpecs(fn) if err != nil { return Schedule{}, err @@ -163,8 +170,9 @@ func loadSchedule(log zerolog.Logger, db *sql.DB, cfg Config, fn string) (Schedu } // RunSchedule is the main entry entrypoint of cheek. -func RunSchedule(log zerolog.Logger, db *sql.DB, cfg Config, scheduleFn string) error { - s, err := loadSchedule(log, db, cfg, scheduleFn) +func RunSchedule(log zerolog.Logger, cfg Config, scheduleFn string) error { + + s, err := loadSchedule(log, cfg, scheduleFn) if err != nil { return err } diff --git a/pkg/utils.go b/pkg/utils.go index 0c6ee4d..b3063ac 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -23,6 +23,7 @@ type Config struct { LogLevel string `yaml:"logLevel"` HomeDir string `yaml:"homedir"` Port string `yaml:"port"` + DBName string `yaml:"dbname"` } func NewConfig() Config { @@ -32,6 +33,7 @@ func NewConfig() Config { LogLevel: "info", HomeDir: CheekPath(), Port: "8081", + DBName: "cheek.sqlite3", } } From 9181018620bf9a1d1e6b7d5466fa78b57c021df1 Mon Sep 17 00:00:00 2001 From: Bart6114 Date: Fri, 1 Dec 2023 07:55:46 +0100 Subject: [PATCH 03/22] integrate in ui --- pkg/http.go | 6 ++---- pkg/job.go | 26 ++++++++++++++++++++++++++ pkg/schedule.go | 4 +++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/pkg/http.go b/pkg/http.go index 2376e42..28bcbe7 100644 --- a/pkg/http.go +++ b/pkg/http.go @@ -113,7 +113,7 @@ func ui(s *Schedule) func(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Errorf("job %s not found", jobId).Error(), http.StatusNotFound) return } else { - job.loadRuns() + job.loadRunsFromDb(50) } } @@ -146,10 +146,8 @@ func ui(s *Schedule) func(w http.ResponseWriter, r *http.Request) { }{SelectedJobName: jobId, JobNames: jobNames, SelectedJobSpec: *job} if jobId == "" { - // pass along all job specs only when in overview - // takes a lot of I/O for _, j := range s.Jobs { - j.loadRuns() + j.loadRunsFromDb(5) } data.JobSpecs = s.Jobs } diff --git a/pkg/job.go b/pkg/job.go index 0d83b58..dd572c7 100644 --- a/pkg/job.go +++ b/pkg/job.go @@ -206,6 +206,32 @@ func (j *JobSpec) loadRuns() { j.Runs = jrs } +func (j *JobSpec) loadRunsFromDb(nruns int) { + if j.db == nil { + j.log.Warn().Str("job", j.Name).Msg("No db connection, not loading job runs from db.") + return + } + query := "SELECT triggered_at, triggered_by, duration, status, message FROM log WHERE job = ? ORDER BY triggered_at DESC LIMIT ?" + rows, err := j.db.Query(query, j.Name, nruns) + if err != nil { + j.log.Warn().Str("job", j.Name).Err(err).Msg("Couldn't load job runs from db.") + return + } + defer rows.Close() + + var jrs []JobRun + for rows.Next() { + var jr JobRun + err = rows.Scan(&jr.TriggeredAt, &jr.TriggeredBy, &jr.Duration, &jr.Status, &jr.Log) + if err != nil { + j.log.Warn().Str("job", j.Name).Err(err).Msg("Couldn't load job runs from db.") + return + } + jrs = append(jrs, jr) + } + j.Runs = jrs +} + func (j *JobSpec) setNextTick(refTime time.Time, includeRefTime bool) error { if j.Cron != "" { t, err := gronx.NextTickAfter(j.Cron, refTime, includeRefTime) diff --git a/pkg/schedule.go b/pkg/schedule.go index 94c90af..8f2ad34 100644 --- a/pkg/schedule.go +++ b/pkg/schedule.go @@ -35,6 +35,8 @@ func (s *Schedule) Run() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + defer s.db.Close() + for { select { case <-ticker.C: @@ -148,7 +150,7 @@ func (s *Schedule) now() time.Time { } func loadSchedule(log zerolog.Logger, cfg Config, fn string) (Schedule, error) { - db, err := sql.Open("sqlite3", path.Join(CheekPath(), cfg.DBName)) + db, err := OpenDB(path.Join(CheekPath(), cfg.DBName)) if err != nil { return Schedule{}, fmt.Errorf("open db: %w", err) } From c0571899fdb482afea4f04a09c349f1976eb060a Mon Sep 17 00:00:00 2001 From: Bart6114 Date: Sat, 2 Dec 2023 07:17:58 +0100 Subject: [PATCH 04/22] bits n pieces of ui refresh --- .gitignore | 3 +- main.go | 1 + package.json | 17 + pkg/http.go | 6 +- pkg/public/styles.css | 70 -- pkg/{public => web_assets}/index.html | 40 +- pkg/{public => web_assets}/jobview.html | 14 +- pkg/{public => web_assets}/overview.html | 6 +- .../static/GeistMonoVariableVF.woff2 | Bin 0 -> 53404 bytes pkg/{public => web_assets/static}/chota.css | 0 .../static}/img/circle-o.svg | 0 .../static}/img/circle.svg | 0 .../static}/img/github-dark.svg | 0 .../static}/img/github-light.svg | 0 .../static}/img/play-dark.svg | 0 .../static}/img/play-light.svg | 0 .../static}/img/refresh-ccw-dark.svg | 0 .../static}/img/refresh-ccw-light.svg | 0 pkg/{public => web_assets/static}/script.js | 0 pkg/web_assets/static/styles.css | 6 + pkg/web_assets/static/tailwind.css | 680 ++++++++++++++++++ pnpm-lock.yaml | 563 +++++++++++++++ tailwind.config.js | 15 + 23 files changed, 1316 insertions(+), 105 deletions(-) create mode 100644 package.json delete mode 100644 pkg/public/styles.css rename pkg/{public => web_assets}/index.html (57%) rename pkg/{public => web_assets}/jobview.html (81%) rename pkg/{public => web_assets}/overview.html (73%) create mode 100644 pkg/web_assets/static/GeistMonoVariableVF.woff2 rename pkg/{public => web_assets/static}/chota.css (100%) rename pkg/{public => web_assets/static}/img/circle-o.svg (100%) rename pkg/{public => web_assets/static}/img/circle.svg (100%) rename pkg/{public => web_assets/static}/img/github-dark.svg (100%) rename pkg/{public => web_assets/static}/img/github-light.svg (100%) rename pkg/{public => web_assets/static}/img/play-dark.svg (100%) rename pkg/{public => web_assets/static}/img/play-light.svg (100%) rename pkg/{public => web_assets/static}/img/refresh-ccw-dark.svg (100%) rename pkg/{public => web_assets/static}/img/refresh-ccw-light.svg (100%) rename pkg/{public => web_assets/static}/script.js (100%) create mode 100644 pkg/web_assets/static/styles.css create mode 100644 pkg/web_assets/static/tailwind.css create mode 100644 pnpm-lock.yaml create mode 100644 tailwind.config.js diff --git a/.gitignore b/.gitignore index 3bc8ff2..60c7f40 100644 --- a/.gitignore +++ b/.gitignore @@ -253,4 +253,5 @@ cheek cover.out butt *sheep -*.sqlite3 \ No newline at end of file +*.sqlite3 +node_modules \ No newline at end of file diff --git a/main.go b/main.go index feff9f3..a244d0c 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "github.com/datarootsio/cheek/cmd" ) +//go:generate npm run build func main() { cmd.Execute() } diff --git a/package.json b/package.json new file mode 100644 index 0000000..dd2c76d --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "cheek", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tailwindcss build -o pkg/web_assets/static/tailwind.css", + "watch": "tailwindcss build -o pkg/web_assets/static/tailwind.css --watch", + "dev": "nodemon --watch pkg/web_assets --ext html,css,js --exec 'go generate && go run . run ./testdata/jobs1.yaml' --signal SIGTERM" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "tailwindcss": "^3.3.5" + } +} \ No newline at end of file diff --git a/pkg/http.go b/pkg/http.go index 28bcbe7..aad28df 100644 --- a/pkg/http.go +++ b/pkg/http.go @@ -19,11 +19,11 @@ type Response struct { Type string `json:"type,omitempty"` } -//go:embed public +//go:embed web_assets var files embed.FS func fsys() fs.FS { - fsys, err := fs.Sub(files, "public") + fsys, err := fs.Sub(files, "web_assets") if err != nil { log.Fatal(err) } @@ -54,7 +54,7 @@ func setupMux(s *Schedule) *http.ServeMux { mux.HandleFunc("/", ui(s)) fs := http.FileServer(http.FS(fsys())) - mux.Handle("/static/", http.StripPrefix("/static/", fs)) + mux.Handle("/static/", fs) return mux diff --git a/pkg/public/styles.css b/pkg/public/styles.css deleted file mode 100644 index 962fdd0..0000000 --- a/pkg/public/styles.css +++ /dev/null @@ -1,70 +0,0 @@ -@import url("chota.css"); - -:root { - --grid-maxWidth: 80rem; - --font-size: 1rem; -} - -body.dark { - --bg-color: #000; - --bg-secondary-color: #131316; - --font-color: #d3d7cf; - --color-grey: #ccc; - --color-darkGrey: #d3d7cf; -} - -.button.icon-only { - padding: 0.3rem; -} - -.button { - margin: 0.3rem !important; -} - -.pad { - padding: 0.2rem; -} - -.pad-top { - padding-top: 0.5rem; -} - -#title { - font-size: 2rem; -} - -.view-container { - position: relative; -} - -.view-header { - position: absolute; - top: 0px; - right: 0px; - padding: 1rem; -} - -.icon-ahref { - overflow: hidden; - height: 16px; - width: 16px; -} - -.no-underline { - border-bottom: none !important; - cursor: inherit !important; - text-decoration: none !important; -} - -.flex-container { - display: flex; - flex-wrap: wrap; -} - -.pre-wrap { - white-space: pre-wrap; /* Since CSS 2.1 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} \ No newline at end of file diff --git a/pkg/public/index.html b/pkg/web_assets/index.html similarity index 57% rename from pkg/public/index.html rename to pkg/web_assets/index.html index 9f13be3..607194c 100644 --- a/pkg/public/index.html +++ b/pkg/web_assets/index.html @@ -7,16 +7,18 @@ cheek + - -
-
+ +
+ -
- +
+ {{range .JobNames}}{{.}}{{end}}
-
-
-
- {{ if .SelectedJobName}} - {{template "jobview" .}} - {{ else }} - {{ template "overview" .}} - {{end}} -
+
+ {{ if .SelectedJobName}} + {{template "jobview" .}} + {{ else }} + {{ template "overview" .}} + {{end}}
+
-
+
- cheek
@@ -29,27 +30,18 @@
-
- {{range .JobNames}}{{.}}{{end}} +
+
-
- {{ if .SelectedJobName}} - {{template "jobview" .}} - {{ else }} - {{ template "overview" .}} - {{end}} -
+ + {{block "content" .}}{{end}} +
- \ No newline at end of file diff --git a/pkg/web_assets/templates/jobview.html b/pkg/web_assets/templates/jobview.html new file mode 100644 index 0000000..9cbc94b --- /dev/null +++ b/pkg/web_assets/templates/jobview.html @@ -0,0 +1,52 @@ +{{ define "content"}} +
+ + +
+
+
+
+
+
+
    + +
+ +
+
+ +
+
+
+

+

+
+
+ +
+
+ + +
+
+
+ +{{end}} \ No newline at end of file diff --git a/pkg/web_assets/templates/overview.html b/pkg/web_assets/templates/overview.html new file mode 100644 index 0000000..16c7b99 --- /dev/null +++ b/pkg/web_assets/templates/overview.html @@ -0,0 +1,18 @@ +{{ define "content"}} +
+ +
+ +

shows statuses up until the last 10 runs

+{{ end }} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 07dd59e..52472e2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,7 +2,7 @@ const defaultTheme = require("tailwindcss/defaultTheme"); /** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./pkg/web_assets/*.html"], + content: ["./pkg/web_assets/templates/*.html"], theme: { extend: { fontFamily: { From 1eb5eff19a97fdeba242a34a2f4237af3aa733ad Mon Sep 17 00:00:00 2001 From: Bart6114 Date: Sun, 3 Dec 2023 15:03:46 +0100 Subject: [PATCH 06/22] refresh & trigger --- pkg/http.go | 4 +-- pkg/web_assets/static/script.js | 12 +++++++ pkg/web_assets/static/tailwind.css | 29 ++++++++++++++--- pkg/web_assets/templates/jobview.html | 45 +++++++++++++++++--------- pkg/web_assets/templates/overview.html | 15 +++++++-- 5 files changed, 81 insertions(+), 24 deletions(-) diff --git a/pkg/http.go b/pkg/http.go index 7be5866..97376d8 100644 --- a/pkg/http.go +++ b/pkg/http.go @@ -44,10 +44,10 @@ func setupRouter(s *Schedule) *httprouter.Router { router.GET("/", getHomePage()) // api endpoints - router.GET("/api/jobs/", getJobs(s)) + router.GET("/api/jobs", getJobs(s)) router.GET("/api/jobs/:jobId", getJob(s)) router.GET("/api/jobs/:jobId/runs/:jobRunId", getJobRun(s)) - router.POST("/api/trigger/:jobId", postTrigger(s)) + router.POST("/api/jobs/:jobId/trigger", postTrigger(s)) fileServer := http.FileServer(http.FS(fsys())) router.GET("/static/*filepath", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { diff --git a/pkg/web_assets/static/script.js b/pkg/web_assets/static/script.js index 60b15ac..5c2127e 100644 --- a/pkg/web_assets/static/script.js +++ b/pkg/web_assets/static/script.js @@ -75,6 +75,18 @@ document.addEventListener('alpine:init', () => { }) +function triggerJob(jobName) { + fetch(`/api/jobs/${jobName}/trigger`, { + method: 'POST', + }).then(response => { + if (response.ok) { + console.log(`Job ${jobName} triggered!`); + } else { + console.error(`Job ${jobName} could not be triggered!`); + } + }); +} + function parseJobUrl(url) { // Using a regular expression to extract jobName and runId const regex = /\/jobs\/([^\/]+)\/([^\/]+)/; diff --git a/pkg/web_assets/static/tailwind.css b/pkg/web_assets/static/tailwind.css index b5d23c9..491377c 100644 --- a/pkg/web_assets/static/tailwind.css +++ b/pkg/web_assets/static/tailwind.css @@ -652,6 +652,18 @@ video { background-clip: text; } +.fill-emerald-500 { + fill: #10b981; +} + +.fill-red-500 { + fill: #ef4444; +} + +.fill-slate-200 { + fill: #e2e8f0; +} + .p-2 { padding: 0.5rem; } @@ -699,6 +711,10 @@ video { line-height: 1rem; } +.font-black { + font-weight: 900; +} + .font-bold { font-weight: 700; } @@ -717,6 +733,11 @@ video { color: rgb(217 249 157 / var(--tw-text-opacity)); } +.text-slate-200 { + --tw-text-opacity: 1; + color: rgb(226 232 240 / var(--tw-text-opacity)); +} + .text-transparent { color: transparent; } @@ -725,15 +746,15 @@ video { text-decoration-line: none; } -.outline { - outline-style: solid; -} - .drop-shadow-md { --tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06)); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } +.hover\:fill-lime-200:hover { + fill: #d9f99d; +} + .hover\:text-lime-200:hover { --tw-text-opacity: 1; color: rgb(217 249 157 / var(--tw-text-opacity)); diff --git a/pkg/web_assets/templates/jobview.html b/pkg/web_assets/templates/jobview.html index 9cbc94b..73c5be3 100644 --- a/pkg/web_assets/templates/jobview.html +++ b/pkg/web_assets/templates/jobview.html @@ -1,19 +1,34 @@ {{ define "content"}} -
- - +
+
+ + + + +
+ +
@@ -36,8 +51,8 @@
-

-

+

+

diff --git a/pkg/web_assets/templates/overview.html b/pkg/web_assets/templates/overview.html index 16c7b99..71dfe0a 100644 --- a/pkg/web_assets/templates/overview.html +++ b/pkg/web_assets/templates/overview.html @@ -2,10 +2,19 @@