add update users and refactor

This commit is contained in:
nochill 2023-10-09 16:25:39 +07:00
parent eb9774cce5
commit f8ecd20220
9 changed files with 269 additions and 83 deletions

View File

@ -67,8 +67,11 @@ func (server *Server) getRoutes() {
// REQUIRE AUTH TOKEN
authRoutes := router.Use(authMiddleware(server.TokenMaker))
authRoutes.POST("/review/location", server.createReview)
authRoutes.PATCH("/user", server.updateUser)
authRoutes.GET("/user/review/location/:location_id", server.getUserReviewByLocation)
authRoutes.GET("/user/profile", server.getUserStats)
authRoutes.PATCH("/user/avatar", server.updateUserAvatar)
authRoutes.DELETE("/user/avatar", server.removeAvatar)
server.Router = router
}

View File

@ -3,7 +3,10 @@ package api
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"time"
db "git.nochill.in/nochill/hiling_go/db/sqlc"
@ -11,6 +14,7 @@ import (
"git.nochill.in/nochill/hiling_go/util/token"
"github.com/gin-gonic/gin"
"github.com/lib/pq"
"github.com/sqlc-dev/pqtype"
)
type createUserRequest struct {
@ -161,6 +165,99 @@ func (server *Server) getUserStats(ctx *gin.Context) {
})
}
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)
}
func (server *Server) login(ctx *gin.Context) {
var req createUserRequest

View File

@ -6,6 +6,7 @@ package mockdb
import (
context "context"
sql "database/sql"
reflect "reflect"
db "git.nochill.in/nochill/hiling_go/db/sqlc"
@ -391,6 +392,21 @@ func (mr *MockStoreMockRecorder) RemoveFollowUser(arg0, arg1 interface{}) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFollowUser", reflect.TypeOf((*MockStore)(nil).RemoveFollowUser), arg0, arg1)
}
// UpdateAvatar mocks base method.
func (m *MockStore) UpdateAvatar(arg0 context.Context, arg1 db.UpdateAvatarParams) (sql.NullString, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAvatar", arg0, arg1)
ret0, _ := ret[0].(sql.NullString)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateAvatar indicates an expected call of UpdateAvatar.
func (mr *MockStoreMockRecorder) UpdateAvatar(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatar", reflect.TypeOf((*MockStore)(nil).UpdateAvatar), arg0, arg1)
}
// UpdateLocationThumbnail mocks base method.
func (m *MockStore) UpdateLocationThumbnail(arg0 context.Context, arg1 db.UpdateLocationThumbnailParams) error {
m.ctrl.T.Helper()
@ -420,10 +436,10 @@ func (mr *MockStoreMockRecorder) UpdatePassword(arg0, arg1 interface{}) *gomock.
}
// UpdateUser mocks base method.
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.User, error) {
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.UpdateUserRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1)
ret0, _ := ret[0].(db.User)
ret0, _ := ret[0].(db.UpdateUserRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}

View File

@ -5,17 +5,13 @@ INSERT INTO users (
) VALUES ($1, $2)
RETURNING *;
-- name: UpdateUser :one
UPDATE users
SET
email = COALESCE(sqlc.narg(email), email),
username = COALESCE(sqlc.narg(username), username),
avatar_picture = COALESCE(sqlc.narg(avatar_picture), avatar_picture)
WHERE
id = sqlc.arg(id)
RETURNING *;
-- name: UpdatePassword :exec
UPDATE users
SET password = $1
WHERE id = $2;
-- name: UpdateAvatar :one
UPDATE users
SET avatar_picture = $1
WHERE id = $2
RETURNING avatar_picture;

View File

@ -269,6 +269,8 @@ type User struct {
SocialMedia pqtype.NullRawMessage `json:"social_media"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
About sql.NullString `json:"about"`
Website sql.NullString `json:"website"`
}
type UserActivity struct {

View File

@ -6,6 +6,7 @@ package db
import (
"context"
"database/sql"
)
type Querier interface {
@ -23,9 +24,9 @@ type Querier interface {
GetSession(ctx context.Context, id int32) (UserSession, error)
GetUserReviewByLocation(ctx context.Context, arg GetUserReviewByLocationParams) (GetUserReviewByLocationRow, error)
RemoveFollowUser(ctx context.Context, arg RemoveFollowUserParams) error
UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error)
UpdateLocationThumbnail(ctx context.Context, arg UpdateLocationThumbnailParams) error
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error)
}
var _ Querier = (*Queries)(nil)

View File

@ -14,6 +14,7 @@ type Store interface {
GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error)
GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error)
GetUser(ctx context.Context, username string) (GetUserRow, error)
UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error)
GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error)
CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error)
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)

View File

@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"github.com/sqlc-dev/pqtype"
)
@ -15,6 +15,9 @@ SELECT
COALESCE(email, '') as email,
password,
username,
COALESCE(google_sign_in_payload, '') as google_sign_in_payload,
COALESCE(about, '') as about,
COALESCE(website, '') as website,
COALESCE(avatar_picture, '') as avatar_picture,
banned_at,
banned_until,
@ -23,25 +26,32 @@ SELECT
is_admin,
is_critics,
is_verified,
social_media
COALESCE(social_media, '[]'),
created_at,
updated_at
FROM USERS
WHERE username = $1
`
type GetUserRow struct {
ID int32 `json:"id"`
Email string `json:"email"`
Password string `json:"-"`
Username string `json:"username"`
AvatarPicture string `json:"avatar_picture"`
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"`
SocialMedia pqtype.NullRawMessage `json:"social_media"`
ID int32 `json:"id"`
Email string `json:"email"`
Password string `json:"-"`
About string `json:"about"`
Website string `json:"website"`
Username string `json:"username"`
GoogleSignInPayload string `json:"google_sign_in_payload"`
AvatarPicture string `json:"avatar_picture"`
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"`
SocialMedia json.RawMessage `json:"social_media"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, error) {
@ -52,6 +62,9 @@ func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, err
&i.Email,
&i.Password,
&i.Username,
&i.GoogleSignInPayload,
&i.About,
&i.Website,
&i.AvatarPicture,
&i.BannedAt,
&i.BannedUntil,
@ -61,6 +74,8 @@ func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, err
&i.IsCritics,
&i.IsVerified,
&i.SocialMedia,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
@ -126,12 +141,94 @@ func (q *Queries) GetUserStats(ctx context.Context, user_id int32) (GetUserStats
&i.ScoreCount,
&i.ScoresDistribution,
)
var r []map[string]any
err = json.Unmarshal(i.ScoresDistribution, &r)
fmt.Println(r)
return i, err
}
const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
about = $1,
social_media = $2,
website = $3
WHERE
id = $4
RETURNING
id,
COALESCE(email, ''),
username,
COALESCE(avatar_picture, ''),
COALESCE(about, ''),
COALESCE(website, ''),
COALESCE(google_sign_in_payload, ''),
banned_at,
banned_until,
COALESCE(ban_reason, ''),
is_permaban,
is_admin,
is_critics,
is_verified,
is_active,
COALESCE(social_media, '[]'),
created_at,
updated_at
`
type UpdateUserParams struct {
About sql.NullString `json:"about"`
SocialMedia pqtype.NullRawMessage `json:"social_media"`
Website sql.NullString `json:"website"`
ID int32 `json:"id"`
}
type UpdateUserRow struct {
ID int32 `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
AvatarPicture string `json:"avatar_picture"`
About string `json:"about"`
Website string `json:"website"`
GoogleSignInPayload string `json:"google_sign_in_payload"`
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"`
IsActive bool `json:"is_active"`
SocialMedia json.RawMessage `json:"social_media"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
row := q.db.QueryRowContext(ctx, updateUser,
arg.About,
arg.SocialMedia,
arg.Website,
arg.ID,
)
var i UpdateUserRow
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.AvatarPicture,
&i.About,
&i.Website,
&i.GoogleSignInPayload,
&i.BannedAt,
&i.BannedUntil,
&i.BanReason,
&i.IsPermaban,
&i.IsAdmin,
&i.IsCritics,
&i.IsVerified,
&i.IsActive,
&i.SocialMedia,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@ -15,7 +15,7 @@ INSERT INTO users (
username,
password
) VALUES ($1, $2)
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at, about, website
`
type CreateUserParams struct {
@ -44,10 +44,31 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
&i.SocialMedia,
&i.CreatedAt,
&i.UpdatedAt,
&i.About,
&i.Website,
)
return i, err
}
const updateAvatar = `-- name: UpdateAvatar :one
UPDATE users
SET avatar_picture = $1
WHERE id = $2
RETURNING avatar_picture
`
type UpdateAvatarParams struct {
AvatarPicture sql.NullString `json:"avatar_picture"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error) {
row := q.db.QueryRowContext(ctx, updateAvatar, arg.AvatarPicture, arg.ID)
var avatar_picture sql.NullString
err := row.Scan(&avatar_picture)
return avatar_picture, err
}
const updatePassword = `-- name: UpdatePassword :exec
UPDATE users
SET password = $1
@ -63,51 +84,3 @@ func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams)
_, err := q.db.ExecContext(ctx, updatePassword, arg.Password, arg.ID)
return err
}
const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
email = COALESCE($1, email),
username = COALESCE($2, username),
avatar_picture = COALESCE($3, avatar_picture)
WHERE
id = $4
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
`
type UpdateUserParams struct {
Email sql.NullString `json:"email"`
Username sql.NullString `json:"username"`
AvatarPicture sql.NullString `json:"avatar_picture"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUser,
arg.Email,
arg.Username,
arg.AvatarPicture,
arg.ID,
)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.Password,
&i.AvatarPicture,
&i.GoogleSignInPayload,
&i.BannedAt,
&i.BannedUntil,
&i.BanReason,
&i.IsPermaban,
&i.IsAdmin,
&i.IsCritics,
&i.IsVerified,
&i.IsActive,
&i.SocialMedia,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}