answer/internal/controller/user_controller.go

540 lines
18 KiB
Go
Raw Normal View History

2022-09-27 17:59:05 +08:00
package controller
import (
"net/http"
"path"
"strings"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/middleware"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service"
"github.com/answerdev/answer/internal/service/action"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/uploader"
2022-09-27 17:59:05 +08:00
"github.com/gin-gonic/gin"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
)
// UserController user controller
type UserController struct {
userService *service.UserService
authService *auth.AuthService
actionService *action.CaptchaService
uploaderService *uploader.UploaderService
emailService *export.EmailService
}
// NewUserController new controller
func NewUserController(
authService *auth.AuthService,
userService *service.UserService,
actionService *action.CaptchaService,
emailService *export.EmailService,
uploaderService *uploader.UploaderService,
) *UserController {
2022-09-27 17:59:05 +08:00
return &UserController{
authService: authService,
userService: userService,
actionService: actionService,
uploaderService: uploaderService,
emailService: emailService,
}
}
// GetUserInfoByUserID get user info, if user no login response http code is 200, but user info is null
2022-09-27 17:59:05 +08:00
// @Summary GetUserInfoByUserID
// @Description get user info, if user no login response http code is 200, but user info is null
2022-09-27 17:59:05 +08:00
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
2022-10-28 15:46:05 +08:00
// @Success 200 {object} handler.RespBody{data=schema.GetUserToSetShowResp}
2022-09-27 17:59:05 +08:00
// @Router /answer/api/v1/user/info [get]
func (uc *UserController) GetUserInfoByUserID(ctx *gin.Context) {
userID := middleware.GetLoginUserIDFromContext(ctx)
token := middleware.ExtractToken(ctx)
// if user is no login return null in data
if len(token) == 0 || len(userID) == 0 {
handler.HandleResponse(ctx, nil, nil)
return
}
2022-09-27 17:59:05 +08:00
resp, err := uc.userService.GetUserInfoByUserID(ctx, token, userID)
handler.HandleResponse(ctx, err, resp)
}
// GetOtherUserInfoByUsername godoc
// @Summary GetOtherUserInfoByUsername
// @Description GetOtherUserInfoByUsername
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param username query string true "username"
// @Success 200 {object} handler.RespBody{data=schema.GetOtherUserInfoResp}
// @Router /answer/api/v1/personal/user/info [get]
func (uc *UserController) GetOtherUserInfoByUsername(ctx *gin.Context) {
req := &schema.GetOtherUserInfoByUsernameReq{}
if handler.BindAndCheck(ctx, req) {
return
}
resp, err := uc.userService.GetOtherUserInfoByUsername(ctx, req.Username)
handler.HandleResponse(ctx, err, resp)
}
2022-09-28 14:22:55 +08:00
// GetUserStatus get user status info
// @Summary get user status info
// @Description get user status info
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Router /answer/api/v1/user/status [get]
func (uc *UserController) GetUserStatus(ctx *gin.Context) {
userID := middleware.GetLoginUserIDFromContext(ctx)
token := middleware.ExtractToken(ctx)
resp, err := uc.userService.GetUserStatus(ctx, userID, token)
2022-09-28 14:22:55 +08:00
handler.HandleResponse(ctx, err, resp)
}
2022-09-27 17:59:05 +08:00
// UserEmailLogin godoc
// @Summary UserEmailLogin
// @Description UserEmailLogin
// @Tags User
// @Accept json
// @Produce json
// @Param data body schema.UserEmailLogin true "UserEmailLogin"
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Router /answer/api/v1/user/login/email [post]
func (uc *UserController) UserEmailLogin(ctx *gin.Context) {
req := &schema.UserEmailLogin{}
if handler.BindAndCheck(ctx, req) {
return
}
2022-10-28 19:28:08 +08:00
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
2022-09-27 17:59:05 +08:00
if !captchaPass {
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
Value: "error.object.verification_failed",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
resp, err := uc.userService.EmailLogin(ctx, req)
if err != nil {
2022-10-28 19:28:08 +08:00
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP())
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
Key: "e_mail",
Value: "error.object.email_or_password_incorrect",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
2022-10-28 19:28:08 +08:00
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, nil, resp)
}
// RetrievePassWord godoc
// @Summary RetrievePassWord
// @Description RetrievePassWord
// @Tags User
// @Accept json
// @Produce json
// @Param data body schema.UserRetrievePassWordRequest true "UserRetrievePassWordRequest"
// @Success 200 {string} string ""
// @Router /answer/api/v1/user/password/reset [post]
func (uc *UserController) RetrievePassWord(ctx *gin.Context) {
req := &schema.UserRetrievePassWordRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
2022-10-28 19:28:08 +08:00
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
2022-09-27 17:59:05 +08:00
if !captchaPass {
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
Value: "error.object.verification_failed",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
2022-10-28 19:28:08 +08:00
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
code, err := uc.userService.RetrievePassWord(ctx, req)
handler.HandleResponse(ctx, err, code)
}
// UseRePassWord godoc
// @Summary UseRePassWord
// @Description UseRePassWord
// @Tags User
// @Accept json
// @Produce json
// @Param data body schema.UserRePassWordRequest true "UserRePassWordRequest"
// @Success 200 {string} string ""
// @Router /answer/api/v1/user/password/replacement [post]
func (uc *UserController) UseRePassWord(ctx *gin.Context) {
req := &schema.UserRePassWordRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
2022-09-27 17:59:05 +08:00
return
}
2022-10-28 19:28:08 +08:00
resp, err := uc.userService.UseRePassword(ctx, req)
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, resp)
}
// UserLogout user logout
// @Summary user logout
// @Description user logout
// @Tags User
// @Accept json
// @Produce json
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/user/logout [get]
func (uc *UserController) UserLogout(ctx *gin.Context) {
accessToken := middleware.ExtractToken(ctx)
_ = uc.authService.RemoveUserCacheInfo(ctx, accessToken)
handler.HandleResponse(ctx, nil, nil)
}
// UserRegisterByEmail godoc
// @Summary UserRegisterByEmail
// @Description UserRegisterByEmail
// @Tags User
// @Accept json
// @Produce json
2022-10-14 17:01:06 +08:00
// @Param data body schema.UserRegisterReq true "UserRegisterReq"
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Router /answer/api/v1/user/register/email [post]
func (uc *UserController) UserRegisterByEmail(ctx *gin.Context) {
2022-10-14 17:01:06 +08:00
req := &schema.UserRegisterReq{}
2022-09-27 17:59:05 +08:00
if handler.BindAndCheck(ctx, req) {
return
}
req.IP = ctx.ClientIP()
resp, err := uc.userService.UserRegisterByEmail(ctx, req)
handler.HandleResponse(ctx, err, resp)
}
// UserVerifyEmail godoc
// @Summary UserVerifyEmail
// @Description UserVerifyEmail
// @Tags User
// @Accept json
// @Produce json
// @Param code query string true "code" default()
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Router /answer/api/v1/user/email/verification [post]
func (uc *UserController) UserVerifyEmail(ctx *gin.Context) {
req := &schema.UserVerifyEmailReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
2022-09-27 17:59:05 +08:00
return
}
resp, err := uc.userService.UserVerifyEmail(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
2022-10-28 19:28:08 +08:00
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, resp)
}
// UserVerifyEmailSend godoc
// @Summary UserVerifyEmailSend
// @Description UserVerifyEmailSend
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param captcha_id query string false "captcha_id" default()
// @Param captcha_code query string false "captcha_code" default()
// @Success 200 {string} string ""
// @Router /answer/api/v1/user/email/verification/send [post]
func (uc *UserController) UserVerifyEmailSend(ctx *gin.Context) {
req := &schema.UserVerifyEmailSendReq{}
if handler.BindAndCheck(ctx, req) {
return
}
userInfo := middleware.GetUserInfoFromContext(ctx)
if userInfo == nil {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
return
}
2022-10-28 19:28:08 +08:00
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP(),
2022-09-27 17:59:05 +08:00
req.CaptchaID, req.CaptchaCode)
if !captchaPass {
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
Value: "error.object.verification_failed",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
2022-10-28 19:28:08 +08:00
uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
err := uc.userService.UserVerifyEmailSend(ctx, userInfo.UserID)
handler.HandleResponse(ctx, err, nil)
}
// UserModifyPassWord godoc
// @Summary UserModifyPassWord
// @Description UserModifyPassWord
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.UserModifyPassWordRequest true "UserModifyPassWordRequest"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/user/password [put]
func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
req := &schema.UserModifyPassWordRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
2022-09-27 17:59:05 +08:00
oldPassVerification, err := uc.userService.UserModifyPassWordVerification(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !oldPassVerification {
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
2022-10-28 14:14:20 +08:00
Key: "old_pass",
2022-10-24 11:21:37 +08:00
Value: "error.object.old_password_verification_failed",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
if req.OldPass == req.Pass {
2022-10-24 11:21:37 +08:00
resp := schema.UserVerifyEmailErrorResponse{
2022-10-28 14:14:20 +08:00
Key: "pass",
2022-10-24 11:21:37 +08:00
Value: "error.object.new_password_same_as_previous_setting",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
2022-09-27 17:59:05 +08:00
return
}
2022-10-28 19:28:08 +08:00
err = uc.userService.UserModifyPassword(ctx, req)
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, nil)
}
2022-09-29 16:39:34 +08:00
// UserUpdateInfo update user info
// @Summary UserUpdateInfo update user info
// @Description UserUpdateInfo update user info
2022-09-27 17:59:05 +08:00
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param Authorization header string true "access-token"
2022-09-29 16:39:34 +08:00
// @Param data body schema.UpdateInfoRequest true "UpdateInfoRequest"
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/user/info [put]
func (uc *UserController) UserUpdateInfo(ctx *gin.Context) {
req := &schema.UpdateInfoRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
2022-09-27 17:59:05 +08:00
err := uc.userService.UpdateInfo(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// UploadUserAvatar godoc
// @Summary UserUpdateInfo
// @Description UserUpdateInfo
// @Tags User
// @Accept multipart/form-data
// @Security ApiKeyAuth
// @Param file formData file true "file"
// @Success 200 {object} handler.RespBody{data=string}
// @Router /answer/api/v1/user/avatar/upload [post]
func (uc *UserController) UploadUserAvatar(ctx *gin.Context) {
// max size
2022-10-27 18:09:27 +08:00
var filesMax int64 = 5 << 20
var valuesMax int64 = 5
ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, filesMax+valuesMax)
2022-09-27 17:59:05 +08:00
_, header, err := ctx.Request.FormFile("file")
if err != nil {
log.Error(err.Error())
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), nil)
return
}
fileExt := strings.ToLower(path.Ext(header.Filename))
if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg" {
log.Errorf("upload file format is not supported: %s", fileExt)
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), nil)
return
}
url, err := uc.uploaderService.UploadAvatarFile(ctx, header, fileExt)
handler.HandleResponse(ctx, err, url)
}
// UploadUserPostFile godoc
// @Summary upload user post file
// @Description upload user post file
// @Tags User
// @Accept multipart/form-data
// @Security ApiKeyAuth
// @Param file formData file true "file"
// @Success 200 {object} handler.RespBody{data=string}
// @Router /answer/api/v1/user/post/file [post]
func (uc *UserController) UploadUserPostFile(ctx *gin.Context) {
// max size
ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, 10*1024*1024)
_, header, err := ctx.Request.FormFile("file")
if err != nil {
log.Error(err.Error())
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), nil)
return
}
fileExt := strings.ToLower(path.Ext(header.Filename))
if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg" {
log.Errorf("upload file format is not supported: %s", fileExt)
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), nil)
return
}
url, err := uc.uploaderService.UploadPostFile(ctx, header, fileExt)
handler.HandleResponse(ctx, err, url)
}
// ActionRecord godoc
// @Summary ActionRecord
// @Description ActionRecord
// @Tags User
// @Param action query string true "action" Enums(login, e_mail, find_pass)
// @Security ApiKeyAuth
// @Success 200 {object} handler.RespBody{data=schema.ActionRecordResp}
// @Router /answer/api/v1/user/action/record [get]
func (uc *UserController) ActionRecord(ctx *gin.Context) {
req := &schema.ActionRecordReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.IP = ctx.ClientIP()
2022-09-27 17:59:05 +08:00
resp, err := uc.actionService.ActionRecord(ctx, req)
handler.HandleResponse(ctx, err, resp)
}
// UserNoticeSet godoc
// @Summary UserNoticeSet
// @Description UserNoticeSet
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.UserNoticeSetRequest true "UserNoticeSetRequest"
// @Success 200 {object} handler.RespBody{data=schema.UserNoticeSetResp}
// @Router /answer/api/v1/user/notice/set [post]
func (uc *UserController) UserNoticeSet(ctx *gin.Context) {
req := &schema.UserNoticeSetRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := uc.userService.UserNoticeSet(ctx, req.UserID, req.NoticeSwitch)
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, resp)
}
// UserChangeEmailSendCode send email to the user email then change their email
// @Summary send email to the user email then change their email
// @Description send email to the user email then change their email
// @Tags User
// @Accept json
// @Produce json
// @Param data body schema.UserChangeEmailSendCodeReq true "UserChangeEmailSendCodeReq"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/api/v1/user/email/change/code [post]
func (uc *UserController) UserChangeEmailSendCode(ctx *gin.Context) {
req := &schema.UserChangeEmailSendCodeReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
// If the user is not logged in, the api cannot be used.
// If the user email is not verified, that also can use this api to modify the email.
2022-10-28 15:07:19 +08:00
2022-11-01 15:38:06 +08:00
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP(), req.CaptchaID, req.CaptchaCode)
2022-10-28 15:07:19 +08:00
if !captchaPass {
resp := schema.UserVerifyEmailErrorResponse{
Key: "captcha_code",
Value: "error.object.verification_failed",
}
resp.Value = translator.GlobalTrans.Tr(handler.GetLang(ctx), resp.Value)
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), resp)
return
}
2022-09-27 17:59:05 +08:00
if len(req.UserID) == 0 {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
return
}
2022-11-01 15:38:06 +08:00
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
err := uc.userService.UserChangeEmailSendCode(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// UserChangeEmailVerify user change email verification
// @Summary user change email verification
// @Description user change email verification
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.UserChangeEmailVerifyReq true "UserChangeEmailVerifyReq"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/api/v1/user/email [put]
func (uc *UserController) UserChangeEmailVerify(ctx *gin.Context) {
req := &schema.UserChangeEmailVerifyReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.Content = uc.emailService.VerifyUrlExpired(ctx, req.Code)
if len(req.Content) == 0 {
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailVerifyURLExpired),
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeURLExpired})
2022-09-27 17:59:05 +08:00
return
}
err := uc.userService.UserChangeEmailVerify(ctx, req.Content)
2022-11-01 15:38:06 +08:00
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, nil)
}