Browse Source

delete team on project deletion, and add /billing endpoint

Alexander Belanger 4 năm trước cách đây
mục cha
commit
5ac2f866e5

+ 14 - 0
api/server/handlers/project/create.go

@@ -51,6 +51,20 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	// create default project usage restriction
+	_, err = p.Repo().ProjectUsage().CreateProjectUsage(&models.ProjectUsage{
+		ProjectID:      proj.ID,
+		ResourceCPU:    types.BasicPlan.ResourceCPU,
+		ResourceMemory: types.BasicPlan.ResourceMemory,
+		Clusters:       types.BasicPlan.Clusters,
+		Users:          types.BasicPlan.Users,
+	})
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
 	p.WriteResult(w, r, proj.ToProjectType())
 
 	// add project to billing team

+ 7 - 0
api/server/handlers/project/delete.go

@@ -35,4 +35,11 @@ func (p *ProjectDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	}
 
 	p.WriteResult(w, r, proj.ToProjectType())
+
+	// delete the billing team
+	if err := p.Config().BillingManager.DeleteTeam(proj); err != nil {
+		// we do not write error response, since setting up billing error can be
+		// resolved later and may not be fatal
+		p.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
+	}
 }

+ 37 - 0
api/server/handlers/project/get_billing.go

@@ -0,0 +1,37 @@
+package project
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type ProjectGetBillingHandler struct {
+	handlers.PorterHandlerWriter
+}
+
+func NewProjectGetBillingHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *ProjectGetBillingHandler {
+	return &ProjectGetBillingHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (p *ProjectGetBillingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	res := &types.GetProjectBillingResponse{}
+
+	// determine if the project has usage attached; if so, set has_billing to true
+	usage, _ := p.Repo().ProjectUsage().ReadProjectUsage(proj.ID)
+
+	res.HasBilling = usage != nil
+
+	p.WriteResult(w, r, res)
+}

+ 27 - 0
api/server/router/project.go

@@ -165,6 +165,33 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/billing -> project.NewProjectGetBillingHandler
+	getBillingEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/billing",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+			},
+		},
+	)
+
+	getBillingHandler := project.NewProjectGetBillingHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getBillingEndpoint,
+		Handler:  getBillingHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/billing/token -> billing.NewBillingGetTokenEndpoint
 	getBillingTokenEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 4 - 0
api/types/project.go

@@ -65,3 +65,7 @@ type DeleteRoleResponse struct {
 type GetBillingTokenResponse struct {
 	Token string `json:"token"`
 }
+
+type GetProjectBillingResponse struct {
+	HasBilling bool `json:"has_billing"`
+}

+ 2 - 2
dashboard/src/main/home/project-settings/ProjectSettings.tsx

@@ -46,7 +46,7 @@ class ProjectSettings extends Component<PropsType, StateType> {
     tabOptions.push({ value: "manage-access", label: "Manage Access" });
 
     if (this.props.isAuthorized("settings", "", ["get", "delete"])) {
-      if (currentProject?.hasBilling) {
+      if (currentProject?.has_billing) {
         tabOptions.push({
           value: "billing",
           label: "Billing",
@@ -73,7 +73,7 @@ class ProjectSettings extends Component<PropsType, StateType> {
 
     if (
       this.state.currentTab === "billing" &&
-      this.context.currentProject?.hasBilling
+      this.context.currentProject?.has_billing
     ) {
       return <BillingPage />;
     }

+ 10 - 0
ee/billing/ironplans.go

@@ -97,6 +97,16 @@ func (c *Client) CreateTeam(proj *cemodels.Project) (string, error) {
 	return resp.ID, err
 }
 
+func (c *Client) DeleteTeam(proj *cemodels.Project) error {
+	projBilling, err := c.repo.ProjectBilling().ReadProjectBillingByProjectID(proj.ID)
+
+	if err != nil {
+		return err
+	}
+
+	return c.deleteRequest(fmt.Sprintf("/teams/v1/%s", projBilling.BillingTeamID), nil, nil)
+}
+
 func (c *Client) GetTeamID(proj *cemodels.Project) (teamID string, err error) {
 	projBilling, err := c.repo.ProjectBilling().ReadProjectBillingByProjectID(proj.ID)
 

+ 7 - 0
internal/billing/billing.go

@@ -13,6 +13,9 @@ type BillingManager interface {
 	// per same team)
 	CreateTeam(proj *models.Project) (teamID string, err error)
 
+	// DeleteTeam deletes a billing team.
+	DeleteTeam(proj *models.Project) (err error)
+
 	// GetTeamID gets the billing team id for a project
 	GetTeamID(proj *models.Project) (teamID string, err error)
 
@@ -46,6 +49,10 @@ func (n *NoopBillingManager) CreateTeam(proj *models.Project) (teamID string, er
 	return fmt.Sprintf("%d", proj.ID), nil
 }
 
+func (n *NoopBillingManager) DeleteTeam(proj *models.Project) (err error) {
+	return nil
+}
+
 func (n *NoopBillingManager) GetTeamID(proj *models.Project) (teamID string, err error) {
 	return fmt.Sprintf("%d", proj.ID), nil
 }