add setting support

Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero 2021-08-13 08:17:07 +08:00 committed by Gaius
parent 16e23b50de
commit b0781a2c0e
No known key found for this signature in database
GPG Key ID: 8B4E5D1290FA2FFB
12 changed files with 319 additions and 86 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/github" "golang.org/x/oauth2/github"
@ -21,11 +22,10 @@ func NewGithubOauth2(name string, clientID string, clientSecret string, db *gorm
oa.Config = &oauth2.Config{ oa.Config = &oauth2.Config{
ClientID: clientID, ClientID: clientID,
ClientSecret: clientSecret, ClientSecret: clientSecret,
Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", Scopes: strings.Split(GithubScopes, ","),
"https://www.googleapis.com/auth/userinfo.email"},
Endpoint: github.Endpoint, Endpoint: github.Endpoint,
} }
oa.UserInfoURL = "https://api.github.com/user" oa.UserInfoURL = GithubUserInfoURL
redirectURL, err := oa.GetRediectURL(db) redirectURL, err := oa.GetRediectURL(db)
if err != nil { if err != nil {

View File

@ -1,6 +1,8 @@
package oauth package oauth
import ( import (
"strings"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
"gorm.io/gorm" "gorm.io/gorm"
@ -17,11 +19,10 @@ func NewGoogleOauth2(name string, clientID string, clientSecret string, db *gorm
oa.Config = &oauth2.Config{ oa.Config = &oauth2.Config{
ClientID: clientID, ClientID: clientID,
ClientSecret: clientSecret, ClientSecret: clientSecret,
Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", Scopes: strings.Split(GoogleScopes, ","),
"https://www.googleapis.com/auth/userinfo.email"},
Endpoint: google.Endpoint, Endpoint: google.Endpoint,
} }
oa.UserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo" oa.UserInfoURL = GithubUserInfoURL
redirectURL, err := oa.GetRediectURL(db) redirectURL, err := oa.GetRediectURL(db)
if err != nil { if err != nil {

View File

@ -16,6 +16,14 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
const (
GoogleScopes = "https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile"
GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo"
GithubScopes = "user,public_repo"
GithubUserInfoURL = "https://api.github.com/user"
)
type baseOauth2 struct { type baseOauth2 struct {
Name string Name string
UserInfoURL string UserInfoURL string

View File

@ -193,33 +193,17 @@ func (h *Handlers) GetOauth(ctx *gin.Context) {
// @Tags Oauth // @Tags Oauth
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param page query int true "current page" default(0)
// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
// @Success 200 {object} []model.Oauth // @Success 200 {object} []model.Oauth
// @Failure 400 {object} HTTPError // @Failure 400 {object} HTTPError
// @Failure 404 {object} HTTPError // @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError // @Failure 500 {object} HTTPError
// @Router /oauths [get] // @Router /oauths [get]
func (h *Handlers) GetOauths(ctx *gin.Context) { func (h *Handlers) GetOauths(ctx *gin.Context) {
var query types.GetOauthsQuery oauths, err := h.Service.GetOauths()
if err := ctx.ShouldBindQuery(&query); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
h.setPaginationDefault(&query.Page, &query.PerPage)
oauths, err := h.Service.GetOauths(query)
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
totalCount, err := h.Service.OauthTotalCount(query)
if err != nil {
ctx.Error(err)
return
}
h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(totalCount))
ctx.JSON(http.StatusOK, oauths) ctx.JSON(http.StatusOK, oauths)
} }

View File

@ -0,0 +1,132 @@
package handlers
import (
"net/http"
"d7y.io/dragonfly/v2/manager/types"
"github.com/gin-gonic/gin"
)
// @Summary Create Setting
// @Description create by json config
// @Tags Setting
// @Accept json
// @Produce json
// @Param Setting body types.CreateSettingRequest true "Setting"
// @Success 200 {object} model.Setting
// @Failure 400 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /settings [post]
func (h *Handlers) CreateSetting(ctx *gin.Context) {
var json types.CreateSettingRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
setting, err := h.Service.CreateSetting(json)
if err != nil {
ctx.Error(err)
return
}
ctx.JSON(http.StatusOK, setting)
}
// @Summary Destroy Setting
// @Description Destroy by key
// @Tags Setting
// @Accept json
// @Produce json
// @Param id path string true "key"
// @Success 200
// @Failure 400 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /settings/{id} [delete]
func (h *Handlers) DestroySetting(ctx *gin.Context) {
var params types.SettingParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
err := h.Service.DestroySetting(params.Key)
if err != nil {
ctx.Error(err)
return
}
ctx.Status(http.StatusOK)
}
// @Summary Update Setting
// @Description Update by json config
// @Tags Setting
// @Accept json
// @Produce json
// @Param key path string true "key"
// @Param Setting body types.UpdateSettingRequest true "Setting"
// @Success 200 {object} model.Setting
// @Failure 400 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /settings/{id} [patch]
func (h *Handlers) UpdateSetting(ctx *gin.Context) {
var params types.SettingParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.Error(err)
return
}
var json types.UpdateSettingRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.Error(err)
return
}
setting, err := h.Service.UpdateSetting(params.Key, json)
if err != nil {
ctx.Error(err)
return
}
ctx.JSON(http.StatusOK, setting)
}
// @Summary Get Settings
// @Description Get Settings
// @Tags Setting
// @Accept json
// @Produce json
// @Param page query int true "current page" default(0)
// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
// @Success 200 {object} []model.Setting
// @Failure 400 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /settings [get]
func (h *Handlers) GetSettings(ctx *gin.Context) {
var query types.GetSettingsQuery
if err := ctx.ShouldBindQuery(&query); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
h.setPaginationDefault(&query.Page, &query.PerPage)
settings, err := h.Service.GetSettings(query)
if err != nil {
ctx.Error(err)
return
}
totalCount, err := h.Service.SettingTotalCount()
if err != nil {
ctx.Error(err)
return
}
h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(totalCount))
ctx.JSON(http.StatusOK, settings)
}

View File

@ -1,7 +1,7 @@
package model package model
type Settings struct { type Settings struct {
Model ID uint `gorm:"primarykey;comment:id" json:"id"`
Key string `gorm:"column:key;type:varchar(256);index:unique;not null;comment: setting key" json:"key"` Key string `gorm:"column:key;type:varchar(256);index:uk_settings_key,unique;not null;comment:setting key" json:"key"`
Value string `gorm:"column:value;type:varchar(256);not null;comment:setting value" json:"value"` Value string `gorm:"column:value;type:varchar(256);not null;comment:setting value" json:"value"`
} }

View File

@ -104,6 +104,13 @@ func Init(console bool, verbose bool, publicPath string, service service.REST, e
s.GET(":id", h.GetScheduler) s.GET(":id", h.GetScheduler)
s.GET("", h.GetSchedulers) s.GET("", h.GetSchedulers)
// Settings
st := apiv1.Group("/settings")
st.POST("", h.CreateSetting)
st.DELETE(":id", h.DestroySetting)
st.PATCH("", h.UpdateSetting)
st.GET("", h.GetSettings)
// CDN Cluster // CDN Cluster
cc := apiv1.Group("/cdn-clusters") cc := apiv1.Group("/cdn-clusters")
cc.POST("", h.CreateCDNCluster) cc.POST("", h.CreateCDNCluster)

View File

@ -1,25 +1,47 @@
package service package service
import ( import (
"d7y.io/dragonfly/v2/manager/auth/oauth"
"d7y.io/dragonfly/v2/manager/model" "d7y.io/dragonfly/v2/manager/model"
"d7y.io/dragonfly/v2/manager/oauth"
"d7y.io/dragonfly/v2/manager/types" "d7y.io/dragonfly/v2/manager/types"
"golang.org/x/oauth2/github"
"golang.org/x/oauth2/google"
) )
func (s *rest) CreateOauth(json types.CreateOauthRequest) (*model.Oauth, error) { func (s *rest) CreateOauth(json types.CreateOauthRequest) (*model.Oauth, error) {
oauth := model.Oauth{ o := model.Oauth{}
o.ClientID = json.ClientID
o.ClientSecret = json.ClientSecret
o.Name = json.Name
switch json.Name {
case "google":
o.AuthURL = google.Endpoint.AuthURL
o.TokenURL = google.Endpoint.TokenURL
o.Scopes = oauth.GoogleScopes
o.UserInfoURL = oauth.GoogleUserInfoURL
case "github":
o.AuthURL = github.Endpoint.AuthURL
o.TokenURL = github.Endpoint.TokenURL
o.Scopes = oauth.GithubScopes
o.UserInfoURL = oauth.GithubUserInfoURL
default:
o = model.Oauth{
ClientID: json.ClientID, ClientID: json.ClientID,
ClientSecret: json.ClientSecret, ClientSecret: json.ClientSecret,
Name: json.Name, Name: json.Name,
Scopes: json.Scopes,
AuthURL: json.AuthURL, AuthURL: json.AuthURL,
TokenURL: json.TokenURL, TokenURL: json.TokenURL,
UserInfoURL: json.UserInfoURL,
}
} }
if err := s.db.Create(&oauth).Error; err != nil { if err := s.db.Create(&o).Error; err != nil {
return nil, err return nil, err
} }
return &oauth, nil return &o, nil
} }
func (s *rest) DestroyOauth(id uint) error { func (s *rest) DestroyOauth(id uint) error {
@ -53,52 +75,40 @@ func (s *rest) GetOauth(id uint) (*model.Oauth, error) {
return &oauth, nil return &oauth, nil
} }
func (s *rest) GetOauths(q types.GetOauthsQuery) (*[]model.Oauth, error) { func (s *rest) GetOauths() (*[]model.Oauth, error) {
oauths := []model.Oauth{} oauths := []model.Oauth{}
if q.Name != "" { if err := s.db.Find(&oauths).Error; err != nil {
if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Where(&model.Oauth{
Name: q.Name,
}).Find(&oauths).Error; err != nil {
return nil, err return nil, err
} }
} else {
if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Find(&oauths).Error; err != nil {
return nil, err
}
}
return &oauths, nil return &oauths, nil
} }
func (s *rest) OauthTotalCount(q types.GetOauthsQuery) (int64, error) {
var count int64
if q.Name != "" {
if err := s.db.Model(&model.Oauth{}).Where(&model.Oauth{
Name: q.Name,
}).Count(&count).Error; err != nil {
return 0, err
}
} else {
if err := s.db.Model(&model.Oauth{}).Where(&model.Oauth{}).Count(&count).Error; err != nil {
return 0, err
}
}
return count, nil
}
func (s *rest) OauthSignin(name string) (string, error) { func (s *rest) OauthSignin(name string) (string, error) {
oauthModel := model.Oauth{} oauthModel := model.Oauth{}
if err := s.db.First(&oauthModel, name).Error; err != nil { if err := s.db.First(&oauthModel, name).Error; err != nil {
return "", err return "", err
} }
o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) var o oauth.Oauther
var err error
switch name {
case "google":
o, err = oauth.NewGoogleOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, s.db)
if err != nil { if err != nil {
return "", err return "", err
} }
case "github":
o, err = oauth.NewGithubOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, s.db)
if err != nil {
return "", err
}
default:
o, err = oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db)
if err != nil {
return "", err
}
}
return o.AuthCodeURL(), nil return o.AuthCodeURL(), nil
} }
@ -107,11 +117,26 @@ func (s *rest) OauthCallback(name, code string) (*model.User, error) {
if err := s.db.First(&oauthModel, name).Error; err != nil { if err := s.db.First(&oauthModel, name).Error; err != nil {
return nil, err return nil, err
} }
var o oauth.Oauther
o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) var err error
switch name {
case "google":
o, err = oauth.NewGoogleOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, s.db)
if err != nil { if err != nil {
return nil, err return nil, err
} }
case "github":
o, err = oauth.NewGithubOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, s.db)
if err != nil {
return nil, err
}
default:
o, err = oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db)
if err != nil {
return nil, err
}
}
user, err := o.ExchangeUserByCode(code, s.db) user, err := o.ExchangeUserByCode(code, s.db)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -81,6 +81,12 @@ type REST interface {
GetSchedulers(types.GetSchedulersQuery) (*[]model.Scheduler, error) GetSchedulers(types.GetSchedulersQuery) (*[]model.Scheduler, error)
SchedulerTotalCount(types.GetSchedulersQuery) (int64, error) SchedulerTotalCount(types.GetSchedulersQuery) (int64, error)
CreateSetting(types.CreateSettingRequest) (*model.Settings, error)
DestroySetting(string) error
UpdateSetting(string, types.UpdateSettingRequest) (*model.Settings, error)
GetSettings(types.GetSettingsQuery) (*[]model.Settings, error)
SettingTotalCount() (int64, error)
CreateSecurityGroup(types.CreateSecurityGroupRequest) (*model.SecurityGroup, error) CreateSecurityGroup(types.CreateSecurityGroupRequest) (*model.SecurityGroup, error)
DestroySecurityGroup(uint) error DestroySecurityGroup(uint) error
UpdateSecurityGroup(uint, types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error) UpdateSecurityGroup(uint, types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error)
@ -97,8 +103,7 @@ type REST interface {
DestroyOauth(uint) error DestroyOauth(uint) error
UpdateOauth(uint, types.UpdateOauthRequest) (*model.Oauth, error) UpdateOauth(uint, types.UpdateOauthRequest) (*model.Oauth, error)
GetOauth(uint) (*model.Oauth, error) GetOauth(uint) (*model.Oauth, error)
GetOauths(types.GetOauthsQuery) (*[]model.Oauth, error) GetOauths() (*[]model.Oauth, error)
OauthTotalCount(types.GetOauthsQuery) (int64, error)
OauthSignin(name string) (string, error) OauthSignin(name string) (string, error)
OauthCallback(name, code string) (*model.User, error) OauthCallback(name, code string) (*model.User, error)
} }

