From 80ad4bed4af8c49385c94682423b043e365f9e14 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 11:10:34 +0000 Subject: [PATCH 01/14] add chi router --- go.mod | 1 + go.sum | 2 + internal/handlers/app.go | 24 ++++++- main.go | 87 ++++++++++++++++---------- views/auth.html | 2 + views/{index.html => home.html} | 2 + views/layouts/{main.html => base.html} | 6 +- 7 files changed, 89 insertions(+), 35 deletions(-) rename views/{index.html => home.html} (96%) rename views/layouts/{main.html => base.html} (70%) diff --git a/go.mod b/go.mod index e3dfcc4..03f200e 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/gofiber/template v1.8.2 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect diff --git a/go.sum b/go.sum index ab1b7c3..fcbb305 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= diff --git a/internal/handlers/app.go b/internal/handlers/app.go index f644686..766153e 100644 --- a/internal/handlers/app.go +++ b/internal/handlers/app.go @@ -1,10 +1,13 @@ package handlers import ( - "github.com/gofiber/fiber/v2" + "html/template" "log/slog" + "net/http" "visio/internal/store" "visio/internal/types" + + "github.com/gofiber/fiber/v2" ) type AppHandler struct { @@ -19,6 +22,25 @@ func NewAppHandler(keys *store.Keys, logger *slog.Logger) *AppHandler { } } +func (h *AppHandler) RenderLandingPage(w http.ResponseWriter, r *http.Request) { + templFiles := []string{ + "views/layouts/base.html", + "views/home.html", + } + ts, err := template.ParseFiles(templFiles...) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = ts.ExecuteTemplate(w, "base", nil) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } +} + func (h *AppHandler) GetLandingPage(c *fiber.Ctx) error { return c.Render("index", fiber.Map{}) } diff --git a/main.go b/main.go index bd53da2..b4caff8 100644 --- a/main.go +++ b/main.go @@ -1,21 +1,25 @@ package main import ( - "encoding/json" + "embed" "fmt" "log/slog" + "net/http" "os" "visio/internal/database" "visio/internal/handlers" - "visio/internal/middlewares" "visio/internal/store" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/recover" - "github.com/gofiber/template/html/v2" + "github.com/go-chi/chi/v5" "github.com/joho/godotenv" ) +//go:embed views/* +var views embed.FS + +//go:embed public/output.css +var publicFS embed.FS + func main() { appEnv := os.Getenv("ENV") if appEnv != "PROD" { @@ -25,48 +29,67 @@ func main() { } } postgresPool := database.NewPostgresPool() - redisClient := database.GetRedisClient() - users := store.NewUsersStore(postgresPool) - sessions := store.NewSessionsStore(redisClient) + // redisClient := database.GetRedisClient() + // users := store.NewUsersStore(postgresPool) + // sessions := store.NewSessionsStore(redisClient) keys := store.NewKeysStore(postgresPool) textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}) appLogger := slog.New(textHandler) appHandler := handlers.NewAppHandler(keys, appLogger) - authHandler := handlers.NewAuthHandler(users, sessions, appLogger) - keyHandler := handlers.NewKeyHandler(keys, sessions, appLogger) - authMiddleware := middlewares.NewAuthMiddleware(sessions, users, appLogger) + // authHandler := handlers.NewAuthHandler(users, sessions, appLogger) + // keyHandler := handlers.NewKeyHandler(keys, sessions, appLogger) + // authMiddleware := middlewares.NewAuthMiddleware(sessions, users, appLogger) - engine := html.New("./views", ".html") - engine.Reload(appEnv != "PROD") - engine.AddFunc("jsonify", func(s interface{}) string { - jsonBytes, err := json.Marshal(s) + r := chi.NewRouter() + + r.Get("/public/output.css", func(w http.ResponseWriter, r *http.Request) { + css, err := publicFS.ReadFile("public/output.css") if err != nil { - return "" + fmt.Printf("Error while reading css file from embedded file system: %s\n", err.Error()) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("")) + return } - return string(jsonBytes) - }) - app := fiber.New(fiber.Config{ - Views: engine, - ViewsLayout: "layouts/main", + w.Header().Set("Content-Type", "text/css") + fmt.Printf("%s", string(css)) + w.Write(css) + return }) - app.Static("/public", "./public") - app.Use(recover.New()) - client := app.Group("/") - client.Get("/", appHandler.GetLandingPage) - client.Get("/auth", appHandler.GetAuthPage) - client.Get("/keys", authMiddleware.CookieAuth, appHandler.GetKeysPage) + r.Get("/", appHandler.RenderLandingPage) - server := app.Group("/api") - server.Post("/auth", authHandler.Signup) - server.Post("/key", authMiddleware.CookieAuth, keyHandler.Create) - server.Delete("/key/:prefix", authMiddleware.CookieAuth, keyHandler.Revoke) + // engine := html.New("./views", ".html") + // engine.Reload(appEnv != "PROD") + // engine.AddFunc("jsonify", func(s interface{}) string { + // jsonBytes, err := json.Marshal(s) + // if err != nil { + // return "" + // } + // return string(jsonBytes) + // }) + // app := fiber.New(fiber.Config{ + // Views: engine, + // ViewsLayout: "layouts/main", + // }) + // app.Static("/public", "./public") + // app.Use(recover.New()) + // + // client := app.Group("/") + // client.Get("/", appHandler.GetLandingPage) + // client.Get("/auth", appHandler.GetAuthPage) + // client.Get("/keys", authMiddleware.CookieAuth, appHandler.GetKeysPage) + // + // server := app.Group("/api") + // server.Post("/auth", authHandler.Signup) + // server.Post("/key", authMiddleware.CookieAuth, keyHandler.Create) + // server.Delete("/key/:prefix", authMiddleware.CookieAuth, keyHandler.Revoke) port := os.Getenv("PORT") if port == "" { panic("Unable to read PORT environment variable") } - err := app.Listen(fmt.Sprintf(":%s", port)) + // err := app.Listen(fmt.Sprintf(":%s", port)) + err := http.ListenAndServe(":8080", r) if err != nil { panic(err) } diff --git a/views/auth.html b/views/auth.html index 51ff92e..20a2f66 100644 --- a/views/auth.html +++ b/views/auth.html @@ -1,3 +1,4 @@ +{{define "content"}} Visio | Authenticate @@ -92,3 +93,4 @@

} } +{{end}} diff --git a/views/index.html b/views/home.html similarity index 96% rename from views/index.html rename to views/home.html index 7ec905a..325c728 100644 --- a/views/index.html +++ b/views/home.html @@ -1,3 +1,4 @@ +{{define "main"}} Visio | Landing page @@ -16,3 +17,4 @@

