mirror of
https://github.com/simon987/task_tracker.git
synced 2025-12-10 21:48:52 +00:00
Initial commit
This commit is contained in:
25
api/error.go
Normal file
25
api/error.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
Message string `json:"message"`
|
||||
StackTrace string `json:"stack_trace"`
|
||||
|
||||
}
|
||||
|
||||
func handleErr(err error, r *Request) {
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(err.Error())
|
||||
//debug.PrintStack()
|
||||
|
||||
r.Json(ErrorResponse{
|
||||
Message: err.Error(),
|
||||
StackTrace: string(debug.Stack()),
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
48
api/helper.go
Normal file
48
api/helper.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Ctx *fasthttp.RequestCtx
|
||||
}
|
||||
|
||||
|
||||
func (r *Request) OkJson(object interface{}) {
|
||||
|
||||
resp, err := json.Marshal(object)
|
||||
handleErr(err, r)
|
||||
|
||||
r.Ctx.Response.Header.Set("Content-Type", "application/json")
|
||||
_, err = r.Ctx.Write(resp)
|
||||
handleErr(err, r)
|
||||
}
|
||||
|
||||
func (r *Request) Json(object interface{}, code int) {
|
||||
|
||||
resp, err := json.Marshal(object)
|
||||
if err != nil {
|
||||
fmt.Fprint(r.Ctx,"Error during json encoding of error")
|
||||
logrus.Error("Error during json encoding of error")
|
||||
}
|
||||
|
||||
r.Ctx.Response.SetStatusCode(code)
|
||||
r.Ctx.Response.Header.Set("Content-Type", "application/json")
|
||||
_, err = r.Ctx.Write(resp)
|
||||
if err != nil {
|
||||
panic(err) //todo handle differently
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *Request) GetJson(x interface{}) bool {
|
||||
|
||||
err := json.Unmarshal(r.Ctx.Request.Body(), x)
|
||||
handleErr(err, r)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
91
api/log.go
Normal file
91
api/log.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/valyala/fasthttp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RequestHandler func(*Request)
|
||||
|
||||
type LogEntry struct {
|
||||
Scope string `json:"scope"`
|
||||
Message string `json:"Message"`
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
func (e *LogEntry) Time() time.Time {
|
||||
|
||||
t := time.Unix(e.TimeStamp, 0)
|
||||
return t
|
||||
}
|
||||
|
||||
func LogRequest(h RequestHandler) fasthttp.RequestHandler {
|
||||
return fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) {
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"path": string(ctx.Path()),
|
||||
}).Info(string(ctx.Method()))
|
||||
|
||||
h(&Request{Ctx: ctx})
|
||||
})
|
||||
}
|
||||
|
||||
func SetupLogger() {
|
||||
logrus.SetLevel(logrus.TraceLevel) //todo: from conf
|
||||
}
|
||||
|
||||
func parseLogEntry(r *Request) *LogEntry {
|
||||
|
||||
entry := LogEntry{}
|
||||
|
||||
if r.GetJson(&entry) {
|
||||
if len(entry.Message) == 0 {
|
||||
handleErr(errors.New("invalid message"), r)
|
||||
} else if len(entry.Scope) == 0 {
|
||||
handleErr(errors.New("invalid scope"), r)
|
||||
} else if entry.TimeStamp <= 0 {
|
||||
handleErr(errors.New("invalid timestamp"), r)
|
||||
}
|
||||
}
|
||||
|
||||
return &entry
|
||||
}
|
||||
|
||||
func LogTrace(r *Request) {
|
||||
|
||||
entry := parseLogEntry(r)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"scope": entry.Scope,
|
||||
}).WithTime(entry.Time()).Trace(entry.Message)
|
||||
}
|
||||
|
||||
func LogInfo(r *Request) {
|
||||
|
||||
entry := parseLogEntry(r)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"scope": entry.Scope,
|
||||
}).WithTime(entry.Time()).Info(entry.Message)
|
||||
}
|
||||
|
||||
func LogWarn(r *Request) {
|
||||
|
||||
entry := parseLogEntry(r)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"scope": entry.Scope,
|
||||
}).WithTime(entry.Time()).Warn(entry.Message)
|
||||
}
|
||||
|
||||
func LogError(r *Request) {
|
||||
|
||||
entry := parseLogEntry(r)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"scope": entry.Scope,
|
||||
}).WithTime(entry.Time()).Error(entry.Message)
|
||||
}
|
||||
|
||||
71
api/main.go
Normal file
71
api/main.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/buaazp/fasthttprouter"
|
||||
"github.com/valyala/fasthttp"
|
||||
"src/task_tracker/config"
|
||||
"src/task_tracker/storage"
|
||||
)
|
||||
|
||||
type WebAPI struct {
|
||||
server *fasthttp.Server
|
||||
router *fasthttprouter.Router
|
||||
Database *storage.Database
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
var info = Info {
|
||||
Name: "task_tracker",
|
||||
Version: "1.0",
|
||||
}
|
||||
|
||||
func Index(r *Request) {
|
||||
r.OkJson(info)
|
||||
}
|
||||
|
||||
func New() *WebAPI {
|
||||
|
||||
SetupLogger()
|
||||
|
||||
api := new(WebAPI)
|
||||
|
||||
api.router = &fasthttprouter.Router{}
|
||||
|
||||
api.server = &fasthttp.Server{
|
||||
Handler: api.router.Handler,
|
||||
Name: info.Name,
|
||||
}
|
||||
|
||||
api.router.GET("/", LogRequest(Index))
|
||||
|
||||
api.router.POST("/log/trace", LogRequest(LogTrace))
|
||||
api.router.POST("/log/info", LogRequest(LogInfo))
|
||||
api.router.POST("/log/warn", LogRequest(LogWarn))
|
||||
api.router.POST("/log/error", LogRequest(LogError))
|
||||
|
||||
api.router.POST("/worker/create", LogRequest(api.WorkerCreate))
|
||||
api.router.GET("/worker/get/:id", LogRequest(api.WorkerGet))
|
||||
|
||||
api.router.POST("/project/create", LogRequest(api.ProjectCreate))
|
||||
api.router.GET("/project/get/:id", LogRequest(api.ProjectGet))
|
||||
|
||||
api.router.POST("/task/create", LogRequest(api.TaskCreate))
|
||||
api.router.GET("/task/get/", LogRequest(api.TaskGet))
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func (api *WebAPI) Run() {
|
||||
|
||||
logrus.Infof("Started web api at address %s", config.Cfg.ServerAddr)
|
||||
|
||||
err := api.server.ListenAndServe(config.Cfg.ServerAddr)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error in ListenAndServe: %s", err)
|
||||
}
|
||||
}
|
||||
92
api/project.go
Normal file
92
api/project.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"src/task_tracker/storage"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CreateProjectRequest struct {
|
||||
Name string `json:"name"`
|
||||
GitUrl string `json:"git_url"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type CreateProjectResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type GetProjectResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Project *storage.Project `json:"project,omitempty"`
|
||||
}
|
||||
|
||||
func (api *WebAPI) ProjectCreate(r *Request) {
|
||||
|
||||
createReq := &CreateProjectRequest{}
|
||||
if r.GetJson(createReq) {
|
||||
|
||||
project := &storage.Project{
|
||||
Name: createReq.Name,
|
||||
Version: createReq.Version,
|
||||
GitUrl: createReq.GitUrl,
|
||||
}
|
||||
|
||||
if isValidProject(project) {
|
||||
id, err := api.Database.SaveProject(project)
|
||||
|
||||
if err != nil {
|
||||
r.Json(CreateProjectResponse{
|
||||
Ok: false,
|
||||
Message:err.Error(),
|
||||
}, 500)
|
||||
} else {
|
||||
r.OkJson(CreateProjectResponse{
|
||||
Ok: true,
|
||||
Id: id,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"project": project,
|
||||
}).Warn("Invalid project")
|
||||
|
||||
r.Json(CreateProjectResponse{
|
||||
Ok: false,
|
||||
Message: "Invalid project",
|
||||
}, 400)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isValidProject(project *storage.Project) bool {
|
||||
if len(project.Name) <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (api *WebAPI) ProjectGet(r *Request) {
|
||||
|
||||
id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64)
|
||||
handleErr(err, r)
|
||||
|
||||
project := api.Database.GetProject(id)
|
||||
|
||||
if project != nil {
|
||||
r.OkJson(GetProjectResponse{
|
||||
Ok: true,
|
||||
Project:project,
|
||||
})
|
||||
} else {
|
||||
r.Json(GetProjectResponse{
|
||||
Ok: false,
|
||||
Message: "Project not found",
|
||||
}, 404)
|
||||
}
|
||||
}
|
||||
73
api/task.go
Normal file
73
api/task.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"src/task_tracker/storage"
|
||||
)
|
||||
|
||||
type CreateTaskRequest struct {
|
||||
Project int64 `json:"project"`
|
||||
MaxRetries int64 `json:"max_retries"`
|
||||
Recipe string `json:"recipe"`
|
||||
}
|
||||
|
||||
type CreateTaskResponse struct {
|
||||
Ok bool
|
||||
Message string
|
||||
}
|
||||
|
||||
func (api *WebAPI) TaskCreate(r *Request) {
|
||||
|
||||
var createReq CreateTaskRequest
|
||||
if r.GetJson(&createReq) {
|
||||
|
||||
task := &storage.Task{
|
||||
Project:createReq.Project,
|
||||
MaxRetries: createReq.MaxRetries,
|
||||
Recipe:createReq.Recipe,
|
||||
}
|
||||
|
||||
if isTaskValid(task) {
|
||||
err := api.Database.SaveTask(task)
|
||||
|
||||
if err != nil {
|
||||
r.Json(CreateTaskResponse{
|
||||
Ok: false,
|
||||
Message: err.Error(), //todo: hide sensitive error?
|
||||
}, 500)
|
||||
} else {
|
||||
r.OkJson(CreateTaskResponse{
|
||||
Ok: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"task": task,
|
||||
}).Warn("Invalid task")
|
||||
r.Json(CreateTaskResponse{
|
||||
Ok: false,
|
||||
Message: "Invalid task",
|
||||
}, 400)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isTaskValid(task *storage.Task) bool {
|
||||
if task.MaxRetries < 0 {
|
||||
return false
|
||||
}
|
||||
if task.Project <= 0 {
|
||||
return false
|
||||
}
|
||||
if len(task.Recipe) <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (api *WebAPI) TaskGet(r *Request) {
|
||||
|
||||
|
||||
}
|
||||
105
api/worker.go
Normal file
105
api/worker.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/google/uuid"
|
||||
"src/task_tracker/storage"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CreateWorkerRequest struct {
|
||||
}
|
||||
|
||||
type CreateWorkerResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Message string `json:"message,omitempty"`
|
||||
WorkerId string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type GetWorkerResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Worker *storage.Worker `json:"worker,omitempty"`
|
||||
}
|
||||
|
||||
func (api *WebAPI) WorkerCreate(r *Request) {
|
||||
|
||||
workerReq := &CreateWorkerRequest{}
|
||||
if r.GetJson(workerReq) {
|
||||
identity := getIdentity(r)
|
||||
|
||||
if canCreateWorker(r, workerReq, identity) {
|
||||
|
||||
id, err := api.workerCreate(workerReq, getIdentity(r))
|
||||
if err != nil {
|
||||
handleErr(err, r)
|
||||
} else {
|
||||
r.OkJson(CreateWorkerResponse{
|
||||
Ok: true,
|
||||
WorkerId: id.String(),
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
r.Json(CreateWorkerResponse{
|
||||
Ok: false,
|
||||
Message: "You are now allowed to create a worker",
|
||||
}, 403)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *WebAPI) WorkerGet(r *Request) {
|
||||
|
||||
id, err := uuid.Parse(r.Ctx.UserValue("id").(string))
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"id": id,
|
||||
}).Warn("Invalid UUID")
|
||||
|
||||
r.Json(GetWorkerResponse{
|
||||
Ok: false,
|
||||
Message:err.Error(),
|
||||
}, 400)
|
||||
return
|
||||
}
|
||||
|
||||
worker := api.Database.GetWorker(id)
|
||||
|
||||
if worker != nil {
|
||||
r.OkJson(GetWorkerResponse{
|
||||
Ok: true,
|
||||
Worker:worker,
|
||||
})
|
||||
} else {
|
||||
r.Json(GetWorkerResponse{
|
||||
Ok: false,
|
||||
Message:"Worker not found",
|
||||
}, 404)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *WebAPI) workerCreate(request *CreateWorkerRequest, identity *storage.Identity) (uuid.UUID, error) {
|
||||
|
||||
worker := storage.Worker{
|
||||
Id: uuid.New(),
|
||||
Created: time.Now().Unix(),
|
||||
Identity: identity,
|
||||
}
|
||||
|
||||
api.Database.SaveWorker(&worker)
|
||||
return worker.Id, nil
|
||||
}
|
||||
|
||||
func canCreateWorker(r *Request, cwr *CreateWorkerRequest, identity *storage.Identity) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func getIdentity(r *Request) *storage.Identity {
|
||||
|
||||
identity := storage.Identity{
|
||||
RemoteAddr: r.Ctx.RemoteAddr().String(),
|
||||
}
|
||||
|
||||
return &identity
|
||||
}
|
||||
Reference in New Issue
Block a user