View File

@ -0,0 +1,57 @@
package service
import (
"d7y.io/dragonfly/v2/manager/model"
"d7y.io/dragonfly/v2/manager/types"
)
func (s *rest) CreateSetting(json types.CreateSettingRequest) (*model.Settings, error) {
setting := model.Settings{
Key: json.Key,
Value: json.Value,
}
if err := s.db.Create(&setting).Error; err != nil {
return nil, err
}
return &setting, nil
}
func (s *rest) DestroySetting(key string) error {
if err := s.db.Unscoped().Delete(&model.Settings{}, model.Settings{Key: key}).Error; err != nil {
return err
}
return nil
}
func (s *rest) UpdateSetting(key string, json types.UpdateSettingRequest) (*model.Settings, error) {
setting := model.Settings{}
if err := s.db.First(&setting, model.Settings{Key: key}).Updates(model.Settings{
Key: json.Key,
Value: json.Value,
}).Error; err != nil {
return nil, err
}
return &setting, nil
}
func (s *rest) GetSettings(q types.GetSettingsQuery) (*[]model.Settings, error) {
settings := []model.Settings{}
if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Find(&settings).Error; err != nil {
return nil, err
}
return &settings, nil
}
func (s *rest) SettingTotalCount() (int64, error) {
var count int64
if err := s.db.Model(&model.Settings{}).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}

