diff --git a/api/main.go b/api/main.go index 1d1875c..5fbb8b6 100644 --- a/api/main.go +++ b/api/main.go @@ -91,6 +91,8 @@ func New() *WebAPI { api.router.POST("/project/request_access", LogRequestMiddleware(api.CreateWorkerAccess)) api.router.POST("/project/accept_request/:id/:wid", LogRequestMiddleware(api.AcceptAccessRequest)) api.router.POST("/project/reject_request/:id/:wid", LogRequestMiddleware(api.RejectAccessRequest)) + api.router.GET("/project/secret/:id", LogRequestMiddleware(api.GetSecret)) + api.router.POST("/project/secret/:id", LogRequestMiddleware(api.SetSecret)) 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 f28686f..01d641c 100644 --- a/api/models.go +++ b/api/models.go @@ -214,6 +214,10 @@ type ReleaseTaskRequest struct { Verification int64 `json:"verification"` } +func (r *ReleaseTaskRequest) IsValid() bool { + return r.TaskId != 0 +} + type ReleaseTaskResponse struct { Updated bool `json:"updated"` } @@ -276,3 +280,11 @@ type Info struct { Name string `json:"name"` Version string `json:"version"` } + +type SetSecretRequest struct { + Secret string `json:"secret"` +} + +type GetSecretResponse struct { + Secret string `json:"secret"` +} diff --git a/api/project.go b/api/project.go index e8e87be..6b7d598 100644 --- a/api/project.go +++ b/api/project.go @@ -10,7 +10,13 @@ import ( func (api *WebAPI) GetProject(r *Request) { id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || id <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } sess := api.Session.StartFasthttp(r.Ctx) manager := sess.Get("manager") @@ -263,7 +269,13 @@ func (api *WebAPI) GetProjectList(r *Request) { func (api *WebAPI) GetAssigneeStatsForProject(r *Request) { id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || id <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } stats := api.Database.GetAssigneeStats(id, 16) @@ -281,7 +293,13 @@ func (api *WebAPI) GetWorkerAccessListForProject(r *Request) { manager := sess.Get("manager") id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || id <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } if !isActionOnProjectAuthorized(id, manager, storage.ROLE_MANAGE_ACCESS, api.Database) { r.Json(JsonResponse{ @@ -352,10 +370,22 @@ func (api *WebAPI) CreateWorkerAccess(r *Request) { func (api *WebAPI) AcceptAccessRequest(r *Request) { pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || pid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } wid, err := strconv.ParseInt(r.Ctx.UserValue("wid").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || wid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } sess := api.Session.StartFasthttp(r.Ctx) manager := sess.Get("manager") @@ -385,10 +415,22 @@ func (api *WebAPI) AcceptAccessRequest(r *Request) { func (api *WebAPI) RejectAccessRequest(r *Request) { pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || pid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid project id", + }, 400) + return + } wid, err := strconv.ParseInt(r.Ctx.UserValue("wid").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || wid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid worker id", + }, 400) + return + } ok := api.Database.RejectAccessRequest(wid, pid) @@ -407,7 +449,13 @@ func (api *WebAPI) RejectAccessRequest(r *Request) { func (api *WebAPI) SetManagerRoleOnProject(r *Request) { pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) - handleErr(err, r) //todo handle invalid id + if err != nil || pid <= 0 { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid project id", + }, 400) + return + } req := &SetManagerRoleOnProjectRequest{} err = json.Unmarshal(r.Ctx.Request.Body(), req) @@ -435,3 +483,95 @@ func (api *WebAPI) SetManagerRoleOnProject(r *Request) { Ok: true, }) } + +func (api *WebAPI) SetSecret(r *Request) { + + pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) + handleErr(err, r) //todo handle invalid id + 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.ROLE_EDIT, api.Database) { + r.Json(JsonResponse{ + Ok: false, + Message: "Unauthorized", + }, 403) + return + } + + req := &SetSecretRequest{} + err = json.Unmarshal(r.Ctx.Request.Body(), req) + if err != nil { + r.Json(JsonResponse{ + Ok: false, + Message: "Could not parse request", + }, 400) + return + } + + api.Database.SetSecret(pid, req.Secret) + + r.OkJson(JsonResponse{ + Ok: true, + }) +} + +func (api *WebAPI) GetSecret(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 + } + + var secret string + + worker, err := api.validateSignature(r) + if err == nil { + secret, err = api.Database.GetSecret(pid, worker.Id) + if err != nil { + r.Json(JsonResponse{ + Ok: false, + Message: "Unauthorized", + }, 403) + return + } + r.OkJson(JsonResponse{ + Ok: true, + Content: GetSecretResponse{ + Secret: secret, + }, + }) + return + } + + sess := api.Session.StartFasthttp(r.Ctx) + manager := sess.Get("manager") + + if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_EDIT, api.Database) { + r.Json(JsonResponse{ + Ok: false, + Message: "Unauthorized", + }, 403) + return + } + + secret, _ = api.Database.GetSecret(pid, 0) + r.OkJson(JsonResponse{ + Ok: true, + Content: GetSecretResponse{ + Secret: secret, + }, + }) +} diff --git a/api/task.go b/api/task.go index 4a6c8b1..ad1e34d 100644 --- a/api/task.go +++ b/api/task.go @@ -209,7 +209,17 @@ func (api *WebAPI) ReleaseTask(r *Request) { Ok: false, Message: "Could not parse request", }, 400) + return } + + if !req.IsValid() { + r.Json(JsonResponse{ + Ok: false, + Message: "Invalid request", + }, 400) + return + } + res := api.Database.ReleaseTask(req.TaskId, worker.Id, req.Result, req.Verification) response := JsonResponse{ diff --git a/config.yml b/config.yml index 56629c3..2a4100c 100755 --- a/config.yml +++ b/config.yml @@ -3,7 +3,8 @@ server: database: conn_str: "user=task_tracker dbname=task_tracker_test sslmode=disable" - log_levels: ["debug", "error", "trace", "info", "warn"] + # log_levels: ["debug", "error", "trace", "info", "warn"] + log_levels: ["error", "info", "warn"] git: webhook_secret: "very_secret_secret" diff --git a/main/main.go b/main/main.go index a9c3500..2be616d 100644 --- a/main/main.go +++ b/main/main.go @@ -3,6 +3,7 @@ package main import ( "github.com/simon987/task_tracker/api" "github.com/simon987/task_tracker/config" + //"github.com/simon987/task_tracker/storage" "math/rand" "time" ) diff --git a/schema.sql b/schema.sql index 669298c..f5e7581 100755 --- a/schema.sql +++ b/schema.sql @@ -1,8 +1,6 @@ DROP TABLE IF EXISTS worker, project, task, log_entry, worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, worker_verifies_task; -DROP TYPE IF EXISTS status; -DROP TYPE IF EXISTS log_level; CREATE TABLE worker ( @@ -25,7 +23,8 @@ CREATE TABLE project clone_url TEXT NOT NULL, git_repo TEXT UNIQUE NOT NULL, version TEXT NOT NULL, - motd TEXT NOT NULL + motd TEXT NOT NULL, + secret TEXT NOT NULL DEFAULT '{}' ); CREATE TABLE worker_access @@ -83,7 +82,8 @@ CREATE TABLE manager_has_role_on_project ( manager INTEGER REFERENCES manager (id) NOT NULL, role SMALLINT NOT NULL, - project INTEGER REFERENCES project (id) NOT NULL + project INTEGER REFERENCES project (id) NOT NULL, + PRIMARY KEY (manager, project) ); CREATE TABLE project_monitoring_snapshot @@ -102,14 +102,18 @@ $$ DECLARE chain INTEGER; BEGIN - UPDATE project SET closed_task_count=closed_task_count + 1 WHERE id = OLD.project returning project.chain into chain; - UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; - IF chain != 0 THEN - INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, - priority, retries, max_retries, status, recipe) - VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, - old.verification_count, old.priority, 0, old.max_retries, 1, - old.recipe); + if OLD.assignee IS NOT NULL THEN + UPDATE project + SET closed_task_count=closed_task_count + 1 + WHERE id = OLD.project returning project.chain into chain; + UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; + IF chain != 0 THEN + INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, + priority, retries, max_retries, status, recipe) + VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, + old.verification_count, old.priority, 0, old.max_retries, 1, + old.recipe); + end if; end if; RETURN OLD; END; @@ -135,7 +139,7 @@ CREATE TRIGGER on_manager_insert FOR EACH ROW EXECUTE PROCEDURE on_manager_insert(); -CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver INT) RETURNS BOOLEAN AS +CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver BIGINT) RETURNS BOOLEAN AS $$ DECLARE res INT = NULL; diff --git a/storage/database.go b/storage/database.go index ba14894..aaba528 100644 --- a/storage/database.go +++ b/storage/database.go @@ -39,6 +39,8 @@ func (database *Database) getDB() *sql.DB { logrus.Fatal(err) } + db.SetMaxOpenConns(50) + database.db = db } else { err := database.db.Ping() diff --git a/storage/project.go b/storage/project.go index b35d600..4e80ed2 100644 --- a/storage/project.go +++ b/storage/project.go @@ -192,3 +192,36 @@ func (database *Database) GetAssigneeStats(pid int64, count int64) *[]AssignedTa return &assignees } + +func (database *Database) GetSecret(pid int64, workerId int64) (secret string, err error) { + + db := database.getDB() + + var row *sql.Row + if workerId == 0 { + row = db.QueryRow(`SELECT secret FROM project WHERE id=$1`, pid) + } else { + row = db.QueryRow(`SELECT secret FROM project + WHERE id =$1 AND ( + SELECT a.role_assign FROM worker_access a WHERE a.worker=$2 AND a.project=$1 + )`, pid, workerId) + } + + err = row.Scan(&secret) + handleErr(err) + return +} + +func (database *Database) SetSecret(pid int64, secret string) { + + db := database.getDB() + + res, err := db.Exec(`UPDATE project SET secret=$1 WHERE id=$2`, secret, pid) + handleErr(err) + + rowsAffected, _ := res.RowsAffected() + logrus.WithFields(logrus.Fields{ + "rowsAffected": rowsAffected, + "project": pid, + }).Info("Set secret") +} diff --git a/storage/task.go b/storage/task.go index d932af2..6ebcfde 100644 --- a/storage/task.go +++ b/storage/task.go @@ -83,7 +83,7 @@ func (database *Database) GetTask(worker *Worker) *Task { INNER JOIN project project on task.project = project.id LEFT JOIN worker_verifies_task wvt on task.id = wvt.task AND wvt.worker=$1 WHERE assignee IS NULL AND task.status=1 - AND (project.public OR EXISTS ( + AND (project.public OR ( SELECT a.role_assign FROM worker_access a WHERE a.worker=$1 AND a.project=project.id )) AND wvt.task IS NULL @@ -144,9 +144,10 @@ func (database Database) ReleaseTask(id int64, workerId int64, result TaskResult var taskUpdated bool if result == TR_OK { - row := db.QueryRow(`SELECT release_task_ok($1,$2,$3)`, workerId, id, verification) + row := db.QueryRow(fmt.Sprintf(`SELECT release_task_ok(%d,%d,%d)`, workerId, id, verification)) - _ = row.Scan(&taskUpdated) + err := row.Scan(&taskUpdated) + handleErr(err) } else if result == TR_FAIL { res, err := db.Exec(`UPDATE task SET (status, assignee, retries) = (CASE WHEN retries+1 >= max_retries THEN 2 ELSE 1 END, NULL, retries+1) diff --git a/test/api_task_test.go b/test/api_task_test.go index 21f8868..6357f62 100644 --- a/test/api_task_test.go +++ b/test/api_task_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/simon987/task_tracker/api" "github.com/simon987/task_tracker/storage" + "math" "testing" ) @@ -780,6 +781,42 @@ func TestTaskChain(t *testing.T) { } } +func TestTaskReleaseBigInt(t *testing.T) { + + createTask(api.SubmitTaskRequest{ + Project: testProject, + VerificationCount: 1, + Recipe: "bigint", + }, testWorker) + createTask(api.SubmitTaskRequest{ + Project: testProject, + VerificationCount: 1, + Recipe: "smallint", + }, testWorker) + + tid := getTaskFromProject(testProject, testWorker).Content.Task.Id + tid2 := getTaskFromProject(testProject, testWorker).Content.Task.Id + + r := releaseTask(api.ReleaseTaskRequest{ + Verification: math.MaxInt64, + Result: storage.TR_OK, + TaskId: tid, + }, testWorker) + + r2 := releaseTask(api.ReleaseTaskRequest{ + Verification: math.MinInt64, + Result: storage.TR_OK, + TaskId: tid2, + }, testWorker) + + if r.Content.Updated != true { + t.Error() + } + if r2.Content.Updated != true { + t.Error() + } +} + func createTask(request api.SubmitTaskRequest, worker *storage.Worker) (ar api.JsonResponse) { r := Post("/task/submit", request, worker, nil) UnmarshalResponse(r, &ar) diff --git a/test/main_test.go b/test/main_test.go index c36c9d7..291883b 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -3,6 +3,7 @@ package test import ( "github.com/simon987/task_tracker/api" "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "net/http" "testing" "time" @@ -12,6 +13,9 @@ var testApi *api.WebAPI var testAdminCtx *http.Client var testUserCtx *http.Client +var testProject int64 +var testWorker *storage.Worker + func TestMain(m *testing.M) { config.SetupConfig() @@ -25,6 +29,19 @@ func TestMain(m *testing.M) { testAdminCtx = getSessionCtx("testadmin", "testadmin", true) testUserCtx = getSessionCtx("testuser", "testuser", false) + testProject = createProjectAsAdmin(api.CreateProjectRequest{ + Name: "generictestproject", + Public: false, + }).Content.Id + testWorker = createWorker(api.CreateWorkerRequest{ + Alias: "generictestworker", + }).Content.Worker + requestAccess(api.CreateWorkerAccessRequest{ + Project: testProject, + Assign: true, + Submit: true, + }, testWorker) + acceptAccessRequest(testProject, testWorker.Id, testAdminCtx) m.Run() } diff --git a/test/schema.sql b/test/schema.sql index 650492b..f5e7581 100755 --- a/test/schema.sql +++ b/test/schema.sql @@ -1,8 +1,6 @@ DROP TABLE IF EXISTS worker, project, task, log_entry, worker_access, manager, manager_has_role_on_project, project_monitoring_snapshot, worker_verifies_task; -DROP TYPE IF EXISTS status; -DROP TYPE IF EXISTS log_level; CREATE TABLE worker ( @@ -25,7 +23,8 @@ CREATE TABLE project clone_url TEXT NOT NULL, git_repo TEXT UNIQUE NOT NULL, version TEXT NOT NULL, - motd TEXT NOT NULL + motd TEXT NOT NULL, + secret TEXT NOT NULL DEFAULT '{}' ); CREATE TABLE worker_access @@ -103,14 +102,18 @@ $$ DECLARE chain INTEGER; BEGIN - UPDATE project SET closed_task_count=closed_task_count + 1 WHERE id = OLD.project returning project.chain into chain; - UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; - IF chain != 0 THEN - INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, - priority, retries, max_retries, status, recipe) - VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, - old.verification_count, old.priority, 0, old.max_retries, 1, - old.recipe); + if OLD.assignee IS NOT NULL THEN + UPDATE project + SET closed_task_count=closed_task_count + 1 + WHERE id = OLD.project returning project.chain into chain; + UPDATE worker SET closed_task_count=closed_task_count + 1 WHERE id = OLD.assignee; + IF chain != 0 THEN + INSERT into task (hash64, project, assignee, max_assign_time, assign_time, verification_count, + priority, retries, max_retries, status, recipe) + VALUES (old.hash64, chain, NULL, old.max_assign_time, NULL, + old.verification_count, old.priority, 0, old.max_retries, 1, + old.recipe); + end if; end if; RETURN OLD; END; @@ -136,7 +139,7 @@ CREATE TRIGGER on_manager_insert FOR EACH ROW EXECUTE PROCEDURE on_manager_insert(); -CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver INT) RETURNS BOOLEAN AS +CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver BIGINT) RETURNS BOOLEAN AS $$ DECLARE res INT = NULL; diff --git a/web/angular/src/app/api.service.ts b/web/angular/src/app/api.service.ts index 236b47a..80c4f1a 100755 --- a/web/angular/src/app/api.service.ts +++ b/web/angular/src/app/api.service.ts @@ -6,7 +6,7 @@ import {Credentials} from "./models/credentials"; @Injectable() export class ApiService { - private url: string = "http://localhost/api"; + public url: string = "http://localhost/api"; private options: { withCredentials: true, responseType: "json" @@ -66,36 +66,44 @@ export class ApiService { } getProjectAccess(project: number) { - return this.http.get(this.url + `/project/access_list/${project}`) + return this.http.get(this.url + `/project/access_list/${project}`, this.options) } getManagerList() { - return this.http.get(this.url + "/manager/list") + return this.http.get(this.url + "/manager/list", this.options) } getManagerListWithRoleOn(project: number) { - return this.http.get(this.url + "/manager/list_for_project/" + project) + return this.http.get(this.url + "/manager/list_for_project/" + project, this.options) } promote(managerId: number) { - return this.http.get(this.url + `/manager/promote/${managerId}`) + return this.http.get(this.url + `/manager/promote/${managerId}`, this.options) } demote(managerId: number) { - return this.http.get(this.url + `/manager/demote/${managerId}`) + return this.http.get(this.url + `/manager/demote/${managerId}`, this.options) } acceptWorkerAccessRequest(wid: number, pid: number) { - return this.http.post(this.url + `/project/accept_request/${pid}/${wid}`, null) + return this.http.post(this.url + `/project/accept_request/${pid}/${wid}`, null, this.options) } rejectWorkerAccessRequest(wid: number, pid: number) { - return this.http.post(this.url + `/project/reject_request/${pid}/${wid}`, null) + return this.http.post(this.url + `/project/reject_request/${pid}/${wid}`, null, this.options) } setManagerRoleOnProject(pid: number, role: number, manager: number) { return this.http.post(this.url + `/manager/set_role_for_project/${pid}`, - {"role": role, "manager": manager}) + {"role": role, "manager": manager}, this.options) + } + + getSecret(pid: number) { + return this.http.get(this.url + `/project/secret/${pid}`,) + } + + setSecret(pid: number, secret: string) { + return this.http.post(this.url + `/project/secret/${pid}`, {"secret": secret}) } } diff --git a/web/angular/src/app/app-routing.module.ts b/web/angular/src/app/app-routing.module.ts index 65f4b76..b8ebdc3 100755 --- a/web/angular/src/app/app-routing.module.ts +++ b/web/angular/src/app/app-routing.module.ts @@ -13,8 +13,11 @@ import {AccountDetailsComponent} from "./account-details/account-details.compone import {WorkerDashboardComponent} from "./worker-dashboard/worker-dashboard.component"; import {ProjectPermsComponent} from "./project-perms/project-perms.component"; import {ManagerListComponent} from "./manager-list/manager-list.component"; +import {IndexComponent} from "./index/index.component"; +import {ProjectSecretComponent} from "./project-secret/project-secret.component"; const routes: Routes = [ + {path: "", component: IndexComponent}, {path: "log", component: LogsComponent}, {path: "login", component: LoginComponent}, {path: "account", component: AccountDetailsComponent}, @@ -22,9 +25,10 @@ const routes: Routes = [ {path: "project/:id", component: ProjectDashboardComponent}, {path: "project/:id/update", component: UpdateProjectComponent}, {path: "project/:id/perms", component: ProjectPermsComponent}, + {path: "project/:id/secret", component: ProjectSecretComponent}, {path: "new_project", component: CreateProjectComponent}, {path: "workers", component: WorkerDashboardComponent}, - {path: "manager_list", component: ManagerListComponent} + {path: "manager_list", component: ManagerListComponent}, ]; @NgModule({ diff --git a/web/angular/src/app/app.module.ts b/web/angular/src/app/app.module.ts index f9ebf6e..b888e25 100755 --- a/web/angular/src/app/app.module.ts +++ b/web/angular/src/app/app.module.ts @@ -27,6 +27,7 @@ import { MatSlideToggleModule, MatSnackBarModule, MatSortModule, + MatStepperModule, MatTableModule, MatTabsModule, MatToolbarModule, @@ -52,6 +53,8 @@ import {ManagerListComponent} from './manager-list/manager-list.component'; import {ProjectSelectComponent} from './project-select/project-select.component'; import {ManagerSelectComponent} from './manager-select/manager-select.component'; import {ProjectIconComponent} from './project-icon/project-icon.component'; +import {IndexComponent} from './index/index.component'; +import {ProjectSecretComponent} from './project-secret/project-secret.component'; export function createTranslateLoader(http: HttpClient) { @@ -76,6 +79,8 @@ export function createTranslateLoader(http: HttpClient) { ProjectSelectComponent, ManagerSelectComponent, ProjectIconComponent, + IndexComponent, + ProjectSecretComponent, ], imports: [ BrowserModule, @@ -114,7 +119,8 @@ export function createTranslateLoader(http: HttpClient) { MatProgressBarModule, MatTabsModule, MatListModule, - MatButtonToggleModule + MatButtonToggleModule, + MatStepperModule ], exports: [], diff --git a/web/angular/src/app/manager-list/manager-list.component.ts b/web/angular/src/app/manager-list/manager-list.component.ts index 989e8cf..dd95ba0 100644 --- a/web/angular/src/app/manager-list/manager-list.component.ts +++ b/web/angular/src/app/manager-list/manager-list.component.ts @@ -6,6 +6,7 @@ import {MatPaginator, MatSort, MatTableDataSource} from "@angular/material"; import * as moment from "moment" import {AuthService} from "../auth.service"; +import {Manager} from "../models/manager"; @Component({ selector: 'app-manager-list', 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 cd73769..a037c13 100644 --- a/web/angular/src/app/project-dashboard/project-dashboard.component.html +++ b/web/angular/src/app/project-dashboard/project-dashboard.component.html @@ -56,11 +56,13 @@ - - + - + 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 bc29c98..fe460eb 100644 --- a/web/angular/src/app/project-dashboard/project-dashboard.component.ts +++ b/web/angular/src/app/project-dashboard/project-dashboard.component.ts @@ -7,6 +7,7 @@ import {Chart} from "chart.js"; import {AssignedTasks, MonitoringSnapshot} from "../models/monitoring"; import {TranslateService} from "@ngx-translate/core"; import {MessengerService} from "../messenger.service"; +import {AuthService} from "../auth.service"; @Component({ @@ -46,6 +47,7 @@ export class ProjectDashboardComponent implements OnInit { constructor(private apiService: ApiService, private route: ActivatedRoute, private translate: TranslateService, + public auth: AuthService, private messenger: MessengerService) { } diff --git a/web/angular/src/app/project-list/project-list.component.html b/web/angular/src/app/project-list/project-list.component.html index 8e3fffe..e97542a 100755 --- a/web/angular/src/app/project-list/project-list.component.html +++ b/web/angular/src/app/project-list/project-list.component.html @@ -29,6 +29,10 @@ *ngIf="authService.logged"> perm_identity {{"project.perms" | translate}} + diff --git a/web/angular/src/app/project-perms/project-perms.component.html b/web/angular/src/app/project-perms/project-perms.component.html index 44cc861..4cb384b 100644 --- a/web/angular/src/app/project-perms/project-perms.component.html +++ b/web/angular/src/app/project-perms/project-perms.component.html @@ -9,7 +9,7 @@ {{"perms.subtitle" | translate}} - +

{{"perms.workers" | translate}}

@@ -65,5 +65,8 @@

+ + + diff --git a/web/angular/src/app/project-secret/project-secret.component.css b/web/angular/src/app/project-secret/project-secret.component.css new file mode 100644 index 0000000..ef0ee71 --- /dev/null +++ b/web/angular/src/app/project-secret/project-secret.component.css @@ -0,0 +1,3 @@ +.mat-form-field { + width: 100%; +} diff --git a/web/angular/src/app/project-secret/project-secret.component.html b/web/angular/src/app/project-secret/project-secret.component.html new file mode 100644 index 0000000..bae32f1 --- /dev/null +++ b/web/angular/src/app/project-secret/project-secret.component.html @@ -0,0 +1,22 @@ +
+ + + {{"secret.title" | translate}} + {{"secret.subtitle" | translate}} + + + + + {{"secret.secret" | translate}} + + + + + + + + +
diff --git a/web/angular/src/app/project-secret/project-secret.component.ts b/web/angular/src/app/project-secret/project-secret.component.ts new file mode 100644 index 0000000..d2ed4f4 --- /dev/null +++ b/web/angular/src/app/project-secret/project-secret.component.ts @@ -0,0 +1,46 @@ +import {Component, OnInit} from '@angular/core'; +import {AuthService} from "../auth.service"; +import {ApiService} from "../api.service"; +import {ActivatedRoute} from "@angular/router"; +import {TranslateService} from "@ngx-translate/core"; +import {MessengerService} from "../messenger.service"; + +@Component({ + selector: 'app-project-secret', + templateUrl: './project-secret.component.html', + styleUrls: ['./project-secret.component.css'] +}) +export class ProjectSecretComponent implements OnInit { + + secret: string; + projectId: number; + + constructor(private auth: AuthService, + private apiService: ApiService, + private translate: TranslateService, + private messenger: MessengerService, + private route: ActivatedRoute) { + } + + ngOnInit() { + this.route.params.subscribe(params => { + this.projectId = params["id"]; + this.getSecret(); + }); + } + + getSecret() { + this.apiService.getSecret(this.projectId).subscribe(data => { + this.secret = data["content"]["secret"] + }, error => { + this.translate.get("messenger.unauthorized").subscribe(t => this.messenger.show(t)) + }) + } + + onUpdate() { + this.apiService.setSecret(this.projectId, this.secret).subscribe(data => { + this.translate.get("secret.ok").subscribe(t => this.messenger.show(t)) + }) + + } +} diff --git a/web/angular/src/app/update-project/update-project.component.html b/web/angular/src/app/update-project/update-project.component.html index e1a6630..628c7ca 100644 --- a/web/angular/src/app/update-project/update-project.component.html +++ b/web/angular/src/app/update-project/update-project.component.html @@ -41,6 +41,7 @@
+ diff --git a/web/angular/src/assets/i18n/en.json b/web/angular/src/assets/i18n/en.json index e4fa40b..dfdefbe 100644 --- a/web/angular/src/assets/i18n/en.json +++ b/web/angular/src/assets/i18n/en.json @@ -67,7 +67,8 @@ "perms": "Permissions", "chain": "Chain tasks to", "manager_select": "Give access to manager", - "version": "Git version (commit hash)" + "version": "Git version (commit hash)", + "secret": "Secret" }, "dashboard": { "title": "Dashboard for", @@ -137,5 +138,16 @@ "project_select": { "list_loading": "Loading project list...", "none": "None" + }, + "index": { + "version": "Latest commit hash here", + "alias": "Relevent alias" + }, + "secret": { + "title": "Project secret", + "subtitle": "You can set project configuration here. It is only accessible by workers with ASSIGN access to this project", + "secret": "Secret", + "update": "Update", + "ok": "Updated" } } diff --git a/web/angular/src/assets/i18n/fr.json b/web/angular/src/assets/i18n/fr.json index 827db1d..b9cad64 100644 --- a/web/angular/src/assets/i18n/fr.json +++ b/web/angular/src/assets/i18n/fr.json @@ -118,7 +118,8 @@ "set": "Changements enregistrés", "workers": "Workers", "no_workers": "Aucun Worker n'a explicitement accès à ce projet", - "managers": "Managers" + "managers": "Managers", + "secret": "Secret" }, "messenger": { "close": "Fermer", @@ -139,6 +140,17 @@ "project_select": { "list_loading": "Chargement de la liste de projets...", "none": "Aucun" + }, + "index": { + "version": "Le dernier hash de commit", + "alias": "Alias pertinent" + }, + "secret": { + "title": "Secret", + "subtitle": "Vous pouvez définir la configuration du projet ici. Ce n'est accessible que par les Workers ayant accès au projet", + "secret": "Secret", + "update": "Mettre à jour", + "ok": "Mis à jour" } }