hiling_go/api/user.go

328 lines
8.8 KiB
Go
Raw Normal View History

2023-09-08 22:25:38 +07:00
package api
import (
2023-09-12 17:07:57 +07:00
"database/sql"
2023-10-04 19:51:29 +07:00
"encoding/json"
2023-10-09 16:25:39 +07:00
"fmt"
2023-09-08 22:25:38 +07:00
"net/http"
2023-10-09 16:25:39 +07:00
"os"
"path/filepath"
"time"
2023-09-08 22:25:38 +07:00
2023-09-12 17:07:57 +07:00
db "git.nochill.in/nochill/hiling_go/db/sqlc"
"git.nochill.in/nochill/hiling_go/util"
2023-10-04 19:51:29 +07:00
"git.nochill.in/nochill/hiling_go/util/token"
2023-09-08 22:25:38 +07:00
"github.com/gin-gonic/gin"
2023-09-12 17:07:57 +07:00
"github.com/lib/pq"
2023-10-09 16:25:39 +07:00
"github.com/sqlc-dev/pqtype"
2023-09-08 22:25:38 +07:00
)
2023-09-12 17:07:57 +07:00
type createUserRequest struct {
Username string `json:"username" binding:"required,alphanum"`
Password string `json:"password" binding:"required,min=7"`
}
type createUserResponse struct {
ID int32 `json:"id"`
Username string `json:"username"`
AvatarPicture string `json:"avatar_picture"` // avatar_url
BannedAt sql.NullTime `json:"banned_at"`
BannedUntil sql.NullTime `json:"banned_until"`
BanReason string `json:"ban_reason"`
IsPermaban bool `json:"is_permaban"`
IsAdmin bool `json:"is_admin"`
IsCritics bool `json:"is_critics"`
IsVerified bool `json:"is_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
2023-09-08 22:25:38 +07:00
}
func (server *Server) createUser(ctx *gin.Context) {
2023-09-12 17:07:57 +07:00
var req createUserRequest
2023-09-08 22:25:38 +07:00
if err := ctx.ShouldBindJSON(&req); err != nil {
2023-09-12 17:07:57 +07:00
if err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
2023-09-12 17:07:57 +07:00
return
}
}
hashedPassword, err := util.HashPassword(req.Password)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
2023-09-08 22:25:38 +07:00
return
}
2023-09-12 17:07:57 +07:00
arg := db.CreateUserParams{
Username: req.Username,
Password: hashedPassword,
}
user, err := server.Store.CreateUser(ctx, arg)
2023-09-08 22:25:38 +07:00
if err != nil {
2023-09-12 17:07:57 +07:00
if pqErr, ok := err.(*pq.Error); ok {
switch pqErr.Code.Name() {
case "foreign_key_violation", "unique_violation":
ctx.JSON(http.StatusConflict, ErrorResponse(err, "Username already used"))
return
2023-09-12 17:07:57 +07:00
}
}
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
2023-09-08 22:25:38 +07:00
return
}
accessToken, _, err := server.TokenMaker.CreateToken(
2023-09-21 21:38:41 +07:00
user.Username,
int(user.ID),
server.Config.TokenDuration,
)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while creating token"))
return
}
2023-09-23 15:24:21 +07:00
// refreshToken, refreshTokenPayload, err := server.TokenMaker.CreateToken(
// user.Username,
// int(user.ID),
// server.Config.RefreshTokenDuration,
// )
2023-09-21 21:38:41 +07:00
_, err = server.Store.CreateSession(ctx, db.CreateSessionParams{
2023-09-23 15:24:21 +07:00
Username: user.Username,
// RefreshToken: refreshToken,
UserAgent: ctx.Request.UserAgent(),
ClientIp: ctx.ClientIP(),
IsBlocked: false,
// ExpiresAt: refreshTokenPayload.ExpiredAt,
2023-09-21 21:38:41 +07:00
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while saving sessions"))
return
}
2023-09-12 17:07:57 +07:00
res := createUserResponse{
ID: user.ID,
Username: user.Username,
AvatarPicture: user.AvatarPicture.String,
BannedAt: sql.NullTime{Valid: user.BannedAt.Valid, Time: user.BannedAt.Time},
BannedUntil: sql.NullTime{Valid: user.BannedUntil.Valid, Time: user.BannedUntil.Time},
BanReason: user.BanReason.String,
IsPermaban: user.IsPermaban.Bool,
IsAdmin: user.IsAdmin.Bool,
IsCritics: user.IsCritics.Bool,
IsVerified: user.IsVerified.Bool,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
2023-09-12 17:07:57 +07:00
}
2023-09-23 15:24:21 +07:00
ctx.SetCookie(
"paseto",
accessToken,
int(server.Config.CookieDuration),
2023-09-23 15:24:21 +07:00
"/",
"localhost",
false,
false,
2023-09-23 15:24:21 +07:00
)
ctx.JSON(http.StatusOK, res)
2023-09-08 22:25:38 +07:00
}
2023-09-23 15:24:21 +07:00
2023-10-04 19:51:29 +07:00
func (server *Server) getUserStats(ctx *gin.Context) {
var scoreDistribution []map[string]int8
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
userStats, err := server.Store.GetUserStats(ctx, int32(authPayload.UserID))
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get user stats"))
return
}
err = json.Unmarshal(userStats.ScoresDistribution, &scoreDistribution)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whilet try to parse score distribution"))
return
}
var userReviews []map[string]any
if userStats.Reviews != nil {
err = json.Unmarshal(userStats.Reviews, &userReviews)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something wrong while try to parse user reviwes stats"))
return
}
} else {
userReviews = make([]map[string]any, 0)
}
ctx.JSON(http.StatusOK, gin.H{
"reviews": userReviews,
"scores_distribution": scoreDistribution,
"user_stats": userStats,
})
}
2023-10-09 16:25:39 +07:00
type UpdateUserRequest struct {
About string `json:"about"`
Website string `json:"website"`
SocialMedia []map[string]string `json:"social_media"`
}
func (server *Server) updateUser(ctx *gin.Context) {
var req UpdateUserRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
fmt.Println(req.About)
social, _ := json.Marshal(req.SocialMedia)
socialArr := pqtype.NullRawMessage{
RawMessage: social,
Valid: len(req.SocialMedia) > 0,
}
user, err := server.Store.UpdateUser(ctx, db.UpdateUserParams{
About: sql.NullString{String: req.About, Valid: true},
SocialMedia: socialArr,
Website: sql.NullString{String: req.Website, Valid: len(req.Website) > 0},
ID: int32(authPayload.UserID),
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user"))
return
}
ctx.JSON(http.StatusOK, user)
}
func (server *Server) updateUserAvatar(ctx *gin.Context) {
file, err := ctx.FormFile("file")
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to parse file"))
return
}
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
fileExt := filepath.Ext(file.Filename)
now := time.Now()
dir := fmt.Sprintf("public/upload/images/user/%d/avatar", authPayload.UserID)
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
imgPath := fmt.Sprintf("%s/%s", dir, osFilename)
if _, err := os.Stat(dir); os.IsNotExist(err) {
os.Mkdir(dir, 0775)
}
if err := ctx.SaveUploadedFile(file, imgPath); err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
return
}
url, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
ID: int32(authPayload.UserID),
AvatarPicture: sql.NullString{Valid: true, String: imgPath},
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save image to database"))
return
}
ctx.JSON(http.StatusOK, gin.H{"image_url": url.String})
}
func (server *Server) removeAvatar(ctx *gin.Context) {
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
_, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
AvatarPicture: sql.NullString{String: "", Valid: false},
ID: int32(authPayload.UserID),
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user avatar"))
return
}
ctx.Writer.WriteHeader(http.StatusNoContent)
}
2023-09-23 15:24:21 +07:00
func (server *Server) login(ctx *gin.Context) {
var req createUserRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
user, err := server.Store.GetUser(ctx, req.Username)
if err != nil {
if err == sql.ErrNoRows {
2023-09-27 22:34:52 +07:00
ctx.JSON(http.StatusNotFound, ErrorResponse(err, "User not found"))
2023-09-23 15:24:21 +07:00
return
}
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whlie try to get user"))
return
}
err = util.CheckPassword(req.Password, user.Password)
if err != nil {
ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "Password not match"))
return
}
accessToken, _, err := server.TokenMaker.CreateToken(user.Username, int(user.ID), server.Config.TokenDuration)
2023-09-23 15:24:21 +07:00
if err != nil {
ctx.JSON(http.StatusInternalServerError, "Something went wrong while try to create token")
return
}
_, err = server.Store.CreateSession(ctx, db.CreateSessionParams{
Username: user.Username,
UserAgent: ctx.Request.UserAgent(),
ClientIp: ctx.ClientIP(),
IsBlocked: false,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to create session"))
return
}
ctx.SetCookie(
"paseto",
accessToken,
int(server.Config.CookieDuration),
"/",
2023-09-23 15:24:21 +07:00
"localhost",
false,
false,
2023-09-23 15:24:21 +07:00
)
ctx.JSON(http.StatusOK, user)
}
func (server *Server) logout(ctx *gin.Context) {
ctx.SetCookie(
"paseto",
"",
-1,
"/",
"",
false,
true,
)
ctx.Writer.WriteHeader(http.StatusNoContent)
}