diff --git a/main.go b/main.go index 7cdfc1d..880d7bc 100644 --- a/main.go +++ b/main.go @@ -13,12 +13,12 @@ import ( "github.com/gorilla/mux" _ "github.com/go-sql-driver/mysql" + "github.com/n1try/wakapi/middlewares" "github.com/n1try/wakapi/models" + "github.com/n1try/wakapi/routes" "github.com/n1try/wakapi/services" ) -var HeartbeatSrvc services.HeartbeatService - func getConfig() models.Config { portPtr := flag.Int("port", 8080, "Port for the webserver to listen on") flag.Parse() @@ -40,8 +40,15 @@ func main() { os.Exit(1) } - // Init Services - HeartbeatSrvc = services.HeartbeatService{db} + // Services + heartbeatSrvc := &services.HeartbeatService{db} + userSrvc := &services.UserService{db} + + // Handlers + heartbeatHandler := &routes.HeartbeatHandler{HeartbeatSrvc: heartbeatSrvc} + + // Middlewares + authenticate := &middlewares.AuthenticateMiddleware{UserSrvc: userSrvc} // Setup Routing router := mux.NewRouter() @@ -51,11 +58,11 @@ func main() { // API Routes heartbeats := apiRouter.Path("/heartbeat").Subrouter() - heartbeats.Methods("POST").HandlerFunc(HeartbeatHandler) + heartbeats.Methods("POST").HandlerFunc(heartbeatHandler.Post) // Sub-Routes Setup router.PathPrefix("/api").Handler(negroni.Classic().With( - negroni.HandlerFunc(AuthenticateMiddleware), + negroni.HandlerFunc(authenticate.Handle), negroni.Wrap(apiRouter), )) diff --git a/middlewares.go b/middlewares.go deleted file mode 100644 index 2c79c64..0000000 --- a/middlewares.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "net/http" - -func AuthenticateMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - next(w, r) -} diff --git a/middlewares/authenticate.go b/middlewares/authenticate.go new file mode 100644 index 0000000..f7c1cd2 --- /dev/null +++ b/middlewares/authenticate.go @@ -0,0 +1,38 @@ +package middlewares + +import ( + "context" + "encoding/base64" + "net/http" + "strings" + + "github.com/n1try/wakapi/models" + "github.com/n1try/wakapi/services" +) + +type AuthenticateMiddleware struct { + UserSrvc *services.UserService +} + +func (m *AuthenticateMiddleware) Handle(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + authHeader := strings.Split(r.Header.Get("Authorization"), " ") + if len(authHeader) != 2 { + w.WriteHeader(401) + return + } + + key, err := base64.StdEncoding.DecodeString(authHeader[1]) + if err != nil { + w.WriteHeader(401) + return + } + + user, err := m.UserSrvc.GetUserByKey(strings.TrimSpace(string(key))) + if err != nil { + w.WriteHeader(401) + return + } + + ctx := context.WithValue(r.Context(), models.UserKey, &user) + next(w, r.WithContext(ctx)) +} diff --git a/models/shared.go b/models/shared.go new file mode 100644 index 0000000..42545f4 --- /dev/null +++ b/models/shared.go @@ -0,0 +1,5 @@ +package models + +const ( + UserKey = "user" +) diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..e4b28b3 --- /dev/null +++ b/models/user.go @@ -0,0 +1,6 @@ +package models + +type User struct { + UserId string `json:"id"` + ApiKey string `json:"api_key"` +} diff --git a/handlers.go b/routes/heartbeat.go similarity index 59% rename from handlers.go rename to routes/heartbeat.go index 8ff99f6..56df7bc 100644 --- a/handlers.go +++ b/routes/heartbeat.go @@ -1,15 +1,21 @@ -package main +package routes import ( "encoding/json" "net/http" "os" + "github.com/n1try/wakapi/services" + _ "github.com/go-sql-driver/mysql" "github.com/n1try/wakapi/models" ) -func HeartbeatHandler(w http.ResponseWriter, r *http.Request) { +type HeartbeatHandler struct { + HeartbeatSrvc *services.HeartbeatService +} + +func (h *HeartbeatHandler) Post(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(415) return @@ -23,7 +29,8 @@ func HeartbeatHandler(w http.ResponseWriter, r *http.Request) { return } - err = HeartbeatSrvc.InsertMulti(heartbeats) + user := r.Context().Value(models.UserKey).(*models.User) + err = h.HeartbeatSrvc.InsertBatch(heartbeats, user) if err != nil { w.WriteHeader(500) os.Stderr.WriteString(err.Error()) diff --git a/services/heartbeat.go b/services/heartbeat.go index 1aba465..7d07f43 100644 --- a/services/heartbeat.go +++ b/services/heartbeat.go @@ -8,25 +8,23 @@ import ( "github.com/n1try/wakapi/models" ) -const ( - TableName = "heartbeat" -) +const TableHeartbeat = "heartbeat" type HeartbeatService struct { Db *sql.DB } -func (srv *HeartbeatService) InsertMulti(heartbeats []models.Heartbeat) error { +func (srv *HeartbeatService) InsertBatch(heartbeats []models.Heartbeat, user *models.User) error { qTpl := "INSERT INTO %+s (user, time, entity, type, category, is_write, project, branch, language) VALUES %+s;" qFill := "" vals := []interface{}{} for _, h := range heartbeats { qFill = "(?, ?, ?, ?, ?, ?, ?, ?, ?)," - vals = append(vals, h.User, h.Time.String(), h.Entity, h.Type, h.Category, h.IsWrite, h.Project, h.Branch, h.Language) + vals = append(vals, user.UserId, h.Time.String(), h.Entity, h.Type, h.Category, h.IsWrite, h.Project, h.Branch, h.Language) } - q := fmt.Sprintf(qTpl, TableName, qFill[0:len(qFill)-1]) + q := fmt.Sprintf(qTpl, TableHeartbeat, qFill[0:len(qFill)-1]) stmt, _ := srv.Db.Prepare(q) result, err := stmt.Exec(vals...) if err != nil { diff --git a/services/user.go b/services/user.go new file mode 100644 index 0000000..4c641d7 --- /dev/null +++ b/services/user.go @@ -0,0 +1,34 @@ +package services + +import ( + "database/sql" + "fmt" + + "github.com/n1try/wakapi/models" +) + +const TableUser = "user" + +type UserService struct { + Db *sql.DB +} + +func (srv *UserService) GetUserById(userId string) (models.User, error) { + q := fmt.Sprintf("SELECT user_id, api_key FROM %+s WHERE user_id = ?;", TableUser) + u := models.User{} + err := srv.Db.QueryRow(q, userId).Scan(&u.UserId, &u.ApiKey) + if err != nil { + return u, err + } + return u, nil +} + +func (srv *UserService) GetUserByKey(key string) (models.User, error) { + q := fmt.Sprintf("SELECT user_id, api_key FROM %+s WHERE api_key = ?;", TableUser) + var u models.User + err := srv.Db.QueryRow(q, key).Scan(&u.UserId, &u.ApiKey) + if err != nil { + return u, err + } + return u, nil +}