mir: add pub api implement for new web service

pull/196/head
Michael Li 2 years ago
parent bc4596721e
commit 058a30794d
No known key found for this signature in database

@ -11,33 +11,38 @@ import (
)
type Pub interface {
TopicList() mir.Error
TweetComments() mir.Error
TopicList(*web.TopicListReq) (*web.TopicListResp, mir.Error)
TweetComments(*web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error)
TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, mir.Error)
SendCaptcha() mir.Error
GetCaptcha() mir.Error
Register() mir.Error
Login() mir.Error
Version() mir.Error
SendCaptcha(*web.SendCaptchaReq) mir.Error
GetCaptcha() (*web.GetCaptchaResp, mir.Error)
Register(*web.RegisterReq) (*web.RegisterResp, mir.Error)
Login(*web.LoginReq) (*web.LoginResp, mir.Error)
Version() (*web.VersionResp, mir.Error)
mustEmbedUnimplementedPubServant()
}
type PubBinding interface {
BindTopicList(*gin.Context) (*web.TopicListReq, mir.Error)
BindTweetComments(*gin.Context) (*web.TweetCommentsReq, mir.Error)
BindTweetDetail(*gin.Context) (*web.TweetDetailReq, mir.Error)
BindSendCaptcha(*gin.Context) (*web.SendCaptchaReq, mir.Error)
BindRegister(*gin.Context) (*web.RegisterReq, mir.Error)
BindLogin(*gin.Context) (*web.LoginReq, mir.Error)
mustEmbedUnimplementedPubBinding()
}
type PubRender interface {
RenderTopicList(*gin.Context, mir.Error)
RenderTweetComments(*gin.Context, mir.Error)
RenderTopicList(*gin.Context, *web.TopicListResp, mir.Error)
RenderTweetComments(*gin.Context, *web.TweetCommentsResp, mir.Error)
RenderTweetDetail(*gin.Context, *web.TweetDetailResp, mir.Error)
RenderSendCaptcha(*gin.Context, mir.Error)
RenderGetCaptcha(*gin.Context, mir.Error)
RenderRegister(*gin.Context, mir.Error)
RenderLogin(*gin.Context, mir.Error)
RenderVersion(*gin.Context, mir.Error)
RenderGetCaptcha(*gin.Context, *web.GetCaptchaResp, mir.Error)
RenderRegister(*gin.Context, *web.RegisterResp, mir.Error)
RenderLogin(*gin.Context, *web.LoginResp, mir.Error)
RenderVersion(*gin.Context, *web.VersionResp, mir.Error)
mustEmbedUnimplementedPubRender()
}
@ -54,7 +59,13 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderTopicList(c, s.TopicList())
req, err := b.BindTopicList(c)
if err != nil {
r.RenderTopicList(c, nil, err)
return
}
resp, err := s.TopicList(req)
r.RenderTopicList(c, resp, err)
})
router.Handle("GET", "/post/comments", func(c *gin.Context) {
@ -64,7 +75,13 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderTweetComments(c, s.TweetComments())
req, err := b.BindTweetComments(c)
if err != nil {
r.RenderTweetComments(c, nil, err)
return
}
resp, err := s.TweetComments(req)
r.RenderTweetComments(c, resp, err)
})
router.Handle("GET", "/post", func(c *gin.Context) {
@ -90,7 +107,12 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderSendCaptcha(c, s.SendCaptcha())
req, err := b.BindSendCaptcha(c)
if err != nil {
r.RenderSendCaptcha(c, err)
return
}
r.RenderSendCaptcha(c, s.SendCaptcha(req))
})
router.Handle("GET", "/captcha", func(c *gin.Context) {
@ -100,7 +122,8 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderGetCaptcha(c, s.GetCaptcha())
resp, err := s.GetCaptcha()
r.RenderGetCaptcha(c, resp, err)
})
router.Handle("POST", "/auth/register", func(c *gin.Context) {
@ -110,7 +133,13 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderRegister(c, s.Register())
req, err := b.BindRegister(c)
if err != nil {
r.RenderRegister(c, nil, err)
return
}
resp, err := s.Register(req)
r.RenderRegister(c, resp, err)
})
router.Handle("POST", "/auth/login", func(c *gin.Context) {
@ -120,7 +149,13 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderLogin(c, s.Login())
req, err := b.BindLogin(c)
if err != nil {
r.RenderLogin(c, nil, err)
return
}
resp, err := s.Login(req)
r.RenderLogin(c, resp, err)
})
router.Handle("GET", "/", func(c *gin.Context) {
@ -130,7 +165,8 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
default:
}
r.RenderVersion(c, s.Version())
resp, err := s.Version()
r.RenderVersion(c, resp, err)
})
}
@ -139,36 +175,36 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
type UnimplementedPubServant struct {
}
func (UnimplementedPubServant) TopicList() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) TweetComments() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) SendCaptcha() mir.Error {
func (UnimplementedPubServant) SendCaptcha(req *web.SendCaptchaReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) GetCaptcha() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) GetCaptcha() (*web.GetCaptchaResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) Register() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) Register(req *web.RegisterReq) (*web.RegisterResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) Login() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) Login(req *web.LoginReq) (*web.LoginResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) Version() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedPubServant) Version() (*web.VersionResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) mustEmbedUnimplementedPubServant() {}
@ -178,12 +214,12 @@ type UnimplementedPubRender struct {
RenderAny func(*gin.Context, any, mir.Error)
}
func (r *UnimplementedPubRender) RenderTopicList(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderTopicList(c *gin.Context, data *web.TopicListResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderTweetComments(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderTweetComments(c *gin.Context, data *web.TweetCommentsResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderTweetDetail(c *gin.Context, data *web.TweetDetailResp, err mir.Error) {
@ -194,20 +230,20 @@ func (r *UnimplementedPubRender) RenderSendCaptcha(c *gin.Context, err mir.Error
r.RenderAny(c, nil, err)
}
func (r *UnimplementedPubRender) RenderGetCaptcha(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderGetCaptcha(c *gin.Context, data *web.GetCaptchaResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderRegister(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderRegister(c *gin.Context, data *web.RegisterResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderLogin(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderLogin(c *gin.Context, data *web.LoginResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderVersion(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedPubRender) RenderVersion(c *gin.Context, data *web.VersionResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) mustEmbedUnimplementedPubRender() {}
@ -217,10 +253,40 @@ type UnimplementedPubBinding struct {
BindAny func(*gin.Context, any) mir.Error
}
func (b *UnimplementedPubBinding) BindTopicList(c *gin.Context) (*web.TopicListReq, mir.Error) {
obj := new(web.TopicListReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindTweetComments(c *gin.Context) (*web.TweetCommentsReq, mir.Error) {
obj := new(web.TweetCommentsReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindTweetDetail(c *gin.Context) (*web.TweetDetailReq, mir.Error) {
obj := new(web.TweetDetailReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindSendCaptcha(c *gin.Context) (*web.SendCaptchaReq, mir.Error) {
obj := new(web.SendCaptchaReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindRegister(c *gin.Context) (*web.RegisterReq, mir.Error) {
obj := new(web.RegisterReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindLogin(c *gin.Context) (*web.LoginReq, mir.Error) {
obj := new(web.LoginReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) mustEmbedUnimplementedPubBinding() {}

@ -4,10 +4,74 @@
package web
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/debug"
)
const (
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
)
type TagType string
type TweetDetailReq struct {
// TODO
TweetId int64 `form:"id"`
}
type TweetDetailResp core.PostFormated
type TweetCommentsReq struct {
TweetId int64 `form:"id"`
Page int `form:"page"`
PageSize int `form:"page_size"`
}
type TweetCommentsResp base.PageResp
type TopicListReq struct {
Type TagType `json:"type" form:"type" binding:"required"`
Num int `json:"num" form:"num" binding:"required"`
}
// TopicListResp 主题返回值
// TODO: 优化内容定义
type TopicListResp struct {
Topics []*core.TagFormated `json:"topics"`
}
type GetCaptchaResp struct {
Id string `json:"id"`
Content string `json:"b64s"`
}
type SendCaptchaReq struct {
Phone string `json:"phone" form:"phone" binding:"required"`
ImgCaptcha string `json:"img_captcha" form:"img_captcha" binding:"required"`
ImgCaptchaID string `json:"img_captcha_id" form:"img_captcha_id" binding:"required"`
}
type VersionResp struct {
BuildInfo *debug.BuildInfo `json:"build_info"`
}
type LoginReq struct {
Username string `json:"username" form:"username" binding:"required"`
Password string `json:"password" form:"password" binding:"required"`
}
type LoginResp struct {
Token string `json:"token"`
}
type RegisterReq struct {
Username string `json:"username" form:"username" binding:"required"`
Password string `json:"password" form:"password" binding:"required"`
}
type TweetDetailResp struct {
// TODO
type RegisterResp struct {
UserId int64 `json:"id"`
Username string `json:"username"`
}

@ -0,0 +1,12 @@
// Copyright 2022 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package assets
import (
_ "embed"
)
//go:embed comic.ttf
var ComicBytes []byte

@ -5,21 +5,51 @@
package web
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"image/color"
"image/png"
"regexp"
"time"
"unicode/utf8"
"github.com/afocus/captcha"
"github.com/alimy/mir/v3"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/rocboss/paopao-ce/internal/servants/web/assets"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/debug"
"github.com/rocboss/paopao-ce/pkg/util"
"github.com/rocboss/paopao-ce/pkg/xerror"
"github.com/sirupsen/logrus"
)
var (
_ api.Pub = (*pubSrv)(nil)
_ api.PubBinding = (*pubBinding)(nil)
_ api.PubRender = (*pubRender)(nil)
_MaxPageSize = conf.AppSetting.MaxPageSize
)
const (
_LoginErrKey = "PaoPaoUserLoginErr"
_MaxLoginErrTimes = 10
_MaxPhoneCaptcha = 10
)
type pubSrv struct {
base.BaseServant
api.UnimplementedPubServant
*base.DaoServant
}
type pubBinding struct {
@ -32,12 +62,300 @@ type pubRender struct {
*api.UnimplementedPubRender
}
func (s *pubSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JwtLoose()}
func (b *pubBinding) BindTweetComments(c *gin.Context) (*web.TweetCommentsReq, mir.Error) {
tweetId := convert.StrTo(c.Query("id")).MustInt64()
page, pageSize := app.GetPageOffset(c)
return &web.TweetCommentsReq{
TweetId: tweetId,
Page: page,
PageSize: pageSize,
}, nil
}
func (s *pubSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
// tags, err := broker.GetPostTags(&param)
num := req.Num
if num > _MaxPageSize {
num = _MaxPageSize
}
conditions := &core.ConditionsT{}
if req.Type == web.TagTypeHot {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "quote_num DESC",
}
} else if req.Type == web.TagTypeNew {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "id DESC",
}
}
tags, err := s.Ds.GetTags(conditions, 0, num)
if err != nil {
return nil, _errGetPostTagsFailed
}
// 获取创建者User IDs
userIds := []int64{}
for _, tag := range tags {
userIds = append(userIds, tag.UserID)
}
users, _ := s.Ds.GetUsersByIDs(userIds)
tagsFormated := []*core.TagFormated{}
for _, tag := range tags {
tagFormated := tag.Format()
for _, user := range users {
if user.ID == tagFormated.UserID {
tagFormated.User = user.Format()
}
}
tagsFormated = append(tagsFormated, tagFormated)
}
return &web.TopicListResp{
Topics: tagsFormated,
}, nil
}
func (s *pubSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
conditions := &core.ConditionsT{
"post_id": req.TweetId,
"ORDER": "id ASC",
}
comments, err := s.Ds.GetComments(conditions, (req.Page-1)*req.PageSize, req.PageSize)
if err != nil {
return nil, _errGetCommentsFailed
}
userIDs := []int64{}
commentIDs := []int64{}
for _, comment := range comments {
userIDs = append(userIDs, comment.UserID)
commentIDs = append(commentIDs, comment.ID)
}
users, err := s.Ds.GetUsersByIDs(userIDs)
if err != nil {
return nil, _errGetCommentsFailed
}
contents, err := s.Ds.GetCommentContentsByIDs(commentIDs)
if err != nil {
return nil, _errGetCommentsFailed
}
replies, err := s.Ds.GetCommentRepliesByID(commentIDs)
if err != nil {
return nil, _errGetCommentsFailed
}
commentsFormated := []*core.CommentFormated{}
for _, comment := range comments {
commentFormated := comment.Format()
for _, content := range contents {
if content.CommentID == comment.ID {
commentFormated.Contents = append(commentFormated.Contents, content)
}
}
for _, reply := range replies {
if reply.CommentID == commentFormated.ID {
commentFormated.Replies = append(commentFormated.Replies, reply)
}
}
for _, user := range users {
if user.ID == comment.UserID {
commentFormated.User = user.Format()
}
}
commentsFormated = append(commentsFormated, commentFormated)
}
// 获取总量
totalRows, _ := s.Ds.GetCommentCount(conditions)
resp := base.PageRespFrom(contents, req.Page, req.PageSize, totalRows)
return (*web.TweetCommentsResp)(resp), nil
}
func (s *pubSrv) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
post, err := s.Ds.GetPostByID(req.TweetId)
if err != nil {
return nil, _errGetPostFailed
}
postContents, err := s.Ds.GetPostContentsByIDs([]int64{post.ID})
if err != nil {
return nil, _errGetPostFailed
}
users, err := s.Ds.GetUsersByIDs([]int64{post.UserID})
if err != nil {
return nil, _errGetPostFailed
}
// 数据整合
postFormated := post.Format()
for _, user := range users {
postFormated.User = user.Format()
}
for _, content := range postContents {
if content.PostID == post.ID {
postFormated.Contents = append(postFormated.Contents, content.Format())
}
}
return (*web.TweetDetailResp)(postFormated), nil
}
func (s *pubSrv) SendCaptcha(req *web.SendCaptchaReq) mir.Error {
ctx := context.Background()
// 验证图片验证码
if res, err := s.Redis.Get(ctx, "PaoPaoCaptcha:"+req.ImgCaptchaID).Result(); err != nil || res != req.ImgCaptcha {
return _errErrorCaptchaPassword
}
s.Redis.Del(ctx, "PaoPaoCaptcha:"+req.ImgCaptchaID).Result()
// 今日频次限制
if res, _ := s.Redis.Get(ctx, "PaoPaoSmsCaptcha:"+req.Phone).Result(); convert.StrTo(res).MustInt() >= _MaxPhoneCaptcha {
return _errTooManyPhoneCaptchaSend
}
if err := s.Ds.SendPhoneCaptcha(req.Phone); err != nil {
return xerror.ServerError
}
// 写入计数缓存
s.Redis.Incr(ctx, "PaoPaoSmsCaptcha:"+req.Phone).Result()
currentTime := time.Now()
endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location())
s.Redis.Expire(ctx, "PaoPaoSmsCaptcha:"+req.Phone, endTime.Sub(currentTime))
return nil
}
func (s *pubSrv) GetCaptcha() (*web.GetCaptchaResp, mir.Error) {
cap := captcha.New()
if err := cap.AddFontFromBytes(assets.ComicBytes); err != nil {
logrus.Errorf("cap.AddFontFromBytes err:%s", err)
return nil, xerror.ServerError
}
cap.SetSize(160, 64)
cap.SetDisturbance(captcha.MEDIUM)
cap.SetFrontColor(color.RGBA{0, 0, 0, 255})
cap.SetBkgColor(color.RGBA{218, 240, 228, 255})
img, password := cap.Create(6, captcha.NUM)
emptyBuff := bytes.NewBuffer(nil)
if err := png.Encode(emptyBuff, img); err != nil {
logrus.Errorf("png.Encode err:%s", err)
return nil, xerror.ServerError
}
key := util.EncodeMD5(uuid.Must(uuid.NewV4()).String())
// 五分钟有效期
s.Redis.SetEX(context.Background(), "PaoPaoCaptcha:"+key, password, time.Minute*5)
return &web.GetCaptchaResp{
Id: key,
Content: "data:image/png;base64," + base64.StdEncoding.EncodeToString(emptyBuff.Bytes()),
}, nil
}
func (s *pubSrv) Register(req *web.RegisterReq) (*web.RegisterResp, mir.Error) {
// 用户名检查
if err := s.validUsername(req.Username); err != nil {
return nil, err
}
// 密码检查
if err := checkPassword(req.Password); err != nil {
logrus.Errorf("scheckPassword err: %v", err)
return nil, _errUserRegisterFailed
}
password, salt := encryptPasswordAndSalt(req.Password)
user := &core.User{
Nickname: req.Username,
Username: req.Username,
Password: password,
Avatar: getRandomAvatar(),
Salt: salt,
Status: core.UserStatusNormal,
}
user, err := s.Ds.CreateUser(user)
if err != nil {
logrus.Errorf("Ds.CreateUser err: %s", err)
return nil, _errUserRegisterFailed
}
return &web.RegisterResp{
UserId: user.ID,
Username: user.Username,
}, nil
}
func (s *pubSrv) Login(req *web.LoginReq) (*web.LoginResp, mir.Error) {
ctx := context.Background()
user, err := s.Ds.GetUserByUsername(req.Username)
if err != nil {
logrus.Errorf("Ds.GetUserByUsername err:%s", err)
return nil, xerror.UnauthorizedAuthNotExist
}
if user.Model != nil && user.ID > 0 {
if errTimes, err := s.Redis.Get(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID)).Result(); err == nil {
if convert.StrTo(errTimes).MustInt() >= _MaxLoginErrTimes {
return nil, _errTooManyLoginError
}
}
// 对比密码是否正确
if validPassword(user.Password, req.Password, user.Salt) {
if user.Status == core.UserStatusClosed {
return nil, _errUserHasBeenBanned
}
// 清空登录计数
s.Redis.Del(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID))
} else {
// 登录错误计数
_, err = s.Redis.Incr(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID)).Result()
if err == nil {
s.Redis.Expire(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID), time.Hour).Result()
}
return nil, xerror.UnauthorizedAuthFailed
}
} else {
return nil, xerror.UnauthorizedAuthNotExist
}
token, err := app.GenerateToken(user)
if err != nil {
logrus.Errorf("app.GenerateToken err: %v", err)
return nil, xerror.UnauthorizedTokenGenerate
}
return &web.LoginResp{
Token: token,
}, nil
}
func (s *pubSrv) Version() (*web.VersionResp, mir.Error) {
return &web.VersionResp{
BuildInfo: debug.ReadBuildInfo(),
}, nil
}
func newPubSrv() api.Pub {
return &pubSrv{}
// validUsername 验证用户
func (s *pubSrv) validUsername(username string) mir.Error {
// 检测用户是否合规
if utf8.RuneCountInString(username) < 3 || utf8.RuneCountInString(username) > 12 {
return _errUsernameLengthLimit
}
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(username) {
return _errUsernameCharLimit
}
// 重复检查
user, _ := s.Ds.GetUserByUsername(username)
if user.Model != nil && user.ID > 0 {
return _errUsernameHasExisted
}
return nil
}
func newPubSrv(s *base.DaoServant) api.Pub {
return &pubSrv{
DaoServant: s,
}
}
func newPubBinding() api.PubBinding {

@ -6,7 +6,6 @@ package api
import (
"bytes"
_ "embed"
"encoding/base64"
"image/color"
"image/png"
@ -17,6 +16,7 @@ import (
"github.com/gofrs/uuid"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/servants/web/assets"
"github.com/rocboss/paopao-ce/internal/servants/web/broker"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/convert"
@ -26,9 +26,6 @@ import (
"github.com/sirupsen/logrus"
)
//go:embed assets/comic.ttf
var comic []byte
const MAX_PHONE_CAPTCHA = 10
func Version(c *gin.Context) {
@ -53,7 +50,7 @@ func SyncSearchIndex(c *gin.Context) {
func GetCaptcha(c *gin.Context) {
cap := captcha.New()
if err := cap.AddFontFromBytes(comic); err != nil {
if err := cap.AddFontFromBytes(assets.ComicBytes); err != nil {
panic(err.Error())
}

@ -6,7 +6,9 @@ package web
import (
"image"
"math/rand"
"strings"
"time"
"unicode/utf8"
"github.com/alimy/mir/v3"
@ -18,6 +20,64 @@ import (
"github.com/sirupsen/logrus"
)
var defaultAvatars = []string{
"https://assets.paopao.info/public/avatar/default/zoe.png",
"https://assets.paopao.info/public/avatar/default/william.png",
"https://assets.paopao.info/public/avatar/default/walter.png",
"https://assets.paopao.info/public/avatar/default/thomas.png",
"https://assets.paopao.info/public/avatar/default/taylor.png",
"https://assets.paopao.info/public/avatar/default/sophia.png",
"https://assets.paopao.info/public/avatar/default/sam.png",
"https://assets.paopao.info/public/avatar/default/ryan.png",
"https://assets.paopao.info/public/avatar/default/ruby.png",
"https://assets.paopao.info/public/avatar/default/quinn.png",
"https://assets.paopao.info/public/avatar/default/paul.png",
"https://assets.paopao.info/public/avatar/default/owen.png",
"https://assets.paopao.info/public/avatar/default/olivia.png",
"https://assets.paopao.info/public/avatar/default/norman.png",
"https://assets.paopao.info/public/avatar/default/nora.png",
"https://assets.paopao.info/public/avatar/default/natalie.png",
"https://assets.paopao.info/public/avatar/default/naomi.png",
"https://assets.paopao.info/public/avatar/default/miley.png",
"https://assets.paopao.info/public/avatar/default/mike.png",
"https://assets.paopao.info/public/avatar/default/lucas.png",
"https://assets.paopao.info/public/avatar/default/kylie.png",
"https://assets.paopao.info/public/avatar/default/julia.png",
"https://assets.paopao.info/public/avatar/default/joshua.png",
"https://assets.paopao.info/public/avatar/default/john.png",
"https://assets.paopao.info/public/avatar/default/jane.png",
"https://assets.paopao.info/public/avatar/default/jackson.png",
"https://assets.paopao.info/public/avatar/default/ivy.png",
"https://assets.paopao.info/public/avatar/default/isaac.png",
"https://assets.paopao.info/public/avatar/default/henry.png",
"https://assets.paopao.info/public/avatar/default/harry.png",
"https://assets.paopao.info/public/avatar/default/harold.png",
"https://assets.paopao.info/public/avatar/default/hanna.png",
"https://assets.paopao.info/public/avatar/default/grace.png",
"https://assets.paopao.info/public/avatar/default/george.png",
"https://assets.paopao.info/public/avatar/default/freddy.png",
"https://assets.paopao.info/public/avatar/default/frank.png",
"https://assets.paopao.info/public/avatar/default/finn.png",
"https://assets.paopao.info/public/avatar/default/emma.png",
"https://assets.paopao.info/public/avatar/default/emily.png",
"https://assets.paopao.info/public/avatar/default/edward.png",
"https://assets.paopao.info/public/avatar/default/clara.png",
"https://assets.paopao.info/public/avatar/default/claire.png",
"https://assets.paopao.info/public/avatar/default/chloe.png",
"https://assets.paopao.info/public/avatar/default/audrey.png",
"https://assets.paopao.info/public/avatar/default/arthur.png",
"https://assets.paopao.info/public/avatar/default/anna.png",
"https://assets.paopao.info/public/avatar/default/andy.png",
"https://assets.paopao.info/public/avatar/default/alfred.png",
"https://assets.paopao.info/public/avatar/default/alexa.png",
"https://assets.paopao.info/public/avatar/default/abigail.png",
}
func getRandomAvatar() string {
rand.Seed(time.Now().UnixMicro())
return defaultAvatars[rand.Intn(len(defaultAvatars))]
}
// checkPassword 密码检查
func checkPassword(password string) mir.Error {
// 检测用户是否合规

@ -28,7 +28,7 @@ func RouteWeb(e *gin.Engine) {
api.RegisterCoreServant(e, newCoreSrv(ds, oss), newCoreBinding(), newCoreRender())
api.RegisterLooseServant(e, newLooseSrv(), newLooseBinding(), newLooseRender())
api.RegisterPrivServant(e, newPrivSrv(ds, oss), newPrivBinding(), newPrivRender())
api.RegisterPubServant(e, newPubSrv(), newPubBinding(), newPubRender())
api.RegisterPubServant(e, newPubSrv(ds), newPubBinding(), newPubRender())
// regster servants if needed by configure
cfg.In(cfg.Actions{
"Alipay": func() {

@ -15,26 +15,26 @@ type Pub struct {
Group Group `mir:"v1"`
// Version 获取后台版本信息
Version func(Get) `mir:"/"`
Version func(Get) web.VersionResp `mir:"/"`
// Login 用户登录
Login func(Post) `mir:"/auth/login"`
Login func(Post, web.LoginReq) web.LoginResp `mir:"/auth/login"`
// Register 用户注册
Register func(Post) `mir:"/auth/register"`
Register func(Post, web.RegisterReq) web.RegisterResp `mir:"/auth/register"`
// GetCaptcha 获取验证码
GetCaptcha func(Get) `mir:"/captcha"`
GetCaptcha func(Get) web.GetCaptchaResp `mir:"/captcha"`
// PostCaptcha 发送验证码
SendCaptcha func(Post) `mir:"/captcha"`
// SendCaptcha 发送验证码
SendCaptcha func(Post, web.SendCaptchaReq) `mir:"/captcha"`
// TweetDetail 获取动态详情
TweetDetail func(Get, web.TweetDetailReq) web.TweetDetailResp `mir:"/post"`
// TweetComments 获取动态评论
TweetComments func(Get) `mir:"/post/comments"`
TweetComments func(Get, web.TweetCommentsReq) web.TweetCommentsResp `mir:"/post/comments"`
// TopicList 获取话题列表
TopicList func(Get) `mir:"/tags"`
TopicList func(Get, web.TopicListReq) web.TopicListResp `mir:"/tags"`
}

@ -11,9 +11,9 @@ import (
var version, commitID, buildDate string
type BuildInfo struct {
Version string
Sum string
BuildDate string
Version string `json:"version"`
Sum string `json:"sum"`
BuildDate string `json:"build_date"`
}
func VersionInfo() string {

Loading…
Cancel
Save