Skip to content

Commit

Permalink
feat: add user login/logout api
Browse files Browse the repository at this point in the history
  • Loading branch information
lanthora committed Jul 18, 2024
1 parent 0b76dc4 commit 272267d
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 33 deletions.
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ func main() {
logger.Info("listen=[%v]", addr)

r := gin.New()
r.Use(user.LoginMiddleware())
r.POST("/api/user/register", user.Register)
r.POST("/api/user/login", user.Login)
r.POST("/api/user/logout", user.Logout)

if err := r.Run(addr); err != nil {
logger.Fatal("service run failed: %v", err)
Expand Down
56 changes: 56 additions & 0 deletions status/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package status

import (
"net/http"

"github.com/gin-gonic/gin"
)

const (
Success int = iota
Unexpected
NotLoggedIn
InvalidRequest
InvalidUsername
InvalidPassword
RegisterTooFrequently
UsernameAlreadyTaken
UsernameOrPasswordIncorrect
)

var statusMessage map[int]string

func init() {
statusMessage = make(map[int]string)
statusMessage[Success] = "success"
statusMessage[Unexpected] = "unexpected"
statusMessage[NotLoggedIn] = "not logged in"
statusMessage[InvalidRequest] = "invalid request "
statusMessage[InvalidUsername] = "invalid username"
statusMessage[InvalidPassword] = "invalid password"
statusMessage[RegisterTooFrequently] = "register too frequently"
statusMessage[UsernameAlreadyTaken] = "username already taken"
statusMessage[UsernameOrPasswordIncorrect] = "username or password is incorret"
}

func UpdateSuccess(c *gin.Context, data gin.H) {
c.JSON(http.StatusOK, gin.H{
"status": Success,
"msg": statusMessage[Success],
"data": data,
})
}

func UpdateUnexpected(c *gin.Context, msg string) {
c.JSON(http.StatusOK, gin.H{
"status": Unexpected,
"msg": msg,
})
}

func UpdateCode(c *gin.Context, code int) {
c.JSON(http.StatusOK, gin.H{
"status": code,
"msg": statusMessage[code],
})
}
133 changes: 100 additions & 33 deletions user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package user
import (
"crypto/sha256"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"time"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/lanthora/cacao/logger"
"github.com/lanthora/cacao/status"
"github.com/lanthora/cacao/storage"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -41,31 +43,60 @@ func sha256sum(data []byte) string {
return fmt.Sprintf("%x", hash[:])
}

func LoginMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.String()
if !strings.HasPrefix(path, "/api/") {
c.Next()
return
}
if path == "/api/user/register" || path == "/api/user/login" {
c.Next()
return
}
idstr, errid := c.Cookie("id")
token, errtoken := c.Cookie("token")
if errid != nil || errtoken != nil || len(idstr) == 0 || len(token) == 0 {
status.UpdateCode(c, status.NotLoggedIn)
c.Abort()
return
}
id, err := strconv.ParseUint(idstr, 10, 64)
if err != nil {
status.UpdateCode(c, status.NotLoggedIn)
c.Abort()
return
}
user := &User{}
user.ID = uint(id)

db := storage.Get()
result := db.Where(user).Take(user)
if result.Error != nil || user.Token != token {
status.UpdateCode(c, status.NotLoggedIn)
c.Abort()
return
}
c.Set("user", user)
c.Next()
}
}

func Register(c *gin.Context) {
var request struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&request); err != nil {
logger.Info("register bind failed: %v", err)
c.JSON(http.StatusOK, gin.H{
"status": 1,
"msg": "bind json failed",
})
status.UpdateCode(c, status.InvalidRequest)
return
}
if len(request.Username) < 3 || !isAlphanumeric(request.Username) {
c.JSON(http.StatusOK, gin.H{
"status": 2,
"msg": "username format invalid",
})
status.UpdateCode(c, status.InvalidUsername)
return
}
if len(request.Password) == 0 {
c.JSON(http.StatusOK, gin.H{
"status": 3,
"msg": "password format invalid",
})
status.UpdateCode(c, status.InvalidPassword)
return
}

Expand All @@ -75,10 +106,7 @@ func Register(c *gin.Context) {
db.Model(&User{}).Where(&User{IP: c.ClientIP(), Role: "normal"}).Where("created_at > ?", time.Now().Add(-24*time.Hour)).Count(&count)
return count > 0
}() {
c.JSON(http.StatusOK, gin.H{
"status": 4,
"msg": fmt.Sprintf("register too frequently: %v", c.ClientIP()),
})
status.UpdateCode(c, status.RegisterTooFrequently)
return
}

Expand All @@ -87,10 +115,7 @@ func Register(c *gin.Context) {
db.Model(&User{}).Where(&User{Name: request.Username}).Count(&count)
return count > 0
}() {
c.JSON(http.StatusOK, gin.H{
"status": 5,
"msg": "username already taken",
})
status.UpdateCode(c, status.UsernameAlreadyTaken)
return
}

Expand All @@ -112,19 +137,61 @@ func Register(c *gin.Context) {
}

if result := db.Create(&user); result.Error != nil {
c.JSON(http.StatusOK, gin.H{
"status": 255,
"msg": result.Error.Error(),
})
status.UpdateUnexpected(c, result.Error.Error())
return
}

c.SetCookie("id", strconv.FormatUint(uint64(user.ID), 10), 86400, "/", "", false, true)
c.SetCookie("token", user.Token, 86400, "/", "", false, true)

status.UpdateSuccess(c, gin.H{
"name": user.Name,
"role": user.Role,
})
}

func Login(c *gin.Context) {
var request struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&request); err != nil {
status.UpdateCode(c, status.InvalidRequest)
return
}

c.JSON(http.StatusOK, gin.H{
"status": 0,
"msg": "success",
"data": gin.H{
"id": user.ID,
"token": user.Token,
},
user := User{
Name: request.Username,
Password: sha256sum([]byte(request.Password)),
}

db := storage.Get()
if result := db.Where(user).Take(&user); result.Error != nil {
status.UpdateCode(c, status.UsernameOrPasswordIncorrect)
return
}

user.Token = uuid.NewString()
db.Save(user)

c.SetCookie("id", strconv.FormatUint(uint64(user.ID), 10), 86400, "/", "", false, true)
c.SetCookie("token", user.Token, 86400, "/", "", false, true)

status.UpdateSuccess(c, gin.H{
"name": user.Name,
"role": user.Role,
})
}

func Logout(c *gin.Context) {
user := c.MustGet("user").(*User)
user.Token = uuid.NewString()

db := storage.Get()
db.Save(user)

c.SetCookie("id", "", -1, "/", "", false, true)
c.SetCookie("token", "", -1, "/", "", false, true)

status.UpdateSuccess(c, nil)
}

0 comments on commit 272267d

Please sign in to comment.