From 80a0a5eb8955ae7e648a76ed92c94e2f04f2b76a Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Mon, 21 Apr 2025 13:54:28 -0400 Subject: [PATCH] Implement thread pruning based on bumped_at --- internal/database/handlers.go | 72 +++++++++++++++++++++++++++++++++++ internal/util/cooldowns.go | 10 +++-- internal/util/posts.go | 10 +---- web/main.go | 10 ++++- 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/internal/database/handlers.go b/internal/database/handlers.go index c8d3317..f13ac37 100644 --- a/internal/database/handlers.go +++ b/internal/database/handlers.go @@ -2,10 +2,15 @@ package database import ( "database/sql" + "os" + "path" + + "github.com/dominicf2001/comfychan/internal/util" ) type Queryer interface { Exec(query string, args ...any) (sql.Result, error) + Query(query string, args ...any) (*sql.Rows, error) QueryRow(query string, args ...any) *sql.Row } @@ -149,6 +154,33 @@ func PutThread(db *sql.DB, boardSlug string, subject string, body string, mediaP return err } + row := tx.QueryRow("SELECT COUNT(*) FROM threads WHERE board_slug = ?", boardSlug) + + var threadCount int + if err = row.Scan(&threadCount); err != nil { + return err + } + + if threadCount > util.MAX_THREAD_COUNT { + row := tx.QueryRow(` + SELECT id FROM threads + WHERE id = ( + SELECT id FROM threads + WHERE board_slug = ? + ORDER BY bumped_at ASC + LIMIT 1)`, boardSlug) + + var pruneThreadId int + err := row.Scan(&pruneThreadId) + if err != nil { + return err + } + + if err := DeleteThread(tx, pruneThreadId); err != nil { + return err + } + } + if err = tx.Commit(); err != nil { return err } @@ -187,3 +219,43 @@ func PutPost(db Queryer, boardSlug string, threadId int, body string, mediaPath return nil } + +func DeleteThread(db Queryer, threadId int) error { + // cleanup images + rows, err := db.Query(` + SELECT media_path + FROM posts + WHERE thread_id = ?`, threadId) + if err != nil { + return err + } + defer rows.Close() + + var pruneMediaPaths []string + for rows.Next() { + var pruneMediaPath string + if err := rows.Scan(&pruneMediaPath); err != nil { + return err + } + pruneMediaPaths = append(pruneMediaPaths, pruneMediaPath) + } + + for _, pruneMediaPath := range pruneMediaPaths { + if err := os.Remove(path.Join(util.POST_IMG_FULL_PATH, pruneMediaPath)); err != nil && !os.IsNotExist(err) { + return err + } + if err := os.Remove(path.Join(util.POST_IMG_THUMB_PATH, pruneMediaPath)); err != nil && !os.IsNotExist(err) { + return err + } + } + + // delete thread + _, err = db.Exec(` + DELETE FROM threads + WHERE id = ?`, threadId) + if err != nil { + return err + } + + return nil +} diff --git a/internal/util/cooldowns.go b/internal/util/cooldowns.go index 2e37049..aab1096 100644 --- a/internal/util/cooldowns.go +++ b/internal/util/cooldowns.go @@ -17,11 +17,13 @@ var ( var CooldownMutex sync.Mutex -// const POST_COOLDOWN = 15 * time.Second -const POST_COOLDOWN = 0 * time.Second +const POST_COOLDOWN = 15 * time.Second -// const THREAD_COOLDOWN = 2 * time.Minute -const THREAD_COOLDOWN = 0 * time.Minute +// const POST_COOLDOWN = 0 * time.Second + +const THREAD_COOLDOWN = 2 * time.Minute + +// const THREAD_COOLDOWN = 0 * time.Minute func IsOnCooldown(ip string, m map[string]time.Time, duration time.Duration) bool { CooldownMutex.Lock() diff --git a/internal/util/posts.go b/internal/util/posts.go index 4dcaf66..3b57256 100644 --- a/internal/util/posts.go +++ b/internal/util/posts.go @@ -13,13 +13,13 @@ import ( "strings" "github.com/disintegration/imaging" - "github.com/dominicf2001/comfychan/internal/database" ) // 10 MB memory limit const FILE_MEM_LIMIT int64 = 10 << 20 const POST_IMG_FULL_PATH = "web/static/img/posts/full" const POST_IMG_THUMB_PATH = "web/static/img/posts/thumb" +const MAX_THREAD_COUNT = 50 func EnrichPost(body string) string { var b strings.Builder @@ -105,14 +105,6 @@ func SavePostFile(file *multipart.File, filename string) error { return nil } -func SumUniquePostIps(posts []database.Post) int { - uniqueIpHashes := map[string]bool{} - for _, post := range posts { - uniqueIpHashes[post.IpHash] = true - } - return len(uniqueIpHashes) -} - type PostImageInfo struct { Size int64 Height int diff --git a/web/main.go b/web/main.go index 9bf2652..f2fdc4d 100644 --- a/web/main.go +++ b/web/main.go @@ -30,6 +30,14 @@ func disableCacheInDevMode(next http.Handler) http.Handler { }) } +func SumUniquePostIps(posts []database.Post) int { + uniqueIpHashes := map[string]bool{} + for _, post := range posts { + uniqueIpHashes[post.IpHash] = true + } + return len(uniqueIpHashes) +} + func main() { // ----------------- // SETUP @@ -244,7 +252,7 @@ func main() { ThreadURL: fmt.Sprintf("/%s/threads/%d", slug, thread.Id), MediaPath: op.MediaPath, ReplyCount: len(posts), - IpCount: util.SumUniquePostIps(posts), + IpCount: SumUniquePostIps(posts), }) }