1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00

chore: add check to validate wakatime api key before accepting it

This commit is contained in:
Ferdinand Mütsch 2021-01-30 10:54:54 +01:00
parent 417d4789ab
commit fd239e4f21
3 changed files with 47 additions and 7 deletions

View File

@ -33,6 +33,12 @@ const (
KeyLatestTotalUsers = "latest_total_users" KeyLatestTotalUsers = "latest_total_users"
) )
const (
WakatimeApiUrl = "https://wakatime.com/api/v1"
WakatimeApiHeartbeatsEndpoint = "/users/current/heartbeats.bulk"
WakatimeApiUserEndpoint = "/users/current"
)
var cfg *Config var cfg *Config
var cFlag = flag.String("config", defaultConfigPath, "config file location") var cFlag = flag.String("config", defaultConfigPath, "config file location")

View File

@ -13,11 +13,6 @@ import (
"time" "time"
) )
const (
WakatimeApiUrl = "https://wakatime.com/api/v1"
WakatimeApiHeartbeatsEndpoint = "/users/current/heartbeats.bulk"
)
/* Middleware to conditionally relay heartbeats to Wakatime */ /* Middleware to conditionally relay heartbeats to Wakatime */
type WakatimeRelayMiddleware struct { type WakatimeRelayMiddleware struct {
httpClient *http.Client httpClient *http.Client
@ -68,7 +63,7 @@ func (m *WakatimeRelayMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Reque
go m.send( go m.send(
http.MethodPost, http.MethodPost,
WakatimeApiUrl+WakatimeApiHeartbeatsEndpoint, config.WakatimeApiUrl+config.WakatimeApiHeartbeatsEndpoint,
bytes.NewReader(body), bytes.NewReader(body),
headers, headers,
) )

View File

@ -1,6 +1,7 @@
package routes package routes
import ( import (
"encoding/base64"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/schema" "github.com/gorilla/schema"
@ -12,6 +13,7 @@ import (
"log" "log"
"net/http" "net/http"
"strconv" "strconv"
"time"
) )
type SettingsHandler struct { type SettingsHandler struct {
@ -21,6 +23,7 @@ type SettingsHandler struct {
aliasSrvc services.IAliasService aliasSrvc services.IAliasService
aggregationSrvc services.IAggregationService aggregationSrvc services.IAggregationService
languageMappingSrvc services.ILanguageMappingService languageMappingSrvc services.ILanguageMappingService
httpClient *http.Client
} }
var credentialsDecoder = schema.NewDecoder() var credentialsDecoder = schema.NewDecoder()
@ -33,6 +36,7 @@ func NewSettingsHandler(userService services.IUserService, summaryService servic
aggregationSrvc: aggregationService, aggregationSrvc: aggregationService,
languageMappingSrvc: languageMappingService, languageMappingSrvc: languageMappingService,
userSrvc: userService, userSrvc: userService,
httpClient: &http.Client{Timeout: 10 * time.Second},
} }
} }
@ -255,7 +259,15 @@ func (h *SettingsHandler) PostSetWakatimeApiKey(w http.ResponseWriter, r *http.R
} }
user := r.Context().Value(models.UserKey).(*models.User) user := r.Context().Value(models.UserKey).(*models.User)
if _, err := h.userSrvc.SetWakatimeApiKey(user, r.PostFormValue("api_key")); err != nil { apiKey := r.PostFormValue("api_key")
// Healthcheck, if a new API key is set, i.e. the feature is activated
if (user.WakatimeApiKey == "" && apiKey != "") && !h.validateWakatimeKey(apiKey) {
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("failed to connect to WakaTime, API key invalid?"))
return
}
if _, err := h.userSrvc.SetWakatimeApiKey(user, apiKey); err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error")) templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
return return
@ -304,6 +316,33 @@ func (h *SettingsHandler) PostRegenerateSummaries(w http.ResponseWriter, r *http
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("summaries are being regenerated this may take a few seconds")) templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("summaries are being regenerated this may take a few seconds"))
} }
func (h *SettingsHandler) validateWakatimeKey(apiKey string) bool {
headers := http.Header{
"Accept": []string{"application/json"},
"Authorization": []string{
fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(apiKey))),
},
}
request, err := http.NewRequest(
http.MethodGet,
conf.WakatimeApiUrl+conf.WakatimeApiUserEndpoint,
nil,
)
if err != nil {
return false
}
request.Header = headers
response, err := h.httpClient.Do(request)
if err != nil || response.StatusCode < 200 || response.StatusCode >= 300 {
return false
}
return true
}
func (h *SettingsHandler) buildViewModel(r *http.Request) *view.SettingsViewModel { func (h *SettingsHandler) buildViewModel(r *http.Request) *view.SettingsViewModel {
user := r.Context().Value(models.UserKey).(*models.User) user := r.Context().Value(models.UserKey).(*models.User)
mappings, _ := h.languageMappingSrvc.GetByUser(user.ID) mappings, _ := h.languageMappingSrvc.GetByUser(user.ID)