diff --git a/pages/editor/jobs/jobs.go b/pages/editor/jobs/jobs.go index 07a3919b..2e54ab58 100644 --- a/pages/editor/jobs/jobs.go +++ b/pages/editor/jobs/jobs.go @@ -1,17 +1,39 @@ package jobs import ( + "sort" + "github.com/aerogo/aero" "github.com/animenotifier/notify.moe/components" "github.com/animenotifier/notify.moe/utils" ) -var running = map[string]bool{} -var lastRun = map[string]string{} +var jobInfo = map[string]*utils.JobInfo{ + "anime-ratings": &utils.JobInfo{ + Name: "anime-ratings", + }, + "twist": &utils.JobInfo{ + Name: "twist", + }, + "refresh-osu": &utils.JobInfo{ + Name: "refresh-osu", + }, +} + +var jobLogs = []string{} // Overview shows all background jobs. func Overview(ctx *aero.Context) string { user := utils.GetUser(ctx) + jobs := []*utils.JobInfo{} - return ctx.HTML(components.EditorJobs(running, lastRun, ctx.URI(), user)) + for _, job := range jobInfo { + jobs = append(jobs, job) + } + + sort.Slice(jobs, func(i, j int) bool { + return jobs[i].Name < jobs[j].Name + }) + + return ctx.HTML(components.EditorJobs(jobs, jobLogs, ctx.URI(), user)) } diff --git a/pages/editor/jobs/jobs.pixy b/pages/editor/jobs/jobs.pixy index 7c7cc05b..afefbb85 100644 --- a/pages/editor/jobs/jobs.pixy +++ b/pages/editor/jobs/jobs.pixy @@ -1,9 +1,18 @@ -component EditorJobs(running bool, url string, user *arn.User) +component EditorJobs(jobs []*utils.JobInfo, jobLogs []string, url string, user *arn.User) EditorTabs(url, user) - h1.editor-list-page-title.mountable Background jobs + h1.mountable Background jobs .buttons - button.mountable.action(data-action="startJob", data-trigger="click", data-job="twist", disabled=running) - Icon("rocket") - span twist \ No newline at end of file + each job in jobs + button.background-job.mountable.action(data-action="startJob", data-trigger="click", data-job=job.Name, data-running=job.IsRunning()) + if job.IsRunning() + Icon("hourglass-start") + else + Icon("rocket") + + span= job.Name + + footer.footer + for i := 0; i < len(jobLogs); i++ + p.mountable= jobLogs[len(jobLogs) - 1 - i] \ No newline at end of file diff --git a/pages/editor/jobs/jobs.scarlet b/pages/editor/jobs/jobs.scarlet new file mode 100644 index 00000000..cd71d7f2 --- /dev/null +++ b/pages/editor/jobs/jobs.scarlet @@ -0,0 +1,5 @@ +.background-job + [data-running="true"] + &.mounted + opacity 0.5 !important + pointer-events none \ No newline at end of file diff --git a/pages/editor/jobs/start.go b/pages/editor/jobs/start.go new file mode 100644 index 00000000..5ed7acea --- /dev/null +++ b/pages/editor/jobs/start.go @@ -0,0 +1,35 @@ +package jobs + +import ( + "net/http" + + "github.com/animenotifier/arn" + + "github.com/aerogo/aero" + "github.com/animenotifier/notify.moe/utils" +) + +// Start will start the specified background job. +func Start(ctx *aero.Context) string { + user := utils.GetUser(ctx) + + if user == nil || (user.Role != "editor" && user.Role != "admin") { + return ctx.Error(http.StatusUnauthorized, "Not authorized", nil) + } + + jobName := ctx.Get("job") + job := jobInfo[jobName] + + if job == nil { + return ctx.Error(http.StatusBadRequest, "Job not available", nil) + } + + if job.IsRunning() { + return ctx.Error(http.StatusBadRequest, "Job is currently running!", nil) + } + + job.Start() + jobLogs = append(jobLogs, user.Nick+" started "+job.Name+" job ("+arn.DateTimeUTC()+").") + + return "ok" +} diff --git a/pages/index/apiroutes/apiroutes.go b/pages/index/apiroutes/apiroutes.go index 5083ad75..b587d06f 100644 --- a/pages/index/apiroutes/apiroutes.go +++ b/pages/index/apiroutes/apiroutes.go @@ -10,6 +10,7 @@ import ( "github.com/animenotifier/notify.moe/pages/animeimport" "github.com/animenotifier/notify.moe/pages/apiview" "github.com/animenotifier/notify.moe/pages/apiview/apidocs" + "github.com/animenotifier/notify.moe/pages/editor/jobs" "github.com/animenotifier/notify.moe/pages/me" "github.com/animenotifier/notify.moe/pages/notifications" "github.com/animenotifier/notify.moe/pages/popular" @@ -45,4 +46,7 @@ func Register(l *layout.Layout, app *aero.Application) { // Import anime app.Post("/api/import/kitsu/anime/:id", animeimport.Kitsu) app.Post("/api/delete/kitsu/anime/:id", animeimport.DeleteKitsu) + + // Jobs + app.Post("/api/job/:job/start", jobs.Start) } diff --git a/scripts/Actions/Editor.ts b/scripts/Actions/Editor.ts index 6946d405..85fa7407 100644 --- a/scripts/Actions/Editor.ts +++ b/scripts/Actions/Editor.ts @@ -75,6 +75,11 @@ export async function multiSearchAnime(arn: AnimeNotifier, textarea: HTMLTextAre // Start background job export async function startJob(arn: AnimeNotifier, button: HTMLButtonElement) { + if(button.dataset.running === "true") { + alert("Job is currently running!") + return + } + let jobName = button.dataset.job if(!confirm(`Are you sure you want to start the "${jobName}" job?`)) { diff --git a/utils/JobInfo.go b/utils/JobInfo.go index e18f7d40..d1c9cf05 100644 --- a/utils/JobInfo.go +++ b/utils/JobInfo.go @@ -1,6 +1,13 @@ package utils -import "time" +import ( + "os/exec" + "path" + "time" + + "github.com/animenotifier/arn" + "github.com/fatih/color" +) // JobInfo gives you information about a background job. type JobInfo struct { @@ -11,6 +18,30 @@ type JobInfo struct { // IsRunning tells you whether the given job is running or not. func (job *JobInfo) IsRunning() bool { - now := time.Now() - return job.LastStarted.After(job.LastFinished) && !now.After(job.LastFinished) + return job.LastStarted.After(job.LastFinished) +} + +// Start will start the job. +func (job *JobInfo) Start() error { + cmd := exec.Command(path.Join(arn.Root, "jobs", job.Name, job.Name)) + err := cmd.Start() + + if err != nil { + return err + } + + job.LastStarted = time.Now() + + // Wait for job finish in another goroutine + go func() { + err := cmd.Wait() + + if err != nil { + color.Red("Job '%s' encountered an error: %s", job.Name, err.Error()) + } + + job.LastFinished = time.Now() + }() + + return nil }