From 2ff678a5d99319617143d3183191c37a4f9a323e Mon Sep 17 00:00:00 2001 From: juls0730 <62722391+juls0730@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:35:03 -0500 Subject: [PATCH] add missing files... oops --- middleware/auth.go | 58 +++++++++++++++++++ models/user.go | 32 +++++++++++ routes/auth.go | 136 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 middleware/auth.go create mode 100644 models/user.go create mode 100644 routes/auth.go diff --git a/middleware/auth.go b/middleware/auth.go new file mode 100644 index 0000000..78049a3 --- /dev/null +++ b/middleware/auth.go @@ -0,0 +1,58 @@ +package middleware + +import ( + "context" + "database/sql" + "filething/models" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +// import ( +// "database/sql" +// "net/http" + +// "github.com/go-pg/pg/v10" + +// "github.com/labstack/echo/v4" +// ) + +const UserContextKey = "user" + +func SessionMiddleware(db *bun.DB) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + // Extract sessionToken from the cookie + cookie, err := c.Cookie("sessionToken") + if err != nil { + if err == http.ErrNoCookie { + return echo.NewHTTPError(http.StatusUnauthorized, "Session token missing") + } + return echo.NewHTTPError(http.StatusBadRequest, "Bad request") + } + + sessionToken := cookie.Value + + // Query the session and user data from PostgreSQL + session := new(models.Session) + err = db.NewSelect().Model(session).Relation("User").WherePK(sessionToken).Scan(context.Background()) + + if err != nil { + if err == sql.ErrNoRows { + return echo.NewHTTPError(http.StatusUnauthorized, "Invalid session token") + } + return echo.NewHTTPError(http.StatusInternalServerError, "Database error") + } + + user := session.User + + // Store the user in the context + c.Set(UserContextKey, user) + + // Continue to the next handler + return next(c) + } + } +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..2139e4f --- /dev/null +++ b/models/user.go @@ -0,0 +1,32 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type LoginData struct { + UsernameOrEmail string `json:"username_or_email"` + Password string `json:"password"` +} + +type SignupData struct { + Username string `json:"username"` + Email string `json:"email"` + Password string `json:"password"` +} + +type User struct { + bun.BaseModel `bun:"table:users,alias:u"` + ID uuid.UUID `bun:",pk,type:uuid,default:uuid_generate_v4()"` + Username string `bun:"username,notnull,unique"` + Email string `bun:"email,notnull,unique"` + PasswordHash string `bun:"passwordHash,notnull"` +} + +type Session struct { + bun.BaseModel `bun:"table:sessions,alias:u"` + ID uuid.UUID `bun:",pk,type:uuid,default:uuid_generate_v4()"` + UserID uuid.UUID `bun:"user_id,notnull"` + User User `bun:"rel:belongs-to,join:user_id=id"` +} diff --git a/routes/auth.go b/routes/auth.go new file mode 100644 index 0000000..28b7b45 --- /dev/null +++ b/routes/auth.go @@ -0,0 +1,136 @@ +package routes + +import ( + "context" + "filething/models" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" + "golang.org/x/crypto/bcrypt" +) + +func LoginHandler(c echo.Context) error { + var loginData models.LoginData + + if err := c.Bind(&loginData); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) + } + + if loginData.UsernameOrEmail == "" || loginData.Password == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"message": "A password, and username or email are required!"}) + } + + db := c.Get("db").(*bun.DB) + + user := new(models.User) + err := db.NewSelect().Model(user).Where("email = ?", loginData.UsernameOrEmail).Scan(context.Background()) + if err != nil { + err := db.NewSelect().Model(user).Where("username = ?", loginData.UsernameOrEmail).Scan(context.Background()) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "User with that username or email not found!"}) + } + } + + session, err := GenerateSessionToken(db, user.ID) + + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) + } + + c.SetCookie(&http.Cookie{ + Name: "sessionToken", + Value: session.ID.String(), + Path: "/", + }) + + return c.JSON(http.StatusOK, map[string]string{"message": "Login successful!"}) + + // sessionID := uuid.New().String() + // session := &models.Session{ID: sessionID, UserID: user.ID, ExpiresAt: time.Now().Add(time.Hour * 24)} + + // key := "session:" + session.ID + // err = client.HSet(ctx, key, session).Err() + + // if err != nil { + // c.JSON(http.StatusInternalServerError, gin.H{"message": "An unknown error occoured!"}) + // return + // } + + // http.SetCookie(c.Writer, &http.Cookie{ + // Name: "sessionToken", + // Value: sessionID, + // Path: "/", + // }) + + // c.JSON(http.StatusOK, gin.H{"message": "Login successful"}) +} + +func SignupHandler(c echo.Context) error { + var signupData models.SignupData + + if err := c.Bind(&signupData); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) + } + + if signupData.Username == "" || signupData.Password == "" || signupData.Email == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"message": "A password, username and email are required!"}) + } + + // if email is not valid + if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(signupData.Email) { + return c.JSON(http.StatusBadRequest, map[string]string{"message": "A valid email is required!"}) + } + + db := c.Get("db").(*bun.DB) + + hash, err := bcrypt.GenerateFromPassword([]byte(signupData.Password), 12) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) + } + + user := &models.User{ + Username: signupData.Username, + Email: signupData.Email, + PasswordHash: string(hash), + } + _, err = db.NewInsert().Model(user).Exec(context.Background()) + + if err != nil { + return c.JSON(http.StatusConflict, map[string]string{"message": "A user with that email or username already exists!"}) + } + + session, err := GenerateSessionToken(db, user.ID) + + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) + } + + c.SetCookie(&http.Cookie{ + Name: "sessionToken", + Value: session.ID.String(), + Path: "/", + }) + + return c.JSON(http.StatusOK, map[string]string{"message": "Signup successful!"}) + + // http.SetCookie(c.Writer, &http.Cookie{ + // Name: "sessionToken", + // Value: sessionID, + // Path: "/", + // }) + + // c.JSON(http.StatusOK, gin.H{"message": "Signup successful"}) +} + +func GenerateSessionToken(db *bun.DB, userId uuid.UUID) (*models.Session, error) { + session := &models.Session{ + UserID: userId, + } + + _, err := db.NewInsert().Model(session).Exec(context.Background()) + + return session, err +}