View File

@ -16,6 +16,7 @@ type OauthBaseRquest struct {
Scopes string `json:"scopes" binding:"omitempty"` Scopes string `json:"scopes" binding:"omitempty"`
AuthURL string `json:"auth_url" binding:"omitempty"` AuthURL string `json:"auth_url" binding:"omitempty"`
TokenURL string `json:"token_url" binding:"omitempty"` TokenURL string `json:"token_url" binding:"omitempty"`
UserInfoURL string `json:"user_info_url" binding:"omitempty"`
} }
type CreateOauthRequest struct { type CreateOauthRequest struct {
@ -25,9 +26,3 @@ type CreateOauthRequest struct {
type UpdateOauthRequest struct { type UpdateOauthRequest struct {
OauthBaseRquest OauthBaseRquest
} }
type GetOauthsQuery struct {
Name string `json:"name" binding:"required"`
Page int `form:"page" binding:"omitempty,gte=1"`
PerPage int `form:"per_page" binding:"omitempty,gte=1,lte=50"`
}

19
manager/types/settings.go Normal file
View File

@ -0,0 +1,19 @@
package types
type SettingParams struct {
Key string `uri:"key" binding:"required"`
}
type CreateSettingRequest struct {
Key string `json:"key" binding:"required"`
Value string `json:"value" binding:"required"`
}
type UpdateSettingRequest struct {
Key string `json:"key" binding:"required"`
Value string `json:"value" binding:"required"`
}
type GetSettingsQuery struct {
Page int `form:"page" binding:"omitempty,gte=1"`
PerPage int `form:"per_page" binding:"omitempty,gte=1,lte=50"`
}