Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apis/notice.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Notice struct {
//}

func (e *Notice) Other(r *gin.RouterGroup) {
r.GET("/notice/unread", response.AuthHandler, e.Unread)
r.GET("/notice/unread", e.Unread)
r.PUT("/notice/read/:id", response.AuthHandler, e.MarkRead)
r.GET("/notice/read/:id", response.AuthHandler, e.Read)
}
Expand Down Expand Up @@ -122,6 +122,10 @@ func (e *Notice) MarkRead(ctx *gin.Context) {
func (e *Notice) Unread(ctx *gin.Context) {
api := response.Make(ctx)
verify := response.VerifyHandler(ctx)
if verify == nil {
api.OK(nil)
return
}
list := make([]*models.Notice, 0)
err := center.Default.GetDB(ctx, &models.Notice{}).Model(&models.Notice{}).
Where(&models.Notice{Read: false, UserID: verify.GetUserID()}).
Expand Down
95 changes: 90 additions & 5 deletions apis/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package apis

import (
"errors"
"fmt"
"gorm.io/gorm"
"net/http"
"time"

"github.com/gin-gonic/gin"
"github.com/mss-boot-io/mss-boot-admin/dto"
"github.com/mss-boot-io/mss-boot-admin/middleware"
"github.com/mss-boot-io/mss-boot-admin/models"
"github.com/mss-boot-io/mss-boot-admin/pkg"
"github.com/mss-boot-io/mss-boot/pkg/config/gormdb"
"github.com/mss-boot-io/mss-boot/pkg/response"
"github.com/mss-boot-io/mss-boot/pkg/response/actions"
"github.com/mss-boot-io/mss-boot/pkg/response/controller"

"github.com/mss-boot-io/mss-boot-admin/center"
"github.com/mss-boot-io/mss-boot-admin/dto"
"github.com/mss-boot-io/mss-boot-admin/middleware"
"github.com/mss-boot-io/mss-boot-admin/models"
"github.com/mss-boot-io/mss-boot-admin/notice/email"
"github.com/mss-boot-io/mss-boot-admin/pkg"
"github.com/mss-boot-io/mss-boot-admin/service"
)

Expand Down Expand Up @@ -46,6 +49,7 @@ type User struct {
// Other handler
func (e *User) Other(r *gin.RouterGroup) {
r.POST("/user/login", middleware.Auth.LoginHandler)
r.POST("/user/fakeCaptcha", e.FakeCaptcha)
r.POST("/user/login/github", middleware.Auth.LoginHandler)
r.GET("/user/refresh-token", middleware.Auth.RefreshHandler)
r.GET("/user/userInfo", middleware.Auth.MiddlewareFunc(), e.UserInfo)
Expand Down Expand Up @@ -153,7 +157,88 @@ func (e *User) RefreshToken(_ *gin.Context) {
// @Param data body dto.FakeCaptchaRequest true "data"
// @Success 200 {object} dto.FakeCaptchaResponse
// @Router /admin/api/user/fakeCaptcha [post]
func (e *User) FakeCaptcha(*gin.Context) {}
func (e *User) FakeCaptcha(ctx *gin.Context) {
api := response.Make(ctx)
req := &dto.FakeCaptchaRequest{}
if api.Bind(req).Error != nil {
api.Err(http.StatusUnprocessableEntity)
return
}
resp := &dto.FakeCaptchaResponse{}
if req.Email != "" {
// setup 01 get user by email
user := &models.User{}
err := center.Default.
GetDB(ctx, &models.User{}).
Where("email = ?", req.Email).
First(user).Error
if err != nil {
api.AddError(err)
if errors.Is(err, gorm.ErrRecordNotFound) {
api.Err(http.StatusNotFound)
return
}
api.Log.Error("GetUser error")
api.Err(http.StatusInternalServerError)
return
}
// setup 02 generate verify code
code, err := center.Default.GenerateCode(ctx, req.Email, 5*time.Minute)
if err != nil {
api.AddError(err).Log.Error("GenerateCode error")
api.Err(http.StatusInternalServerError)
return
}
// setup 03 send email
smtpHost, ok := center.GetAppConfig().GetAppConfig(ctx, "email.smtpHost")
if !ok {
api.AddError(fmt.Errorf("not support send email")).
Err(http.StatusNotImplemented)
return
}
smtpPort, ok := center.GetAppConfig().GetAppConfig(ctx, "email.smtpPort")
if !ok {
api.AddError(fmt.Errorf("not support send email")).
Err(http.StatusNotImplemented)
return
}
username, ok := center.GetAppConfig().GetAppConfig(ctx, "email.username")
if !ok {
api.AddError(fmt.Errorf("not support send email")).
Err(http.StatusNotImplemented)
return
}
password, ok := center.GetAppConfig().GetAppConfig(ctx, "email.password")
if !ok {
api.AddError(fmt.Errorf("not support send email")).
Err(http.StatusNotImplemented)
return
}
organization, ok := center.GetAppConfig().GetAppConfig(ctx, "base.websiteName")
if !ok || organization == "" {
organization = "mss-boot-io"
}
err = email.SendVerifyCode(
smtpHost, smtpPort,
username, password,
user.Username,
user.Email,
code,
organization)
if err != nil {
api.AddError(err).Log.Error("send email error")
api.Err(http.StatusInternalServerError)
return
}

resp.Status = "ok"
api.OK(resp)
return
}
err := fmt.Errorf("not support phone")
api.AddError(err).Err(http.StatusNotImplemented)
return
}

// UserInfo 获取登录用户信息
// @Summary 获取登录用户信息
Expand Down
18 changes: 18 additions & 0 deletions center/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type DefaultCenter struct {
storage.AdapterCache
storage.AdapterQueue
storage.AdapterLocker
VerifyCodeStoreImp
}

func (d *DefaultCenter) SetNotice(n NoticeImp) {
Expand Down Expand Up @@ -112,6 +113,10 @@ func (d *DefaultCenter) SetLocker(l storage.AdapterLocker) {
d.AdapterLocker = l
}

func (d *DefaultCenter) SetVerifyCodeStore(v VerifyCodeStoreImp) {
d.VerifyCodeStoreImp = v
}

func (d *DefaultCenter) GetNotice() NoticeImp {
return d.NoticeImp
}
Expand Down Expand Up @@ -180,6 +185,10 @@ func (d *DefaultCenter) GetLocker() storage.AdapterLocker {
return d.AdapterLocker
}

func (d *DefaultCenter) GetVerifyCodeStore() VerifyCodeStoreImp {
return d.VerifyCodeStoreImp
}

func (d *DefaultCenter) Stage() string {
stage := os.Getenv("STAGE")
if stage == "" {
Expand Down Expand Up @@ -280,6 +289,11 @@ func SetLocker(l storage.AdapterLocker) *DefaultCenter {
return Default
}

func SetVerifyCodeStore(v VerifyCodeStoreImp) *DefaultCenter {
Default.SetVerifyCodeStore(v)
return Default
}

func GetNotice() NoticeImp {
return Default.GetNotice()
}
Expand Down Expand Up @@ -351,3 +365,7 @@ func GetQueue() storage.AdapterQueue {
func GetLocker() storage.AdapterLocker {
return Default.GetLocker()
}

func GetVerifyCodeStore() VerifyCodeStoreImp {
return Default.GetVerifyCodeStore()
}
12 changes: 11 additions & 1 deletion center/type.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package center

import (
"context"
"time"

"github.com/gin-gonic/gin"
"github.com/mss-boot-io/mss-boot-admin/storage"
"github.com/mss-boot-io/mss-boot/core/server"
"github.com/mss-boot-io/mss-boot/pkg/config/source"
"github.com/mss-boot-io/mss-boot/pkg/security"
"github.com/mss-boot-io/mss-boot/virtual/model"
"google.golang.org/grpc"
"gorm.io/gorm"
"gorm.io/gorm/schema"

"github.com/mss-boot-io/mss-boot-admin/storage"
)

/*
Expand All @@ -36,6 +40,7 @@ type Center interface {
storage.AdapterCache
storage.AdapterQueue
storage.AdapterLocker
VerifyCodeStoreImp
}

type GRPCClientImp interface {
Expand Down Expand Up @@ -110,3 +115,8 @@ type StatisticsImp interface {
NowIncrease(ctx *gin.Context, object StatisticsObject) error
NowReduce(ctx *gin.Context, object StatisticsObject) error
}

type VerifyCodeStoreImp interface {
GenerateCode(ctx context.Context, key string, expire time.Duration) (string, error)
VerifyCode(ctx context.Context, key, code string) (bool, error)
}
5 changes: 4 additions & 1 deletion config/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ func (e Cache) Init() {
_redis = r.GetClient()
}
center.SetCache(r)
center.SetVerifyCodeStore(cache.NewVerifyCode(r))
}
if e.Memory != nil {
center.SetCache(cache.NewMemory(opts...))
m := cache.NewMemory(opts...)
center.SetCache(m)
center.SetVerifyCodeStore(cache.NewVerifyCode(m))
}
if e.QueryCache && e.QueryCacheDuration > 0 && gormdb.DB != nil {
cache.NewExpiration(context.Background(), e.QueryCacheDuration)
Expand Down
4 changes: 2 additions & 2 deletions dto/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ type LoginResponse struct {
}

type FakeCaptchaRequest struct {
Phone string `json:"phone" binding:"required"`
Phone string `json:"phone"`
Email string `json:"email"`
}

type FakeCaptchaResponse struct {
Code int8 `json:"code"`
Status string `json:"status"`
}

Expand Down
76 changes: 53 additions & 23 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,35 @@ func GetUserByUsername(ctx *gin.Context, username string) (*User, error) {
return &user, nil
}

// GetUserByEmail get user by email
func GetUserByEmail(ctx *gin.Context, email string) (*User, error) {
var user User
err := center.GetDB(ctx, &user).Preload("Role").First(&user, "email = ?", email).Error
if err != nil {
return nil, err
}
return &user, nil
}

type UserLogin struct {
RoleID string `json:"roleID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Role *Role `json:"role" gorm:"foreignKey:RoleID;references:ID"`
PostID string `json:"postID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Post *Post `json:"post" gorm:"foreignKey:PostID;references:ID"`
DepartmentID string `json:"departmentID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Department *Department `json:"department" gorm:"foreignKey:DepartmentID;references:ID"`
Username string `json:"username" gorm:"type:varchar(20);index"`
Email string `json:"email" gorm:"type:varchar(100);index"`
Password string `json:"password,omitempty" gorm:"-"`
PasswordHash string `json:"-" gorm:"size:255;comment:密码hash" swaggerignore:"true"`
PasswordStrength string `json:"passwordStrength" gorm:"size:20;comment:密码强度"`
Salt string `json:"-" gorm:"size:255;comment:加盐" swaggerignore:"true"`
Status enum.Status `json:"status" gorm:"size:10"`
OAuth2 []*UserOAuth2 `json:"oauth2" gorm:"foreignKey:UserID;references:ID"`
Provider pkg.OAuth2Provider `json:"type" gorm:"-"`
RefreshTokenDisable bool `json:"-" gorm:"-"`
PersonAccessToken string `json:"-" gorm:"-"`
RoleID string `json:"roleID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Role *Role `json:"role" gorm:"foreignKey:RoleID;references:ID"`
PostID string `json:"postID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Post *Post `json:"post" gorm:"foreignKey:PostID;references:ID"`
DepartmentID string `json:"departmentID" gorm:"index;type:varchar(64)" swaggerignore:"true"`
Department *Department `json:"department" gorm:"foreignKey:DepartmentID;references:ID"`
Username string `json:"username" gorm:"type:varchar(20);index"`
Email string `json:"email" gorm:"type:varchar(100);index"`
Password string `json:"password,omitempty" gorm:"-"`
PasswordHash string `json:"-" gorm:"size:255;comment:密码hash" swaggerignore:"true"`
PasswordStrength string `json:"passwordStrength" gorm:"size:20;comment:密码强度"`
Salt string `json:"-" gorm:"size:255;comment:加盐" swaggerignore:"true"`
Status enum.Status `json:"status" gorm:"size:10"`
OAuth2 []*UserOAuth2 `json:"oauth2" gorm:"foreignKey:UserID;references:ID"`
Provider pkg.LoginProvider `json:"type" gorm:"-"`
RefreshTokenDisable bool `json:"-" gorm:"-"`
PersonAccessToken string `json:"-" gorm:"-"`
Captcha string `json:"captcha" gorm:"-"`
}

func (e *UserLogin) TableName() string {
Expand Down Expand Up @@ -229,7 +240,7 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
defaultRole := &Role{Default: true}
_ = center.GetDB(ctx.(*gin.Context), &Role{}).Where(*defaultRole).First(defaultRole).Error
switch e.Provider {
case pkg.OAuth2GithubProvider:
case pkg.GithubLoginProvider:
// get user from github, then add user to db
// github user
clientID, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientId")
Expand Down Expand Up @@ -290,14 +301,14 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
Website: githubUser.HTMLURL,
EmailVerified: true,
Locale: githubUser.Location,
Provider: pkg.OAuth2GithubProvider,
Provider: pkg.GithubLoginProvider,
User: &User{
UserLogin: UserLogin{
RoleID: defaultRole.ID,
Username: githubUser.Email,
Email: githubUser.Email,
Password: e.Password,
Provider: pkg.OAuth2GithubProvider,
Provider: pkg.GithubLoginProvider,
Status: enum.Enabled,
},
Name: githubUser.Login,
Expand All @@ -319,7 +330,7 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
userOAuth2.User.Role = defaultRole
}
return true, userOAuth2.User, nil
case pkg.OAuth2LarkProvider:
case pkg.LarkLoginProvider:
client := http.Client{}
req, err := http.NewRequest(http.MethodGet, "https://open.larksuite.com/open-apis/authen/v1/user_info", nil)
if err != nil {
Expand Down Expand Up @@ -370,14 +381,14 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
Picture: *result.Data.AvatarUrl,
NickName: *result.Data.Name,
EmailVerified: email != "",
Provider: pkg.OAuth2LarkProvider,
Provider: pkg.LarkLoginProvider,
User: &User{
UserLogin: UserLogin{
RoleID: defaultRole.ID,
Username: *result.Data.UserId,
Email: email,
Password: e.Password,
Provider: pkg.OAuth2LarkProvider,
Provider: pkg.LarkLoginProvider,
Status: enum.Enabled,
},
Name: *result.Data.Name,
Expand All @@ -399,6 +410,25 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
userOAuth2.User.Role = defaultRole
}
return true, userOAuth2.User, nil
case pkg.EmailLoginProvider:
fmt.Println("email login", e)
// verify captcha
if e.Captcha == "" {
return false, nil, nil
}
ok, err := center.Default.VerifyCode(c, e.Email, e.Captcha)
if err != nil {
return false, nil, err
}
if !ok {
return false, nil, nil
}
// get user from db
user, err := GetUserByEmail(c, e.Email)
if err != nil {
return false, nil, err
}
return true, user, nil
}
// username and password
user, err := GetUserByUsername(ctx.(*gin.Context), e.Username)
Expand Down
Loading