add weight log
parent
b689c91d1b
commit
a4e5e06b67
|
|
@ -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…
Reference in New Issue