create.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 := &models.Project{
  37. Name: request.Name,
  38. }
  39. proj, err := p.Repo().Project().CreateProject(proj)
  40. if err != nil {
  41. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  42. return
  43. }
  44. err = createDefaultProjectRoles(proj.ID, user.ID, p.Repo())
  45. if err != nil {
  46. // we need to first delete the default project roles we just created
  47. deleteAllProjectRoles(proj.ID, p.Repo())
  48. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  49. return
  50. }
  51. // create onboarding flow set to the first step
  52. _, err = p.Repo().Onboarding().CreateProjectOnboarding(&models.Onboarding{
  53. ProjectID: proj.ID,
  54. CurrentStep: types.StepConnectSource,
  55. })
  56. if err != nil {
  57. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  58. return
  59. }
  60. // create default project usage restriction
  61. _, err = p.Repo().ProjectUsage().CreateProjectUsage(&models.ProjectUsage{
  62. ProjectID: proj.ID,
  63. ResourceCPU: types.BasicPlan.ResourceCPU,
  64. ResourceMemory: types.BasicPlan.ResourceMemory,
  65. Clusters: types.BasicPlan.Clusters,
  66. Users: types.BasicPlan.Users,
  67. })
  68. if err != nil {
  69. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  70. return
  71. }
  72. p.WriteResult(w, r, proj.ToProjectType())
  73. // add project to billing team
  74. _, err = p.Config().BillingManager.CreateTeam(user, proj)
  75. if err != nil {
  76. // we do not write error response, since setting up billing error can be
  77. // resolved later and may not be fatal
  78. p.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  79. }
  80. p.Config().AnalyticsClient.Track(analytics.ProjectCreateTrack(&analytics.ProjectCreateTrackOpts{
  81. ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
  82. }))
  83. }
  84. func createDefaultProjectRoles(projectID, userID uint, repo repository.Repository) error {
  85. for _, kind := range []types.RoleKind{types.RoleAdmin, types.RoleDeveloper, types.RoleViewer} {
  86. uid, err := encryption.GenerateRandomBytes(16)
  87. if err != nil {
  88. return err
  89. }
  90. var policyBytes []byte
  91. switch kind {
  92. case types.RoleAdmin:
  93. policyBytes, err = json.Marshal(types.AdminPolicy)
  94. if err != nil {
  95. return err
  96. }
  97. case types.RoleDeveloper:
  98. policyBytes, err = json.Marshal(types.DeveloperPolicy)
  99. if err != nil {
  100. return err
  101. }
  102. case types.RoleViewer:
  103. policyBytes, err = json.Marshal(types.ViewerPolicy)
  104. if err != nil {
  105. return err
  106. }
  107. }
  108. policy, err := repo.Policy().CreatePolicy(&models.Policy{
  109. UniqueID: uid,
  110. ProjectID: projectID,
  111. CreatedByUserID: userID,
  112. Name: fmt.Sprintf("%s-project-role-policy", kind),
  113. PolicyBytes: policyBytes,
  114. })
  115. if err != nil {
  116. return err
  117. }
  118. role, err := repo.ProjectRole().CreateProjectRole(&models.ProjectRole{
  119. UniqueID: fmt.Sprintf("%d-%s", projectID, kind),
  120. ProjectID: projectID,
  121. PolicyUID: policy.UniqueID,
  122. Name: string(kind),
  123. })
  124. if err != nil {
  125. return err
  126. }
  127. if kind == types.RoleAdmin {
  128. err := repo.ProjectRole().UpdateUsersInProjectRole(projectID, role.UniqueID, []uint{userID})
  129. if err != nil {
  130. return err
  131. }
  132. }
  133. }
  134. return nil
  135. }