create.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. //go:build ee
  2. // +build ee
  3. package invite
  4. import (
  5. "fmt"
  6. "net/http"
  7. "time"
  8. "github.com/porter-dev/porter/internal/telemetry"
  9. "github.com/porter-dev/porter/api/server/handlers"
  10. "github.com/porter-dev/porter/api/server/shared"
  11. "github.com/porter-dev/porter/api/server/shared/apierrors"
  12. "github.com/porter-dev/porter/api/server/shared/config"
  13. "github.com/porter-dev/porter/api/types"
  14. "github.com/porter-dev/porter/internal/models"
  15. "github.com/porter-dev/porter/internal/notifier"
  16. "github.com/porter-dev/porter/internal/oauth"
  17. )
  18. type InviteCreateHandler struct {
  19. handlers.PorterHandlerReadWriter
  20. }
  21. func NewInviteCreateHandler(
  22. config *config.Config,
  23. decoderValidator shared.RequestDecoderValidator,
  24. writer shared.ResultWriter,
  25. ) http.Handler {
  26. return &InviteCreateHandler{
  27. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  28. }
  29. }
  30. func (c *InviteCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  31. ctx, span := telemetry.NewSpan(r.Context(), "serve-invite-create")
  32. defer span.End()
  33. user, _ := ctx.Value(types.UserScope).(*models.User)
  34. project, _ := ctx.Value(types.ProjectScope).(*models.Project)
  35. request := &types.CreateInviteRequest{}
  36. if ok := c.DecodeAndValidate(w, r, request); !ok {
  37. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "message", Value: "failed to decode and validate request"})
  38. return
  39. }
  40. // create invite model
  41. invite, err := CreateInviteWithProject(request, project.ID)
  42. if err != nil {
  43. c.HandleAPIError(w, r, apierrors.NewErrInternal(telemetry.Error(ctx, span, err, "error creating invite with project")))
  44. return
  45. }
  46. telemetry.WithAttributes(span,
  47. telemetry.AttributeKV{Key: "project-id", Value: invite.ProjectID},
  48. telemetry.AttributeKV{Key: "user-id", Value: invite.UserID},
  49. telemetry.AttributeKV{Key: "kind", Value: invite.Kind},
  50. )
  51. // write to database
  52. invite, err = c.Repo().Invite().CreateInvite(invite)
  53. if err != nil {
  54. c.HandleAPIError(w, r, apierrors.NewErrInternal(telemetry.Error(ctx, span, err, "error creating invite in repo")))
  55. return
  56. }
  57. // app.Logger.Info().Msgf("New invite created: %d", invite.ID)
  58. if err := c.Config().UserNotifier.SendProjectInviteEmail(
  59. &notifier.SendProjectInviteEmailOpts{
  60. InviteeEmail: request.Email,
  61. URL: fmt.Sprintf("%s/api/projects/%d/invites/%s", c.Config().ServerConf.ServerURL, project.ID, invite.Token),
  62. Project: project.Name,
  63. ProjectOwnerEmail: user.Email,
  64. },
  65. ); err != nil {
  66. c.HandleAPIError(w, r, apierrors.NewErrInternal(telemetry.Error(ctx, span, err, "error sending project invite email")))
  67. return
  68. }
  69. res := types.CreateInviteResponse{
  70. Invite: invite.ToInviteType(),
  71. }
  72. c.WriteResult(w, r, res)
  73. }
  74. func CreateInviteWithProject(invite *types.CreateInviteRequest, projectID uint) (*models.Invite, error) {
  75. // generate a token and an expiry time
  76. expiry := time.Now().Add(7 * 24 * time.Hour)
  77. return &models.Invite{
  78. Email: invite.Email,
  79. Kind: invite.Kind,
  80. Expiry: &expiry,
  81. ProjectID: projectID,
  82. Token: oauth.CreateRandomState(),
  83. }, nil
  84. }