diff --git a/manager/oauth/github.go b/manager/auth/oauth/github.go similarity index 84% rename from manager/oauth/github.go rename to manager/auth/oauth/github.go index 8ad39d9d0..cdd46709a 100644 --- a/manager/oauth/github.go +++ b/manager/auth/oauth/github.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io/ioutil" "net/http" + "strings" "golang.org/x/oauth2" "golang.org/x/oauth2/github" @@ -21,11 +22,10 @@ func NewGithubOauth2(name string, clientID string, clientSecret string, db *gorm oa.Config = &oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, - Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", - "https://www.googleapis.com/auth/userinfo.email"}, - Endpoint: github.Endpoint, + Scopes: strings.Split(GithubScopes, ","), + Endpoint: github.Endpoint, } - oa.UserInfoURL = "https://api.github.com/user" + oa.UserInfoURL = GithubUserInfoURL redirectURL, err := oa.GetRediectURL(db) if err != nil { diff --git a/manager/oauth/google.go b/manager/auth/oauth/google.go similarity index 69% rename from manager/oauth/google.go rename to manager/auth/oauth/google.go index 036366ad4..90bd61a33 100644 --- a/manager/oauth/google.go +++ b/manager/auth/oauth/google.go @@ -1,6 +1,8 @@ package oauth import ( + "strings" + "golang.org/x/oauth2" "golang.org/x/oauth2/google" "gorm.io/gorm" @@ -17,11 +19,10 @@ func NewGoogleOauth2(name string, clientID string, clientSecret string, db *gorm oa.Config = &oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, - Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", - "https://www.googleapis.com/auth/userinfo.email"}, - Endpoint: google.Endpoint, + Scopes: strings.Split(GoogleScopes, ","), + Endpoint: google.Endpoint, } - oa.UserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo" + oa.UserInfoURL = GithubUserInfoURL redirectURL, err := oa.GetRediectURL(db) if err != nil { diff --git a/manager/oauth/oauth.go b/manager/auth/oauth/oauth.go similarity index 91% rename from manager/oauth/oauth.go rename to manager/auth/oauth/oauth.go index 6159c4d9a..f39a6f367 100644 --- a/manager/oauth/oauth.go +++ b/manager/auth/oauth/oauth.go @@ -16,6 +16,14 @@ import ( "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 { Name string UserInfoURL string diff --git a/manager/handlers/oauth.go b/manager/handlers/oauth.go index 2882a77eb..461f0d08a 100644 --- a/manager/handlers/oauth.go +++ b/manager/handlers/oauth.go @@ -193,33 +193,17 @@ func (h *Handlers) GetOauth(ctx *gin.Context) { // @Tags Oauth // @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.Oauth // @Failure 400 {object} HTTPError // @Failure 404 {object} HTTPError // @Failure 500 {object} HTTPError // @Router /oauths [get] func (h *Handlers) GetOauths(ctx *gin.Context) { - var query types.GetOauthsQuery - 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) + oauths, err := h.Service.GetOauths() if err != nil { ctx.Error(err) 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) } diff --git a/manager/handlers/settings.go b/manager/handlers/settings.go new file mode 100644 index 000000000..3db7f10c7 --- /dev/null +++ b/manager/handlers/settings.go @@ -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(¶ms); 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(¶ms); 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) +} diff --git a/manager/model/settings.go b/manager/model/settings.go index d25a364ce..781187a1b 100644 --- a/manager/model/settings.go +++ b/manager/model/settings.go @@ -1,7 +1,7 @@ package model type Settings struct { - Model - Key string `gorm:"column:key;type:varchar(256);index:unique;not null;comment: setting key" json:"key"` - Value string `gorm:"column:value;type:varchar(256);not null;comment: setting value" json:"value"` + ID uint `gorm:"primarykey;comment:id" json:"id"` + 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"` } diff --git a/manager/router/router.go b/manager/router/router.go index 2a14a8ff8..8620998db 100644 --- a/manager/router/router.go +++ b/manager/router/router.go @@ -104,6 +104,13 @@ func Init(console bool, verbose bool, publicPath string, service service.REST, e s.GET(":id", h.GetScheduler) 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 cc := apiv1.Group("/cdn-clusters") cc.POST("", h.CreateCDNCluster) diff --git a/manager/service/oauth.go b/manager/service/oauth.go index 474391bfc..6c172ee4c 100644 --- a/manager/service/oauth.go +++ b/manager/service/oauth.go @@ -1,25 +1,47 @@ package service import ( + "d7y.io/dragonfly/v2/manager/auth/oauth" "d7y.io/dragonfly/v2/manager/model" - "d7y.io/dragonfly/v2/manager/oauth" "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) { - oauth := model.Oauth{ - ClientID: json.ClientID, - ClientSecret: json.ClientSecret, - Name: json.Name, - AuthURL: json.AuthURL, - TokenURL: json.TokenURL, + 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, + ClientSecret: json.ClientSecret, + Name: json.Name, + Scopes: json.Scopes, + AuthURL: json.AuthURL, + 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 &oauth, nil + return &o, nil } func (s *rest) DestroyOauth(id uint) error { @@ -53,52 +75,40 @@ func (s *rest) GetOauth(id uint) (*model.Oauth, error) { return &oauth, nil } -func (s *rest) GetOauths(q types.GetOauthsQuery) (*[]model.Oauth, error) { +func (s *rest) GetOauths() (*[]model.Oauth, error) { oauths := []model.Oauth{} - if q.Name != "" { - 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 - } - - } else { - if err := s.db.Scopes(model.Paginate(q.Page, q.PerPage)).Find(&oauths).Error; err != nil { - return nil, err - } + if err := s.db.Find(&oauths).Error; err != nil { + return nil, err } 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) { oauthModel := model.Oauth{} if err := s.db.First(&oauthModel, name).Error; err != nil { return "", err } - o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) - if err != nil { - return "", err + 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 { + 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 } @@ -107,11 +117,26 @@ func (s *rest) OauthCallback(name, code string) (*model.User, error) { if err := s.db.First(&oauthModel, name).Error; err != nil { return nil, err } - - o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) - if err != nil { - return nil, err + 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 { + 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) if err != nil { return nil, err diff --git a/manager/service/service.go b/manager/service/service.go index 27966c47b..33e906a27 100644 --- a/manager/service/service.go +++ b/manager/service/service.go @@ -81,6 +81,12 @@ type REST interface { GetSchedulers(types.GetSchedulersQuery) (*[]model.Scheduler, 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) DestroySecurityGroup(uint) error UpdateSecurityGroup(uint, types.UpdateSecurityGroupRequest) (*model.SecurityGroup, error) @@ -97,8 +103,7 @@ type REST interface { DestroyOauth(uint) error UpdateOauth(uint, types.UpdateOauthRequest) (*model.Oauth, error) GetOauth(uint) (*model.Oauth, error) - GetOauths(types.GetOauthsQuery) (*[]model.Oauth, error) - OauthTotalCount(types.GetOauthsQuery) (int64, error) + GetOauths() (*[]model.Oauth, error) OauthSignin(name string) (string, error) OauthCallback(name, code string) (*model.User, error) } diff --git a/manager/service/settings.go b/manager/service/settings.go new file mode 100644 index 000000000..baa5034dd --- /dev/null +++ b/manager/service/settings.go @@ -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 +} diff --git a/manager/types/oauth.go b/manager/types/oauth.go index 2214affe0..988359481 100644 --- a/manager/types/oauth.go +++ b/manager/types/oauth.go @@ -13,9 +13,10 @@ type OauthBaseRquest struct { ClientID string `json:"client_id" binding:"required"` ClientSecret string `json:"client_secret" binding:"required"` // scope list split by ',' - Scopes string `json:"scopes" binding:"omitempty"` - AuthURL string `json:"auth_url" binding:"omitempty"` - TokenURL string `json:"token_url" binding:"omitempty"` + Scopes string `json:"scopes" binding:"omitempty"` + AuthURL string `json:"auth_url" binding:"omitempty"` + TokenURL string `json:"token_url" binding:"omitempty"` + UserInfoURL string `json:"user_info_url" binding:"omitempty"` } type CreateOauthRequest struct { @@ -25,9 +26,3 @@ type CreateOauthRequest struct { type UpdateOauthRequest struct { 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"` -} diff --git a/manager/types/settings.go b/manager/types/settings.go new file mode 100644 index 000000000..994dae19f --- /dev/null +++ b/manager/types/settings.go @@ -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"` +}