From 03aaf413b83c65527a2291a6c2a4b1da9e1fa38d Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Thu, 5 Dec 2024 07:01:17 -0600 Subject: [PATCH] Moving the welcome link to a async task. --- accounts/processors.go | 88 ++++++++++++++++++++++++++++++++++++++++++ accounts/userfetch.go | 74 ++++++----------------------------- 2 files changed, 99 insertions(+), 63 deletions(-) diff --git a/accounts/processors.go b/accounts/processors.go index b00e9cf..8aa0fa4 100644 --- a/accounts/processors.go +++ b/accounts/processors.go @@ -2,15 +2,103 @@ package accounts import ( "context" + "fmt" + "links" "links/models" "strings" + "git.sr.ht/~emersion/gqlclient" work "git.sr.ht/~sircmpwn/dowork" + sq "github.com/Masterminds/squirrel" sendy "hg.code.netlandish.com/~netlandish/sendygo" "netlandish.com/x/gobwebs" + "netlandish.com/x/gobwebs/crypto" + "netlandish.com/x/gobwebs/database" "netlandish.com/x/gobwebs/server" + "netlandish.com/x/gobwebs/timezone" ) +func addWelcomeLink(ctx context.Context, user gobwebs.User) error { + opts := &database.FilterOptions{ + Filter: sq.And{ + sq.Eq{"o.owner_id": user.GetID()}, + sq.Eq{"o.org_type": models.OrgTypeUser}, + }, + Limit: 1, + } + orgs, err := models.GetOrganizations(ctx, opts) + if err != nil { + return err + } + if len(orgs) == 0 { + // Should never be reached + return fmt.Errorf("User has no organizations") + } + + type GraphQLResponse struct { + Link models.OrgLink `json:"addLink"` + } + + var result GraphQLResponse + q := `mutation AddLink($title: String!, $url: String!, $description: String, + $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!, + $archive: Boolean! $slug: String!, $tags: String, $override: Boolean!) { + addLink(input: { + title: $title, + url: $url, + description: $description, + visibility: $visibility, + unread: $unread, + starred: $starred, + archive: $archive, + orgSlug: $slug, + tags: $tags, + override: $override}) { + id + } + }` + op := gqlclient.NewOperation(q) + op.Var("title", "Welcome to LinkTaco! Getting Started Guide") + op.Var("url", "https://man.code.netlandish.com/~netlandish/links/getting-started.md") + op.Var("description", "Welcome! This is your getting started guide for LinkTaco. Please click the link to read it and start saving your links right away!") + op.Var("visibility", models.OrgLinkVisibilityPrivate) + op.Var("tags", "help, linktaco, documentation, getting started") + op.Var("unread", true) + op.Var("starred", true) + op.Var("archive", false) + op.Var("slug", orgs[0].Slug) + op.Var("override", true) + + return links.Execute(ctx, op, &result) +} + +func AddWelcomeLinkTask(user gobwebs.User, gctx *server.Context) *work.Task { + return work.NewTask(func(ctx context.Context) error { + // If ever there was a use case for context merge functionality + // https://github.com/golang/go/issues/36503 + ctx = crypto.Context(ctx, crypto.ForContext(gctx.Request().Context())) + ctx = server.ServerContext(ctx, gctx.Server) + ctx = database.Context(ctx, gctx.Server.DB) + ctx = timezone.Context(ctx, "UTC") + return addWelcomeLink(ctx, user) + }).Retries(3).Before(func(ctx context.Context, task *work.Task) { + gobwebs.TaskIDWork(task) + gctx.Server.Logger().Printf( + "Running task AddWelcomeLinkTask %s for the %d attempt.", + task.Metadata["id"], task.Attempts()) + }).After(func(ctx context.Context, task *work.Task) { + if task.Result() == nil { + gctx.Server.Logger().Printf( + "Completed task AddWelcomeLinkTask %s after %d attempts.", + task.Metadata["id"], task.Attempts()) + } else { + gctx.Server.Logger().Printf( + "Failed task AddWelcomeLinkTask %s after %d attempts: %v", + task.Metadata["id"], task.Attempts(), task.Result()) + } + }) +} + func sendSendySubscribe(ctx context.Context, user gobwebs.User, gctx *server.Context) error { u := user.(*models.User) file := gctx.Server.Config.File diff --git a/accounts/userfetch.go b/accounts/userfetch.go index 4e22f54..c368bfd 100644 --- a/accounts/userfetch.go +++ b/accounts/userfetch.go @@ -7,11 +7,8 @@ import ( "links/internal/localizer" "links/models" - "git.sr.ht/~emersion/gqlclient" - sq "github.com/Masterminds/squirrel" "github.com/labstack/echo/v4" "netlandish.com/x/gobwebs" - "netlandish.com/x/gobwebs/database" "netlandish.com/x/gobwebs/messages" "netlandish.com/x/gobwebs/server" ) @@ -165,15 +162,23 @@ func (u *UserFetch) ProcessSuccessfulEmailUpdate(c echo.Context) error { return nil } -// ProcessSuccessfulEmailConfirmation handle tasks after user is logged in +// ProcessSuccessfulEmailConfirmation handle tasks after user confirms their email address func (u *UserFetch) ProcessSuccessfulEmailConfirmation(c echo.Context) error { lt := localizer.GetSessionLocalizer(c) // Sendy users list. Log on error user := c.Get("user").(*models.User) gctx := c.(*server.Context) - err := gctx.Server.QueueTask("general", SendySubscribeTask(user, gctx)) + + // Add welcome link + err := gctx.Server.QueueTask("general", AddWelcomeLinkTask(user, gctx)) if err != nil { - gctx.Server.Logger().Printf("Error queueing sendSendySubscribeTask: %v", err) + gctx.Server.Logger().Printf("Error queueing AddWelcomeLinkTask: %v", err) + } + + // Sendy + err = gctx.Server.QueueTask("general", SendySubscribeTask(user, gctx)) + if err != nil { + gctx.Server.Logger().Printf("Error queueing SendySubscribeTask: %v", err) } messages.Success(c, lt.Translate("You've successfully confirmed your email address.")) return nil @@ -230,63 +235,6 @@ func (u *UserFetch) ProcessSuccessfulLogin(c echo.Context, user gobwebs.User) er lt := localizer.GetSessionLocalizer(c) messages.Success(c, lt.Translate("Successful login.")) - if !lUser.LastLogin.Valid { - // User's first time logging in. Let's set them a welcome link to the getting started doc - opts := &database.FilterOptions{ - Filter: sq.And{ - sq.Eq{"o.owner_id": user.GetID()}, - sq.Eq{"o.org_type": models.OrgTypeUser}, - }, - Limit: 1, - } - orgs, err := models.GetOrganizations(c.Request().Context(), opts) - if err != nil { - return err - } - if len(orgs) == 0 { - // Should never be reached - return fmt.Errorf("User has no organizations") - } - - type GraphQLResponse struct { - Link models.OrgLink `json:"addLink"` - } - - var result GraphQLResponse - q := `mutation AddLink($title: String!, $url: String!, $description: String, - $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!, - $archive: Boolean! $slug: String!, $tags: String, $override: Boolean!) { - addLink(input: { - title: $title, - url: $url, - description: $description, - visibility: $visibility, - unread: $unread, - starred: $starred, - archive: $archive, - orgSlug: $slug, - tags: $tags, - override: $override}) { - id - } - }` - op := gqlclient.NewOperation(q) - op.Var("title", "Welcome to LinkTaco! Getting Started Guide") - op.Var("url", "https://man.code.netlandish.com/~netlandish/links/getting-started.md") - op.Var("description", "Welcome! This is your getting started guide for LinkTaco. Please click the link to read it and start saving your links right away!") - op.Var("visibility", models.OrgLinkVisibilityPrivate) - op.Var("tags", "help, linktaco, documentation, getting started") - op.Var("unread", true) - op.Var("starred", true) - op.Var("archive", false) - op.Var("slug", orgs[0].Slug) - op.Var("override", true) - - err = links.Execute(c.Request().Context(), op, &result) - if err != nil { - return err - } - } return nil } -- 2.45.2