mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-17 09:26:43 +00:00
some bug fixes, some optimizations
This commit is contained in:
parent
51eb9ae6da
commit
4edf354f8d
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
<a href='https://github.com/jpoles1/gopherbadger' target='_blank'></a>
|
<a href='https://github.com/jpoles1/gopherbadger' target='_blank'></a>
|
||||||
|
|
||||||
### Running tests
|
### Running tests
|
||||||
```bash
|
```bash
|
||||||
cd test/
|
cd test/
|
||||||
go test -bench
|
go test
|
||||||
```
|
```
|
13
api/log.go
13
api/log.go
@ -30,6 +30,11 @@ type GetLogResponse struct {
|
|||||||
Logs *[]storage.LogEntry `json:"logs"`
|
Logs *[]storage.LogEntry `json:"logs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogResponse struct {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
func (e *LogRequest) Time() time.Time {
|
func (e *LogRequest) Time() time.Time {
|
||||||
|
|
||||||
t := time.Unix(e.TimeStamp, 0)
|
t := time.Unix(e.TimeStamp, 0)
|
||||||
@ -89,7 +94,7 @@ func (api *WebAPI) LogTrace(r *Request) {
|
|||||||
|
|
||||||
entry, err := api.parseLogEntry(r)
|
entry, err := api.parseLogEntry(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Json(GetLogResponse{
|
r.Json(LogResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Message: "Could not parse request",
|
Message: "Could not parse request",
|
||||||
}, 400)
|
}, 400)
|
||||||
@ -106,7 +111,7 @@ func (api *WebAPI) LogInfo(r *Request) {
|
|||||||
|
|
||||||
entry, err := api.parseLogEntry(r)
|
entry, err := api.parseLogEntry(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Json(GetLogResponse{
|
r.Json(LogResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Message: "Could not parse request",
|
Message: "Could not parse request",
|
||||||
}, 400)
|
}, 400)
|
||||||
@ -123,7 +128,7 @@ func (api *WebAPI) LogWarn(r *Request) {
|
|||||||
|
|
||||||
entry, err := api.parseLogEntry(r)
|
entry, err := api.parseLogEntry(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Json(GetLogResponse{
|
r.Json(LogResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Message: "Could not parse request",
|
Message: "Could not parse request",
|
||||||
}, 400)
|
}, 400)
|
||||||
@ -140,7 +145,7 @@ func (api *WebAPI) LogError(r *Request) {
|
|||||||
|
|
||||||
entry, err := api.parseLogEntry(r)
|
entry, err := api.parseLogEntry(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Json(GetLogResponse{
|
r.Json(LogResponse{
|
||||||
Ok: false,
|
Ok: false,
|
||||||
Message: "Could not parse request",
|
Message: "Could not parse request",
|
||||||
}, 400)
|
}, 400)
|
||||||
|
@ -202,7 +202,15 @@ func (api *WebAPI) ProjectGet(r *Request) {
|
|||||||
|
|
||||||
func (api *WebAPI) ProjectGetAllProjects(r *Request) {
|
func (api *WebAPI) ProjectGetAllProjects(r *Request) {
|
||||||
|
|
||||||
projects := api.Database.GetAllProjects()
|
worker, _ := api.validateSignature(r)
|
||||||
|
|
||||||
|
var id int64
|
||||||
|
if worker == nil {
|
||||||
|
id = 0
|
||||||
|
} else {
|
||||||
|
id = worker.Id
|
||||||
|
}
|
||||||
|
projects := api.Database.GetAllProjects(id)
|
||||||
|
|
||||||
r.OkJson(GetAllProjectsResponse{
|
r.OkJson(GetAllProjectsResponse{
|
||||||
Ok: true,
|
Ok: true,
|
||||||
|
17
api/task.go
17
api/task.go
@ -156,11 +156,20 @@ func (api *WebAPI) TaskGet(r *Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task := api.Database.GetTask(worker)
|
task := api.Database.GetTask(worker)
|
||||||
|
if task == nil {
|
||||||
|
|
||||||
r.OkJson(GetTaskResponse{
|
r.OkJson(GetTaskResponse{
|
||||||
Ok: true,
|
Ok: false,
|
||||||
Task: task,
|
Message: "No task available",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
r.OkJson(GetTaskResponse{
|
||||||
|
Ok: true,
|
||||||
|
Task: task,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api WebAPI) validateSignature(r *Request) (*storage.Worker, error) {
|
func (api WebAPI) validateSignature(r *Request) (*storage.Worker, error) {
|
||||||
|
@ -8,9 +8,9 @@ database:
|
|||||||
git:
|
git:
|
||||||
webhook_secret: "very_secret_secret"
|
webhook_secret: "very_secret_secret"
|
||||||
# Github: sha1, Gogs: sha256
|
# Github: sha1, Gogs: sha256
|
||||||
webhook_hash: "sha1"
|
webhook_hash: "sha256"
|
||||||
# Github: 'X-Hub-Signature', Gogs: 'X-Gogs-Signature'
|
# Github: 'X-Hub-Signature', Gogs: 'X-Gogs-Signature'
|
||||||
webhook_sig_header: "X-Hub-Signature"
|
webhook_sig_header: "X-Gogs-Signature"
|
||||||
|
|
||||||
log:
|
log:
|
||||||
# panic, fatal, error, warn, info, debug, trace
|
# panic, fatal, error, warn, info, debug, trace
|
||||||
|
33
schema.sql
33
schema.sql
@ -113,3 +113,36 @@ CREATE TRIGGER on_task_delete
|
|||||||
ON task
|
ON task
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE PROCEDURE on_task_delete_proc();
|
EXECUTE PROCEDURE on_task_delete_proc();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver INT) RETURNS BOOLEAN AS
|
||||||
|
$$
|
||||||
|
DECLARE
|
||||||
|
res INT = NULL;
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res;
|
||||||
|
|
||||||
|
IF res IS NULL THEN
|
||||||
|
INSERT INTO worker_verifies_task (worker, verification_hash, task)
|
||||||
|
SELECT wid, ver, task.id
|
||||||
|
FROM task
|
||||||
|
WHERE assignee = wid;
|
||||||
|
|
||||||
|
DELETE
|
||||||
|
FROM task
|
||||||
|
WHERE id = tid
|
||||||
|
AND assignee = wid
|
||||||
|
AND (SELECT COUNT(*) as vcnt
|
||||||
|
FROM worker_verifies_task wvt
|
||||||
|
WHERE task = tid
|
||||||
|
GROUP BY wvt.verification_hash
|
||||||
|
ORDER BY vcnt DESC
|
||||||
|
LIMIT 1) >= task.verification_count RETURNING task.id INTO res;
|
||||||
|
|
||||||
|
IF res IS NULL THEN
|
||||||
|
UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
RETURN res IS NOT NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE 'plpgsql';
|
||||||
|
@ -104,8 +104,8 @@ func (database *Database) UpdateManager(manager *Manager) {
|
|||||||
func (database *Database) UpdateManagerPassword(manager *Manager, newPassword []byte) {
|
func (database *Database) UpdateManagerPassword(manager *Manager, newPassword []byte) {
|
||||||
|
|
||||||
hash := crypto.SHA512.New()
|
hash := crypto.SHA512.New()
|
||||||
hash.Write([]byte(manager.Username))
|
|
||||||
hash.Write(newPassword)
|
hash.Write(newPassword)
|
||||||
|
hash.Write([]byte(manager.Username))
|
||||||
hashedPassword := hash.Sum(nil)
|
hashedPassword := hash.Sum(nil)
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
|
@ -131,14 +131,27 @@ func (database *Database) UpdateProject(project *Project) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (database Database) GetAllProjects() *[]Project {
|
func (database Database) GetAllProjects(workerId int64) *[]Project {
|
||||||
projects := make([]Project, 0)
|
projects := make([]Project, 0)
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
rows, err := db.Query(`SELECT
|
var rows *sql.Rows
|
||||||
|
var err error
|
||||||
|
if workerId == 0 {
|
||||||
|
rows, err = db.Query(`SELECT
|
||||||
Id, priority, name, clone_url, git_repo, version, motd, public
|
Id, priority, name, clone_url, git_repo, version, motd, public
|
||||||
FROM project
|
FROM project
|
||||||
|
LEFT JOIN worker_has_access_to_project whatp ON whatp.project = id
|
||||||
|
WHERE public
|
||||||
ORDER BY name`)
|
ORDER BY name`)
|
||||||
|
} else {
|
||||||
|
rows, err = db.Query(`SELECT
|
||||||
|
Id, priority, name, clone_url, git_repo, version, motd, public
|
||||||
|
FROM project
|
||||||
|
LEFT JOIN worker_has_access_to_project whatp ON whatp.project = id
|
||||||
|
WHERE public OR whatp.worker = $1
|
||||||
|
ORDER BY name`, workerId)
|
||||||
|
}
|
||||||
handleErr(err)
|
handleErr(err)
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
@ -134,52 +134,34 @@ func (database Database) ReleaseTask(id int64, workerId int64, result TaskResult
|
|||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
|
|
||||||
var rowsAffected int64
|
var taskUpdated bool
|
||||||
if result == TR_OK {
|
if result == TR_OK {
|
||||||
var pid int64
|
row := db.QueryRow(`SELECT release_task_ok($1,$2,$3)`, workerId, id, verification)
|
||||||
|
|
||||||
//If no verification is required
|
|
||||||
row := db.QueryRow(`DELETE FROM task WHERE id=$1 AND assignee=$2 AND verification_count < 2
|
|
||||||
RETURNING project`, id, workerId)
|
|
||||||
err := row.Scan(&pid)
|
|
||||||
if err == nil {
|
|
||||||
rowsAffected = 1
|
|
||||||
} else {
|
|
||||||
//If verification is required
|
|
||||||
_, err = db.Exec(`INSERT INTO worker_verifies_task (worker, verification_hash, task)
|
|
||||||
SELECT $1,$2,task.id FROM task WHERE assignee=$1`, workerId, verification)
|
|
||||||
handleErr(err)
|
|
||||||
|
|
||||||
res, _ := db.Exec(`DELETE FROM task WHERE id=$1 AND assignee=$2 AND
|
|
||||||
(SELECT COUNT(*) as vcnt FROM worker_verifies_task wvt WHERE task=$1
|
|
||||||
GROUP BY wvt.verification_hash ORDER BY vcnt DESC LIMIT 1) >= task.verification_count`,
|
|
||||||
id, workerId)
|
|
||||||
rowsAffected, _ = res.RowsAffected()
|
|
||||||
|
|
||||||
_, _ = db.Exec(`UPDATE task SET assignee=NULL WHERE id=$1 AND assignee=$2`, id, workerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
_ = row.Scan(&taskUpdated)
|
||||||
} else if result == TR_FAIL {
|
} else if result == TR_FAIL {
|
||||||
res, err := db.Exec(`UPDATE task SET (status, assignee, retries) =
|
res, err := db.Exec(`UPDATE task SET (status, assignee, retries) =
|
||||||
(CASE WHEN retries+1 >= max_retries THEN 2 ELSE 1 END, NULL, retries+1)
|
(CASE WHEN retries+1 >= max_retries THEN 2 ELSE 1 END, NULL, retries+1)
|
||||||
WHERE id=$1 AND assignee=$2`, id, workerId)
|
WHERE id=$1 AND assignee=$2`, id, workerId)
|
||||||
handleErr(err)
|
handleErr(err)
|
||||||
rowsAffected, _ = res.RowsAffected()
|
rowsAffected, _ := res.RowsAffected()
|
||||||
|
taskUpdated = rowsAffected == 1
|
||||||
} else if result == TR_SKIP {
|
} else if result == TR_SKIP {
|
||||||
res, err := db.Exec(`UPDATE task SET (status, assignee) = (1, NULL)
|
res, err := db.Exec(`UPDATE task SET (status, assignee) = (1, NULL)
|
||||||
WHERE id=$1 AND assignee=$2`, id, workerId)
|
WHERE id=$1 AND assignee=$2`, id, workerId)
|
||||||
handleErr(err)
|
handleErr(err)
|
||||||
rowsAffected, _ = res.RowsAffected()
|
rowsAffected, _ := res.RowsAffected()
|
||||||
|
taskUpdated = rowsAffected == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"rowsAffected": rowsAffected,
|
"taskUpdated": taskUpdated,
|
||||||
"taskId": id,
|
"taskId": id,
|
||||||
"workerId": workerId,
|
"workerId": workerId,
|
||||||
"verification": verification,
|
"verification": verification,
|
||||||
}).Trace("Database.ReleaseTask")
|
}).Trace("Database.ReleaseTask")
|
||||||
|
|
||||||
return rowsAffected == 1
|
return taskUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *Task {
|
func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *Task {
|
||||||
|
@ -113,3 +113,36 @@ CREATE TRIGGER on_task_delete
|
|||||||
ON task
|
ON task
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE PROCEDURE on_task_delete_proc();
|
EXECUTE PROCEDURE on_task_delete_proc();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION release_task_ok(wid INT, tid INT, ver INT) RETURNS BOOLEAN AS
|
||||||
|
$$
|
||||||
|
DECLARE
|
||||||
|
res INT = NULL;
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM task WHERE id = tid AND assignee = wid AND verification_count < 2 RETURNING project INTO res;
|
||||||
|
|
||||||
|
IF res IS NULL THEN
|
||||||
|
INSERT INTO worker_verifies_task (worker, verification_hash, task)
|
||||||
|
SELECT wid, ver, task.id
|
||||||
|
FROM task
|
||||||
|
WHERE assignee = wid;
|
||||||
|
|
||||||
|
DELETE
|
||||||
|
FROM task
|
||||||
|
WHERE id = tid
|
||||||
|
AND assignee = wid
|
||||||
|
AND (SELECT COUNT(*) as vcnt
|
||||||
|
FROM worker_verifies_task wvt
|
||||||
|
WHERE task = tid
|
||||||
|
GROUP BY wvt.verification_hash
|
||||||
|
ORDER BY vcnt DESC
|
||||||
|
LIMIT 1) >= task.verification_count RETURNING task.id INTO res;
|
||||||
|
|
||||||
|
IF res IS NULL THEN
|
||||||
|
UPDATE task SET assignee= NULL WHERE id = tid AND assignee = wid;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
RETURN res IS NOT NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE 'plpgsql';
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
gopherbadger -md="README.md" -png=false -style flat-square
|
|
||||||
rm coverage.out
|
|
@ -4,35 +4,31 @@
|
|||||||
<mat-card-subtitle>{{"project.create_subtitle" | translate}}</mat-card-subtitle>
|
<mat-card-subtitle>{{"project.create_subtitle" | translate}}</mat-card-subtitle>
|
||||||
|
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<form (ngSubmit)="onSubmit()">
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>{{"project.name" | translate}}</mat-label>
|
<mat-label>{{"project.name" | translate}}</mat-label>
|
||||||
<input type="text" matInput [(ngModel)]="project.name" name="name" [placeholder]="'project.name' | translate">
|
<input type="text" matInput [(ngModel)]="project.name" [placeholder]="'project.name' | translate">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>{{ "project.clone_url" | translate}}</mat-label>
|
<mat-label>{{ "project.clone_url" | translate}}</mat-label>
|
||||||
<input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url"
|
<input type="text" matInput [(ngModel)]="project.clone_url"
|
||||||
[placeholder]="'project.clone_url_placeholder' | translate">
|
[placeholder]="'project.clone_url_placeholder' | translate">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>{{ "project.git_repo" | translate }}</mat-label>
|
<mat-label>{{ "project.git_repo" | translate }}</mat-label>
|
||||||
<input type="text" matInput [(ngModel)]="project.git_repo" name="git_repo"
|
<input type="text" matInput [(ngModel)]="project.git_repo"
|
||||||
[placeholder]="'project.git_repo_placeholder' | translate">
|
[placeholder]="'project.git_repo_placeholder' | translate">
|
||||||
<mat-hint align="start">
|
<mat-hint align="start">
|
||||||
{{"project.git_repo_hint" | translate}}
|
{{"project.git_repo_hint" | translate}}
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-checkbox matInput [(ngModel)]="project.public" name="public" style="padding-top: 1em">
|
<mat-checkbox matInput [(ngModel)]="project.public" style="padding-top: 1em">
|
||||||
{{"project.public" | translate}}</mat-checkbox>
|
{{"project.public" | translate}}</mat-checkbox>
|
||||||
|
|
||||||
<input type="hidden" name="version" value="{{project.version}}">
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-raised-button color="primary" type="submit">{{'project.create' | translate}}</button>
|
<button mat-raised-button color="primary" (click)="onSubmit()">{{'project.create' | translate}}</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,7 +71,7 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.noTasks = false;
|
this.noTasks = false;
|
||||||
|
|
||||||
this.timeline.data.labels = this.snapshots.map(s => s.time_stamp as any);
|
this.timeline.data.labels = this.snapshots.map(s => s.time_stamp * 1000 as any);
|
||||||
this.timeline.data.datasets = this.makeTimelineDataset(this.snapshots);
|
this.timeline.data.datasets = this.makeTimelineDataset(this.snapshots);
|
||||||
this.timeline.update();
|
this.timeline.update();
|
||||||
this.statusPie.data.datasets = [
|
this.statusPie.data.datasets = [
|
||||||
@ -164,7 +164,7 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
this.timeline = new Chart(ctx, {
|
this.timeline = new Chart(ctx, {
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: {
|
data: {
|
||||||
labels: this.snapshots.map(s => s.time_stamp as any),
|
labels: this.snapshots.map(s => s.time_stamp * 1000 as any),
|
||||||
datasets: this.makeTimelineDataset(this.snapshots),
|
datasets: this.makeTimelineDataset(this.snapshots),
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
@ -198,11 +198,16 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
|
|
||||||
private setupStatusPie() {
|
private setupStatusPie() {
|
||||||
|
|
||||||
if (this.lastSnapshot == null || (this.lastSnapshot.awaiting_verification_count == 0 &&
|
if (this.lastSnapshot == undefined || (this.lastSnapshot.awaiting_verification_count == 0 &&
|
||||||
this.lastSnapshot.closed_task_count == 0 &&
|
this.lastSnapshot.closed_task_count == 0 &&
|
||||||
this.lastSnapshot.new_task_count == 0 &&
|
this.lastSnapshot.new_task_count == 0 &&
|
||||||
this.lastSnapshot.failed_task_count == 0)) {
|
this.lastSnapshot.failed_task_count == 0)) {
|
||||||
this.noTasks = true;
|
this.noTasks = true;
|
||||||
|
|
||||||
|
this.lastSnapshot = {
|
||||||
|
closed_task_count: 0, time_stamp: 0, failed_task_count: 0,
|
||||||
|
new_task_count: 0, awaiting_verification_count: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let elem = document.getElementById("status-pie") as any;
|
let elem = document.getElementById("status-pie") as any;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user