dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit 3e05c9421d40975196801552db9b3cf904725285
parent 61052c4596b5ca94a5340d86d30d2c4348c1acb0
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Mon, 12 Jun 2023 19:46:56 -0700

move code

Diffstat:
Apkg/web/handlers/forum.go | 370+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/web/handlers/handlers.go | 356-------------------------------------------------------------------------------
2 files changed, 370 insertions(+), 356 deletions(-)

diff --git a/pkg/web/handlers/forum.go b/pkg/web/handlers/forum.go @@ -0,0 +1,370 @@ +package handlers + +import ( + "dkforest/pkg/config" + "dkforest/pkg/database" + "dkforest/pkg/utils" + hutils "dkforest/pkg/web/handlers/utils" + "fmt" + "github.com/asaskevich/govalidator" + "github.com/labstack/echo" + "github.com/sirupsen/logrus" + html2 "html" + "net/http" + "strings" +) + +func ForumHandler(c echo.Context) error { + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + var data forumData + data.ForumCategories, _ = db.GetForumCategories() + data.ForumThreads, _ = db.GetPublicForumCategoryThreads(authUser.ID, 1) + return c.Render(http.StatusOK, "forum", data) +} + +func ForumCategoryHandler(c echo.Context) error { + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + categorySlug := c.Param("categorySlug") + var data forumCategoryData + category, err := db.GetForumCategoryBySlug(categorySlug) + if err != nil { + return c.Redirect(http.StatusFound, "/forum") + } + data.ForumThreads, _ = db.GetPublicForumCategoryThreads(authUser.ID, category.ID) + return c.Render(http.StatusOK, "forum", data) +} + +func ForumSearchHandler(c echo.Context) error { + db := c.Get("database").(*database.DkfDB) + var data forumSearchData + data.Search = c.QueryParam("search") + data.AuthorFilter = c.QueryParam("author") + + if data.AuthorFilter != "" { + if err := db.DB().Raw(`select +t.*, +u.username as author, +u.chat_color as author_chat_color, +lu.username as last_msg_author, +lu.chat_color as last_msg_chat_color, +lu.chat_font as last_msg_chat_font, +m.created_at as last_msg_created_at, +mmm.replies_count +from fts5_forum_threads ft +inner join forum_threads t on t.id = ft.id +-- Count replies +LEFT JOIN (SELECT mm.thread_id, COUNT(mm.id) as replies_count FROM forum_messages mm GROUP BY mm.thread_id) as mmm ON mmm.thread_id = t.id +-- Join author user +INNER JOIN users u ON u.id = t.user_id +-- Find last message for thread +LEFT JOIN forum_messages m ON m.thread_id = t.id AND m.id = (SELECT max(id) FROM forum_messages WHERE thread_id = t.id) +-- Join last message user +INNER JOIN users lu ON lu.id = m.user_id +where u.username = ? and t.is_club = 0 order by id desc limit 100`, data.AuthorFilter).Scan(&data.ForumThreads).Error; err != nil { + logrus.Error(err) + } + return c.Render(http.StatusOK, "forum-search", data) + } + + if err := db.DB().Raw(`select m.uuid, snippet(fts5_forum_messages,-1, '[', ']', '...', 10) as snippet, t.uuid as thread_uuid, t.name as thread_name, +u.username as author, +u.chat_color as author_chat_color, +u.chat_font as author_chat_font, +mm.created_at as created_at +from fts5_forum_messages m +inner join forum_threads t on t.id = m.thread_id +-- Find message +LEFT JOIN forum_messages mm ON mm.uuid = m.uuid +-- Join author user +INNER JOIN users u ON u.id = mm.user_id +where fts5_forum_messages match ? and t.is_club = 0 order by rank limit 100`, data.Search).Scan(&data.ForumMessages).Error; err != nil { + logrus.Error(err) + } + + if err := db.DB().Raw(`select +t.*, +u.username as author, +u.chat_color as author_chat_color, +lu.username as last_msg_author, +lu.chat_color as last_msg_chat_color, +lu.chat_font as last_msg_chat_font, +m.created_at as last_msg_created_at, +mmm.replies_count +from fts5_forum_threads ft +inner join forum_threads t on t.id = ft.id +-- Count replies +LEFT JOIN (SELECT mm.thread_id, COUNT(mm.id) as replies_count FROM forum_messages mm GROUP BY mm.thread_id) as mmm ON mmm.thread_id = t.id +-- Join author user +INNER JOIN users u ON u.id = t.user_id +-- Find last message for thread +LEFT JOIN forum_messages m ON m.thread_id = t.id AND m.id = (SELECT max(id) FROM forum_messages WHERE thread_id = t.id) +-- Join last message user +INNER JOIN users lu ON lu.id = m.user_id +where fts5_forum_threads match ? and t.is_club = 0 order by rank limit 100`, data.Search).Scan(&data.ForumThreads).Error; err != nil { + logrus.Error(err) + } + + return c.Render(http.StatusOK, "forum-search", data) +} + +func ThreadEditHandler(c echo.Context) error { + db := c.Get("database").(*database.DkfDB) + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + if !authUser.IsAdmin { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) + thread, err := db.GetForumThreadByUUID(threadUUID) + if err != nil { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + var data editForumThreadData + data.Thread = thread + data.Categories, _ = db.GetForumCategories() + + if c.Request().Method == http.MethodPost { + thread.CategoryID = database.ForumCategoryID(utils.DoParseInt64(c.Request().PostFormValue("category_id"))) + thread.DoSave(db) + return c.Redirect(http.StatusFound, "/forum") + } + + return c.Render(http.StatusOK, "thread-edit", data) +} + +func ThreadDeleteHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) + thread, err := db.GetForumThreadByUUID(threadUUID) + if err != nil { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + + if !authUser.IsAdmin { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + + var data deleteForumThreadData + data.Thread = thread + + if c.Request().Method == http.MethodPost { + if err := db.DeleteForumThreadByID(thread.ID); err != nil { + logrus.Error(err) + } + return c.Redirect(http.StatusFound, "/forum") + } + + return c.Render(http.StatusOK, "thread-delete", data) +} + +func ThreadReplyHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + + threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) + thread, err := db.GetForumThreadByUUID(threadUUID) + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + var data threadReplyData + data.Thread = thread + + if c.Request().Method == http.MethodPost { + data.Message = c.Request().PostFormValue("message") + if !govalidator.RuneLength(data.Message, "3", "10000") { + data.ErrorMessage = "Message must have at least 3 characters" + return c.Render(http.StatusOK, "thread-reply", data) + } + if isForumSpam(data.Message) { + db.NewAudit(*authUser, fmt.Sprintf("spam forum thread reply %s (#%d)", authUser.Username, authUser.ID)) + authUser.CanUseForum = false + authUser.DoSave(db) + return c.Redirect(http.StatusFound, "/") + } + message := database.MakeForumMessage(data.Message, authUser.ID, thread.ID) + message.IsSigned = message.ValidateSignature(authUser.GPGPublicKey) + if err := db.DB().Create(&message).Error; err != nil { + logrus.Error(err) + } + // Send notifications + subs, _ := db.GetUsersSubscribedToForumThread(thread.ID) + for _, sub := range subs { + if sub.UserID != authUser.ID { + threadName := html2.EscapeString(thread.Name) + msg := fmt.Sprintf(`New reply in thread &quot;<a href="/t/%s#%s">%s</a>&quot;`, thread.UUID, message.UUID, threadName) + db.CreateNotification(msg, sub.UserID) + } + } + return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) + } + + return c.Render(http.StatusOK, "thread-reply", data) +} + +func ThreadRawMessageHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + db := c.Get("database").(*database.DkfDB) + messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) + msg, err := db.GetForumMessageByUUID(messageUUID) + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + return c.String(http.StatusOK, msg.Message) +} + +func ThreadEditMessageHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) + messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) + thread, err := db.GetForumThreadByUUID(threadUUID) + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + msg, err := db.GetForumMessageByUUID(messageUUID) + if err != nil { + return c.Redirect(http.StatusFound, "/") + } + if msg.UserID != authUser.ID && !authUser.IsAdmin { + return c.Redirect(http.StatusFound, "/") + } + var data threadReplyData + data.IsEdit = true + data.Thread = thread + data.Message = msg.Message + + if c.Request().Method == http.MethodPost { + data.Message = c.Request().PostFormValue("message") + if !govalidator.RuneLength(data.Message, "3", "20000") { + data.ErrorMessage = "Message must have 3 to 20k characters" + return c.Render(http.StatusOK, "thread-reply", data) + } + if isForumSpam(data.Message) { + db.NewAudit(*authUser, fmt.Sprintf("spam forum edit msg %s (#%d)", authUser.Username, authUser.ID)) + authUser.CanUseForum = false + authUser.DoSave(db) + return c.Redirect(http.StatusFound, "/") + } + msg.Message = data.Message + msg.IsSigned = msg.ValidateSignature(authUser.GPGPublicKey) + msg.DoSave(db) + return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) + } + + return c.Render(http.StatusOK, "thread-reply", data) +} + +func isForumSpam(msg string) bool { + if strings.Contains(strings.ToLower(msg), "profjerry") { + return true + } + return false +} + +func ThreadDeleteMessageHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) + msg, err := db.GetForumMessageByUUID(messageUUID) + if err != nil { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + + if authUser.ID != msg.UserID && !authUser.IsAdmin { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + + if !msg.CanEdit() && !authUser.IsAdmin { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + + var data deleteForumMessageData + data.Thread, err = db.GetForumThreadByID(msg.ThreadID) + if err != nil { + return c.Redirect(http.StatusFound, c.Request().Referer()) + } + data.Message = msg + + if c.Request().Method == http.MethodPost { + if err := db.DeleteForumMessageByID(msg.ID); err != nil { + logrus.Error(err) + } + return c.Redirect(http.StatusFound, "/t/"+string(data.Thread.UUID)) + } + + return c.Render(http.StatusOK, "thread-message-delete", data) +} + +func NewThreadHandler(c echo.Context) error { + if config.ForumEnabled.IsFalse() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) + } + authUser := c.Get("authUser").(*database.User) + db := c.Get("database").(*database.DkfDB) + if !authUser.CanUseForumFn() { + return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) + } + var data newThreadData + + if c.Request().Method == http.MethodPost { + data.ThreadName = c.Request().PostFormValue("thread_name") + data.Message = c.Request().PostFormValue("message") + if !govalidator.RuneLength(data.ThreadName, "3", "255") { + data.ErrorThreadName = "Thread name must have 3-255 characters" + return c.Render(http.StatusOK, "new-thread", data) + } + if !govalidator.RuneLength(data.Message, "3", "20000") { + data.ErrorMessage = "Thread message must have at least 3-20000 characters" + return c.Render(http.StatusOK, "new-thread", data) + } + if isForumSpam(data.Message) { + db.NewAudit(*authUser, fmt.Sprintf("spam forum new thread %s (#%d)", authUser.Username, authUser.ID)) + authUser.CanUseForum = false + authUser.DoSave(db) + return c.Redirect(http.StatusFound, "/") + } + thread := database.MakeForumThread(data.ThreadName, authUser.ID, 1) + db.DB().Create(&thread) + message := database.MakeForumMessage(data.Message, authUser.ID, thread.ID) + message.IsSigned = message.ValidateSignature(authUser.GPGPublicKey) + db.DB().Create(&message) + _ = db.SubscribeToForumThread(authUser.ID, thread.ID) + return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) + } + + return c.Render(http.StatusOK, "new-thread", data) +} diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go @@ -23,7 +23,6 @@ import ( "github.com/jinzhu/gorm" _ "golang.org/x/image/bmp" _ "golang.org/x/image/webp" - html2 "html" "image" _ "image/gif" "image/png" @@ -1215,79 +1214,6 @@ func NewsHandler(c echo.Context) error { return c.Render(http.StatusOK, "news", data) } -func ForumSearchHandler(c echo.Context) error { - db := c.Get("database").(*database.DkfDB) - var data forumSearchData - data.Search = c.QueryParam("search") - data.AuthorFilter = c.QueryParam("author") - - if data.AuthorFilter != "" { - if err := db.DB().Raw(`select -t.*, -u.username as author, -u.chat_color as author_chat_color, -lu.username as last_msg_author, -lu.chat_color as last_msg_chat_color, -lu.chat_font as last_msg_chat_font, -m.created_at as last_msg_created_at, -mmm.replies_count -from fts5_forum_threads ft -inner join forum_threads t on t.id = ft.id --- Count replies -LEFT JOIN (SELECT mm.thread_id, COUNT(mm.id) as replies_count FROM forum_messages mm GROUP BY mm.thread_id) as mmm ON mmm.thread_id = t.id --- Join author user -INNER JOIN users u ON u.id = t.user_id --- Find last message for thread -LEFT JOIN forum_messages m ON m.thread_id = t.id AND m.id = (SELECT max(id) FROM forum_messages WHERE thread_id = t.id) --- Join last message user -INNER JOIN users lu ON lu.id = m.user_id -where u.username = ? and t.is_club = 0 order by id desc limit 100`, data.AuthorFilter).Scan(&data.ForumThreads).Error; err != nil { - logrus.Error(err) - } - return c.Render(http.StatusOK, "forum-search", data) - } - - if err := db.DB().Raw(`select m.uuid, snippet(fts5_forum_messages,-1, '[', ']', '...', 10) as snippet, t.uuid as thread_uuid, t.name as thread_name, -u.username as author, -u.chat_color as author_chat_color, -u.chat_font as author_chat_font, -mm.created_at as created_at -from fts5_forum_messages m -inner join forum_threads t on t.id = m.thread_id --- Find message -LEFT JOIN forum_messages mm ON mm.uuid = m.uuid --- Join author user -INNER JOIN users u ON u.id = mm.user_id -where fts5_forum_messages match ? and t.is_club = 0 order by rank limit 100`, data.Search).Scan(&data.ForumMessages).Error; err != nil { - logrus.Error(err) - } - - if err := db.DB().Raw(`select -t.*, -u.username as author, -u.chat_color as author_chat_color, -lu.username as last_msg_author, -lu.chat_color as last_msg_chat_color, -lu.chat_font as last_msg_chat_font, -m.created_at as last_msg_created_at, -mmm.replies_count -from fts5_forum_threads ft -inner join forum_threads t on t.id = ft.id --- Count replies -LEFT JOIN (SELECT mm.thread_id, COUNT(mm.id) as replies_count FROM forum_messages mm GROUP BY mm.thread_id) as mmm ON mmm.thread_id = t.id --- Join author user -INNER JOIN users u ON u.id = t.user_id --- Find last message for thread -LEFT JOIN forum_messages m ON m.thread_id = t.id AND m.id = (SELECT max(id) FROM forum_messages WHERE thread_id = t.id) --- Join last message user -INNER JOIN users lu ON lu.id = m.user_id -where fts5_forum_threads match ? and t.is_club = 0 order by rank limit 100`, data.Search).Scan(&data.ForumThreads).Error; err != nil { - logrus.Error(err) - } - - return c.Render(http.StatusOK, "forum-search", data) -} - func LinksHandler(c echo.Context) error { authUser := c.Get("authUser").(*database.User) db := c.Get("database").(*database.DkfDB) @@ -1870,28 +1796,6 @@ func ClaimCertificateLinkHandler(c echo.Context) error { return c.String(http.StatusOK, link.SignedCertificate) } -func ForumHandler(c echo.Context) error { - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - var data forumData - data.ForumCategories, _ = db.GetForumCategories() - data.ForumThreads, _ = db.GetPublicForumCategoryThreads(authUser.ID, 1) - return c.Render(http.StatusOK, "forum", data) -} - -func ForumCategoryHandler(c echo.Context) error { - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - categorySlug := c.Param("categorySlug") - var data forumCategoryData - category, err := db.GetForumCategoryBySlug(categorySlug) - if err != nil { - return c.Redirect(http.StatusFound, "/forum") - } - data.ForumThreads, _ = db.GetPublicForumCategoryThreads(authUser.ID, category.ID) - return c.Render(http.StatusOK, "forum", data) -} - func ThreadHandler(c echo.Context) error { authUser := c.Get("authUser").(*database.User) db := c.Get("database").(*database.DkfDB) @@ -2006,96 +1910,6 @@ func WerewolfHandler(c echo.Context) error { return c.Render(http.StatusOK, "werewolf", nil) } -func ThreadReplyHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - - threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) - thread, err := db.GetForumThreadByUUID(threadUUID) - if err != nil { - return c.Redirect(http.StatusFound, "/") - } - var data threadReplyData - data.Thread = thread - - if c.Request().Method == http.MethodPost { - data.Message = c.Request().PostFormValue("message") - if !govalidator.RuneLength(data.Message, "3", "10000") { - data.ErrorMessage = "Message must have at least 3 characters" - return c.Render(http.StatusOK, "thread-reply", data) - } - if isForumSpam(data.Message) { - db.NewAudit(*authUser, fmt.Sprintf("spam forum thread reply %s (#%d)", authUser.Username, authUser.ID)) - authUser.CanUseForum = false - authUser.DoSave(db) - return c.Redirect(http.StatusFound, "/") - } - message := database.MakeForumMessage(data.Message, authUser.ID, thread.ID) - message.IsSigned = message.ValidateSignature(authUser.GPGPublicKey) - if err := db.DB().Create(&message).Error; err != nil { - logrus.Error(err) - } - // Send notifications - subs, _ := db.GetUsersSubscribedToForumThread(thread.ID) - for _, sub := range subs { - if sub.UserID != authUser.ID { - threadName := html2.EscapeString(thread.Name) - msg := fmt.Sprintf(`New reply in thread &quot;<a href="/t/%s#%s">%s</a>&quot;`, thread.UUID, message.UUID, threadName) - db.CreateNotification(msg, sub.UserID) - } - } - return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) - } - - return c.Render(http.StatusOK, "thread-reply", data) -} - -func ThreadDeleteMessageHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) - msg, err := db.GetForumMessageByUUID(messageUUID) - if err != nil { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - - if authUser.ID != msg.UserID && !authUser.IsAdmin { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - - if !msg.CanEdit() && !authUser.IsAdmin { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - - var data deleteForumMessageData - data.Thread, err = db.GetForumThreadByID(msg.ThreadID) - if err != nil { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - data.Message = msg - - if c.Request().Method == http.MethodPost { - if err := db.DeleteForumMessageByID(msg.ID); err != nil { - logrus.Error(err) - } - return c.Redirect(http.StatusFound, "/t/"+string(data.Thread.UUID)) - } - - return c.Render(http.StatusOK, "thread-message-delete", data) -} - func LinkDeleteHandler(c echo.Context) error { authUser := c.Get("authUser").(*database.User) db := c.Get("database").(*database.DkfDB) @@ -2185,176 +1999,6 @@ func LinkMirrorDeleteHandler(c echo.Context) error { return c.Render(http.StatusOK, "link-mirror-delete", data) } -func ThreadEditHandler(c echo.Context) error { - db := c.Get("database").(*database.DkfDB) - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - if !authUser.IsAdmin { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) - thread, err := db.GetForumThreadByUUID(threadUUID) - if err != nil { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - var data editForumThreadData - data.Thread = thread - data.Categories, _ = db.GetForumCategories() - - if c.Request().Method == http.MethodPost { - thread.CategoryID = database.ForumCategoryID(utils.DoParseInt64(c.Request().PostFormValue("category_id"))) - thread.DoSave(db) - return c.Redirect(http.StatusFound, "/forum") - } - - return c.Render(http.StatusOK, "thread-edit", data) -} - -func ThreadDeleteHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) - thread, err := db.GetForumThreadByUUID(threadUUID) - if err != nil { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - - if !authUser.IsAdmin { - return c.Redirect(http.StatusFound, c.Request().Referer()) - } - - var data deleteForumThreadData - data.Thread = thread - - if c.Request().Method == http.MethodPost { - if err := db.DeleteForumThreadByID(thread.ID); err != nil { - logrus.Error(err) - } - return c.Redirect(http.StatusFound, "/forum") - } - - return c.Render(http.StatusOK, "thread-delete", data) -} - -func ThreadEditMessageHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - threadUUID := database.ForumThreadUUID(c.Param("threadUUID")) - messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) - thread, err := db.GetForumThreadByUUID(threadUUID) - if err != nil { - return c.Redirect(http.StatusFound, "/") - } - msg, err := db.GetForumMessageByUUID(messageUUID) - if err != nil { - return c.Redirect(http.StatusFound, "/") - } - if msg.UserID != authUser.ID && !authUser.IsAdmin { - return c.Redirect(http.StatusFound, "/") - } - var data threadReplyData - data.IsEdit = true - data.Thread = thread - data.Message = msg.Message - - if c.Request().Method == http.MethodPost { - data.Message = c.Request().PostFormValue("message") - if !govalidator.RuneLength(data.Message, "3", "20000") { - data.ErrorMessage = "Message must have 3 to 20k characters" - return c.Render(http.StatusOK, "thread-reply", data) - } - if isForumSpam(data.Message) { - db.NewAudit(*authUser, fmt.Sprintf("spam forum edit msg %s (#%d)", authUser.Username, authUser.ID)) - authUser.CanUseForum = false - authUser.DoSave(db) - return c.Redirect(http.StatusFound, "/") - } - msg.Message = data.Message - msg.IsSigned = msg.ValidateSignature(authUser.GPGPublicKey) - msg.DoSave(db) - return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) - } - - return c.Render(http.StatusOK, "thread-reply", data) -} - -func ThreadRawMessageHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - db := c.Get("database").(*database.DkfDB) - messageUUID := database.ForumMessageUUID(c.Param("messageUUID")) - msg, err := db.GetForumMessageByUUID(messageUUID) - if err != nil { - return c.Redirect(http.StatusFound, "/") - } - return c.String(http.StatusOK, msg.Message) -} - -func isForumSpam(msg string) bool { - if strings.Contains(strings.ToLower(msg), "profjerry") { - return true - } - return false -} - -func NewThreadHandler(c echo.Context) error { - if config.ForumEnabled.IsFalse() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: "Forum is temporarily disabled", Redirect: "/", Type: "alert-danger"}) - } - authUser := c.Get("authUser").(*database.User) - db := c.Get("database").(*database.DkfDB) - if !authUser.CanUseForumFn() { - return c.Render(http.StatusOK, "flash", FlashResponse{Message: hutils.AccountTooYoungErr.Error(), Redirect: c.Request().Referer(), Type: "alert-danger"}) - } - var data newThreadData - - if c.Request().Method == http.MethodPost { - data.ThreadName = c.Request().PostFormValue("thread_name") - data.Message = c.Request().PostFormValue("message") - if !govalidator.RuneLength(data.ThreadName, "3", "255") { - data.ErrorThreadName = "Thread name must have 3-255 characters" - return c.Render(http.StatusOK, "new-thread", data) - } - if !govalidator.RuneLength(data.Message, "3", "20000") { - data.ErrorMessage = "Thread message must have at least 3-20000 characters" - return c.Render(http.StatusOK, "new-thread", data) - } - if isForumSpam(data.Message) { - db.NewAudit(*authUser, fmt.Sprintf("spam forum new thread %s (#%d)", authUser.Username, authUser.ID)) - authUser.CanUseForum = false - authUser.DoSave(db) - return c.Redirect(http.StatusFound, "/") - } - thread := database.MakeForumThread(data.ThreadName, authUser.ID, 1) - db.DB().Create(&thread) - message := database.MakeForumMessage(data.Message, authUser.ID, thread.ID) - message.IsSigned = message.ValidateSignature(authUser.GPGPublicKey) - db.DB().Create(&message) - _ = db.SubscribeToForumThread(authUser.ID, thread.ID) - return c.Redirect(http.StatusFound, "/t/"+string(thread.UUID)) - } - - return c.Render(http.StatusOK, "new-thread", data) -} - func VipHandler(c echo.Context) error { db := c.Get("database").(*database.DkfDB) var data vipData