Add a new /api/user/channels endpoint that returns only enabled channels with non-sensitive fields (id, name, type, remark) for token channel binding. Update EditTokenModal to use this endpoint and display remark instead of ID in the channel dropdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>feat/alipay-payment
| @@ -2094,3 +2094,30 @@ func OllamaVersion(c *gin.Context) { | |||||
| }, | }, | ||||
| }) | }) | ||||
| } | } | ||||
| // GetUserChannelsForBinding 获取用户可用于绑定的渠道列表 | |||||
| // 只返回 id、name、type 和 remark,不包含敏感信息(如key) | |||||
| func GetUserChannelsForBinding(c *gin.Context) { | |||||
| channels, err := model.GetAllChannelsForBinding() | |||||
| if err != nil { | |||||
| common.ApiError(c, err) | |||||
| return | |||||
| } | |||||
| // 构建返回结果 | |||||
| result := make([]gin.H, 0, len(channels)) | |||||
| for _, ch := range channels { | |||||
| result = append(result, gin.H{ | |||||
| "id": ch.Id, | |||||
| "name": ch.Name, | |||||
| "type": ch.Type, | |||||
| "remark": ch.Remark, | |||||
| }) | |||||
| } | |||||
| c.JSON(http.StatusOK, gin.H{ | |||||
| "success": true, | |||||
| "message": "", | |||||
| "data": result, | |||||
| }) | |||||
| } | |||||
| @@ -275,6 +275,17 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan | |||||
| return channels, err | return channels, err | ||||
| } | } | ||||
| // GetAllChannelsForBinding 获取所有启用的渠道(用于用户绑定渠道) | |||||
| // 只返回 id, name, type, remark,不包含敏感信息 | |||||
| func GetAllChannelsForBinding() ([]*Channel, error) { | |||||
| var channels []*Channel | |||||
| err := DB.Select("id, name, type, remark"). | |||||
| Where("status = ?", common.ChannelStatusEnabled). | |||||
| Order("priority desc"). | |||||
| Find(&channels).Error | |||||
| return channels, err | |||||
| } | |||||
| func GetChannelsByTag(tag string, idSort bool, selectAll bool) ([]*Channel, error) { | func GetChannelsByTag(tag string, idSort bool, selectAll bool) ([]*Channel, error) { | ||||
| var channels []*Channel | var channels []*Channel | ||||
| order := "priority desc" | order := "priority desc" | ||||
| @@ -70,6 +70,7 @@ func SetApiRouter(router *gin.Engine) { | |||||
| selfRoute.GET("/self/groups", controller.GetUserGroups) | selfRoute.GET("/self/groups", controller.GetUserGroups) | ||||
| selfRoute.GET("/self", controller.GetSelf) | selfRoute.GET("/self", controller.GetSelf) | ||||
| selfRoute.GET("/models", controller.GetUserModels) | selfRoute.GET("/models", controller.GetUserModels) | ||||
| selfRoute.GET("/channels", controller.GetUserChannelsForBinding) | |||||
| selfRoute.PUT("/self", controller.UpdateSelf) | selfRoute.PUT("/self", controller.UpdateSelf) | ||||
| selfRoute.DELETE("/self", controller.DeleteSelf) | selfRoute.DELETE("/self", controller.DeleteSelf) | ||||
| selfRoute.GET("/token", controller.GenerateAccessToken) | selfRoute.GET("/token", controller.GenerateAccessToken) | ||||
| @@ -152,11 +152,13 @@ const EditTokenModal = (props) => { | |||||
| }; | }; | ||||
| const loadChannels = async () => { | const loadChannels = async () => { | ||||
| let res = await API.get(`/api/channel/`); | |||||
| let res = await API.get(`/api/user/channels`); | |||||
| const { success, message, data } = res.data; | const { success, message, data } = res.data; | ||||
| if (success) { | if (success) { | ||||
| let localChannelOptions = (data || []).map((channel) => ({ | let localChannelOptions = (data || []).map((channel) => ({ | ||||
| label: `${channel.name} (ID: ${channel.id})`, | |||||
| label: channel.remark | |||||
| ? `${channel.name} (${channel.remark})` | |||||
| : channel.name, | |||||
| value: channel.id, | value: channel.id, | ||||
| })); | })); | ||||
| setChannels(localChannelOptions); | setChannels(localChannelOptions); | ||||