Skip to content

Commit 6b19c84

Browse files
authored
Merge pull request #2993 from seefs001/feature/user-oauth-detail
feat: move user bindings to dedicated management modal
2 parents e9fa2a4 + 3a954e1 commit 6b19c84

File tree

6 files changed

+644
-68
lines changed

6 files changed

+644
-68
lines changed

controller/custom_oauth.go

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ type CustomOAuthProviderResponse struct {
3838
AccessDeniedMessage string `json:"access_denied_message"`
3939
}
4040

41+
type UserOAuthBindingResponse struct {
42+
ProviderId int `json:"provider_id"`
43+
ProviderName string `json:"provider_name"`
44+
ProviderSlug string `json:"provider_slug"`
45+
ProviderIcon string `json:"provider_icon"`
46+
ProviderUserId string `json:"provider_user_id"`
47+
}
48+
4149
func toCustomOAuthProviderResponse(p *model.CustomOAuthProvider) *CustomOAuthProviderResponse {
4250
return &CustomOAuthProviderResponse{
4351
Id: p.Id,
@@ -433,6 +441,30 @@ func DeleteCustomOAuthProvider(c *gin.Context) {
433441
})
434442
}
435443

444+
func buildUserOAuthBindingsResponse(userId int) ([]UserOAuthBindingResponse, error) {
445+
bindings, err := model.GetUserOAuthBindingsByUserId(userId)
446+
if err != nil {
447+
return nil, err
448+
}
449+
450+
response := make([]UserOAuthBindingResponse, 0, len(bindings))
451+
for _, binding := range bindings {
452+
provider, err := model.GetCustomOAuthProviderById(binding.ProviderId)
453+
if err != nil {
454+
continue
455+
}
456+
response = append(response, UserOAuthBindingResponse{
457+
ProviderId: binding.ProviderId,
458+
ProviderName: provider.Name,
459+
ProviderSlug: provider.Slug,
460+
ProviderIcon: provider.Icon,
461+
ProviderUserId: binding.ProviderUserId,
462+
})
463+
}
464+
465+
return response, nil
466+
}
467+
436468
// GetUserOAuthBindings returns all OAuth bindings for the current user
437469
func GetUserOAuthBindings(c *gin.Context) {
438470
userId := c.GetInt("id")
@@ -441,34 +473,43 @@ func GetUserOAuthBindings(c *gin.Context) {
441473
return
442474
}
443475

444-
bindings, err := model.GetUserOAuthBindingsByUserId(userId)
476+
response, err := buildUserOAuthBindingsResponse(userId)
445477
if err != nil {
446478
common.ApiError(c, err)
447479
return
448480
}
449481

450-
// Build response with provider info
451-
type BindingResponse struct {
452-
ProviderId int `json:"provider_id"`
453-
ProviderName string `json:"provider_name"`
454-
ProviderSlug string `json:"provider_slug"`
455-
ProviderIcon string `json:"provider_icon"`
456-
ProviderUserId string `json:"provider_user_id"`
482+
c.JSON(http.StatusOK, gin.H{
483+
"success": true,
484+
"message": "",
485+
"data": response,
486+
})
487+
}
488+
489+
func GetUserOAuthBindingsByAdmin(c *gin.Context) {
490+
userIdStr := c.Param("id")
491+
userId, err := strconv.Atoi(userIdStr)
492+
if err != nil {
493+
common.ApiErrorMsg(c, "invalid user id")
494+
return
457495
}
458496

459-
response := make([]BindingResponse, 0)
460-
for _, binding := range bindings {
461-
provider, err := model.GetCustomOAuthProviderById(binding.ProviderId)
462-
if err != nil {
463-
continue // Skip if provider not found
464-
}
465-
response = append(response, BindingResponse{
466-
ProviderId: binding.ProviderId,
467-
ProviderName: provider.Name,
468-
ProviderSlug: provider.Slug,
469-
ProviderIcon: provider.Icon,
470-
ProviderUserId: binding.ProviderUserId,
471-
})
497+
targetUser, err := model.GetUserById(userId, false)
498+
if err != nil {
499+
common.ApiError(c, err)
500+
return
501+
}
502+
503+
myRole := c.GetInt("role")
504+
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
505+
common.ApiErrorMsg(c, "no permission")
506+
return
507+
}
508+
509+
response, err := buildUserOAuthBindingsResponse(userId)
510+
if err != nil {
511+
common.ApiError(c, err)
512+
return
472513
}
473514

474515
c.JSON(http.StatusOK, gin.H{
@@ -503,3 +544,41 @@ func UnbindCustomOAuth(c *gin.Context) {
503544
"message": "解绑成功",
504545
})
505546
}
547+
548+
func UnbindCustomOAuthByAdmin(c *gin.Context) {
549+
userIdStr := c.Param("id")
550+
userId, err := strconv.Atoi(userIdStr)
551+
if err != nil {
552+
common.ApiErrorMsg(c, "invalid user id")
553+
return
554+
}
555+
556+
targetUser, err := model.GetUserById(userId, false)
557+
if err != nil {
558+
common.ApiError(c, err)
559+
return
560+
}
561+
562+
myRole := c.GetInt("role")
563+
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
564+
common.ApiErrorMsg(c, "no permission")
565+
return
566+
}
567+
568+
providerIdStr := c.Param("provider_id")
569+
providerId, err := strconv.Atoi(providerIdStr)
570+
if err != nil {
571+
common.ApiErrorMsg(c, "invalid provider id")
572+
return
573+
}
574+
575+
if err := model.DeleteUserOAuthBinding(userId, providerId); err != nil {
576+
common.ApiError(c, err)
577+
return
578+
}
579+
580+
c.JSON(http.StatusOK, gin.H{
581+
"success": true,
582+
"message": "success",
583+
})
584+
}

controller/user.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,44 @@ func UpdateUser(c *gin.Context) {
582582
return
583583
}
584584

585+
func AdminClearUserBinding(c *gin.Context) {
586+
id, err := strconv.Atoi(c.Param("id"))
587+
if err != nil {
588+
common.ApiErrorI18n(c, i18n.MsgInvalidParams)
589+
return
590+
}
591+
592+
bindingType := strings.ToLower(strings.TrimSpace(c.Param("binding_type")))
593+
if bindingType == "" {
594+
common.ApiErrorI18n(c, i18n.MsgInvalidParams)
595+
return
596+
}
597+
598+
user, err := model.GetUserById(id, false)
599+
if err != nil {
600+
common.ApiError(c, err)
601+
return
602+
}
603+
604+
myRole := c.GetInt("role")
605+
if myRole <= user.Role && myRole != common.RoleRootUser {
606+
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
607+
return
608+
}
609+
610+
if err := user.ClearBinding(bindingType); err != nil {
611+
common.ApiError(c, err)
612+
return
613+
}
614+
615+
model.RecordLog(user.Id, model.LogTypeManage, fmt.Sprintf("admin cleared %s binding for user %s", bindingType, user.Username))
616+
617+
c.JSON(http.StatusOK, gin.H{
618+
"success": true,
619+
"message": "success",
620+
})
621+
}
622+
585623
func UpdateSelf(c *gin.Context) {
586624
var requestData map[string]interface{}
587625
err := json.NewDecoder(c.Request.Body).Decode(&requestData)

model/user.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,37 @@ func (user *User) Edit(updatePassword bool) error {
536536
return updateUserCache(*user)
537537
}
538538

539+
func (user *User) ClearBinding(bindingType string) error {
540+
if user.Id == 0 {
541+
return errors.New("user id is empty")
542+
}
543+
544+
bindingColumnMap := map[string]string{
545+
"email": "email",
546+
"github": "github_id",
547+
"discord": "discord_id",
548+
"oidc": "oidc_id",
549+
"wechat": "wechat_id",
550+
"telegram": "telegram_id",
551+
"linuxdo": "linux_do_id",
552+
}
553+
554+
column, ok := bindingColumnMap[bindingType]
555+
if !ok {
556+
return errors.New("invalid binding type")
557+
}
558+
559+
if err := DB.Model(&User{}).Where("id = ?", user.Id).Update(column, "").Error; err != nil {
560+
return err
561+
}
562+
563+
if err := DB.Where("id = ?", user.Id).First(user).Error; err != nil {
564+
return err
565+
}
566+
567+
return updateUserCache(*user)
568+
}
569+
539570
func (user *User) Delete() error {
540571
if user.Id == 0 {
541572
return errors.New("id 为空!")

router/api-router.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ func SetApiRouter(router *gin.Engine) {
114114
adminRoute.GET("/topup", controller.GetAllTopUps)
115115
adminRoute.POST("/topup/complete", controller.AdminCompleteTopUp)
116116
adminRoute.GET("/search", controller.SearchUsers)
117+
adminRoute.GET("/:id/oauth/bindings", controller.GetUserOAuthBindingsByAdmin)
118+
adminRoute.DELETE("/:id/oauth/bindings/:provider_id", controller.UnbindCustomOAuthByAdmin)
119+
adminRoute.DELETE("/:id/bindings/:binding_type", controller.AdminClearUserBinding)
117120
adminRoute.GET("/:id", controller.GetUser)
118121
adminRoute.POST("/", controller.CreateUser)
119122
adminRoute.POST("/manage", controller.ManageUser)

0 commit comments

Comments
 (0)