create.go 3.2 KB

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