diff --git a/api/main.go b/api/main.go index efc3140..4cbe2bf 100644 --- a/api/main.go +++ b/api/main.go @@ -106,6 +106,7 @@ func New() *WebAPI { api.router.POST("/project/webhook_secret/:id", LogRequestMiddleware(api.SetWebhookSecret)) api.router.POST("/project/reset_failed_tasks/:id", LogRequestMiddleware(api.ResetFailedTasks)) api.router.POST("/project/hard_reset/:id", LogRequestMiddleware(api.HardReset)) + api.router.POST("/project/reclaim_assigned_tasks/:id", LogRequestMiddleware(api.ReclaimAssignedTasks)) api.router.POST("/task/submit", LogRequestMiddleware(api.SubmitTask)) api.router.GET("/task/get/:project", LogRequestMiddleware(api.GetTaskFromProject)) diff --git a/api/models.go b/api/models.go index 8061e72..ea372c6 100644 --- a/api/models.go +++ b/api/models.go @@ -318,3 +318,7 @@ type ResetFailedTaskResponse struct { type HardResetResponse struct { AffectedTasks int64 `json:"affected_tasks"` } + +type ReclaimAssignedTasksResponse struct { + AffectedTasks int64 `json:"affected_tasks"` +} diff --git a/api/project.go b/api/project.go index fad5d73..6f0f6dc 100644 --- a/api/project.go +++ b/api/project.go @@ -756,3 +756,35 @@ func (api *WebAPI) HardReset(r *Request) { }, }) } + +func (api *WebAPI) ReclaimAssignedTasks(r *Request) { + + pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) + if err != nil || pid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid project id", + }, 400) + return + } + + sess := api.Session.StartFasthttp(r.Ctx) + manager := sess.Get("manager") + + if !isActionOnProjectAuthorized(pid, manager, storage.RoleMaintenance, api.Database) { + r.Json(JsonResponse{ + Ok: false, + Message: "Unauthorized", + }, 403) + return + } + + res := api.Database.ReclaimAssignedTasks(pid) + + r.OkJson(JsonResponse{ + Ok: true, + Content: ReclaimAssignedTasksResponse{ + AffectedTasks: res, + }, + }) +} diff --git a/storage/maintenance.go b/storage/maintenance.go index 88d87ea..30f06a5 100644 --- a/storage/maintenance.go +++ b/storage/maintenance.go @@ -50,3 +50,21 @@ func (database Database) HardReset(pid int64) int64 { return rowsAffected } + +func (database Database) ReclaimAssignedTasks(pid int64) int64 { + + db := database.getDB() + + res, err := db.Exec(`UPDATE task SET assignee=NULL, assign_time=NULL + WHERE project=$1 AND status=1`, pid) + handleErr(err) + + rowsAffected, _ := res.RowsAffected() + + logrus.WithFields(logrus.Fields{ + "rowsAffected": rowsAffected, + "project": pid, + }).Info("Reclaim assigned tasks") + + return rowsAffected +} diff --git a/web/angular/src/app/api.service.ts b/web/angular/src/app/api.service.ts index 42b0f73..0c40d08 100755 --- a/web/angular/src/app/api.service.ts +++ b/web/angular/src/app/api.service.ts @@ -122,4 +122,8 @@ export class ApiService { return this.http.post(this.url + `/project/hard_reset/${pid}`, null, this.options); } + reclaimAssignedTasks(pid: number) { + return this.http.post(this.url + `/project/reclaim_assigned_tasks/${pid}`, null, this.options); + } + } diff --git a/web/angular/src/app/project-dashboard/project-dashboard.component.html b/web/angular/src/app/project-dashboard/project-dashboard.component.html index da03a7a..b7dc301 100644 --- a/web/angular/src/app/project-dashboard/project-dashboard.component.html +++ b/web/angular/src/app/project-dashboard/project-dashboard.component.html @@ -85,6 +85,10 @@ warning {{"dashboard.hard_reset"|translate}} + diff --git a/web/angular/src/app/project-dashboard/project-dashboard.component.ts b/web/angular/src/app/project-dashboard/project-dashboard.component.ts index a47871d..897115a 100644 --- a/web/angular/src/app/project-dashboard/project-dashboard.component.ts +++ b/web/angular/src/app/project-dashboard/project-dashboard.component.ts @@ -442,4 +442,20 @@ export class ProjectDashboardComponent implements OnInit { } }); } + + reclaimAssignedTasks() { + this.dialog.open(AreYouSureComponent, { + width: '250px', + }).afterClosed().subscribe(result => { + if (result) { + this.apiService.reclaimAssignedTasks(this.project.id).subscribe(data => { + this.translate.get('project.reclaim_response').subscribe(t => + this.messenger.show(t + data['content']['affected_tasks'])); + }, error => { + this.translate.get('messenger.unauthorized').subscribe(t => + this.messenger.show(t)); + }); + } + }); + } } diff --git a/web/angular/src/assets/i18n/en.json b/web/angular/src/assets/i18n/en.json index b57e3ce..101e6cd 100644 --- a/web/angular/src/assets/i18n/en.json +++ b/web/angular/src/assets/i18n/en.json @@ -72,6 +72,7 @@ "secret": "Secret", "reset_response": "Reset failed tasks: ", "hard_reset_response": "Deleted tasks: ", + "reclaim_response": "Reclaimed tasks: ", "assign_rate": "Task assign rate limit", "submit_rate": "Task submit rate limit", "rate": "per second", @@ -87,7 +88,8 @@ "reset_failed": "Reset failed tasks", "pause": "Pause", "resume": "Resume", - "hard_reset": "Hard_reset" + "hard_reset": "Hard reset", + "reclaim": "Reclaim assigned tasks" }, "login": { "title": "Login", diff --git a/web/angular/src/assets/i18n/fr.json b/web/angular/src/assets/i18n/fr.json index 151d041..6e4e264 100644 --- a/web/angular/src/assets/i18n/fr.json +++ b/web/angular/src/assets/i18n/fr.json @@ -72,6 +72,7 @@ "version": "Version git (hash du commit)", "reset_response": "Réinitialisé les tâches en échec: ", "hard_reset_response": "Supprimé toutes les tâches: ", + "reclaim_response": "Désaffecté les tâches: ", "assign_rate": "Taux d'assignation de tâches", "submit_rate": "Taux de soumission de tâches", "rate": "par seconde", @@ -83,7 +84,8 @@ "metadata": "Métadonnés du projet", "empty": "Aucune tâche", "refresh": "Rafraîchir", - "actions": "Actions" + "actions": "Actions", + "reclaim": "Déaffecter les tâches" }, "login": { "title": "Ouvrir un session",