From b88776af0cfddd50a9de901a8d0658cb674644b2 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Wed, 8 Dec 2021 22:02:11 -0600 Subject: [PATCH] Cap the number of personal projects per user --- src/templates/src/user_profile.html | 6 +++++- src/website/projects.go | 24 ++++++++++++++++++++++++ src/website/user.go | 13 +++++++++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/templates/src/user_profile.html b/src/templates/src/user_profile.html index 256a8cb..88c24af 100644 --- a/src/templates/src/user_profile.html +++ b/src/templates/src/user_profile.html @@ -45,7 +45,11 @@ {{ end }} {{ if .OwnProfile }} - + New Project + {{ if .CanAddProject }} + + New Project + {{ else }} + You have reached the maximum number of personal projects. + {{ end }} {{ end }} {{ end }} diff --git a/src/website/projects.go b/src/website/projects.go index e8397eb..17d80d3 100644 --- a/src/website/projects.go +++ b/src/website/projects.go @@ -26,6 +26,8 @@ import ( "github.com/jackc/pgx/v4" ) +const maxPersonalProjects = 5 + type ProjectTemplateData struct { templates.BaseData @@ -387,6 +389,17 @@ type ProjectEditData struct { } func ProjectNew(c *RequestContext) ResponseData { + numProjects, err := hmndata.CountProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ + OwnerIDs: []int{c.CurrentUser.ID}, + Types: hmndata.PersonalProjects, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check number of personal projects")) + } + if numProjects >= maxPersonalProjects { + return RejectRequest(c, fmt.Sprintf("You have already reached the maximum of %d personal projects.", maxPersonalProjects)) + } + var project templates.ProjectSettings project.Owners = append(project.Owners, templates.UserToTemplate(c.CurrentUser, c.Theme)) project.Personal = true @@ -417,6 +430,17 @@ func ProjectNewSubmit(c *RequestContext) ResponseData { } defer tx.Rollback(c.Context()) + numProjects, err := hmndata.CountProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ + OwnerIDs: []int{c.CurrentUser.ID}, + Types: hmndata.PersonalProjects, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check number of personal projects")) + } + if numProjects >= maxPersonalProjects { + return RejectRequest(c, fmt.Sprintf("You have already reached the maximum of %d personal projects.", maxPersonalProjects)) + } + var projectId int err = tx.QueryRow(c.Context(), ` diff --git a/src/website/user.go b/src/website/user.go index 16038c3..de68fc6 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -29,7 +29,9 @@ type UserProfileTemplateData struct { TimelineItems []templates.TimelineItem OwnProfile bool ShowcaseUrl string - NewProjectUrl string + + CanAddProject bool + NewProjectUrl string } func UserProfile(c *RequestContext) ResponseData { @@ -108,10 +110,15 @@ func UserProfile(c *RequestContext) ResponseData { projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery) templateProjects := make([]templates.Project, 0, len(projectsAndStuff)) + numPersonalProjects := 0 for _, p := range projectsAndStuff { templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage()) templateProject.AddLogo(p.LogoURL(c.Theme)) templateProjects = append(templateProjects, templateProject) + + if p.Project.Personal { + numPersonalProjects++ + } } c.Perf.EndBlock() @@ -182,7 +189,9 @@ func UserProfile(c *RequestContext) ResponseData { TimelineItems: timelineItems, OwnProfile: (c.CurrentUser != nil && c.CurrentUser.ID == profileUser.ID), ShowcaseUrl: hmnurl.BuildShowcase(), - NewProjectUrl: hmnurl.BuildProjectNew(), + + CanAddProject: numPersonalProjects < maxPersonalProjects, + NewProjectUrl: hmnurl.BuildProjectNew(), }, c.Perf) return res }