diff --git a/api/main.go b/api/main.go index 310928c..efc3140 100644 --- a/api/main.go +++ b/api/main.go @@ -105,6 +105,7 @@ func New() *WebAPI { api.router.GET("/project/webhook_secret/:id", LogRequestMiddleware(api.GetWebhookSecret)) 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("/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 27d0255..8061e72 100644 --- a/api/models.go +++ b/api/models.go @@ -314,3 +314,7 @@ type GetWebhookSecretResponse struct { type ResetFailedTaskResponse struct { AffectedTasks int64 `json:"affected_tasks"` } + +type HardResetResponse struct { + AffectedTasks int64 `json:"affected_tasks"` +} diff --git a/api/project.go b/api/project.go index 4754308..fad5d73 100644 --- a/api/project.go +++ b/api/project.go @@ -724,3 +724,35 @@ func (api *WebAPI) ResetFailedTasks(r *Request) { }, }) } + +func (api *WebAPI) HardReset(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.HardReset(pid) + + r.OkJson(JsonResponse{ + Ok: true, + Content: HardResetResponse{ + AffectedTasks: res, + }, + }) +} diff --git a/storage/maintenance.go b/storage/maintenance.go index da17714..88d87ea 100644 --- a/storage/maintenance.go +++ b/storage/maintenance.go @@ -32,3 +32,21 @@ func (database *Database) ResetTimedOutTasks() { "rowsAffected": rowsAffected, }).Info("Reset timed out tasks") } + +func (database Database) HardReset(pid int64) int64 { + + db := database.getDB() + + _, err := db.Exec(`UPDATE task SET assignee=NULL WHERE project=$1`, pid) + handleErr(err) + res, err := db.Exec(`DELETE FROM task WHERE project=$1`, pid) + + rowsAffected, _ := res.RowsAffected() + + logrus.WithFields(logrus.Fields{ + "rowsAffected": rowsAffected, + "project": pid, + }).Info("Hard reset") + + return rowsAffected +} diff --git a/web/angular/src/app/api.service.ts b/web/angular/src/app/api.service.ts index 93f26ac..42b0f73 100755 --- a/web/angular/src/app/api.service.ts +++ b/web/angular/src/app/api.service.ts @@ -118,4 +118,8 @@ export class ApiService { return this.http.post(this.url + `/project/reset_failed_tasks/${pid}`, null, this.options); } + hardReset(pid: number) { + return this.http.post(this.url + `/project/hard_reset/${pid}`, null, this.options); + } + } 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 1bfa41e..a47871d 100644 --- a/web/angular/src/app/project-dashboard/project-dashboard.component.ts +++ b/web/angular/src/app/project-dashboard/project-dashboard.component.ts @@ -414,18 +414,32 @@ export class ProjectDashboardComponent implements OnInit { this.dialog.open(AreYouSureComponent, { width: '250px', }).afterClosed().subscribe(result => { - this.project.paused = paused; - this.apiService.updateProject(this.project).subscribe(() => { - this.translate.get('messenger.acknowledged').subscribe(t => - this.messenger.show(t)); - }, error => { - this.translate.get('messenger.unauthorized').subscribe(t => - this.messenger.show(t)); - }); + if (result) { + this.project.paused = paused; + this.apiService.updateProject(this.project).subscribe(() => { + this.translate.get('messenger.acknowledged').subscribe(t => + this.messenger.show(t)); + }, error => { + this.translate.get('messenger.unauthorized').subscribe(t => + this.messenger.show(t)); + }); + } }); } hardReset() { - + this.dialog.open(AreYouSureComponent, { + width: '250px', + }).afterClosed().subscribe(result => { + if (result) { + this.apiService.hardReset(this.project.id).subscribe(data => { + this.translate.get('project.hard_reset_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 91a0561..b57e3ce 100644 --- a/web/angular/src/assets/i18n/en.json +++ b/web/angular/src/assets/i18n/en.json @@ -71,6 +71,7 @@ "version": "Git version (commit hash)", "secret": "Secret", "reset_response": "Reset failed tasks: ", + "hard_reset_response": "Deleted tasks: ", "assign_rate": "Task assign rate limit", "submit_rate": "Task submit rate limit", "rate": "per second", diff --git a/web/angular/src/assets/i18n/fr.json b/web/angular/src/assets/i18n/fr.json index 8451e1a..151d041 100644 --- a/web/angular/src/assets/i18n/fr.json +++ b/web/angular/src/assets/i18n/fr.json @@ -71,6 +71,7 @@ "manager_select": "Donner accès à", "version": "Version git (hash du commit)", "reset_response": "Réinitialisé les tâches en échec: ", + "hard_reset_response": "Supprimé toutes les tâches: ", "assign_rate": "Taux d'assignation de tâches", "submit_rate": "Taux de soumission de tâches", "rate": "par seconde",