Try it

+{{end}} diff --git a/views/layouts/main.html b/views/layouts/base.html similarity index 70% rename from views/layouts/main.html rename to views/layouts/base.html index 53f7fe3..f8f144f 100644 --- a/views/layouts/main.html +++ b/views/layouts/base.html @@ -1,15 +1,17 @@ +{{define "base"}} - + - {{embed}} + {{template "main" .}} +{{end}} From 00dd4118086d908faced049f1f7c556dbc20df01 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 11:11:40 +0000 Subject: [PATCH 02/14] remove debug logs --- main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/main.go b/main.go index b4caff8..ef132cd 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,6 @@ func main() { return } w.Header().Set("Content-Type", "text/css") - fmt.Printf("%s", string(css)) w.Write(css) return }) From e7f54a12476b41cd8b61f5da7ce3bdb9e45d4cd9 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 11:23:34 +0000 Subject: [PATCH 03/14] add auth page endpoint --- internal/handlers/app.go | 27 +++++++++++++++++---------- main.go | 3 ++- views/auth.html | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/internal/handlers/app.go b/internal/handlers/app.go index 766153e..e4683c4 100644 --- a/internal/handlers/app.go +++ b/internal/handlers/app.go @@ -41,16 +41,23 @@ func (h *AppHandler) RenderLandingPage(w http.ResponseWriter, r *http.Request) { } } -func (h *AppHandler) GetLandingPage(c *fiber.Ctx) error { - return c.Render("index", fiber.Map{}) -} - -func (h *AppHandler) GetAuthPage(c *fiber.Ctx) error { - return c.Render("auth", fiber.Map{}) -} - -func (h *AppHandler) GetHomePage(c *fiber.Ctx) error { - return c.Render("home", fiber.Map{}) +func (h *AppHandler) RenderAuthPage(w http.ResponseWriter, r *http.Request) { + templateFiles := []string{ + "views/layouts/base.html", + "views/auth.html", + } + ts, err := template.ParseFiles(templateFiles...) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = ts.ExecuteTemplate(w, "base", nil) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } } func (h *AppHandler) GetKeysPage(c *fiber.Ctx) error { diff --git a/main.go b/main.go index ef132cd..4819fb0 100644 --- a/main.go +++ b/main.go @@ -56,6 +56,7 @@ func main() { }) r.Get("/", appHandler.RenderLandingPage) + r.Get("/auth", appHandler.RenderAuthPage) // engine := html.New("./views", ".html") // engine.Reload(appEnv != "PROD") @@ -87,7 +88,7 @@ func main() { if port == "" { panic("Unable to read PORT environment variable") } - // err := app.Listen(fmt.Sprintf(":%s", port)) + fmt.Printf("Server listening on port %s\n", port) err := http.ListenAndServe(":8080", r) if err != nil { panic(err) diff --git a/views/auth.html b/views/auth.html index 20a2f66..d9eac2d 100644 --- a/views/auth.html +++ b/views/auth.html @@ -1,4 +1,4 @@ -{{define "content"}} +{{define "main"}} Visio | Authenticate From dbb100e69d35f985c83008731a58aebb6410069f Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 12:43:32 +0000 Subject: [PATCH 04/14] feat: updated database schema and rewrote authentication using chi --- internal/handlers/auth.go | 85 ++++++++++++++++++++++----------------- internal/store/users.go | 4 +- internal/types/user.go | 10 ++--- main.go | 17 ++++---- schema.sql | 4 +- 5 files changed, 67 insertions(+), 53 deletions(-) diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index 3f5698e..9bed7c2 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -1,14 +1,15 @@ package handlers import ( + "encoding/json" "errors" "fmt" "log/slog" + "net/http" "time" "visio/internal/store" "visio/internal/types" "visio/pkg" - "github.com/gofiber/fiber/v2" "github.com/oklog/ulid/v2" ) @@ -26,83 +27,95 @@ func NewAuthHandler(usersStore *store.Users, sessionsStore *store.Sessions, logg } } -func (h *AuthHandler) Signup(c *fiber.Ctx) error { - reqPayload := new(struct { +func (h *AuthHandler) Authicate(w http.ResponseWriter, r *http.Request) { + payload := new(struct { Email string `json:"email"` Password string `json:"password"` }) - action := c.Query("action") - if action == "" { - return c.SendStatus(fiber.ErrBadRequest.Code) + action := r.URL.Query().Get("action") + fmt.Println(action) + if action != "Login" && action != "Register" { + w.WriteHeader(http.StatusBadRequest) + return } - if err := c.BodyParser(reqPayload); err != nil { - h.logger.Error(fmt.Sprintf("Error while parsing body: %v", err)) - return c.SendStatus(fiber.ErrInternalServerError.Code) + err := json.NewDecoder(r.Body).Decode(payload) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return } switch action { case "Register": - count, err := h.users.CountByEmail(reqPayload.Email) + count, err := h.users.CountByEmail(payload.Email) if err != nil { h.logger.Error(err.Error()) - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } - if count != 0 { - return c.SendStatus(fiber.ErrConflict.Code) + if count > 0 { + w.WriteHeader(http.StatusConflict) + return } - hash, err := pkg.Hash(reqPayload.Password) + hash, err := pkg.Hash(payload.Password) if err != nil { h.logger.Error(err.Error()) - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } newUser := &types.User{ - Id: ulid.Make().String(), - Email: reqPayload.Email, - Password: hash, - SignupDate: time.Now().UTC(), + Id: ulid.Make().String(), + Email: payload.Email, + PasswordHash: hash, + SignupDate: time.Now().UTC().Format("January, 2 2006"), } err = h.users.Insert(newUser) if err != nil { h.logger.Error(err.Error()) - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } sessionId := ulid.Make().String() err = h.sessions.Create(sessionId, newUser.Id) if err != nil { h.logger.Error(err.Error()) - c.Set("X-Err-Context", "ERR_SESSION_CREATION") - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } - authCookie := &fiber.Cookie{ + authCookie := &http.Cookie{ Name: "session", Value: sessionId, } - c.Cookie(authCookie) - return c.SendStatus(fiber.StatusCreated) + http.SetCookie(w, authCookie) + w.WriteHeader(http.StatusCreated) + return case "Login": - dbUser, err := h.users.GetByEmail(reqPayload.Email) + dbUser, err := h.users.GetByEmail(payload.Email) if err != nil { if errors.Is(err, types.ErrUserNotFound) { - return c.SendStatus(fiber.ErrBadRequest.Code) + w.WriteHeader(http.StatusBadRequest) + return } h.logger.Error(err.Error()) - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } - if !pkg.PasswordMatches(reqPayload.Password, dbUser.Password) { - return c.SendStatus(fiber.ErrBadRequest.Code) + if !pkg.PasswordMatches(payload.Password, dbUser.PasswordHash) { + w.WriteHeader(http.StatusBadRequest) + return } sessionId := ulid.Make().String() err = h.sessions.Create(sessionId, dbUser.Id) if err != nil { h.logger.Error(err.Error()) - c.Set("X-Err-Context", "ERR_SESSION_CREATION") - return c.SendStatus(fiber.ErrInternalServerError.Code) + w.WriteHeader(http.StatusInternalServerError) + return } - authCookie := &fiber.Cookie{ + authCookie := &http.Cookie{ Name: "session", Value: sessionId, } - c.Cookie(authCookie) - return c.SendStatus(fiber.StatusOK) + http.SetCookie(w, authCookie) + w.WriteHeader(http.StatusOK) + return } - return c.SendStatus(fiber.ErrBadRequest.Code) } diff --git a/internal/store/users.go b/internal/store/users.go index b7b49f2..74e2469 100644 --- a/internal/store/users.go +++ b/internal/store/users.go @@ -21,8 +21,8 @@ func NewUsersStore(db *sqlx.DB) *Users { func (s *Users) Insert(user *types.User) error { _, err := s.db.NamedExec( ` - insert into users(id, email, password, signup_date) - values (:id, :email, :password, :signup_date) + insert into users(id, email, password_hash, signup_date) + values (:id, :email, :password_hash, :signup_date) `, user, ) diff --git a/internal/types/user.go b/internal/types/user.go index 0c7e06b..e6173bd 100644 --- a/internal/types/user.go +++ b/internal/types/user.go @@ -1,10 +1,8 @@ package types -import "time" - type User struct { - Id string `json:"id" db:"id"` - Email string `json:"email" db:"email"` - Password string `json:"password" db:"password"` - SignupDate time.Time `json:"signup_date" db:"signup_date"` + Id string `json:"id" db:"id"` + Email string `json:"email" db:"email"` + PasswordHash string `json:"password_hash" db:"password_hash"` + SignupDate string `json:"signup_date" db:"signup_date"` } diff --git a/main.go b/main.go index 4819fb0..0a6058b 100644 --- a/main.go +++ b/main.go @@ -3,15 +3,14 @@ package main import ( "embed" "fmt" + "github.com/go-chi/chi/v5" + "github.com/joho/godotenv" "log/slog" "net/http" "os" "visio/internal/database" "visio/internal/handlers" "visio/internal/store" - - "github.com/go-chi/chi/v5" - "github.com/joho/godotenv" ) //go:embed views/* @@ -29,14 +28,14 @@ func main() { } } postgresPool := database.NewPostgresPool() - // redisClient := database.GetRedisClient() - // users := store.NewUsersStore(postgresPool) - // sessions := store.NewSessionsStore(redisClient) + redisClient := database.GetRedisClient() + users := store.NewUsersStore(postgresPool) + sessions := store.NewSessionsStore(redisClient) keys := store.NewKeysStore(postgresPool) textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}) appLogger := slog.New(textHandler) appHandler := handlers.NewAppHandler(keys, appLogger) - // authHandler := handlers.NewAuthHandler(users, sessions, appLogger) + authHandler := handlers.NewAuthHandler(users, sessions, appLogger) // keyHandler := handlers.NewKeyHandler(keys, sessions, appLogger) // authMiddleware := middlewares.NewAuthMiddleware(sessions, users, appLogger) @@ -58,6 +57,10 @@ func main() { r.Get("/", appHandler.RenderLandingPage) r.Get("/auth", appHandler.RenderAuthPage) + r.Route("/api", func(r chi.Router) { + r.Post("/auth", authHandler.Authicate) + }) + // engine := html.New("./views", ".html") // engine.Reload(appEnv != "PROD") // engine.AddFunc("jsonify", func(s interface{}) string { diff --git a/schema.sql b/schema.sql index d3fd020..8516184 100644 --- a/schema.sql +++ b/schema.sql @@ -1,8 +1,8 @@ create table if not exists users ( id text not null primary key, email text not null unique, - password text not null, - signup_date timestamp not null + password_hash text not null, + signup_date text not null ); create table if not exists keys ( From e420a1a9ba7525eec8837d50b88af2521beb61cd Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 12:45:28 +0000 Subject: [PATCH 05/14] remove some debug logs --- internal/handlers/auth.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index 9bed7c2..1783830 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -3,7 +3,6 @@ package handlers import ( "encoding/json" "errors" - "fmt" "log/slog" "net/http" "time" @@ -33,7 +32,6 @@ func (h *AuthHandler) Authicate(w http.ResponseWriter, r *http.Request) { Password string `json:"password"` }) action := r.URL.Query().Get("action") - fmt.Println(action) if action != "Login" && action != "Register" { w.WriteHeader(http.StatusBadRequest) return From c0fc8207b8aeb568b6ecf167978559806e74774a Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 13:00:38 +0000 Subject: [PATCH 06/14] ref: rewrite cookie auth middleware --- internal/handlers/keys.go | 74 +----------------------------------- internal/middlewares/auth.go | 60 +++++++++++++++-------------- main.go | 26 ------------- 3 files changed, 34 insertions(+), 126 deletions(-) diff --git a/internal/handlers/keys.go b/internal/handlers/keys.go index 6f583d0..91640ea 100644 --- a/internal/handlers/keys.go +++ b/internal/handlers/keys.go @@ -1,16 +1,10 @@ package handlers import ( - "errors" - "fmt" - "github.com/gofiber/fiber/v2" - "github.com/oklog/ulid/v2" "log/slog" "math/rand" - "time" + "net/http" "visio/internal/store" - "visio/internal/types" - "visio/pkg" ) type KeyHandler struct { @@ -37,70 +31,6 @@ func generateRandomString(length int) string { return key } -func (h *KeyHandler) Create(c *fiber.Ctx) error { - currentUser, ok := c.Locals("currentUser").(*types.User) - if !ok { - err := errors.New("Error during currentUser type conversion") - h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusUnauthorized) - } - const KEY_LIMIT = 3 - key_count, err := h.keys.CountByOwnerId(currentUser.Id) - if err != nil { - h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusInternalServerError) - } - if key_count > KEY_LIMIT { - return c.SendStatus(fiber.StatusForbidden) - } - prefix := generateRandomString(7) - suffix := generateRandomString(23) - hashedKey, err := pkg.Hash(suffix) - if err != nil { - h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusInternalServerError) - } - generatedKey := &types.Key{ - Id: ulid.Make().String(), - UserId: currentUser.Id, - Prefix: prefix, - KeyHash: hashedKey, - CreationDate: time.Now().UTC().Format("January, 2 2006"), - } - if err := h.keys.Insert(generatedKey); err != nil { - if errors.Is(err, types.ErrDuplicatePrefix) { - h.logger.Debug("Duplicate prefix error triggered") - } - h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusInternalServerError) - } - key := fmt.Sprintf("%s.%s", prefix, suffix) - err = c.JSON( - map[string]interface{}{ - "data": map[string]string{ - "key": key, - }, - }, - ) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - return c.SendStatus(fiber.StatusCreated) -} +func (h *KeyHandler) Create(w http.ResponseWriter, r *http.Request) { -func (h *KeyHandler) Revoke(c *fiber.Ctx) error { - currentUser, ok := c.Locals("currentUser").(*types.User) - if !ok { - return c.SendStatus(fiber.StatusUnauthorized) - } - keyPrefix := c.Params("prefix") - if keyPrefix == "" { - return c.SendStatus(fiber.StatusBadRequest) - } - err := h.keys.Delete(keyPrefix, currentUser.Id) - if err != nil { - h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusInternalServerError) - } - return c.SendStatus(fiber.StatusOK) } diff --git a/internal/middlewares/auth.go b/internal/middlewares/auth.go index 34e7117..12878fd 100644 --- a/internal/middlewares/auth.go +++ b/internal/middlewares/auth.go @@ -1,12 +1,12 @@ package middlewares import ( + "context" "errors" "log/slog" + "net/http" "visio/internal/store" "visio/internal/types" - - "github.com/gofiber/fiber/v2" ) type AuthMiddleware struct { @@ -23,32 +23,36 @@ func NewAuthMiddleware(sessions *store.Sessions, users *store.Users, logger *slo } } -func (m *AuthMiddleware) CookieAuth(c *fiber.Ctx) error { - sessionId := c.Cookies("session") - if sessionId == "" { - return c.Redirect("/auth", fiber.StatusFound) - } - sessionValue, err := m.sessions.Get(sessionId) - if err != nil { - if errors.Is(err, types.ErrSessionNotFound) { - return c.Redirect("/auth", fiber.StatusFound) +func (m *AuthMiddleware) CookieAuth(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + sessionCookie, err := r.Cookie("session") + if err != nil { + if err == http.ErrNoCookie { + w.WriteHeader(http.StatusUnauthorized) + return + } } - m.logger.Error(err.Error()) - return c.Redirect("/auth", fiber.StatusFound) - } - sessionUser, err := m.users.GetById(sessionValue) - if err != nil { - if errors.Is(err, types.ErrUserNotFound) { - return c.Redirect("/auth", fiber.StatusFound) + sessionValue, err := m.sessions.Get(sessionCookie.Name) + if err != nil { + if errors.Is(err, types.ErrSessionNotFound) { + w.WriteHeader(http.StatusUnauthorized) + return + } + m.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return } - m.logger.Error(err.Error()) - return c.Redirect("/auth", fiber.StatusFound) - } - - c.Locals("currentUser", sessionUser) - return c.Next() -} - -func (m *AuthMiddleware) KeyAuth(c *fiber.Ctx) error { - return c.Next() + sessionUser, err := m.users.GetById(sessionValue) + if err != nil { + if errors.Is(err, types.ErrUserNotFound) { + w.WriteHeader(http.StatusUnauthorized) + return + } + m.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + ctx := context.WithValue(r.Context(), "currentUser", sessionUser) + next.ServeHTTP(w, r.WithContext(ctx)) + }) } diff --git a/main.go b/main.go index 0a6058b..e32be90 100644 --- a/main.go +++ b/main.go @@ -61,32 +61,6 @@ func main() { r.Post("/auth", authHandler.Authicate) }) - // engine := html.New("./views", ".html") - // engine.Reload(appEnv != "PROD") - // engine.AddFunc("jsonify", func(s interface{}) string { - // jsonBytes, err := json.Marshal(s) - // if err != nil { - // return "" - // } - // return string(jsonBytes) - // }) - // app := fiber.New(fiber.Config{ - // Views: engine, - // ViewsLayout: "layouts/main", - // }) - // app.Static("/public", "./public") - // app.Use(recover.New()) - // - // client := app.Group("/") - // client.Get("/", appHandler.GetLandingPage) - // client.Get("/auth", appHandler.GetAuthPage) - // client.Get("/keys", authMiddleware.CookieAuth, appHandler.GetKeysPage) - // - // server := app.Group("/api") - // server.Post("/auth", authHandler.Signup) - // server.Post("/key", authMiddleware.CookieAuth, keyHandler.Create) - // server.Delete("/key/:prefix", authMiddleware.CookieAuth, keyHandler.Revoke) - port := os.Getenv("PORT") if port == "" { panic("Unable to read PORT environment variable") From 7390c842e572efe5d8ec1a49a338659f24a58aba Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 13:15:16 +0000 Subject: [PATCH 07/14] ref: rewrite keys creation --- internal/handlers/keys.go | 71 +++++++++++++++++++++++++++++++++++++++ main.go | 10 ++++-- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/internal/handlers/keys.go b/internal/handlers/keys.go index 91640ea..b310fd9 100644 --- a/internal/handlers/keys.go +++ b/internal/handlers/keys.go @@ -1,10 +1,22 @@ package handlers import ( + "encoding/json" + "errors" + "fmt" "log/slog" "math/rand" "net/http" + "time" "visio/internal/store" + "visio/internal/types" + "visio/pkg" + + "github.com/oklog/ulid/v2" +) + +const ( + KeyLimit = 5 ) type KeyHandler struct { @@ -32,5 +44,64 @@ func generateRandomString(length int) string { } func (h *KeyHandler) Create(w http.ResponseWriter, r *http.Request) { + currentUser, ok := r.Context().Value("currentUser").(*types.User) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + keysCount, err := h.keys.CountByOwnerId(currentUser.Id) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + if keysCount == KeyLimit { + w.WriteHeader(http.StatusForbidden) + return + } + prefix := generateRandomString(7) + suffix := generateRandomString(23) + key := fmt.Sprintf("%s.%s", prefix, suffix) + keyHash, err := pkg.Hash(suffix) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + newKey := &types.Key{ + Id: ulid.Make().String(), + UserId: currentUser.Id, + Prefix: prefix, + KeyHash: keyHash, + CreationDate: time.Now().UTC().Format("January, 2 2006"), + } + err = h.keys.Insert(newKey) + if err != nil { + if errors.Is(err, types.ErrDuplicatePrefix) { + h.logger.Debug("A duplicate prefix error occured") + } + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + data, err := json.Marshal( + map[string]interface{}{ + "data": map[string]string{ + "key": key, + }, + }, + ) + if err != nil { + //Delete created key and add header to tell client + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusCreated) + w.Write(data) + return +} + +func (h *KeyHandler) Revoke(w http.ResponseWriter, r *http.Request) { } diff --git a/main.go b/main.go index e32be90..8d9ff13 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "os" "visio/internal/database" "visio/internal/handlers" + "visio/internal/middlewares" "visio/internal/store" ) @@ -36,8 +37,8 @@ func main() { appLogger := slog.New(textHandler) appHandler := handlers.NewAppHandler(keys, appLogger) authHandler := handlers.NewAuthHandler(users, sessions, appLogger) - // keyHandler := handlers.NewKeyHandler(keys, sessions, appLogger) - // authMiddleware := middlewares.NewAuthMiddleware(sessions, users, appLogger) + keyHandler := handlers.NewKeyHandler(keys, sessions, appLogger) + authMiddleware := middlewares.NewAuthMiddleware(sessions, users, appLogger) r := chi.NewRouter() @@ -61,6 +62,11 @@ func main() { r.Post("/auth", authHandler.Authicate) }) + r.Route("/keys", func(r chi.Router) { + r.With(authMiddleware.CookieAuth).Post("/", keyHandler.Create) + r.With(authMiddleware.CookieAuth).Post("/{prefix}", keyHandler.Revoke) + }) + port := os.Getenv("PORT") if port == "" { panic("Unable to read PORT environment variable") From 415c2f48a3c82a85aa8acb99949532c66d1f0b1a Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 13:33:33 +0000 Subject: [PATCH 08/14] render keys page --- internal/handlers/app.go | 36 +++++++++++++++++++++++++++--------- internal/handlers/auth.go | 6 ++++-- internal/middlewares/auth.go | 12 ++++++++---- main.go | 1 + views/keys.html | 4 +++- views/layouts/app.html | 4 +++- 6 files changed, 46 insertions(+), 17 deletions(-) diff --git a/internal/handlers/app.go b/internal/handlers/app.go index e4683c4..a0d09d8 100644 --- a/internal/handlers/app.go +++ b/internal/handlers/app.go @@ -6,8 +6,6 @@ import ( "net/http" "visio/internal/store" "visio/internal/types" - - "github.com/gofiber/fiber/v2" ) type AppHandler struct { @@ -50,9 +48,9 @@ func (h *AppHandler) RenderAuthPage(w http.ResponseWriter, r *http.Request) { if err != nil { h.logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) - return + return } - err = ts.ExecuteTemplate(w, "base", nil) + err = ts.ExecuteTemplate(w, "base", nil) if err != nil { h.logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) @@ -60,15 +58,35 @@ func (h *AppHandler) RenderAuthPage(w http.ResponseWriter, r *http.Request) { } } -func (h *AppHandler) GetKeysPage(c *fiber.Ctx) error { - currentUser, ok := c.Locals("currentUser").(*types.User) +func (h *AppHandler) GetKeysPage(w http.ResponseWriter, r *http.Request) { + currentUser, ok := r.Context().Value("currentUser").(*types.User) if !ok { - return c.SendStatus(fiber.StatusUnauthorized) + http.Redirect(w, r, "/auth", http.StatusTemporaryRedirect) + return } userKeys, err := h.keys.GetByUserId(currentUser.Id) if err != nil { h.logger.Error(err.Error()) - return c.SendStatus(fiber.StatusInternalServerError) + w.WriteHeader(http.StatusInternalServerError) + return + } + templateFiles := []string{ + "views/layouts/app.html", + "views/keys.html", + } + ts, err := template.ParseFiles(templateFiles...) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + templData := map[string]interface{}{ + "Keys": userKeys, + } + err = ts.ExecuteTemplate(w, "app", templData) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return } - return c.Render("keys", fiber.Map{"Keys": userKeys}, "layouts/app") } diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index 1783830..f698953 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -3,13 +3,13 @@ package handlers import ( "encoding/json" "errors" + "github.com/oklog/ulid/v2" "log/slog" "net/http" "time" "visio/internal/store" "visio/internal/types" "visio/pkg" - "github.com/oklog/ulid/v2" ) type AuthHandler struct { @@ -26,7 +26,7 @@ func NewAuthHandler(usersStore *store.Users, sessionsStore *store.Sessions, logg } } -func (h *AuthHandler) Authicate(w http.ResponseWriter, r *http.Request) { +func (h *AuthHandler) Authenticate(w http.ResponseWriter, r *http.Request) { payload := new(struct { Email string `json:"email"` Password string `json:"password"` @@ -82,6 +82,7 @@ func (h *AuthHandler) Authicate(w http.ResponseWriter, r *http.Request) { authCookie := &http.Cookie{ Name: "session", Value: sessionId, + Path: "/", } http.SetCookie(w, authCookie) w.WriteHeader(http.StatusCreated) @@ -111,6 +112,7 @@ func (h *AuthHandler) Authicate(w http.ResponseWriter, r *http.Request) { authCookie := &http.Cookie{ Name: "session", Value: sessionId, + Path: "/", } http.SetCookie(w, authCookie) w.WriteHeader(http.StatusOK) diff --git a/internal/middlewares/auth.go b/internal/middlewares/auth.go index 12878fd..ef4de61 100644 --- a/internal/middlewares/auth.go +++ b/internal/middlewares/auth.go @@ -3,6 +3,7 @@ package middlewares import ( "context" "errors" + "fmt" "log/slog" "net/http" "visio/internal/store" @@ -28,14 +29,16 @@ func (m *AuthMiddleware) CookieAuth(next http.Handler) http.Handler { sessionCookie, err := r.Cookie("session") if err != nil { if err == http.ErrNoCookie { - w.WriteHeader(http.StatusUnauthorized) + fmt.Println("No cookie? ;(") + http.Redirect(w, r, "/auth", http.StatusTemporaryRedirect) return } } - sessionValue, err := m.sessions.Get(sessionCookie.Name) + sessionValue, err := m.sessions.Get(sessionCookie.Value) if err != nil { if errors.Is(err, types.ErrSessionNotFound) { - w.WriteHeader(http.StatusUnauthorized) + fmt.Println("No session 0_0") + http.Redirect(w, r, "/auth", http.StatusTemporaryRedirect) return } m.logger.Error(err.Error()) @@ -45,7 +48,8 @@ func (m *AuthMiddleware) CookieAuth(next http.Handler) http.Handler { sessionUser, err := m.users.GetById(sessionValue) if err != nil { if errors.Is(err, types.ErrUserNotFound) { - w.WriteHeader(http.StatusUnauthorized) + fmt.Println("No user o_o") + http.Redirect(w, r, "/auth", http.StatusTemporaryRedirect) return } m.logger.Error(err.Error()) diff --git a/main.go b/main.go index 8d9ff13..a31cc09 100644 --- a/main.go +++ b/main.go @@ -63,6 +63,7 @@ func main() { }) r.Route("/keys", func(r chi.Router) { + r.With(authMiddleware.CookieAuth).Get("/", appHandler.GetKeysPage) r.With(authMiddleware.CookieAuth).Post("/", keyHandler.Create) r.With(authMiddleware.CookieAuth).Post("/{prefix}", keyHandler.Revoke) }) diff --git a/views/keys.html b/views/keys.html index 8685d5a..f5789b0 100644 --- a/views/keys.html +++ b/views/keys.html @@ -1,8 +1,9 @@ +{{define "main"}} Visio | Keys -
+

