From 1c48aab8634615bbbcacae24d5813d7b48b46803 Mon Sep 17 00:00:00 2001 From: Asaf Gartner Date: Mon, 13 Dec 2021 18:58:26 +0200 Subject: [PATCH 1/3] Fixed admin queue --- src/hmndata/threads_and_posts_helper.go | 18 +++++++++++++----- src/website/admin.go | 16 ++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/hmndata/threads_and_posts_helper.go b/src/hmndata/threads_and_posts_helper.go index 8011b86..cbe7b9f 100644 --- a/src/hmndata/threads_and_posts_helper.go +++ b/src/hmndata/threads_and_posts_helper.go @@ -696,20 +696,28 @@ func DeletePost( tx pgx.Tx, threadId, postId int, ) (threadDeleted bool) { - isFirstPost, err := db.QueryBool(ctx, tx, + type threadInfo struct { + FirstPostID int `db:"first_id"` + Deleted bool `db:"deleted"` + } + ti, err := db.QueryOne(ctx, tx, threadInfo{}, ` - SELECT thread.first_id = $1 + SELECT $columns FROM handmade_thread AS thread WHERE - thread.id = $2 + thread.id = $1 `, - postId, threadId, ) if err != nil { - panic(oops.New(err, "failed to check if post was the first post in the thread")) + panic(oops.New(err, "failed to fetch thread info")) } + info := ti.(*threadInfo) + if info.Deleted { + return true + } + isFirstPost := info.FirstPostID == postId if isFirstPost { // Just delete the whole thread and all its posts. diff --git a/src/website/admin.go b/src/website/admin.go index 8ac5af6..0771151 100644 --- a/src/website/admin.go +++ b/src/website/admin.go @@ -172,7 +172,7 @@ func AdminApprovalQueueSubmit(c *RequestContext) ResponseData { if errors.Is(err, db.NotFound) { return RejectRequest(c, "User not found") } else { - return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "failed to fetch user")) + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user")) } } user := u.(*models.User) @@ -189,7 +189,7 @@ func AdminApprovalQueueSubmit(c *RequestContext) ResponseData { user.ID, ) if err != nil { - return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "failed to set user to approved")) + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to set user to approved")) } whatHappened = fmt.Sprintf("%s approved successfully", user.Username) } else if action == ApprovalQueueActionSpammer { @@ -203,13 +203,16 @@ func AdminApprovalQueueSubmit(c *RequestContext) ResponseData { user.ID, ) if err != nil { - return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "failed to set user to banned")) + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to set user to banned")) } err = auth.DeleteSessionForUser(c.Context(), c.Conn, user.Username) if err != nil { - return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "failed to log out user")) + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to log out user")) } err = deleteAllPostsForUser(c.Context(), c.Conn, user.ID) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete spammer's posts")) + } whatHappened = fmt.Sprintf("%s banned successfully", user.Username) } else { whatHappened = fmt.Sprintf("Unrecognized action: %s", action) @@ -240,10 +243,11 @@ func fetchUnapprovedPosts(c *RequestContext) ([]*UnapprovedPost, error) { JOIN auth_user AS author ON author.id = post.author_id WHERE NOT thread.deleted - AND author.status = $1 + AND NOT post.deleted + AND author.status = ANY($1) ORDER BY post.postdate DESC `, - models.UserStatusConfirmed, + []models.UserStatus{models.UserStatusConfirmed}, ) if err != nil { return nil, oops.New(err, "failed to fetch unapproved posts") From c84b6842e2939015f2bf6dfbf787346ad543e636 Mon Sep 17 00:00:00 2001 From: Asaf Gartner Date: Wed, 15 Dec 2021 03:17:42 +0200 Subject: [PATCH 2/3] Admin actions on user profile --- src/hmnurl/hmnurl_test.go | 2 + src/hmnurl/urls.go | 14 +++++ src/templates/mapping.go | 1 + src/templates/src/user_profile.html | 89 +++++++++++++++++++++++++++++ src/templates/types.go | 1 + src/website/routes.go | 2 + src/website/user.go | 65 +++++++++++++++++++++ 7 files changed, 174 insertions(+) diff --git a/src/hmnurl/hmnurl_test.go b/src/hmnurl/hmnurl_test.go index 77dba56..9c18d64 100644 --- a/src/hmnurl/hmnurl_test.go +++ b/src/hmnurl/hmnurl_test.go @@ -105,6 +105,8 @@ func TestUserSettings(t *testing.T) { func TestAdmin(t *testing.T) { AssertRegexMatch(t, BuildAdminAtomFeed(), RegexAdminAtomFeed, nil) AssertRegexMatch(t, BuildAdminApprovalQueue(), RegexAdminApprovalQueue, nil) + AssertRegexMatch(t, BuildAdminSetUserStatus(), RegexAdminSetUserStatus, nil) + AssertRegexMatch(t, BuildAdminNukeUser(), RegexAdminNukeUser, nil) } func TestSnippet(t *testing.T) { diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go index 4e8e8ba..560f5db 100644 --- a/src/hmnurl/urls.go +++ b/src/hmnurl/urls.go @@ -216,6 +216,20 @@ func BuildAdminApprovalQueue() string { return Url("/admin/approvals", nil) } +var RegexAdminSetUserStatus = regexp.MustCompile(`^/admin/setuserstatus$`) + +func BuildAdminSetUserStatus() string { + defer CatchPanic() + return Url("/admin/setuserstatus", nil) +} + +var RegexAdminNukeUser = regexp.MustCompile(`^/admin/nukeuser$`) + +func BuildAdminNukeUser() string { + defer CatchPanic() + return Url("/admin/nukeuser", nil) +} + /* * Snippets */ diff --git a/src/templates/mapping.go b/src/templates/mapping.go index f6f178c..b2848db 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -193,6 +193,7 @@ func UserToTemplate(u *models.User, currentTheme string) User { Username: u.Username, Email: email, IsStaff: u.IsStaff, + Status: int(u.Status), Name: u.BestName(), Bio: u.Bio, diff --git a/src/templates/src/user_profile.html b/src/templates/src/user_profile.html index 88c24af..adffc7d 100644 --- a/src/templates/src/user_profile.html +++ b/src/templates/src/user_profile.html @@ -1,5 +1,32 @@ {{ template "base.html" . }} +{{ define "extrahead" }} + +{{ end }} + {{ define "content" }}
+
+ Admin actions +
+ Unlock +
+
+
+
+
User status:
+
+ {{ csrftoken .Session }} + + + + +
Only sets status. Doesn't delete anything.
+
+
+
+
Danger zone:
+
+ {{ csrftoken .Session }} + + + +
+
+
+ +
+ {{ end }} + {{ end }}
{{ if or .OwnProfile .ProfileUserProjects }} diff --git a/src/templates/types.go b/src/templates/types.go index 1d95d7f..73bda64 100644 --- a/src/templates/types.go +++ b/src/templates/types.go @@ -157,6 +157,7 @@ type User struct { Username string Email string IsStaff bool + Status int Name string Blurb string diff --git a/src/website/routes.go b/src/website/routes.go index e4b3a13..e9c948d 100644 --- a/src/website/routes.go +++ b/src/website/routes.go @@ -190,6 +190,8 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool) ht hmnOnly.GET(hmnurl.RegexAdminAtomFeed, AdminAtomFeed) hmnOnly.GET(hmnurl.RegexAdminApprovalQueue, adminMiddleware(AdminApprovalQueue)) hmnOnly.POST(hmnurl.RegexAdminApprovalQueue, adminMiddleware(csrfMiddleware(AdminApprovalQueueSubmit))) + hmnOnly.POST(hmnurl.RegexAdminSetUserStatus, adminMiddleware(csrfMiddleware(UserProfileAdminSetStatus))) + hmnOnly.POST(hmnurl.RegexAdminNukeUser, adminMiddleware(csrfMiddleware(UserProfileAdminNuke))) hmnOnly.GET(hmnurl.RegexFeed, Feed) hmnOnly.GET(hmnurl.RegexAtomFeed, AtomFeed) diff --git a/src/website/user.go b/src/website/user.go index 9d48c2f..7d05530 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "sort" + "strconv" "strings" "time" @@ -32,6 +33,9 @@ type UserProfileTemplateData struct { CanAddProject bool NewProjectUrl string + + AdminSetStatusUrl string + AdminNukeUrl string } func UserProfile(c *RequestContext) ResponseData { @@ -191,6 +195,9 @@ func UserProfile(c *RequestContext) ResponseData { CanAddProject: numPersonalProjects < maxPersonalProjects, NewProjectUrl: hmnurl.BuildProjectNew(), + + AdminSetStatusUrl: hmnurl.BuildAdminSetUserStatus(), + AdminNukeUrl: hmnurl.BuildAdminNukeUser(), }, c.Perf) return res } @@ -440,6 +447,64 @@ func UserSettingsSave(c *RequestContext) ResponseData { return res } +func UserProfileAdminSetStatus(c *RequestContext) ResponseData { + c.Req.ParseForm() + + userIdStr := c.Req.Form.Get("user_id") + userId, err := strconv.Atoi(userIdStr) + if err != nil { + return RejectRequest(c, "No user id provided") + } + + status := c.Req.Form.Get("status") + var desiredStatus models.UserStatus + switch status { + case "inactive": + desiredStatus = models.UserStatusInactive + case "confirmed": + desiredStatus = models.UserStatusConfirmed + case "approved": + desiredStatus = models.UserStatusApproved + case "banned": + desiredStatus = models.UserStatusBanned + default: + return RejectRequest(c, "No legal user status provided") + } + + _, err = c.Conn.Exec(c.Context(), + ` + UPDATE auth_user + SET status = $1 + WHERE id = $2 + `, + desiredStatus, + userId, + ) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user status")) + } + res := c.Redirect(hmnurl.BuildUserProfile(c.Req.Form.Get("username")), http.StatusSeeOther) + res.AddFutureNotice("success", "Successfully set status") + return res +} + +func UserProfileAdminNuke(c *RequestContext) ResponseData { + c.Req.ParseForm() + userIdStr := c.Req.Form.Get("user_id") + userId, err := strconv.Atoi(userIdStr) + if err != nil { + return RejectRequest(c, "No user id provided") + } + + err = deleteAllPostsForUser(c.Context(), c.Conn, userId) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete user posts")) + } + res := c.Redirect(hmnurl.BuildUserProfile(c.Req.Form.Get("username")), http.StatusSeeOther) + res.AddFutureNotice("success", "Successfully nuked user") + return res +} + func updatePassword(c *RequestContext, tx pgx.Tx, old, new, confirm string) *ResponseData { if new != confirm { res := RejectRequest(c, "Your password and password confirmation did not match.") From b5eb7186155af5d5b5fbe97fb3fff291ceebed8e Mon Sep 17 00:00:00 2001 From: Asaf Gartner Date: Wed, 15 Dec 2021 03:36:37 +0200 Subject: [PATCH 3/3] Query automatically does ToSlice. Use QueryIterator if you need an iterator. --- src/db/db.go | 13 +++++++++++-- src/discord/gateway.go | 3 +-- src/discord/history.go | 3 +-- src/discord/showcase.go | 9 +++------ src/hmndata/project_helper.go | 9 +++------ src/hmndata/snippet_helper.go | 9 +++------ src/hmndata/tag_helper.go | 3 +-- src/hmndata/threads_and_posts_helper.go | 10 ++++------ src/models/subforum.go | 3 +-- src/website/admin.go | 4 ++-- src/website/discord.go | 6 ++++-- src/website/podcast.go | 2 +- src/website/projects.go | 9 ++++----- src/website/user.go | 6 ++---- 14 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/db/db.go b/src/db/db.go index 7c21119..49492e8 100644 --- a/src/db/db.go +++ b/src/db/db.go @@ -231,7 +231,16 @@ func followPathThroughStructs(structPtrVal reflect.Value, path []int) (reflect.V return val, field } -func Query(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (*StructQueryIterator, error) { +func Query(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) ([]interface{}, error) { + it, err := QueryIterator(ctx, conn, destExample, query, args...) + if err != nil { + return nil, err + } else { + return it.ToSlice(), nil + } +} + +func QueryIterator(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (*StructQueryIterator, error) { destType := reflect.TypeOf(destExample) columnNames, fieldPaths, err := getColumnNamesAndPaths(destType, nil, "") if err != nil { @@ -347,7 +356,7 @@ result but find nothing. var NotFound = errors.New("not found") func QueryOne(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (interface{}, error) { - rows, err := Query(ctx, conn, destExample, query, args...) + rows, err := QueryIterator(ctx, conn, destExample, query, args...) if err != nil { return nil, err } diff --git a/src/discord/gateway.go b/src/discord/gateway.go index 75075e6..8e3a4b2 100644 --- a/src/discord/gateway.go +++ b/src/discord/gateway.go @@ -408,7 +408,7 @@ func (bot *botInstance) doSender(ctx context.Context) { } defer tx.Rollback(ctx) - itMessages, err := db.Query(ctx, tx, models.DiscordOutgoingMessage{}, ` + msgs, err := db.Query(ctx, tx, models.DiscordOutgoingMessage{}, ` SELECT $columns FROM discord_outgoingmessages ORDER BY id ASC @@ -418,7 +418,6 @@ func (bot *botInstance) doSender(ctx context.Context) { return } - msgs := itMessages.ToSlice() for _, imsg := range msgs { msg := imsg.(*models.DiscordOutgoingMessage) if time.Now().After(msg.ExpiresAt) { diff --git a/src/discord/history.go b/src/discord/history.go index 343d752..4573dff 100644 --- a/src/discord/history.go +++ b/src/discord/history.go @@ -64,7 +64,7 @@ func fetchMissingContent(ctx context.Context, dbConn *pgxpool.Pool) { type query struct { Message models.DiscordMessage `db:"msg"` } - result, err := db.Query(ctx, dbConn, query{}, + imessagesWithoutContent, err := db.Query(ctx, dbConn, query{}, ` SELECT $columns FROM @@ -82,7 +82,6 @@ func fetchMissingContent(ctx context.Context, dbConn *pgxpool.Pool) { log.Error().Err(err).Msg("failed to check for messages without content") return } - imessagesWithoutContent := result.ToSlice() if len(imessagesWithoutContent) > 0 { log.Info().Msgf("There are %d Discord messages without content, fetching their content now...", len(imessagesWithoutContent)) diff --git a/src/discord/showcase.go b/src/discord/showcase.go index a145d8a..81bb596 100644 --- a/src/discord/showcase.go +++ b/src/discord/showcase.go @@ -749,7 +749,7 @@ func UpdateSnippetTagsIfAny(ctx context.Context, dbConn db.ConnOrTx, msg *Messag type tagsRow struct { Tag models.Tag `db:"tags"` } - itUserTags, err := db.Query(ctx, tx, tagsRow{}, + iUserTags, err := db.Query(ctx, tx, tagsRow{}, ` SELECT $columns FROM @@ -764,7 +764,6 @@ func UpdateSnippetTagsIfAny(ctx context.Context, dbConn db.ConnOrTx, msg *Messag if err != nil { return oops.New(err, "failed to fetch tags for user projects") } - iUserTags := itUserTags.ToSlice() var tagIDs []int for _, itag := range iUserTags { @@ -805,7 +804,7 @@ var RESnippetableUrl = regexp.MustCompile(`^https?://(youtu\.be|(www\.)?youtube\ func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.DiscordMessage) (*uuid.UUID, *string, error) { // Check attachments - itAttachments, err := db.Query(ctx, tx, models.DiscordMessageAttachment{}, + attachments, err := db.Query(ctx, tx, models.DiscordMessageAttachment{}, ` SELECT $columns FROM handmade_discordmessageattachment @@ -816,14 +815,13 @@ func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.Disco if err != nil { return nil, nil, oops.New(err, "failed to fetch message attachments") } - attachments := itAttachments.ToSlice() for _, iattachment := range attachments { attachment := iattachment.(*models.DiscordMessageAttachment) return &attachment.AssetID, nil, nil } // Check embeds - itEmbeds, err := db.Query(ctx, tx, models.DiscordMessageEmbed{}, + embeds, err := db.Query(ctx, tx, models.DiscordMessageEmbed{}, ` SELECT $columns FROM handmade_discordmessageembed @@ -834,7 +832,6 @@ func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.Disco if err != nil { return nil, nil, oops.New(err, "failed to fetch discord embeds") } - embeds := itEmbeds.ToSlice() for _, iembed := range embeds { embed := iembed.(*models.DiscordMessageEmbed) if embed.VideoID != nil { diff --git a/src/hmndata/project_helper.go b/src/hmndata/project_helper.go index 8f2b867..7c85ac2 100644 --- a/src/hmndata/project_helper.go +++ b/src/hmndata/project_helper.go @@ -145,11 +145,10 @@ func FetchProjects( } // Do the query - itProjects, err := db.Query(ctx, dbConn, projectRow{}, qb.String(), qb.Args()...) + iprojects, err := db.Query(ctx, dbConn, projectRow{}, qb.String(), qb.Args()...) if err != nil { return nil, oops.New(err, "failed to fetch projects") } - iprojects := itProjects.ToSlice() // Fetch project owners to do permission checks projectIds := make([]int, len(iprojects)) @@ -340,7 +339,7 @@ func FetchMultipleProjectsOwners( UserID int `db:"user_id"` ProjectID int `db:"project_id"` } - it, err := db.Query(ctx, tx, userProject{}, + iuserprojects, err := db.Query(ctx, tx, userProject{}, ` SELECT $columns FROM handmade_user_projects @@ -351,7 +350,6 @@ func FetchMultipleProjectsOwners( if err != nil { return nil, oops.New(err, "failed to fetch project IDs") } - iuserprojects := it.ToSlice() // Get the unique user IDs from this set and fetch the users from the db var userIds []int @@ -368,7 +366,7 @@ func FetchMultipleProjectsOwners( userIds = append(userIds, userProject.UserID) } } - it, err = db.Query(ctx, tx, models.User{}, + iusers, err := db.Query(ctx, tx, models.User{}, ` SELECT $columns FROM auth_user @@ -380,7 +378,6 @@ func FetchMultipleProjectsOwners( if err != nil { return nil, oops.New(err, "failed to fetch users for projects") } - iusers := it.ToSlice() // Build the final result set with real user data res := make([]ProjectOwners, len(projectIds)) diff --git a/src/hmndata/snippet_helper.go b/src/hmndata/snippet_helper.go index 0feb709..c2b2215 100644 --- a/src/hmndata/snippet_helper.go +++ b/src/hmndata/snippet_helper.go @@ -47,7 +47,7 @@ func FetchSnippets( type snippetIDRow struct { SnippetID int `db:"snippet_id"` } - itSnippetIDs, err := db.Query(ctx, tx, snippetIDRow{}, + iSnippetIDs, err := db.Query(ctx, tx, snippetIDRow{}, ` SELECT DISTINCT snippet_id FROM @@ -61,7 +61,6 @@ func FetchSnippets( if err != nil { return nil, oops.New(err, "failed to get snippet IDs for tag") } - iSnippetIDs := itSnippetIDs.ToSlice() // special early-out: no snippets found for these tags at all if len(iSnippetIDs) == 0 { @@ -125,11 +124,10 @@ func FetchSnippets( DiscordMessage *models.DiscordMessage `db:"discord_message"` } - it, err := db.Query(ctx, tx, resultRow{}, qb.String(), qb.Args()...) + iresults, err := db.Query(ctx, tx, resultRow{}, qb.String(), qb.Args()...) if err != nil { return nil, oops.New(err, "failed to fetch threads") } - iresults := it.ToSlice() result := make([]SnippetAndStuff, len(iresults)) // allocate extra space because why not snippetIDs := make([]int, len(iresults)) @@ -151,7 +149,7 @@ func FetchSnippets( SnippetID int `db:"snippet_tags.snippet_id"` Tag *models.Tag `db:"tags"` } - itSnippetTags, err := db.Query(ctx, tx, snippetTagRow{}, + iSnippetTags, err := db.Query(ctx, tx, snippetTagRow{}, ` SELECT $columns FROM @@ -165,7 +163,6 @@ func FetchSnippets( if err != nil { return nil, oops.New(err, "failed to fetch tags for snippets") } - iSnippetTags := itSnippetTags.ToSlice() // associate tags with snippets resultBySnippetId := make(map[int]*SnippetAndStuff) diff --git a/src/hmndata/tag_helper.go b/src/hmndata/tag_helper.go index ca201b4..fab5b94 100644 --- a/src/hmndata/tag_helper.go +++ b/src/hmndata/tag_helper.go @@ -40,11 +40,10 @@ func FetchTags(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) ([]*models.T qb.Add(`LIMIT $? OFFSET $?`, q.Limit, q.Offset) } - it, err := db.Query(ctx, dbConn, models.Tag{}, qb.String(), qb.Args()...) + itags, err := db.Query(ctx, dbConn, models.Tag{}, qb.String(), qb.Args()...) if err != nil { return nil, oops.New(err, "failed to fetch tags") } - itags := it.ToSlice() res := make([]*models.Tag, len(itags)) for i, itag := range itags { diff --git a/src/hmndata/threads_and_posts_helper.go b/src/hmndata/threads_and_posts_helper.go index cbe7b9f..f745273 100644 --- a/src/hmndata/threads_and_posts_helper.go +++ b/src/hmndata/threads_and_posts_helper.go @@ -143,11 +143,10 @@ func FetchThreads( ForumLastReadTime *time.Time `db:"slri.lastread"` } - it, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...) + iresults, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...) if err != nil { return nil, oops.New(err, "failed to fetch threads") } - iresults := it.ToSlice() result := make([]ThreadAndStuff, len(iresults)) for i, iresult := range iresults { @@ -398,11 +397,10 @@ func FetchPosts( qb.Add(`LIMIT $? OFFSET $?`, q.Limit, q.Offset) } - it, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...) + iresults, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...) if err != nil { return nil, oops.New(err, "failed to fetch posts") } - iresults := it.ToSlice() result := make([]PostAndStuff, len(iresults)) for i, iresult := range iresults { @@ -856,7 +854,7 @@ func CreatePostVersion(ctx context.Context, tx pgx.Tx, postId int, unparsedConte var values [][]interface{} - for _, asset := range assetResult.ToSlice() { + for _, asset := range assetResult { values = append(values, []interface{}{postId, asset.(*assetId).AssetID}) } @@ -892,7 +890,7 @@ func FixThreadPostIds(ctx context.Context, tx pgx.Tx, threadId int) error { } var firstPost, lastPost *models.Post - for _, ipost := range postsIter.ToSlice() { + for _, ipost := range postsIter { post := ipost.(*models.Post) if firstPost == nil || post.PostDate.Before(firstPost.PostDate) { diff --git a/src/models/subforum.go b/src/models/subforum.go index 38089f3..d65346a 100644 --- a/src/models/subforum.go +++ b/src/models/subforum.go @@ -47,7 +47,7 @@ func GetFullSubforumTree(ctx context.Context, conn *pgxpool.Pool) SubforumTree { type subforumRow struct { Subforum Subforum `db:"sf"` } - rows, err := db.Query(ctx, conn, subforumRow{}, + rowsSlice, err := db.Query(ctx, conn, subforumRow{}, ` SELECT $columns FROM @@ -59,7 +59,6 @@ func GetFullSubforumTree(ctx context.Context, conn *pgxpool.Pool) SubforumTree { panic(oops.New(err, "failed to fetch subforum tree")) } - rowsSlice := rows.ToSlice() sfTreeMap := make(map[int]*SubforumTreeNode, len(rowsSlice)) for _, row := range rowsSlice { sf := row.(*subforumRow).Subforum diff --git a/src/website/admin.go b/src/website/admin.go index 0771151..2557ce0 100644 --- a/src/website/admin.go +++ b/src/website/admin.go @@ -253,7 +253,7 @@ func fetchUnapprovedPosts(c *RequestContext) ([]*UnapprovedPost, error) { return nil, oops.New(err, "failed to fetch unapproved posts") } var res []*UnapprovedPost - for _, iresult := range it.ToSlice() { + for _, iresult := range it { res = append(res, iresult.(*UnapprovedPost)) } return res, nil @@ -285,7 +285,7 @@ func deleteAllPostsForUser(ctx context.Context, conn *pgxpool.Pool, userId int) return oops.New(err, "failed to fetch posts to delete for user") } - for _, iResult := range it.ToSlice() { + for _, iResult := range it { row := iResult.(*toDelete) hmndata.DeletePost(ctx, tx, row.ThreadID, row.PostID) } diff --git a/src/website/discord.go b/src/website/discord.go index 634e5b4..32bf0c8 100644 --- a/src/website/discord.go +++ b/src/website/discord.go @@ -157,7 +157,7 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData { type messageIdQuery struct { MessageID string `db:"msg.id"` } - itMsgIds, err := db.Query(c.Context(), c.Conn, messageIdQuery{}, + iMsgIDs, err := db.Query(c.Context(), c.Conn, messageIdQuery{}, ` SELECT $columns FROM @@ -169,7 +169,9 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData { duser.UserID, config.Config.Discord.ShowcaseChannelID, ) - iMsgIDs := itMsgIds.ToSlice() + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, err) + } var msgIDs []string for _, imsgId := range iMsgIDs { diff --git a/src/website/podcast.go b/src/website/podcast.go index e7152c3..e6e800e 100644 --- a/src/website/podcast.go +++ b/src/website/podcast.go @@ -573,7 +573,7 @@ func FetchPodcast(c *RequestContext, projectId int, fetchEpisodes bool, episodeG if err != nil { return result, oops.New(err, "failed to fetch podcast episodes") } - for _, episodeRow := range podcastEpisodeQueryResult.ToSlice() { + for _, episodeRow := range podcastEpisodeQueryResult { result.Episodes = append(result.Episodes, &episodeRow.(*podcastEpisodeQuery).Episode) } } else { diff --git a/src/website/projects.go b/src/website/projects.go index e4a9cb6..33d975a 100644 --- a/src/website/projects.go +++ b/src/website/projects.go @@ -323,15 +323,15 @@ func ProjectHomepage(c *RequestContext) ResponseData { } } - for _, screenshot := range screenshotQueryResult.ToSlice() { + for _, screenshot := range screenshotQueryResult { templateData.Screenshots = append(templateData.Screenshots, hmnurl.BuildUserFile(screenshot.(*screenshotQuery).Filename)) } - for _, link := range projectLinkResult.ToSlice() { + for _, link := range projectLinkResult { templateData.ProjectLinks = append(templateData.ProjectLinks, templates.LinkToTemplate(&link.(*projectLinkQuery).Link)) } - for _, post := range postQueryResult.ToSlice() { + for _, post := range postQueryResult { templateData.RecentActivity = append(templateData.RecentActivity, PostToTimelineItem( c.UrlContext, lineageBuilder, @@ -801,7 +801,7 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P } } - ownerResult, err := db.Query(ctx, tx, models.User{}, + ownerRows, err := db.Query(ctx, tx, models.User{}, ` SELECT $columns FROM auth_user @@ -812,7 +812,6 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P if err != nil { return oops.New(err, "Failed to query users") } - ownerRows := ownerResult.ToSlice() _, err = tx.Exec(ctx, ` diff --git a/src/website/user.go b/src/website/user.go index 7d05530..97f3f9c 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -85,7 +85,7 @@ func UserProfile(c *RequestContext) ResponseData { type userLinkQuery struct { UserLink models.Link `db:"link"` } - userLinkQueryResult, err := db.Query(c.Context(), c.Conn, userLinkQuery{}, + userLinksSlice, err := db.Query(c.Context(), c.Conn, userLinkQuery{}, ` SELECT $columns FROM @@ -99,7 +99,6 @@ func UserProfile(c *RequestContext) ResponseData { if err != nil { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch links for user: %s", username)) } - userLinksSlice := userLinkQueryResult.ToSlice() profileUserLinks := make([]templates.Link, 0, len(userLinksSlice)) for _, l := range userLinksSlice { profileUserLinks = append(profileUserLinks, templates.LinkToTemplate(&l.(*userLinkQuery).UserLink)) @@ -223,7 +222,7 @@ func UserSettings(c *RequestContext) ResponseData { DiscordShowcaseBacklogUrl string } - ilinks, err := db.Query(c.Context(), c.Conn, models.Link{}, + links, err := db.Query(c.Context(), c.Conn, models.Link{}, ` SELECT $columns FROM handmade_links @@ -235,7 +234,6 @@ func UserSettings(c *RequestContext) ResponseData { if err != nil { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links")) } - links := ilinks.ToSlice() linksText := "" for _, ilink := range links {