| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package schema |
| |
| import ( |
| "context" |
| "encoding/json" |
| |
| "github.com/apache/answer/internal/base/handler" |
| "github.com/apache/answer/internal/base/reason" |
| "github.com/apache/answer/internal/base/translator" |
| "github.com/apache/answer/pkg/day" |
| "github.com/segmentfault/pacman/errors" |
| |
| "github.com/apache/answer/internal/base/constant" |
| "github.com/apache/answer/internal/base/validator" |
| "github.com/apache/answer/internal/entity" |
| "github.com/apache/answer/pkg/checker" |
| "github.com/apache/answer/pkg/converter" |
| "github.com/jinzhu/copier" |
| ) |
| |
| // UserVerifyEmailReq user verify email request |
| type UserVerifyEmailReq struct { |
| // code |
| Code string `validate:"required,gt=0,lte=500" form:"code"` |
| // content |
| Content string `json:"-"` |
| } |
| |
| // UserLoginResp get user response |
| type UserLoginResp struct { |
| // user id |
| ID string `json:"id"` |
| // create time |
| CreatedAt int64 `json:"created_at"` |
| // last login date |
| LastLoginDate int64 `json:"last_login_date"` |
| // username |
| Username string `json:"username"` |
| // email |
| EMail string `json:"e_mail"` |
| // mail status(1 pass 2 to be verified) |
| MailStatus int `json:"mail_status"` |
| // notice status(1 on 2off) |
| NoticeStatus int `json:"notice_status"` |
| // follow count |
| FollowCount int `json:"follow_count"` |
| // answer count |
| AnswerCount int `json:"answer_count"` |
| // question count |
| QuestionCount int `json:"question_count"` |
| // rank |
| Rank int `json:"rank"` |
| // authority group |
| AuthorityGroup int `json:"authority_group"` |
| // display name |
| DisplayName string `json:"display_name"` |
| // avatar |
| Avatar string `json:"avatar"` |
| // mobile |
| Mobile string `json:"mobile"` |
| // bio markdown |
| Bio string `json:"bio"` |
| // bio html |
| BioHTML string `json:"bio_html"` |
| // website |
| Website string `json:"website"` |
| // location |
| Location string `json:"location"` |
| // language |
| Language string `json:"language"` |
| // Color scheme |
| ColorScheme string `json:"color_scheme"` |
| // access token |
| AccessToken string `json:"access_token"` |
| // role id |
| RoleID int `json:"role_id"` |
| // user status |
| Status string `json:"status"` |
| // user have password |
| HavePassword bool `json:"have_password"` |
| // visit token |
| VisitToken string `json:"visit_token"` |
| // suspended until timestamp |
| SuspendedUntil int64 `json:"suspended_until"` |
| } |
| |
| func (r *UserLoginResp) ConvertFromUserEntity(userInfo *entity.User) { |
| _ = copier.Copy(r, userInfo) |
| r.CreatedAt = userInfo.CreatedAt.Unix() |
| r.LastLoginDate = userInfo.LastLoginDate.Unix() |
| r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) |
| r.HavePassword = len(userInfo.Pass) > 0 |
| if !userInfo.SuspendedUntil.IsZero() { |
| r.SuspendedUntil = userInfo.SuspendedUntil.Unix() |
| } |
| } |
| |
| type GetCurrentLoginUserInfoResp struct { |
| *UserLoginResp |
| Avatar *AvatarInfo `json:"avatar"` |
| } |
| |
| func (r *GetCurrentLoginUserInfoResp) ConvertFromUserEntity(userInfo *entity.User) { |
| _ = copier.Copy(r, userInfo) |
| r.CreatedAt = userInfo.CreatedAt.Unix() |
| r.LastLoginDate = userInfo.LastLoginDate.Unix() |
| r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) |
| if len(r.ColorScheme) == 0 { |
| r.ColorScheme = constant.ColorSchemeDefault |
| } |
| if !userInfo.SuspendedUntil.IsZero() { |
| r.SuspendedUntil = userInfo.SuspendedUntil.Unix() |
| } |
| } |
| |
| // GetOtherUserInfoByUsernameResp get user response |
| type GetOtherUserInfoByUsernameResp struct { |
| // user id |
| ID string `json:"id"` |
| // create time |
| CreatedAt int64 `json:"created_at"` |
| // last login date |
| LastLoginDate int64 `json:"last_login_date"` |
| // username |
| Username string `json:"username"` |
| // email |
| // follow count |
| FollowCount int `json:"follow_count"` |
| // answer count |
| AnswerCount int `json:"answer_count"` |
| // question count |
| QuestionCount int `json:"question_count"` |
| // rank |
| Rank int `json:"rank"` |
| // display name |
| DisplayName string `json:"display_name"` |
| // avatar |
| Avatar string `json:"avatar"` |
| // mobile |
| Mobile string `json:"mobile"` |
| // bio markdown |
| Bio string `json:"bio"` |
| // bio html |
| BioHTML string `json:"bio_html"` |
| // website |
| Website string `json:"website"` |
| // location |
| Location string `json:"location"` |
| Status string `json:"status"` |
| StatusMsg string `json:"status_msg,omitempty"` |
| // suspended until timestamp |
| SuspendedUntil int64 `json:"suspended_until"` |
| } |
| |
| func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity.User) { |
| _ = copier.Copy(r, userInfo) |
| r.CreatedAt = userInfo.CreatedAt.Unix() |
| r.LastLoginDate = userInfo.LastLoginDate.Unix() |
| r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) |
| if !userInfo.SuspendedUntil.IsZero() { |
| r.SuspendedUntil = userInfo.SuspendedUntil.Unix() |
| } |
| r.StatusMsg = "" |
| } |
| |
| func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntityWithLang(ctx context.Context, userInfo *entity.User) { |
| _ = copier.Copy(r, userInfo) |
| r.CreatedAt = userInfo.CreatedAt.Unix() |
| r.LastLoginDate = userInfo.LastLoginDate.Unix() |
| r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) |
| |
| lang := handler.GetLangByCtx(ctx) |
| if userInfo.MailStatus == entity.EmailStatusToBeVerified { |
| r.StatusMsg = translator.Tr(lang, reason.UserStatusInactive) |
| } |
| switch userInfo.Status { |
| case entity.UserStatusSuspended: |
| if userInfo.SuspendedUntil.IsZero() || userInfo.SuspendedUntil.Year() >= 2099 { |
| r.StatusMsg = translator.Tr(lang, reason.UserStatusSuspendedForever) |
| } else { |
| r.SuspendedUntil = userInfo.SuspendedUntil.Unix() |
| trans := translator.GlobalTrans.Tr(lang, "ui.dates.long_date_with_time") |
| suspendedUntilFormatted := day.Format(userInfo.SuspendedUntil.Unix(), trans, "UTC") |
| r.StatusMsg = translator.TrWithData(lang, reason.UserStatusSuspendedUntil, map[string]any{ |
| "SuspendedUntil": suspendedUntilFormatted, |
| }) |
| } |
| case entity.UserStatusDeleted: |
| r.StatusMsg = translator.Tr(lang, reason.UserStatusDeleted) |
| } |
| } |
| |
| // UserEmailLoginReq user email login request |
| type UserEmailLoginReq struct { |
| Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"` |
| Pass string `validate:"required,gte=8,lte=32" json:"pass"` |
| CaptchaID string `json:"captcha_id"` |
| CaptchaCode string `json:"captcha_code"` |
| } |
| |
| // UserRegisterReq user register request |
| type UserRegisterReq struct { |
| Name string `validate:"required,gte=2,lte=30" json:"name"` |
| Email string `validate:"required,email,gt=0,lte=500" json:"e_mail" ` |
| Pass string `validate:"required,gte=8,lte=32" json:"pass"` |
| CaptchaID string `json:"captcha_id"` |
| CaptchaCode string `json:"captcha_code"` |
| IP string `json:"-" ` |
| } |
| |
| func (u *UserRegisterReq) Check() (errFields []*validator.FormErrorField, err error) { |
| if err = checker.CheckPassword(u.Pass); err != nil { |
| errFields = append(errFields, &validator.FormErrorField{ |
| ErrorField: "pass", |
| ErrorMsg: err.Error(), |
| }) |
| return errFields, err |
| } |
| return nil, nil |
| } |
| |
| type UserModifyPasswordReq struct { |
| OldPass string `validate:"omitempty,gte=8,lte=32" json:"old_pass"` |
| Pass string `validate:"required,gte=8,lte=32" json:"pass"` |
| CaptchaID string `json:"captcha_id"` |
| CaptchaCode string `json:"captcha_code"` |
| UserID string `json:"-"` |
| AccessToken string `json:"-"` |
| } |
| |
| func (u *UserModifyPasswordReq) Check() (errFields []*validator.FormErrorField, err error) { |
| if err = checker.CheckPassword(u.Pass); err != nil { |
| errFields = append(errFields, &validator.FormErrorField{ |
| ErrorField: "pass", |
| ErrorMsg: err.Error(), |
| }) |
| return errFields, err |
| } |
| return nil, nil |
| } |
| |
| type UpdateInfoRequest struct { |
| DisplayName string `validate:"omitempty,gte=2,lte=30" json:"display_name"` |
| Username string `validate:"omitempty,gte=2,lte=30" json:"username"` |
| Avatar AvatarInfo `json:"avatar"` |
| Bio string `validate:"omitempty,gt=0,lte=4096" json:"bio"` |
| BioHTML string `json:"-"` |
| Website string `validate:"omitempty,gt=0,lte=500" json:"website"` |
| Location string `validate:"omitempty,gt=0,lte=100" json:"location"` |
| UserID string `json:"-"` |
| IsAdmin bool `json:"-"` |
| } |
| |
| type AvatarInfo struct { |
| Type string `validate:"omitempty,gt=0,lte=100" json:"type"` |
| Gravatar string `validate:"omitempty,gt=0,lte=200" json:"gravatar"` |
| Custom string `validate:"omitempty,gt=0,lte=200" json:"custom"` |
| } |
| |
| func (a *AvatarInfo) ToJsonString() string { |
| data, _ := json.Marshal(a) |
| return string(data) |
| } |
| |
| func (a *AvatarInfo) GetURL() string { |
| switch a.Type { |
| case constant.AvatarTypeGravatar: |
| return a.Gravatar |
| case constant.AvatarTypeCustom: |
| return a.Custom |
| default: |
| return "" |
| } |
| } |
| |
| func CustomAvatar(url string) *AvatarInfo { |
| return &AvatarInfo{ |
| Type: constant.AvatarTypeCustom, |
| Custom: url, |
| } |
| } |
| |
| func (req *UpdateInfoRequest) Check() (errFields []*validator.FormErrorField, err error) { |
| req.BioHTML = converter.Markdown2BasicHTML(req.Bio) |
| if len(req.Website) > 0 && !checker.IsURL(req.Website) { |
| return append(errFields, &validator.FormErrorField{ |
| ErrorField: "website", |
| ErrorMsg: reason.InvalidURLError, |
| }), errors.BadRequest(reason.InvalidURLError) |
| } |
| return nil, nil |
| } |
| |
| // UpdateUserInterfaceRequest update user interface request |
| type UpdateUserInterfaceRequest struct { |
| // language |
| Language string `validate:"required,gt=1,lte=100" json:"language"` |
| // Color scheme |
| ColorScheme string `validate:"required,gt=1,lte=100" json:"color_scheme"` |
| // user id |
| UserId string `json:"-"` |
| } |
| |
| func (req *UpdateUserInterfaceRequest) Check() (errFields []*validator.FormErrorField, err error) { |
| if !translator.CheckLanguageIsValid(req.Language) { |
| return nil, errors.BadRequest(reason.LangNotFound) |
| } |
| if req.ColorScheme != constant.ColorSchemeDefault && |
| req.ColorScheme != constant.ColorSchemeLight && |
| req.ColorScheme != constant.ColorSchemeDark && |
| req.ColorScheme != constant.ColorSchemeSystem { |
| req.ColorScheme = constant.ColorSchemeDefault |
| } |
| return nil, nil |
| } |
| |
| type UserRetrievePassWordRequest struct { |
| Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"` |
| CaptchaID string `json:"captcha_id"` |
| CaptchaCode string `json:"captcha_code"` |
| } |
| |
| type UserRePassWordRequest struct { |
| Code string `validate:"required,gt=0,lte=100" json:"code"` |
| Pass string `validate:"required,gt=0,lte=32" json:"pass"` |
| Content string `json:"-"` |
| } |
| |
| func (u *UserRePassWordRequest) Check() (errFields []*validator.FormErrorField, err error) { |
| if err = checker.CheckPassword(u.Pass); err != nil { |
| errFields = append(errFields, &validator.FormErrorField{ |
| ErrorField: "pass", |
| ErrorMsg: err.Error(), |
| }) |
| return errFields, err |
| } |
| return nil, nil |
| } |
| |
| type ActionRecordReq struct { |
| Action string `validate:"required,oneof=email password edit_userinfo question answer comment edit invitation_answer search report delete vote" form:"action"` |
| IP string `json:"-"` |
| UserID string `json:"-"` |
| } |
| |
| type ActionRecordResp struct { |
| CaptchaID string `json:"captcha_id"` |
| CaptchaImg string `json:"captcha_img"` |
| Verify bool `json:"verify"` |
| } |
| |
| type UserBasicInfo struct { |
| ID string `json:"id"` |
| Username string `json:"username"` |
| Rank int `json:"rank"` |
| DisplayName string `json:"display_name"` |
| Avatar string `json:"avatar"` |
| Website string `json:"website"` |
| Location string `json:"location"` |
| Language string `json:"language"` |
| Status string `json:"status"` |
| SuspendedUntil int64 `json:"suspended_until"` |
| } |
| |
| type GetOtherUserInfoByUsernameReq struct { |
| Username string `validate:"required,gt=0,lte=500" form:"username"` |
| UserID string `json:"-"` |
| IsAdmin bool `json:"-"` |
| } |
| |
| type GetOtherUserInfoResp struct { |
| Info *GetOtherUserInfoByUsernameResp `json:"info"` |
| } |
| |
| type UserChangeEmailSendCodeReq struct { |
| UserVerifyEmailSendReq |
| Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"` |
| Pass string `validate:"omitempty,gte=8,lte=32" json:"pass"` |
| UserID string `json:"-"` |
| } |
| |
| type UserChangeEmailVerifyReq struct { |
| Code string `validate:"required,gt=0,lte=500" json:"code"` |
| Content string `json:"-"` |
| } |
| |
| type UserVerifyEmailSendReq struct { |
| CaptchaID string `json:"captcha_id"` |
| CaptchaCode string `json:"captcha_code"` |
| } |
| |
| // UserRankingResp user ranking response |
| type UserRankingResp struct { |
| UsersWithTheMostReputation []*UserRankingSimpleInfo `json:"users_with_the_most_reputation"` |
| UsersWithTheMostVote []*UserRankingSimpleInfo `json:"users_with_the_most_vote"` |
| Staffs []*UserRankingSimpleInfo `json:"staffs"` |
| } |
| |
| // UserRankingSimpleInfo user ranking simple info |
| type UserRankingSimpleInfo struct { |
| // username |
| Username string `json:"username"` |
| // rank |
| Rank int `json:"rank"` |
| // vote |
| VoteCount int `json:"vote_count"` |
| // display name |
| DisplayName string `json:"display_name"` |
| // avatar |
| Avatar string `json:"avatar"` |
| } |
| |
| // UserUnsubscribeNotificationReq user unsubscribe email notification request |
| type UserUnsubscribeNotificationReq struct { |
| Code string `validate:"required,gt=0,lte=500" json:"code"` |
| Content string `json:"-"` |
| } |
| |
| // GetUserStaffReq get user staff request |
| type GetUserStaffReq struct { |
| Username string `validate:"omitempty,gt=0,lte=500" form:"username"` |
| PageSize int `validate:"omitempty,min=1" form:"page_size"` |
| } |
| |
| // GetUserStaffResp get user staff response |
| type GetUserStaffResp struct { |
| // username |
| Username string `json:"username"` |
| // display name |
| DisplayName string `json:"display_name"` |
| // avatar |
| Avatar string `json:"avatar"` |
| } |