Manage your API keys

@@ -102,3 +103,4 @@

} } +{{end}} diff --git a/views/layouts/app.html b/views/layouts/app.html index b96acc6..62880fb 100644 --- a/views/layouts/app.html +++ b/views/layouts/app.html @@ -1,3 +1,4 @@ +{{define "app"}} @@ -21,8 +22,9 @@

Discord

- {{embed}} + {{template "main" .}}
+{{end}} From 50a782e2a5985ff997b34c14c789b6fb96aeb768 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 13:45:00 +0000 Subject: [PATCH 09/14] rewrote keys page --- internal/handlers/app.go | 12 +++++++++++- main.go | 4 ++-- views/keys.html | 6 +++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/internal/handlers/app.go b/internal/handlers/app.go index a0d09d8..bc518e2 100644 --- a/internal/handlers/app.go +++ b/internal/handlers/app.go @@ -1,6 +1,7 @@ package handlers import ( + "encoding/json" "html/template" "log/slog" "net/http" @@ -74,7 +75,16 @@ func (h *AppHandler) GetKeysPage(w http.ResponseWriter, r *http.Request) { "views/layouts/app.html", "views/keys.html", } - ts, err := template.ParseFiles(templateFiles...) + + ts, err := template.New("").Funcs(template.FuncMap{ + "jsonify": func(v interface{}) string { + b, err := json.Marshal(v) + if err != nil { + return "" + } + return string(b) + }, + }).ParseFiles(templateFiles...) if err != nil { h.logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) diff --git a/main.go b/main.go index a31cc09..fd44296 100644 --- a/main.go +++ b/main.go @@ -59,13 +59,13 @@ func main() { r.Get("/auth", appHandler.RenderAuthPage) r.Route("/api", func(r chi.Router) { - r.Post("/auth", authHandler.Authicate) + r.Post("/auth", authHandler.Authenticate) }) r.Route("/keys", func(r chi.Router) { r.With(authMiddleware.CookieAuth).Get("/", appHandler.GetKeysPage) r.With(authMiddleware.CookieAuth).Post("/", keyHandler.Create) - r.With(authMiddleware.CookieAuth).Post("/{prefix}", keyHandler.Revoke) + r.With(authMiddleware.CookieAuth).Delete("/{prefix}", keyHandler.Revoke) }) port := os.Getenv("PORT") diff --git a/views/keys.html b/views/keys.html index f5789b0..266cf44 100644 --- a/views/keys.html +++ b/views/keys.html @@ -3,7 +3,7 @@ Visio | Keys -
+

Manage your API keys

@@ -54,7 +54,7 @@

try { console.log("Creating key") const response = await fetch( - `/api/key`, + `/keys`, { method: "POST", headers: { @@ -79,7 +79,7 @@

revokeKey: async function (keyPrefix) { try { const response = await fetch( - `/api/key/${keyPrefix}`, + `/keys/${keyPrefix}`, { method: "DELETE", headers: { From 2416617bf47b14a6d097b65bfb0fc0ab8efe5b14 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 13:56:59 +0000 Subject: [PATCH 10/14] rewrite revoke key feature --- internal/handlers/keys.go | 20 +++++++++++++++++++- views/keys.html | 18 ++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/internal/handlers/keys.go b/internal/handlers/keys.go index b310fd9..e5968ec 100644 --- a/internal/handlers/keys.go +++ b/internal/handlers/keys.go @@ -12,6 +12,7 @@ import ( "visio/internal/types" "visio/pkg" + "github.com/go-chi/chi/v5" "github.com/oklog/ulid/v2" ) @@ -103,5 +104,22 @@ func (h *KeyHandler) Create(w http.ResponseWriter, r *http.Request) { } func (h *KeyHandler) Revoke(w http.ResponseWriter, r *http.Request) { - + currentUser, ok := r.Context().Value("currentUser").(*types.User) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + prefix := chi.URLParam(r, "prefix") + if prefix == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + err := h.keys.Delete(prefix, currentUser.Id) + if err != nil { + h.logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) + return } diff --git a/views/keys.html b/views/keys.html index 266cf44..d576e49 100644 --- a/views/keys.html +++ b/views/keys.html @@ -1,4 +1,5 @@ {{define "main"}} + Visio | Keys @@ -67,8 +68,14 @@

switch (status) { case 201: window.location.reload() - default: - console.log(status) + break + case 401: + window.location.href = "/auth" + break + case 403: + alert("You can not create more than 5 keys") + break + case 500: alert("Something went wrong. Please retry or contact us") } } catch (err) { @@ -91,8 +98,11 @@

switch (status) { case 200: window.location.reload() - default: - console.log(status) + break + case 401: + window.location.href = "/auth" + break + case 500: alert("Something went wrong. Please retry or contact us") } } catch (err) { From b73d90d0aab0a2519ab84230f5ea03bfab5c5484 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 14:02:02 +0000 Subject: [PATCH 11/14] add command to build css --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8d4a70d..0df5b40 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,11 @@ push-image: @docker tag visio:latest thewisepigeon/visio:latest @docker push thewisepigeon/visio:latest -tailwind-compilation: +start-tailwind-compilation: @npx tailwindcss -i ./assets/app.css -o ./public/output.css --minify --watch +build-css: + @npx tailwindcss -i ./assets/app.css -o ./public/output.css --minify + test: @go test -v ./... From a14cd3f04c33da57c583d9d42cceac0a74621375 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 14:05:20 +0000 Subject: [PATCH 12/14] update workflow: build css before building binary --- .github/workflows/go.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9267245..981bfe6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,6 +1,3 @@ -# This workflow will build a golang project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go - name: Go on: @@ -21,6 +18,14 @@ jobs: with: go-version: '1.21.5' + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Compile css + run: make build-css + - name: Build run: go build -v ./... From 2846f81daf75d9972bac87a459ec2e9808692188 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 14:09:04 +0000 Subject: [PATCH 13/14] fix: install dependencies before building css --- .github/workflows/go.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 981bfe6..b6f7709 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,6 +23,9 @@ jobs: with: node-version: 18 + - name: Install dependencies + run: npm ci + - name: Compile css run: make build-css From 756905a3383818df9728c90db340777b4245cf86 Mon Sep 17 00:00:00 2001 From: TheWisePigeon Date: Wed, 10 Jan 2024 14:14:36 +0000 Subject: [PATCH 14/14] adapt tests to new table structure --- internal/store/users_test.go | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/store/users_test.go b/internal/store/users_test.go index 88601d2..50438c8 100644 --- a/internal/store/users_test.go +++ b/internal/store/users_test.go @@ -89,8 +89,8 @@ func migrateTestDB(db *sqlx.DB) error { create table if not exists users ( id text not null primary key, email text not null unique, - password text not null, - signup_date timestamp not null + password_hash text not null, + signup_date text not null );` _, err := db.Exec(q) return err @@ -101,18 +101,18 @@ func TestUsers_Insert(t *testing.T) { t.Run("duplicate email", func(t *testing.T) { err := s.Insert(&types.User{ - Id: "1", - Email: "foo@gmail.com", - Password: "password1", - SignupDate: time.Now(), + Id: "1", + Email: "foo@gmail.com", + PasswordHash: "password1", + SignupDate: time.Now().UTC().Format("January, 2 2006"), }) require.NoError(t, err) err = s.Insert(&types.User{ - Id: "2", - Email: "foo@gmail.com", - Password: "password2", - SignupDate: time.Now(), + Id: "2", + Email: "foo@gmail.com", + PasswordHash: "password2", + SignupDate: time.Now().UTC().Format("January, 2 2006"), }) require.Error(t, err) var pqErr *pq.Error @@ -126,18 +126,18 @@ func TestUsers_Insert(t *testing.T) { t.Run("success", func(t *testing.T) { err := s.Insert(&types.User{ - Id: "1", - Email: "foo@gmail.com", - Password: "password1", - SignupDate: time.Now(), + Id: "1", + Email: "foo@gmail.com", + PasswordHash: "password1", + SignupDate: time.Now().UTC().Format("January, 2 2006"), }) require.NoError(t, err) err = s.Insert(&types.User{ - Id: "2", - Email: "bar@gmail.com", - Password: "password2", - SignupDate: time.Now(), + Id: "2", + Email: "bar@gmail.com", + PasswordHash: "password2", + SignupDate: time.Now().UTC().Format("January, 2 2006"), }) require.NoError(t, err) @@ -158,10 +158,10 @@ func TestUsers_GetById(t *testing.T) { t.Run("user exists", func(t *testing.T) { existingUser := types.User{ - Id: "1", - Email: "emailz", - Password: "passwordz", - SignupDate: time.Now().Truncate(time.Hour).UTC(), + Id: "1", + Email: "emailz", + PasswordHash: "passwordz", + SignupDate: time.Now().Truncate(time.Hour).UTC().Format("January, 2 2006"), } require.NoError(t, s.Insert(&existingUser)) @@ -169,8 +169,8 @@ func TestUsers_GetById(t *testing.T) { user, err := s.GetById("1") require.Equal(t, user.Id, existingUser.Id) require.Equal(t, user.Email, existingUser.Email) - require.Equal(t, user.Password, existingUser.Password) - require.Equal(t, user.SignupDate.In(time.UTC), existingUser.SignupDate) + require.Equal(t, user.PasswordHash, existingUser.PasswordHash) + require.Equal(t, user.SignupDate, existingUser.SignupDate) require.NoError(t, err) testDB.Exec("TRUNCATE users;")