Move data-fetching helpers to a separate package

This commit is contained in:
Ben Visness 2021-12-08 20:04:15 -06:00
parent 73824a027b
commit 37fcbb205c
18 changed files with 153 additions and 140 deletions

View File

@ -5,9 +5,9 @@ import (
"fmt" "fmt"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/parsing" "git.handmade.network/hmn/hmn/src/parsing"
"git.handmade.network/hmn/hmn/src/website"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -46,7 +46,7 @@ func addCreateProjectCommand(projectCommand *cobra.Command) {
} }
defer tx.Rollback(ctx) defer tx.Rollback(ctx)
p, err := website.FetchProject(ctx, tx, nil, models.HMNProjectID, website.ProjectsQuery{ p, err := hmndata.FetchProject(ctx, tx, nil, models.HMNProjectID, hmndata.ProjectsQuery{
IncludeHidden: true, IncludeHidden: true,
Lifecycles: models.AllProjectLifecycles, Lifecycles: models.AllProjectLifecycles,
}) })
@ -163,7 +163,7 @@ func addProjectTagCommand(projectCommand *cobra.Command) {
conn := db.NewConnPool(1, 1) conn := db.NewConnPool(1, 1)
defer conn.Close() defer conn.Close()
resultTag, err := website.SetProjectTag(ctx, conn, projectID, tag) resultTag, err := hmndata.SetProjectTag(ctx, conn, projectID, tag)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -1,15 +1,15 @@
package website package hmndata
import ( import (
"context" "context"
"fmt" "fmt"
"git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/templates"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/perf"
"git.handmade.network/hmn/hmn/src/templates"
) )
type ProjectTypeQuery int type ProjectTypeQuery int
@ -61,7 +61,7 @@ func FetchProjects(
currentUser *models.User, currentUser *models.User,
q ProjectsQuery, q ProjectsQuery,
) ([]ProjectAndStuff, error) { ) ([]ProjectAndStuff, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch projects") perf.StartBlock("SQL", "Fetch projects")
defer perf.EndBlock() defer perf.EndBlock()
@ -312,7 +312,7 @@ func CountProjects(
currentUser *models.User, currentUser *models.User,
q ProjectsQuery, q ProjectsQuery,
) (int, error) { ) (int, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Count projects") perf.StartBlock("SQL", "Count projects")
defer perf.EndBlock() defer perf.EndBlock()
@ -328,25 +328,6 @@ func CountProjects(
return len(projects), nil return len(projects), nil
} }
func CanEditProject(c *RequestContext, user *models.User, projectId int) (bool, error) {
if user != nil {
if user.IsStaff {
return true, nil
} else {
owners, err := FetchProjectOwners(c.Context(), c.Conn, projectId)
if err != nil {
return false, err
}
for _, owner := range owners {
if owner.ID == user.ID {
return true, nil
}
}
}
}
return false, nil
}
type ProjectOwners struct { type ProjectOwners struct {
ProjectID int ProjectID int
Owners []*models.User Owners []*models.User
@ -366,7 +347,7 @@ func FetchMultipleProjectsOwners(
dbConn db.ConnOrTx, dbConn db.ConnOrTx,
projectIds []int, projectIds []int,
) ([]ProjectOwners, error) { ) ([]ProjectOwners, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch owners for multiple projects") perf.StartBlock("SQL", "Fetch owners for multiple projects")
defer perf.EndBlock() defer perf.EndBlock()
@ -470,7 +451,7 @@ func FetchProjectOwners(
dbConn db.ConnOrTx, dbConn db.ConnOrTx,
projectId int, projectId int,
) ([]*models.User, error) { ) ([]*models.User, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch owners for project") perf.StartBlock("SQL", "Fetch owners for project")
defer perf.EndBlock() defer perf.EndBlock()

View File

@ -1,12 +1,12 @@
package website package hmndata
import ( import (
"context" "context"
"git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/perf"
) )
type SnippetQuery struct { type SnippetQuery struct {
@ -31,7 +31,7 @@ func FetchSnippets(
currentUser *models.User, currentUser *models.User,
q SnippetQuery, q SnippetQuery,
) ([]SnippetAndStuff, error) { ) ([]SnippetAndStuff, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch snippets") perf.StartBlock("SQL", "Fetch snippets")
defer perf.EndBlock() defer perf.EndBlock()

View File

@ -1,4 +1,4 @@
package website package hmndata
import ( import (
"context" "context"
@ -6,6 +6,7 @@ import (
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/perf"
) )
type TagQuery struct { type TagQuery struct {
@ -16,7 +17,7 @@ type TagQuery struct {
} }
func FetchTags(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) ([]*models.Tag, error) { func FetchTags(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) ([]*models.Tag, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch snippets") perf.StartBlock("SQL", "Fetch snippets")
defer perf.EndBlock() defer perf.EndBlock()

View File

@ -1,4 +1,4 @@
package website package hmndata
import ( import (
"context" "context"
@ -14,6 +14,7 @@ import (
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/parsing" "git.handmade.network/hmn/hmn/src/parsing"
"git.handmade.network/hmn/hmn/src/perf"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4"
) )
@ -54,7 +55,7 @@ func FetchThreads(
currentUser *models.User, currentUser *models.User,
q ThreadsQuery, q ThreadsQuery,
) ([]ThreadAndStuff, error) { ) ([]ThreadAndStuff, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch threads") perf.StartBlock("SQL", "Fetch threads")
defer perf.EndBlock() defer perf.EndBlock()
@ -197,7 +198,7 @@ func CountThreads(
currentUser *models.User, currentUser *models.User,
q ThreadsQuery, q ThreadsQuery,
) (int, error) { ) (int, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Count threads") perf.StartBlock("SQL", "Count threads")
defer perf.EndBlock() defer perf.EndBlock()
@ -299,7 +300,7 @@ func FetchPosts(
currentUser *models.User, currentUser *models.User,
q PostsQuery, q PostsQuery,
) ([]PostAndStuff, error) { ) ([]PostAndStuff, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Fetch posts") perf.StartBlock("SQL", "Fetch posts")
defer perf.EndBlock() defer perf.EndBlock()
@ -520,7 +521,7 @@ func CountPosts(
currentUser *models.User, currentUser *models.User,
q PostsQuery, q PostsQuery,
) (int, error) { ) (int, error) {
perf := ExtractPerf(ctx) perf := perf.ExtractPerf(ctx)
perf.StartBlock("SQL", "Count posts") perf.StartBlock("SQL", "Count posts")
defer perf.EndBlock() defer perf.EndBlock()

View File

@ -167,3 +167,13 @@ func LogPerf(perf *RequestPerf, log *zerolog.Event) {
} }
log.Msg(fmt.Sprintf("Served [%s] %s in %.4fms", perf.Method, perf.Path, float64(perf.End.Sub(perf.Start).Nanoseconds())/1000/1000)) log.Msg(fmt.Sprintf("Served [%s] %s in %.4fms", perf.Method, perf.Path, float64(perf.End.Sub(perf.Start).Nanoseconds())/1000/1000))
} }
const PerfContextKey = "HMNPerf"
func ExtractPerf(ctx context.Context) *RequestPerf {
iperf := ctx.Value(PerfContextKey)
if iperf == nil {
return nil
}
return iperf.(*RequestPerf)
}

View File

@ -12,6 +12,7 @@ import (
"git.handmade.network/hmn/hmn/src/auth" "git.handmade.network/hmn/hmn/src/auth"
"git.handmade.network/hmn/hmn/src/config" "git.handmade.network/hmn/hmn/src/config"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
@ -137,7 +138,7 @@ func AdminApprovalQueue(c *RequestContext) ResponseData {
for _, p := range posts { for _, p := range posts {
post := templates.PostToTemplate(&p.Post, &p.Author, c.Theme) post := templates.PostToTemplate(&p.Post, &p.Author, c.Theme)
post.AddContentVersion(p.CurrentVersion, &p.Author) // NOTE(asaf): Don't care about editors here post.AddContentVersion(p.CurrentVersion, &p.Author) // NOTE(asaf): Don't care about editors here
post.Url = UrlForGenericPost(UrlContextForProject(&p.Project), &p.Thread, &p.Post, lineageBuilder) post.Url = UrlForGenericPost(hmndata.UrlContextForProject(&p.Project), &p.Thread, &p.Post, lineageBuilder)
data.Posts = append(data.Posts, postWithTitle{ data.Posts = append(data.Posts, postWithTitle{
Post: post, Post: post,
Title: p.Thread.Title, Title: p.Thread.Title,
@ -282,7 +283,7 @@ func deleteAllPostsForUser(ctx context.Context, conn *pgxpool.Pool, userId int)
for _, iResult := range it.ToSlice() { for _, iResult := range it.ToSlice() {
row := iResult.(*toDelete) row := iResult.(*toDelete)
DeletePost(ctx, tx, row.ThreadID, row.PostID) hmndata.DeletePost(ctx, tx, row.ThreadID, row.PostID)
} }
err = tx.Commit(ctx) err = tx.Commit(ctx)
if err != nil { if err != nil {

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
@ -35,7 +36,7 @@ func BlogIndex(c *RequestContext) ResponseData {
const postsPerPage = 5 const postsPerPage = 5
numThreads, err := CountThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ numThreads, err := hmndata.CountThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -49,7 +50,7 @@ func BlogIndex(c *RequestContext) ResponseData {
c.Redirect(c.UrlContext.BuildBlog(page), http.StatusSeeOther) c.Redirect(c.UrlContext.BuildBlog(page), http.StatusSeeOther)
} }
threads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ threads, err := hmndata.FetchThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
Limit: postsPerPage, Limit: postsPerPage,
@ -75,7 +76,7 @@ func BlogIndex(c *RequestContext) ResponseData {
canCreate := false canCreate := false
if c.CurrentProject.HasBlog() && c.CurrentUser != nil { if c.CurrentProject.HasBlog() && c.CurrentUser != nil {
isProjectOwner := false isProjectOwner := false
owners, err := FetchProjectOwners(c.Context(), c.Conn, c.CurrentProject.ID) owners, err := hmndata.FetchProjectOwners(c.Context(), c.Conn, c.CurrentProject.ID)
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project owners")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project owners"))
} }
@ -124,7 +125,7 @@ func BlogThread(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
thread, posts, err := FetchThreadPosts(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, PostsQuery{ thread, posts, err := hmndata.FetchThreadPosts(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -192,7 +193,7 @@ func BlogPostRedirectToThread(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
thread, err := FetchThread(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, ThreadsQuery{ thread, err := hmndata.FetchThread(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -261,7 +262,7 @@ func BlogNewThreadSubmit(c *RequestContext) ResponseData {
} }
// Create everything else // Create everything else
CreateNewPost(c.Context(), tx, c.CurrentProject.ID, threadId, models.ThreadTypeProjectBlogPost, c.CurrentUser.ID, nil, unparsed, c.Req.Host) hmndata.CreateNewPost(c.Context(), tx, c.CurrentProject.ID, threadId, models.ThreadTypeProjectBlogPost, c.CurrentUser.ID, nil, unparsed, c.Req.Host)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -278,11 +279,11 @@ func BlogPostEdit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -322,7 +323,7 @@ func BlogPostEditSubmit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
@ -332,7 +333,7 @@ func BlogPostEditSubmit(c *RequestContext) ResponseData {
} }
defer tx.Rollback(c.Context()) defer tx.Rollback(c.Context())
post, err := FetchThreadPost(c.Context(), tx, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), tx, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -353,7 +354,7 @@ func BlogPostEditSubmit(c *RequestContext) ResponseData {
return RejectRequest(c, "You must provide a post body.") return RejectRequest(c, "You must provide a post body.")
} }
CreatePostVersion(c.Context(), tx, post.Post.ID, unparsed, c.Req.Host, editReason, &c.CurrentUser.ID) hmndata.CreatePostVersion(c.Context(), tx, post.Post.ID, unparsed, c.Req.Host, editReason, &c.CurrentUser.ID)
if title != "" { if title != "" {
_, err := tx.Exec(c.Context(), _, err := tx.Exec(c.Context(),
@ -383,7 +384,7 @@ func BlogPostReply(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -432,7 +433,7 @@ func BlogPostReplySubmit(c *RequestContext) ResponseData {
return RejectRequest(c, "Your reply cannot be empty.") return RejectRequest(c, "Your reply cannot be empty.")
} }
newPostId, _ := CreateNewPost(c.Context(), tx, c.CurrentProject.ID, cd.ThreadID, models.ThreadTypeProjectBlogPost, c.CurrentUser.ID, &cd.PostID, unparsed, c.Req.Host) newPostId, _ := hmndata.CreateNewPost(c.Context(), tx, c.CurrentProject.ID, cd.ThreadID, models.ThreadTypeProjectBlogPost, c.CurrentUser.ID, &cd.PostID, unparsed, c.Req.Host)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -449,11 +450,11 @@ func BlogPostDelete(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })
@ -499,7 +500,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
@ -509,7 +510,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData {
} }
defer tx.Rollback(c.Context()) defer tx.Rollback(c.Context())
threadDeleted := DeletePost(c.Context(), tx, cd.ThreadID, cd.PostID) threadDeleted := hmndata.DeletePost(c.Context(), tx, cd.ThreadID, cd.PostID)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -519,7 +520,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData {
if threadDeleted { if threadDeleted {
return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther) return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther)
} else { } else {
thread, err := FetchThread(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, ThreadsQuery{ thread, err := hmndata.FetchThread(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
}) })

View File

@ -7,13 +7,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/google/uuid" "git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
"git.handmade.network/hmn/hmn/src/utils" "git.handmade.network/hmn/hmn/src/utils"
"github.com/google/uuid"
) )
type FeedData struct { type FeedData struct {
@ -33,7 +33,7 @@ var feedThreadTypes = []models.ThreadType{
} }
func Feed(c *RequestContext) ResponseData { func Feed(c *RequestContext) ResponseData {
numPosts, err := CountPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ numPosts, err := hmndata.CountPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ThreadTypes: feedThreadTypes, ThreadTypes: feedThreadTypes,
}) })
if err != nil { if err != nil {
@ -156,17 +156,17 @@ func AtomFeed(c *RequestContext) ResponseData {
if hasAll { if hasAll {
itemsPerFeed = 100000 itemsPerFeed = 100000
} }
projectsAndStuff, err := FetchProjects(c.Context(), c.Conn, nil, ProjectsQuery{ projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, nil, hmndata.ProjectsQuery{
Lifecycles: models.VisibleProjectLifecycles, Lifecycles: models.VisibleProjectLifecycles,
Limit: itemsPerFeed, Limit: itemsPerFeed,
Types: OfficialProjects, Types: hmndata.OfficialProjects,
OrderBy: "date_approved DESC", OrderBy: "date_approved DESC",
}) })
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects"))
} }
for _, p := range projectsAndStuff { for _, p := range projectsAndStuff {
templateProject := templates.ProjectToTemplate(&p.Project, UrlContextForProject(&p.Project).BuildHomepage()) templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
templateProject.UUID = uuid.NewSHA1(uuid.NameSpaceURL, []byte(templateProject.Url)).URN() templateProject.UUID = uuid.NewSHA1(uuid.NameSpaceURL, []byte(templateProject.Url)).URN()
for _, owner := range p.Owners { for _, owner := range p.Owners {
templateProject.Owners = append(templateProject.Owners, templates.UserToTemplate(owner, "")) templateProject.Owners = append(templateProject.Owners, templates.UserToTemplate(owner, ""))
@ -189,7 +189,7 @@ func AtomFeed(c *RequestContext) ResponseData {
feedData.AtomFeedUrl = hmnurl.BuildAtomFeedForShowcase() feedData.AtomFeedUrl = hmnurl.BuildAtomFeedForShowcase()
feedData.FeedUrl = hmnurl.BuildShowcase() feedData.FeedUrl = hmnurl.BuildShowcase()
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{ snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{
Limit: itemsPerFeed, Limit: itemsPerFeed,
}) })
if err != nil { if err != nil {
@ -216,7 +216,7 @@ func AtomFeed(c *RequestContext) ResponseData {
} }
func fetchAllPosts(c *RequestContext, offset int, limit int) ([]templates.PostListItem, error) { func fetchAllPosts(c *RequestContext, offset int, limit int) ([]templates.PostListItem, error) {
postsAndStuff, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ postsAndStuff, err := hmndata.FetchPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ThreadTypes: feedThreadTypes, ThreadTypes: feedThreadTypes,
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/logging" "git.handmade.network/hmn/hmn/src/logging"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
@ -68,7 +69,7 @@ func getEditorDataForNew(urlContext *hmnurl.UrlContext, currentUser *models.User
return result return result
} }
func getEditorDataForEdit(urlContext *hmnurl.UrlContext, currentUser *models.User, baseData templates.BaseData, p PostAndStuff) editorData { func getEditorDataForEdit(urlContext *hmnurl.UrlContext, currentUser *models.User, baseData templates.BaseData, p hmndata.PostAndStuff) editorData {
return editorData{ return editorData{
BaseData: baseData, BaseData: baseData,
Title: p.Thread.Title, Title: p.Thread.Title,
@ -90,7 +91,7 @@ func Forum(c *RequestContext) ResponseData {
currentSubforumSlugs := cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID) currentSubforumSlugs := cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID)
numThreads, err := CountThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ numThreads, err := hmndata.CountThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
SubforumIDs: []int{cd.SubforumID}, SubforumIDs: []int{cd.SubforumID},
@ -106,7 +107,7 @@ func Forum(c *RequestContext) ResponseData {
} }
howManyThreadsToSkip := (page - 1) * threadsPerPage howManyThreadsToSkip := (page - 1) * threadsPerPage
mainThreads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ mainThreads, err := hmndata.FetchThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
SubforumIDs: []int{cd.SubforumID}, SubforumIDs: []int{cd.SubforumID},
@ -114,7 +115,7 @@ func Forum(c *RequestContext) ResponseData {
Offset: howManyThreadsToSkip, Offset: howManyThreadsToSkip,
}) })
makeThreadListItem := func(row ThreadAndStuff) templates.ThreadListItem { makeThreadListItem := func(row hmndata.ThreadAndStuff) templates.ThreadListItem {
return templates.ThreadListItem{ return templates.ThreadListItem{
Title: row.Thread.Title, Title: row.Thread.Title,
Url: c.UrlContext.BuildForumThread(cd.LineageBuilder.GetSubforumLineageSlugs(*row.Thread.SubforumID), row.Thread.ID, row.Thread.Title, 1), Url: c.UrlContext.BuildForumThread(cd.LineageBuilder.GetSubforumLineageSlugs(*row.Thread.SubforumID), row.Thread.ID, row.Thread.Title, 1),
@ -140,7 +141,7 @@ func Forum(c *RequestContext) ResponseData {
subforumNodes := cd.SubforumTree[cd.SubforumID].Children subforumNodes := cd.SubforumTree[cd.SubforumID].Children
for _, sfNode := range subforumNodes { for _, sfNode := range subforumNodes {
numThreads, err := CountThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ numThreads, err := hmndata.CountThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
SubforumIDs: []int{sfNode.ID}, SubforumIDs: []int{sfNode.ID},
@ -149,7 +150,7 @@ func Forum(c *RequestContext) ResponseData {
panic(oops.New(err, "failed to get count of threads")) panic(oops.New(err, "failed to get count of threads"))
} }
subforumThreads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ subforumThreads, err := hmndata.FetchThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
SubforumIDs: []int{sfNode.ID}, SubforumIDs: []int{sfNode.ID},
@ -333,7 +334,7 @@ func ForumThread(c *RequestContext) ResponseData {
currentSubforumSlugs := cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID) currentSubforumSlugs := cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID)
threads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ threads, err := hmndata.FetchThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadIDs: []int{cd.ThreadID}, ThreadIDs: []int{cd.ThreadID},
}) })
@ -346,7 +347,7 @@ func ForumThread(c *RequestContext) ResponseData {
threadResult := threads[0] threadResult := threads[0]
thread := threadResult.Thread thread := threadResult.Thread
numPosts, err := CountPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ numPosts, err := hmndata.CountPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
ThreadIDs: []int{cd.ThreadID}, ThreadIDs: []int{cd.ThreadID},
@ -369,7 +370,7 @@ func ForumThread(c *RequestContext) ResponseData {
PreviousUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page-1, numPages)), PreviousUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page-1, numPages)),
} }
postsAndStuff, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ postsAndStuff, err := hmndata.FetchPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadIDs: []int{thread.ID}, ThreadIDs: []int{thread.ID},
Limit: threadPostsPerPage, Limit: threadPostsPerPage,
@ -440,7 +441,7 @@ func ForumPostRedirect(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
posts, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ posts, err := hmndata.FetchPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
ThreadIDs: []int{cd.ThreadID}, ThreadIDs: []int{cd.ThreadID},
@ -449,7 +450,7 @@ func ForumPostRedirect(c *RequestContext) ResponseData {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch posts for redirect")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch posts for redirect"))
} }
var post PostAndStuff var post hmndata.PostAndStuff
postIdx := -1 postIdx := -1
for i, p := range posts { for i, p := range posts {
if p.Post.ID == cd.PostID { if p.Post.ID == cd.PostID {
@ -539,7 +540,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
} }
// Create everything else // Create everything else
CreateNewPost(c.Context(), tx, c.CurrentProject.ID, threadId, models.ThreadTypeForumPost, c.CurrentUser.ID, nil, unparsed, c.Req.Host) hmndata.CreateNewPost(c.Context(), tx, c.CurrentProject.ID, threadId, models.ThreadTypeForumPost, c.CurrentUser.ID, nil, unparsed, c.Req.Host)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -556,7 +557,7 @@ func ForumPostReply(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
}) })
@ -605,7 +606,7 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData {
return RejectRequest(c, "Your reply cannot be empty.") return RejectRequest(c, "Your reply cannot be empty.")
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
}) })
@ -619,7 +620,7 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData {
replyPostId = &cd.PostID replyPostId = &cd.PostID
} }
newPostId, _ := CreateNewPost(c.Context(), tx, c.CurrentProject.ID, cd.ThreadID, models.ThreadTypeForumPost, c.CurrentUser.ID, replyPostId, unparsed, c.Req.Host) newPostId, _ := hmndata.CreateNewPost(c.Context(), tx, c.CurrentProject.ID, cd.ThreadID, models.ThreadTypeForumPost, c.CurrentUser.ID, replyPostId, unparsed, c.Req.Host)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -636,11 +637,11 @@ func ForumPostEdit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
}) })
@ -673,7 +674,7 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
@ -683,7 +684,7 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData {
} }
defer tx.Rollback(c.Context()) defer tx.Rollback(c.Context())
post, err := FetchThreadPost(c.Context(), tx, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), tx, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
}) })
@ -704,7 +705,7 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData {
return RejectRequest(c, "You must provide a body for your post.") return RejectRequest(c, "You must provide a body for your post.")
} }
CreatePostVersion(c.Context(), tx, cd.PostID, unparsed, c.Req.Host, editReason, &c.CurrentUser.ID) hmndata.CreatePostVersion(c.Context(), tx, cd.PostID, unparsed, c.Req.Host, editReason, &c.CurrentUser.ID)
if title != "" { if title != "" {
_, err := tx.Exec(c.Context(), _, err := tx.Exec(c.Context(),
@ -734,11 +735,11 @@ func ForumPostDelete(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
post, err := FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, PostsQuery{ post, err := hmndata.FetchThreadPost(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, cd.PostID, hmndata.PostsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeForumPost},
}) })
@ -778,7 +779,7 @@ func ForumPostDeleteSubmit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
if !UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) { if !hmndata.UserCanEditPost(c.Context(), c.Conn, *c.CurrentUser, cd.PostID) {
return FourOhFour(c) return FourOhFour(c)
} }
@ -788,7 +789,7 @@ func ForumPostDeleteSubmit(c *RequestContext) ResponseData {
} }
defer tx.Rollback(c.Context()) defer tx.Rollback(c.Context())
threadDeleted := DeletePost(c.Context(), tx, cd.ThreadID, cd.PostID) threadDeleted := hmndata.DeletePost(c.Context(), tx, cd.ThreadID, cd.PostID)
err = tx.Commit(c.Context()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
@ -811,7 +812,7 @@ func WikiArticleRedirect(c *RequestContext) ResponseData {
panic(err) panic(err)
} }
thread, err := FetchThread(c.Context(), c.Conn, c.CurrentUser, threadId, ThreadsQuery{ thread, err := hmndata.FetchThread(c.Context(), c.Conn, c.CurrentUser, threadId, hmndata.ThreadsQuery{
ProjectIDs: []int{c.CurrentProject.ID}, ProjectIDs: []int{c.CurrentProject.ID},
// This is the rare query where we want all thread types! // This is the rare query where we want all thread types!
}) })

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
@ -19,7 +20,7 @@ func JamIndex(c *RequestContext) ResponseData {
} }
tagId := -1 tagId := -1
jamTag, err := FetchTag(c.Context(), c.Conn, TagQuery{ jamTag, err := hmndata.FetchTag(c.Context(), c.Conn, hmndata.TagQuery{
Text: []string{"wheeljam"}, Text: []string{"wheeljam"},
}) })
if err == nil { if err == nil {
@ -28,7 +29,7 @@ func JamIndex(c *RequestContext) ResponseData {
c.Logger.Warn().Err(err).Msg("failed to fetch jam tag; will fetch all snippets as a result") c.Logger.Warn().Err(err).Msg("failed to fetch jam tag; will fetch all snippets as a result")
} }
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{ snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{
Tags: []int{tagId}, Tags: []int{tagId},
}) })
if err != nil { if err != nil {

View File

@ -5,6 +5,7 @@ import (
"math" "math"
"net/http" "net/http"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
@ -39,7 +40,7 @@ func Index(c *RequestContext) ResponseData {
var timelineItems []templates.TimelineItem var timelineItems []templates.TimelineItem
numPosts, err := CountPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ numPosts, err := hmndata.CountPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ThreadTypes: feedThreadTypes, ThreadTypes: feedThreadTypes,
}) })
if err != nil { if err != nil {
@ -64,7 +65,7 @@ func Index(c *RequestContext) ResponseData {
} }
// This is essentially an alternate for feed page 1. // This is essentially an alternate for feed page 1.
posts, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ posts, err := hmndata.FetchPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
ThreadTypes: feedThreadTypes, ThreadTypes: feedThreadTypes,
Limit: feedPostsPerPage, Limit: feedPostsPerPage,
SortDescending: true, SortDescending: true,
@ -73,7 +74,7 @@ func Index(c *RequestContext) ResponseData {
c.Logger.Warn().Err(err).Msg("failed to fetch latest posts") c.Logger.Warn().Err(err).Msg("failed to fetch latest posts")
} }
for _, p := range posts { for _, p := range posts {
item := PostToTimelineItem(UrlContextForProject(&p.Project), lineageBuilder, &p.Post, &p.Thread, p.Author, c.Theme) item := PostToTimelineItem(hmndata.UrlContextForProject(&p.Project), lineageBuilder, &p.Post, &p.Thread, p.Author, c.Theme)
if p.Thread.Type == models.ThreadTypeProjectBlogPost && p.Post.ID == p.Thread.FirstID { if p.Thread.Type == models.ThreadTypeProjectBlogPost && p.Post.ID == p.Thread.FirstID {
// blog post // blog post
item.Description = template.HTML(p.CurrentVersion.TextParsed) item.Description = template.HTML(p.CurrentVersion.TextParsed)
@ -83,7 +84,7 @@ func Index(c *RequestContext) ResponseData {
} }
c.Perf.StartBlock("SQL", "Get news") c.Perf.StartBlock("SQL", "Get news")
newsThreads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ newsThreads, err := hmndata.FetchThreads(c.Context(), c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{models.HMNProjectID}, ProjectIDs: []int{models.HMNProjectID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
Limit: 1, Limit: 1,
@ -94,7 +95,7 @@ func Index(c *RequestContext) ResponseData {
var newsPostItem *templates.TimelineItem var newsPostItem *templates.TimelineItem
if len(newsThreads) > 0 { if len(newsThreads) > 0 {
t := newsThreads[0] t := newsThreads[0]
item := PostToTimelineItem(UrlContextForProject(&t.Project), lineageBuilder, &t.FirstPost, &t.Thread, t.FirstPostAuthor, c.Theme) item := PostToTimelineItem(hmndata.UrlContextForProject(&t.Project), lineageBuilder, &t.FirstPost, &t.Thread, t.FirstPostAuthor, c.Theme)
item.OwnerAvatarUrl = "" item.OwnerAvatarUrl = ""
item.Breadcrumbs = nil item.Breadcrumbs = nil
item.TypeTitle = "" item.TypeTitle = ""
@ -105,7 +106,7 @@ func Index(c *RequestContext) ResponseData {
} }
c.Perf.EndBlock() c.Perf.EndBlock()
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{ snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{
Limit: 40, Limit: 40,
}) })
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
package website package website
import ( import (
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
@ -88,7 +89,7 @@ func MakePostListItem(
) templates.PostListItem { ) templates.PostListItem {
var result templates.PostListItem var result templates.PostListItem
urlContext := UrlContextForProject(project) urlContext := hmndata.UrlContextForProject(project)
result.Title = thread.Title result.Title = thread.Title
result.User = templates.UserToTemplate(user, currentTheme) result.User = templates.UserToTemplate(user, currentTheme)

View File

@ -15,6 +15,7 @@ import (
"git.handmade.network/hmn/hmn/src/assets" "git.handmade.network/hmn/hmn/src/assets"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
@ -42,8 +43,8 @@ func ProjectIndex(c *RequestContext) ResponseData {
const maxCarouselProjects = 10 const maxCarouselProjects = 10
const maxPersonalProjects = 10 const maxPersonalProjects = 10
officialProjects, err := FetchProjects(c.Context(), c.Conn, c.CurrentUser, ProjectsQuery{ officialProjects, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
Types: OfficialProjects, Types: hmndata.OfficialProjects,
}) })
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects"))
@ -72,7 +73,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
var restProjects []templates.Project var restProjects []templates.Project
now := time.Now() now := time.Now()
for _, p := range officialProjects { for _, p := range officialProjects {
templateProject := templates.ProjectToTemplate(&p.Project, UrlContextForProject(&p.Project).BuildHomepage()) templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
templateProject.AddLogo(p.LogoURL(c.Theme)) templateProject.AddLogo(p.LogoURL(c.Theme))
if p.Project.Slug == "hero" { if p.Project.Slug == "hero" {
@ -118,8 +119,8 @@ func ProjectIndex(c *RequestContext) ResponseData {
// Fetch and highlight a random selection of personal projects // Fetch and highlight a random selection of personal projects
var personalProjects []templates.Project var personalProjects []templates.Project
{ {
projects, err := FetchProjects(c.Context(), c.Conn, c.CurrentUser, ProjectsQuery{ projects, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
Types: PersonalProjects, Types: hmndata.PersonalProjects,
}) })
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch personal projects")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch personal projects"))
@ -135,7 +136,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
if i >= maxPersonalProjects { if i >= maxPersonalProjects {
break break
} }
templateProject := templates.ProjectToTemplate(&p.Project, UrlContextForProject(&p.Project).BuildHomepage()) templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
templateProject.AddLogo(p.LogoURL(c.Theme)) templateProject.AddLogo(p.LogoURL(c.Theme))
personalProjects = append(personalProjects, templateProject) personalProjects = append(personalProjects, templateProject)
} }
@ -177,7 +178,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
// There are no further permission checks to do, because permissions are // There are no further permission checks to do, because permissions are
// checked whatever way we fetch the project. // checked whatever way we fetch the project.
owners, err := FetchProjectOwners(c.Context(), c.Conn, c.CurrentProject.ID) owners, err := hmndata.FetchProjectOwners(c.Context(), c.Conn, c.CurrentProject.ID)
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, err) return c.ErrorResponse(http.StatusInternalServerError, err)
} }
@ -261,7 +262,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
Value: c.CurrentProject.Blurb, Value: c.CurrentProject.Blurb,
}) })
p, err := FetchProject(c.Context(), c.Conn, c.CurrentUser, c.CurrentProject.ID, ProjectsQuery{}) p, err := hmndata.FetchProject(c.Context(), c.Conn, c.CurrentUser, c.CurrentProject.ID, hmndata.ProjectsQuery{})
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project details")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project details"))
} }
@ -340,7 +341,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
tagId = *c.CurrentProject.TagID tagId = *c.CurrentProject.TagID
} }
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{ snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{
Tags: []int{tagId}, Tags: []int{tagId},
}) })
if err != nil { if err != nil {
@ -459,14 +460,14 @@ func ProjectEdit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
p, err := FetchProject( p, err := hmndata.FetchProject(
c.Context(), c.Conn, c.Context(), c.Conn,
c.CurrentUser, c.CurrentProject.ID, c.CurrentUser, c.CurrentProject.ID,
ProjectsQuery{ hmndata.ProjectsQuery{
IncludeHidden: true, IncludeHidden: true,
}, },
) )
owners, err := FetchProjectOwners(c.Context(), c.Conn, p.Project.ID) owners, err := hmndata.FetchProjectOwners(c.Context(), c.Conn, p.Project.ID)
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch project owners")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch project owners"))
} }
@ -709,7 +710,7 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P
return oops.New(err, "Failed to update project") return oops.New(err, "Failed to update project")
} }
SetProjectTag(ctx, tx, payload.ProjectID, payload.Tag) hmndata.SetProjectTag(ctx, tx, payload.ProjectID, payload.Tag)
if user.IsStaff { if user.IsStaff {
_, err = tx.Exec(ctx, _, err = tx.Exec(ctx,
@ -855,3 +856,22 @@ func GetFormImage(c *RequestContext, fieldName string) (FormImage, error) {
return res, nil return res, nil
} }
func CanEditProject(c *RequestContext, user *models.User, projectId int) (bool, error) {
if user != nil {
if user.IsStaff {
return true, nil
} else {
owners, err := hmndata.FetchProjectOwners(c.Context(), c.Conn, projectId)
if err != nil {
return false, err
}
for _, owner := range owners {
if owner.ID == user.ID {
return true, nil
}
}
}
}
return false, nil
}

View File

@ -17,6 +17,7 @@ import (
"git.handmade.network/hmn/hmn/src/config" "git.handmade.network/hmn/hmn/src/config"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/email" "git.handmade.network/hmn/hmn/src/email"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/logging" "git.handmade.network/hmn/hmn/src/logging"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
@ -299,7 +300,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool) ht
if err != nil { if err != nil {
panic(oops.New(err, "project id was not numeric (bad regex in routing)")) panic(oops.New(err, "project id was not numeric (bad regex in routing)"))
} }
p, err := FetchProject(c.Context(), c.Conn, c.CurrentUser, id, ProjectsQuery{}) p, err := hmndata.FetchProject(c.Context(), c.Conn, c.CurrentUser, id, hmndata.ProjectsQuery{})
if err != nil { if err != nil {
if errors.Is(err, db.NotFound) { if errors.Is(err, db.NotFound) {
return FourOhFour(c) return FourOhFour(c)
@ -309,7 +310,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool) ht
} }
c.CurrentProject = &p.Project c.CurrentProject = &p.Project
c.UrlContext = UrlContextForProject(c.CurrentProject) c.UrlContext = hmndata.UrlContextForProject(c.CurrentProject)
if !p.Project.Personal { if !p.Project.Personal {
return c.Redirect(c.UrlContext.RewriteProjectUrl(c.URL()), http.StatusSeeOther) return c.Redirect(c.UrlContext.RewriteProjectUrl(c.URL()), http.StatusSeeOther)
@ -463,7 +464,7 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
var owners []*models.User var owners []*models.User
if len(slug) > 0 { if len(slug) > 0 {
dbProject, err := FetchProjectBySlug(c.Context(), c.Conn, c.CurrentUser, slug, ProjectsQuery{}) dbProject, err := hmndata.FetchProjectBySlug(c.Context(), c.Conn, c.CurrentUser, slug, hmndata.ProjectsQuery{})
if err == nil { if err == nil {
c.CurrentProject = &dbProject.Project c.CurrentProject = &dbProject.Project
owners = dbProject.Owners owners = dbProject.Owners
@ -477,7 +478,7 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
} }
if c.CurrentProject == nil { if c.CurrentProject == nil {
dbProject, err := FetchProject(c.Context(), c.Conn, c.CurrentUser, models.HMNProjectID, ProjectsQuery{ dbProject, err := hmndata.FetchProject(c.Context(), c.Conn, c.CurrentUser, models.HMNProjectID, hmndata.ProjectsQuery{
IncludeHidden: true, IncludeHidden: true,
}) })
if err != nil { if err != nil {
@ -505,7 +506,7 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
} }
c.CurrentUserCanEditCurrentProject = canEditProject c.CurrentUserCanEditCurrentProject = canEditProject
c.UrlContext = UrlContextForProject(c.CurrentProject) c.UrlContext = hmndata.UrlContextForProject(c.CurrentProject)
} }
c.Theme = "light" c.Theme = "light"
@ -560,11 +561,9 @@ func getCurrentUserAndSession(c *RequestContext, sessionId string) (*models.User
return user, session, nil return user, session, nil
} }
const PerfContextKey = "HMNPerf"
func TrackRequestPerf(c *RequestContext) (after func()) { func TrackRequestPerf(c *RequestContext) (after func()) {
c.Perf = perf.MakeNewRequestPerf(c.Route, c.Req.Method, c.Req.URL.Path) c.Perf = perf.MakeNewRequestPerf(c.Route, c.Req.Method, c.Req.URL.Path)
c.ctx = context.WithValue(c.Context(), PerfContextKey, c.Perf) c.ctx = context.WithValue(c.Context(), perf.PerfContextKey, c.Perf)
return func() { return func() {
c.Perf.EndRequest() c.Perf.EndRequest()
@ -582,14 +581,6 @@ func TrackRequestPerf(c *RequestContext) (after func()) {
} }
} }
func ExtractPerf(ctx context.Context) *perf.RequestPerf {
iperf := ctx.Value(PerfContextKey)
if iperf == nil {
return nil
}
return iperf.(*perf.RequestPerf)
}
func LogContextErrors(c *RequestContext, errs ...error) { func LogContextErrors(c *RequestContext, errs ...error) {
for _, err := range errs { for _, err := range errs {
c.Logger.Error().Timestamp().Stack().Str("Requested", c.FullUrl()).Err(err).Msg("error occurred during request") c.Logger.Error().Timestamp().Stack().Str("Requested", c.FullUrl()).Err(err).Msg("error occurred during request")

View File

@ -3,6 +3,7 @@ package website
import ( import (
"net/http" "net/http"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
@ -15,7 +16,7 @@ type ShowcaseData struct {
} }
func Showcase(c *RequestContext) ResponseData { func Showcase(c *RequestContext) ResponseData {
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{}) snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{})
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
} }

View File

@ -7,6 +7,7 @@ import (
"strconv" "strconv"
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
) )
@ -29,7 +30,7 @@ func Snippet(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
s, err := FetchSnippet(c.Context(), c.Conn, c.CurrentUser, snippetId, SnippetQuery{}) s, err := hmndata.FetchSnippet(c.Context(), c.Conn, c.CurrentUser, snippetId, hmndata.SnippetQuery{})
if err != nil { if err != nil {
if errors.Is(err, db.NotFound) { if errors.Is(err, db.NotFound) {
return FourOhFour(c) return FourOhFour(c)

View File

@ -13,6 +13,7 @@ import (
"git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/discord" "git.handmade.network/hmn/hmn/src/discord"
hmnemail "git.handmade.network/hmn/hmn/src/email" hmnemail "git.handmade.network/hmn/hmn/src/email"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
@ -99,29 +100,29 @@ func UserProfile(c *RequestContext) ResponseData {
} }
c.Perf.EndBlock() c.Perf.EndBlock()
projectsQuery := ProjectsQuery{ projectsQuery := hmndata.ProjectsQuery{
OwnerIDs: []int{profileUser.ID}, OwnerIDs: []int{profileUser.ID},
Lifecycles: models.VisibleProjectLifecycles, Lifecycles: models.VisibleProjectLifecycles,
OrderBy: "all_last_updated DESC", OrderBy: "all_last_updated DESC",
} }
projectsAndStuff, err := FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery) projectsAndStuff, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, projectsQuery)
templateProjects := make([]templates.Project, 0, len(projectsAndStuff)) templateProjects := make([]templates.Project, 0, len(projectsAndStuff))
for _, p := range projectsAndStuff { for _, p := range projectsAndStuff {
templateProject := templates.ProjectToTemplate(&p.Project, UrlContextForProject(&p.Project).BuildHomepage()) templateProject := templates.ProjectToTemplate(&p.Project, hmndata.UrlContextForProject(&p.Project).BuildHomepage())
templateProject.AddLogo(p.LogoURL(c.Theme)) templateProject.AddLogo(p.LogoURL(c.Theme))
templateProjects = append(templateProjects, templateProject) templateProjects = append(templateProjects, templateProject)
} }
c.Perf.EndBlock() c.Perf.EndBlock()
c.Perf.StartBlock("SQL", "Fetch posts") c.Perf.StartBlock("SQL", "Fetch posts")
posts, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ posts, err := hmndata.FetchPosts(c.Context(), c.Conn, c.CurrentUser, hmndata.PostsQuery{
UserIDs: []int{profileUser.ID}, UserIDs: []int{profileUser.ID},
SortDescending: true, SortDescending: true,
}) })
c.Perf.EndBlock() c.Perf.EndBlock()
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{ snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{
OwnerIDs: []int{profileUser.ID}, OwnerIDs: []int{profileUser.ID},
}) })
if err != nil { if err != nil {
@ -138,7 +139,7 @@ func UserProfile(c *RequestContext) ResponseData {
for _, post := range posts { for _, post := range posts {
timelineItems = append(timelineItems, PostToTimelineItem( timelineItems = append(timelineItems, PostToTimelineItem(
UrlContextForProject(&post.Project), hmndata.UrlContextForProject(&post.Project),
lineageBuilder, lineageBuilder,
&post.Post, &post.Post,
&post.Thread, &post.Thread,