create.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package project
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "github.com/porter-dev/porter/api/server/handlers"
  7. "github.com/porter-dev/porter/api/server/shared"
  8. "github.com/porter-dev/porter/api/server/shared/apierrors"
  9. "github.com/porter-dev/porter/api/server/shared/config"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/internal/analytics"
  12. "github.com/porter-dev/porter/internal/encryption"
  13. "github.com/porter-dev/porter/internal/models"
  14. "github.com/porter-dev/porter/internal/repository"
  15. )
  16. type ProjectCreateHandler struct {
  17. handlers.PorterHandlerReadWriter
  18. }
  19. func NewProjectCreateHandler(
  20. config *config.Config,
  21. decoderValidator shared.RequestDecoderValidator,
  22. writer shared.ResultWriter,
  23. ) *ProjectCreateHandler {
  24. return &ProjectCreateHandler{
  25. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  26. }
  27. }
  28. func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  29. request := &types.CreateProjectRequest{}
  30. ok := p.DecodeAndValidate(w, r, request)
  31. if !ok {
  32. return
  33. }
  34. // read the user from context
  35. user, _ := r.Context().Value(types.UserScope).(*models.User)
  36. proj, err := p.Repo().Project().CreateProject(&models.Project{
  37. Name: request.Name,
  38. })
  39. if err != nil {
  40. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  41. return
  42. }
  43. err = createDefaultProjectRoles(proj.ID, user.ID, p.Repo())
  44. if err != nil {
  45. // we need to first delete the default project roles we just created
  46. deleteAllProjectRoles(proj.ID, p.Repo())
  47. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  48. return
  49. }
  50. // create onboarding flow set to the first step
  51. _, err = p.Repo().Onboarding().CreateProjectOnboarding(&models.Onboarding{
  52. ProjectID: proj.ID,
  53. CurrentStep: types.StepConnectSource,
  54. })
  55. if err != nil {
  56. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  57. return
  58. }
  59. // create default project usage restriction
  60. _, err = p.Repo().ProjectUsage().CreateProjectUsage(&models.ProjectUsage{
  61. ProjectID: proj.ID,
  62. ResourceCPU: types.BasicPlan.ResourceCPU,
  63. ResourceMemory: types.BasicPlan.ResourceMemory,
  64. Clusters: types.BasicPlan.Clusters,
  65. Users: types.BasicPlan.Users,
  66. })
  67. if err != nil {
  68. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  69. return
  70. }
  71. p.WriteResult(w, r, proj.ToProjectType())
  72. // add project to billing team
  73. _, err = p.Config().BillingManager.CreateTeam(user, proj)
  74. if err != nil {
  75. // we do not write error response, since setting up billing error can be
  76. // resolved later and may not be fatal
  77. p.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  78. }
  79. p.Config().AnalyticsClient.Track(analytics.ProjectCreateTrack(&analytics.ProjectCreateTrackOpts{
  80. ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
  81. }))
  82. }
  83. func createDefaultProjectRoles(projectID, userID uint, repo repository.Repository) error {
  84. for _, kind := range []types.RoleKind{types.RoleAdmin, types.RoleDeveloper, types.RoleViewer} {
  85. uid, err := encryption.GenerateRandomBytes(16)
  86. if err != nil {
  87. return err
  88. }
  89. var policyBytes []byte
  90. switch kind {
  91. case types.RoleAdmin:
  92. policyBytes, err = json.Marshal(types.AdminPolicy)
  93. if err != nil {
  94. return err
  95. }
  96. case types.RoleDeveloper:
  97. policyBytes, err = json.Marshal(types.DeveloperPolicy)
  98. if err != nil {
  99. return err
  100. }
  101. case types.RoleViewer:
  102. policyBytes, err = json.Marshal(types.ViewerPolicy)
  103. if err != nil {
  104. return err
  105. }
  106. }
  107. policy, err := repo.Policy().CreatePolicy(&models.Policy{
  108. UniqueID: uid,
  109. ProjectID: projectID,
  110. CreatedByUserID: userID,
  111. Name: fmt.Sprintf("%s-project-role-policy", kind),
  112. PolicyBytes: policyBytes,
  113. })
  114. if err != nil {
  115. return err
  116. }
  117. role, err := repo.ProjectRole().CreateProjectRole(&models.ProjectRole{
  118. UniqueID: fmt.Sprintf("%d-%s", projectID, kind),
  119. ProjectID: projectID,
  120. PolicyUID: policy.UniqueID,
  121. Name: string(kind),
  122. })
  123. if err != nil {
  124. return err
  125. }
  126. if kind == types.RoleAdmin {
  127. err := repo.ProjectRole().UpdateUsersInProjectRole(projectID, role.UniqueID, []uint{userID})
  128. if err != nil {
  129. return err
  130. }
  131. }
  132. }
  133. return nil
  134. }