add weight log

trunk
Adam Veldhousen 3 years ago
parent b689c91d1b
commit a4e5e06b67
Signed by: adam
GPG Key ID: 6DB29003C6DD1E4B

@ -22,7 +22,7 @@ func (a APIResp) Write(res http.ResponseWriter) {
res.WriteHeader(a.Status)
}
res.Header().Set("Content-Type", "application/json; utf-8")
res.Header().Set("content-type", "application/json; utf-8")
enc := json.NewEncoder(res)
enc.SetIndent("\n", "\t")
enc.Encode(a)

@ -0,0 +1,106 @@
package handlers
import (
"log"
"net/http"
"time"
"git.vdhsn.com/adam/bodytrack/internal/services"
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
)
type Stats struct {
Conn services.DB
}
func (s Stats) Subscribe(c chi.Router) error {
c.Group(func(r chi.Router) {
r.Use(services.NewJWTVerifier())
r.Use(jwtauth.Authenticator)
r.Put("/stats/weight", s.logWeight)
r.Get("/stats/weight", s.getWeightLog)
})
return nil
}
func (s Stats) getWeightLog(res http.ResponseWriter, req *http.Request) {
fail := APIResp{Payload: "Unauthorized", Status: http.StatusUnauthorized}
userId, err := services.GetUserIDFromClaims(req)
if err != nil {
fail.Payload = err.Error()
fail.Write(res)
return
}
startStr := req.URL.Query().Get("start")
endStr := req.URL.Query().Get("end")
start := time.Now().UTC().Add(-time.Hour * 24 * 30)
if startStr != "" {
start, _ = time.Parse(time.RFC3339, startStr)
}
end := time.Now().UTC()
if endStr != "" {
end, _ = time.Parse(time.RFC3339, endStr)
}
wlog, err := s.Conn.GetWeightLog(userId, start, end)
if err != nil {
log.Println(err)
fail.Write(res)
return
}
APIResp{
Success: true,
Payload: struct {
Log []services.WeightLog `json:"log"`
Count int `json:"count"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
}{
Log: wlog,
Count: len(wlog),
Start: start,
End: end,
},
}.Write(res)
}
func (s Stats) logWeight(res http.ResponseWriter, req *http.Request) {
fail := APIResp{Payload: "Unauthorized", Status: http.StatusUnauthorized}
userId, err := services.GetUserIDFromClaims(req)
if err != nil {
fail.Payload = err.Error()
fail.Write(res)
return
}
type logWeightInput struct {
services.WeightLog
}
var in logWeightInput
if err := ParseCall(&in, res, req); err != nil {
fail.Payload = "Could not read arguments"
fail.Status = http.StatusUnprocessableEntity
fail.Write(res)
return
}
if err := s.Conn.LogWeight(userId, in.WeightLog); err != nil {
log.Println(err)
fail.Write(res)
return
}
APIResp{Success: true}.Write(res)
}

@ -21,8 +21,7 @@ func (u UserHandler) Subscribe(c chi.Router) error {
// c.PUT("/user", u.register)
c.Group(func(r chi.Router) {
r.Use(services.JWTVerifier)
r.Use(services.NewJWTVerifier())
r.Use(jwtauth.Authenticator)
r.Get("/user", u.getProfile)
@ -142,6 +141,7 @@ func (u UserHandler) login(res http.ResponseWriter, req *http.Request) {
http.SetCookie(res, &http.Cookie{
Name: "jwt",
Value: token,
Path: "/v1/api/",
Expires: time.Now().Add(duration),
})

@ -28,7 +28,10 @@ func PasswordEqual(hashedPassword, plainTextPass string) bool {
func InitJWT(secretTxt string) {
tokenEncoder = jwtauth.New("HS256", []byte(secretTxt), nil)
JWTVerifier = jwtauth.Verifier(tokenEncoder)
}
func NewJWTVerifier() func(http.Handler) http.Handler {
return jwtauth.Verifier(tokenEncoder)
}
func GenerateJWT(u User) string {

@ -4,6 +4,8 @@ import (
"context"
"database/sql"
"fmt"
"log"
"time"
_ "github.com/lib/pq"
)
@ -13,9 +15,11 @@ var dbConn *sql.DB
type DB interface {
// VerifyEmail(email string) error
// RegisterUser(email, username, password string) (User, error)
UpdateProfile(id int64, u User) error
GetProfileByID(id int64) (User, error)
GetProfileByEmail(email string) (User, error)
GetWeightLog(int64, time.Time, time.Time) ([]WeightLog, error)
LogWeight(int64, WeightLog) error
UpdateProfile(int64, User) error
GetProfileByID(int64) (User, error)
GetProfileByEmail(string) (User, error)
}
func Connect(host, user, password string) (DB, error) {
@ -34,6 +38,60 @@ func Connect(host, user, password string) (DB, error) {
type pgdb struct{ *sql.DB }
func (db pgdb) GetWeightLog(id int64, start, end time.Time) ([]WeightLog, error) {
getWeightLogSql := `
SELECT
value, recordedTs
FROM stats.weightlog
WHERE
userId = $1 AND recordedTs >= $2 AND recordedTs <= $3
ORDER BY recordedTs DESC;`
var results []WeightLog
rows, err := db.Query(getWeightLogSql, id, start, end)
if err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
for rows.Next() {
var wl WeightLog
if err := rows.Scan(&wl.Value, &wl.RecordedTS); err != nil {
log.Printf("could no scane val for weightlog: %v", err)
}
results = append(results, wl)
}
defer rows.Close()
return results, nil
}
func (db pgdb) LogWeight(id int64, w WeightLog) error {
logWeightSql := `
INSERT INTO stats.weightlog (userId, value, recordedTs)
VALUES ($1, $2, $3);
`
tx, err := db.BeginTx(context.Background(), nil)
if err != nil {
return err
}
defer tx.Commit()
if _, err := tx.Exec(logWeightSql, id, w.Value, w.RecordedTS); err != nil {
tx.Rollback()
return err
}
return nil
}
func (db pgdb) UpdateProfile(id int64, u User) error {
updateProfileSql := `
UPDATE accounts.profile SET

@ -0,0 +1,12 @@
package services
import (
"time"
)
type WeightLog struct {
ID uint64 `json:"id"`
UserID uint64 `json:"userId"`
Value float64 `json:"value"`
RecordedTS time.Time `json:"recordedTs"`
}

@ -23,11 +23,18 @@ func main() {
handlers := []handlers.ApiHandler{
handlers.UserHandler{Conn: db},
handlers.Stats{Conn: db},
}
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Timeout(60 * time.Second))
r.Use(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
r.Header.Set("Content-Type", "application/json; utf-8")
h.ServeHTTP(rw, r)
})
})
r.Route("/v1/api/", func(inner chi.Router) {
for _, h := range handlers {

@ -4,8 +4,8 @@ CREATE TABLE Stats.WeightLog (
id SERIAL PRIMARY KEY,
userId INT NOT NULL REFERENCES Accounts.Users(id),
value DECIMAL NOT NULL,
recordedTs TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(userId)
recordedTs TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX weightlog_userId ON stats.weightlog USING btree (userId);

@ -3,9 +3,11 @@ CREATE ROLE api LOGIN PASSWORD 'api-user';
GRANT SELECT, UPDATE, INSERT ON ALL TABLES IN SCHEMA "accounts" TO api;
GRANT USAGE ON SCHEMA "accounts" TO api;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "accounts" TO api;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA accounts TO api;
GRANT SELECT, UPDATE, INSERT ON ALL TABLES IN SCHEMA "stats" TO api;
GRANT USAGE ON SCHEMA "stats" TO api;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "stats" TO api;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA stats TO api;

Loading…
Cancel
Save