Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-29 02:18:46 |
Commit | 594ec417f426c15db7124a13480e9792dacd0205 |
Parent | d737f58666affab00577c1433ca7d04fd54d9ee2 |
Add admin cron page and log last job time
Diffstat
A | res/admin/cron.html | | | 30 | ++++++++++++++++++++++++++++++ |
M | res/admin/header.html | | | 1 | + |
M | res/res.go | | | 3 | +++ |
M | res/style.css | | | 1 | + |
A | src/admin/cron.go | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/admin/repos.go | | | 2 | +- |
M | src/cron/cron.go | | | 48 | ++++++++++++++++++++++++++++++------------------ |
M | src/cron/schedule.go | | | 19 | +++++++++++++++++++ |
M | src/goit/http.go | | | 1 | + |
M | src/main.go | | | 1 | + |
10 files changed, 145 insertions, 19 deletions
diff --git a/res/admin/cron.html b/res/admin/cron.html new file mode 100644 index 0000000..a14243d --- /dev/null +++ b/res/admin/cron.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<head lang="en-GB">{{template "base/head" .}}</head> +<body> + <header>{{template "admin/header" .}}</header><hr> + <main> + <table class="highlight-row"> + <thead> + <tr> + <td><b>ID</b></td> + <td><b>Repository</b></td> + <td><b>Schedule</b></td> + <td><b>Next</b></td> + <td><b>Last</b></td> + </tr> + </thead> + <tbody> + {{range .Jobs}} + <tr> + <td>{{.Id}}</td> + <td><a href="/{{.Repo}}">{{.Repo}}</a></td> + <td>{{.Schedule}}</td> + <td>{{.Next}}</td> + <td>{{.Last}}</td> + </tr> + {{end}} + </tbody> + </table><hr> + <span>Schedule format is month, day, weekday, hour, minute, second, where an asterisk represents "any".</span> + </main> +</body> diff --git a/res/admin/header.html b/res/admin/header.html index dcf3bf2..1e3c660 100644 --- a/res/admin/header.html +++ b/res/admin/header.html @@ -7,6 +7,7 @@ <a href="/admin/status">Status</a> | <a href="/admin/users">Users</a> | <a href="/admin/repos">Repositories</a> + | <a href="/admin/cron">Cron</a> | <a href="/admin/user/create">Create User</a> </td></tr> </table> diff --git a/res/res.go b/res/res.go index f087216..af125bd 100644 --- a/res/res.go +++ b/res/res.go @@ -34,6 +34,9 @@ var AdminRepos string //go:embed admin/repo_edit.html var AdminRepoEdit string +//go:embed admin/cron.html +var AdminCron string + //go:embed user/header.html var UserHeader string diff --git a/res/style.css b/res/style.css index ddf0589..5417322 100644 --- a/res/style.css +++ b/res/style.css @@ -5,6 +5,7 @@ a { color: #FF7E00; text-decoration: none; } a:hover { text-decoration: underline; } h1, h2 { font-size: 1em; margin: 0; } hr { border: 0; height: 1rem; margin: 0; } +main > span { padding: 0 0.4rem; } footer { padding: 0.4rem 0.4rem 1rem; } diff --git a/src/admin/cron.go b/src/admin/cron.go new file mode 100644 index 0000000..ea12693 --- /dev/null +++ b/src/admin/cron.go @@ -0,0 +1,58 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +package admin + +import ( + "fmt" + "log" + "net/http" + "time" + + "github.com/Jamozed/Goit/src/goit" + "github.com/Jamozed/Goit/src/util" +) + +func HandleCron(w http.ResponseWriter, r *http.Request) { + auth, user, err := goit.Auth(w, r, true) + if err != nil { + log.Println("[/admin/cron]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } + + if !auth || !user.IsAdmin { + goit.HttpError(w, http.StatusNotFound) + return + } + + type row struct{ Id, Repo, Schedule, Next, Last string } + data := struct { + Title string + Jobs []row + }{Title: "Admin - Cron"} + + for _, job := range goit.Cron.Jobs() { + repo := &goit.Repo{} + + if job.Rid != -1 { + if r, err := goit.GetRepo(job.Rid); err != nil { + log.Println("[/admin/cron]", err.Error()) + } else if r != nil { + repo = r + } + } + + data.Jobs = append(data.Jobs, row{ + Id: fmt.Sprint(job.Id), + Repo: repo.Name, + Schedule: job.Schedule.String(), + Next: job.Next.String(), + Last: util.If(job.Last == time.Time{}, "never", job.Last.String()), + }) + } + + if err := goit.Tmpl.ExecuteTemplate(w, "admin/cron", data); err != nil { + log.Println("[/admin/cron]", err.Error()) + } +} diff --git a/src/admin/repos.go b/src/admin/repos.go index 80f557c..826d959 100644 --- a/src/admin/repos.go +++ b/src/admin/repos.go @@ -22,7 +22,7 @@ import ( func HandleRepos(w http.ResponseWriter, r *http.Request) { auth, user, err := goit.Auth(w, r, true) if err != nil { - log.Println("[admin/users]", err.Error()) + log.Println("[admin/repos]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } diff --git a/src/cron/cron.go b/src/cron/cron.go index 3fdf619..6357732 100644 --- a/src/cron/cron.go +++ b/src/cron/cron.go @@ -24,11 +24,11 @@ type Cron struct { } type Job struct { - id uint64 - rid int64 - schedule Schedule - next time.Time - fn func() + Id uint64 + Rid int64 + Schedule Schedule + Next, Last time.Time + fn func() } const maxDuration time.Duration = 1<<63 - 1 @@ -52,7 +52,7 @@ func (c *Cron) Start() { } for _, job := range c.jobs { - job.next = job.schedule.Next(time.Now().UTC()) + job.Next = job.Schedule.Next(time.Now().UTC()) } go func() { @@ -65,7 +65,7 @@ func (c *Cron) Start() { if len(c.jobs) == 0 { timer = time.NewTimer(maxDuration) } else { - timer = time.NewTimer(c.jobs[0].next.Sub(time.Now().UTC())) + timer = time.NewTimer(c.jobs[0].Next.Sub(time.Now().UTC())) } c.mutex.Unlock() @@ -81,12 +81,12 @@ func (c *Cron) Start() { tmp := c.jobs[:0] for _, job := range c.jobs { - if job.next.After(now) || job.next.IsZero() { + if job.Next.After(now) || job.Next.IsZero() { tmp = append(tmp, job) continue } - log.Println("[cron] running job", job.id, job.rid) + log.Println("[cron] running job", job.Id, job.Rid) j := job c.waiter.Add(1) @@ -95,8 +95,9 @@ func (c *Cron) Start() { j.fn() }() - if !job.schedule.IsImmediate() { - job.next = job.schedule.Next(now) + if !job.Schedule.IsImmediate() { + job.Next = job.Schedule.Next(now) + job.Last = now tmp = append(tmp, job) } } @@ -153,10 +154,21 @@ func (c *Cron) _update() { now := time.Now().UTC() slices.SortFunc(c.jobs, func(a, b Job) int { - return a.schedule.Next(now).Compare(b.schedule.Next(now)) + return a.Schedule.Next(now).Compare(b.Schedule.Next(now)) }) } +func (c *Cron) Jobs() []Job { + c.mutex.Lock() + util.Debugln("[cron.Jobs] Cron mutex lock") + defer c.mutex.Unlock() + defer util.Debugln("[cron.Jobs] Cron mutex unlock") + + jobs := make([]Job, len(c.jobs)) + copy(jobs, c.jobs) + return jobs +} + func (c *Cron) Add(rid int64, schedule Schedule, fn func()) uint64 { c.mutex.Lock() util.Debugln("[cron.Add] Cron mutex lock") @@ -165,12 +177,12 @@ func (c *Cron) Add(rid int64, schedule Schedule, fn func()) uint64 { c.lastId += 1 - job := Job{id: c.lastId, rid: rid, schedule: schedule, fn: fn} - job.next = job.schedule.Next(time.Now().UTC()) + job := Job{Id: c.lastId, Rid: rid, Schedule: schedule, fn: fn} + job.Next = job.Schedule.Next(time.Now().UTC()) c.jobs = append(c.jobs, job) - log.Println("[cron] added job", job.id, "for", job.rid) - return job.id + log.Println("[cron] added job", job.Id, "for", job.Rid) + return job.Id } func (c *Cron) RemoveFor(rid int64) { @@ -181,10 +193,10 @@ func (c *Cron) RemoveFor(rid int64) { tmp := c.jobs[:0] for _, job := range c.jobs { - if job.rid != rid { + if job.Rid != rid { tmp = append(tmp, job) } else { - log.Println("[cron] removing job", job.id, "for", job.rid) + log.Println("[cron] removing job", job.Id, "for", job.Rid) } } diff --git a/src/cron/schedule.go b/src/cron/schedule.go index 4a83656..be7d4e9 100644 --- a/src/cron/schedule.go +++ b/src/cron/schedule.go @@ -4,7 +4,10 @@ package cron import ( + "fmt" "time" + + "github.com/Jamozed/Goit/src/util" ) type Schedule struct{ Month, Day, Weekday, Hour, Minute, Second int64 } @@ -96,3 +99,19 @@ wrap: func (s Schedule) IsImmediate() bool { return s == Immediate } + +func (s Schedule) String() string { + if s.IsImmediate() { + return "immediate" + } + + return fmt.Sprintf( + "%s %s %s %s %s %s", + util.If(s.Month == -1, "*", fmt.Sprint(s.Month)), + util.If(s.Day == -1, "*", fmt.Sprint(s.Day)), + util.If(s.Weekday == -1, "*", fmt.Sprint(s.Weekday)), + util.If(s.Hour == -1, "*", fmt.Sprint(s.Hour)), + util.If(s.Minute == -1, "*", fmt.Sprint(s.Minute)), + util.If(s.Second == -1, "*", fmt.Sprint(s.Second)), + ) +} diff --git a/src/goit/http.go b/src/goit/http.go index 3fc333b..1fea509 100644 --- a/src/goit/http.go +++ b/src/goit/http.go @@ -25,6 +25,7 @@ func init() { template.Must(Tmpl.New("admin/user/edit").Parse(res.AdminUserEdit)) template.Must(Tmpl.New("admin/repos").Parse(res.AdminRepos)) template.Must(Tmpl.New("admin/repo/edit").Parse(res.AdminRepoEdit)) + template.Must(Tmpl.New("admin/cron").Parse(res.AdminCron)) template.Must(Tmpl.New("user/header").Parse(res.UserHeader)) template.Must(Tmpl.New("user/login").Parse(res.UserLogin)) diff --git a/src/main.go b/src/main.go index 4a88489..229ec0b 100644 --- a/src/main.go +++ b/src/main.go @@ -125,6 +125,7 @@ func main() { r.Get("/admin/repos", admin.HandleRepos) r.Get("/admin/repo/edit", admin.HandleRepoEdit) r.Post("/admin/repo/edit", admin.HandleRepoEdit) + r.Get("/admin/cron", admin.HandleCron) r.Get("/static/style.css", handleStyle) r.Get("/static/favicon.png", handleFavicon)