diff --git a/internal/util/cooldowns.go b/internal/util/cooldowns.go index aab1096..e1576f4 100644 --- a/internal/util/cooldowns.go +++ b/internal/util/cooldowns.go @@ -25,16 +25,16 @@ 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 { +func IsOnCooldown(ip string, m map[string]time.Time, duration time.Duration) time.Duration { CooldownMutex.Lock() defer CooldownMutex.Unlock() last, exists := m[ip] if !exists || time.Since(last) >= duration { m[ip] = time.Now() - return false + return time.Duration(0) } - return true + return duration - time.Since(last) } func GetIP(r *http.Request) string { diff --git a/web/main.go b/web/main.go index f2fdc4d..b7e24e9 100644 --- a/web/main.go +++ b/web/main.go @@ -126,8 +126,12 @@ func main() { // CREATE THREAD r.Post("/{slug}/threads", func(w http.ResponseWriter, r *http.Request) { ip := util.GetIP(r) - if util.IsOnCooldown(ip, util.ThreadCooldowns, util.THREAD_COOLDOWN) { - http.Error(w, "Too many posts at once.", http.StatusTooManyRequests) + + timeRemaining := util.IsOnCooldown(ip, util.ThreadCooldowns, util.THREAD_COOLDOWN) + if timeRemaining > 0 { + response := fmt.Sprintf("Please wait %.0f seconds.", timeRemaining.Seconds()) + + http.Error(w, response, http.StatusTooManyRequests) return } @@ -167,8 +171,12 @@ func main() { // CREATE POST r.Post("/{slug}/threads/{threadId}", func(w http.ResponseWriter, r *http.Request) { ip := util.GetIP(r) - if util.IsOnCooldown(ip, util.PostCooldowns, util.POST_COOLDOWN) { - http.Error(w, "Too many posts at once.", http.StatusTooManyRequests) + + timeRemaining := util.IsOnCooldown(ip, util.PostCooldowns, util.POST_COOLDOWN) + if timeRemaining > 0 { + response := fmt.Sprintf("Please wait %.0f seconds.", timeRemaining.Seconds()) + + http.Error(w, response, http.StatusTooManyRequests) return } diff --git a/web/static/index.css b/web/static/index.css index ab280d7..2cc6b40 100644 --- a/web/static/index.css +++ b/web/static/index.css @@ -128,6 +128,10 @@ body { color: #ff0000 } +#newPostForm table { + margin: auto; +} + #newPostForm button, #newThreadForm button { display: block; diff --git a/web/views/board.templ b/web/views/board.templ index 21cdbe8..2dd2c0b 100644 --- a/web/views/board.templ +++ b/web/views/board.templ @@ -9,9 +9,7 @@ import ( ) templ NewThreadForm(board database.Board) { - +
diff --git a/web/views/thread.templ b/web/views/thread.templ index 9a2e01f..0c08c16 100644 --- a/web/views/thread.templ +++ b/web/views/thread.templ @@ -1,61 +1,17 @@ package views import ( -"fmt" -"github.com/dominicf2001/comfychan/internal/database" -"github.com/dominicf2001/comfychan/internal/util" -"github.com/dominicf2001/comfychan/web/views/shared" -"strconv" + "fmt" + "github.com/dominicf2001/comfychan/internal/database" + "github.com/dominicf2001/comfychan/internal/util" + "github.com/dominicf2001/comfychan/web/views/shared" + "strconv" ) templ PostOriginal(post database.Post, thread *database.Thread) { -{{ imgInfo := util.GetPostImageInfo(post.MediaPath) }} -
-
- File: - - { post.MediaPath } - -
- ({ util.FormatBytes(imgInfo.Size) }, - { strconv.Itoa(imgInfo.Width) }x{ strconv.Itoa(imgInfo.Height) }) -
-
- -
-

{ thread.Subject } -

- - - { post.CreatedAt.Format("15:04") } - >%d\n'", post.Number) } - class=" post-num"> - No.{ strconv.Itoa(post.Number) } - - -
-

- @templ.Raw(util.EnrichPost(post.Body)) -

-
-} - -templ PostReply(post database.Post) { -{{ imgInfo := util.GetPostImageInfo(post.MediaPath) }} -
-
- - - { post.CreatedAt.Format("15:04") } - >%d\n'", post.Number) } - class=" post-num"> - No.{ strconv.Itoa(post.Number) } - - -
- if post.MediaPath != "" { -
-
+ {{ imgInfo := util.GetPostImageInfo(post.MediaPath) }} +
- -
- } -

- @templ.Raw(util.EnrichPost(post.Body)) -

-
+ +
+

{ thread.Subject } -

+ + + { post.CreatedAt.Format("15:04") } + >%d\n'", post.Number) } + class=" post-num" + > + No.{ strconv.Itoa(post.Number) } + + +
+

+ @templ.Raw(util.EnrichPost(post.Body)) +

+ +} + +templ PostReply(post database.Post) { + {{ imgInfo := util.GetPostImageInfo(post.MediaPath) }} +
+
+ + + { post.CreatedAt.Format("15:04") } + >%d\n'", post.Number) } + class=" post-num" + > + No.{ strconv.Itoa(post.Number) } + + +
+ if post.MediaPath != "" { +
+
+ File: + + { post.MediaPath } + +
+ ({ util.FormatBytes(imgInfo.Size) }, + { strconv.Itoa(imgInfo.Width) }x{ strconv.Itoa(imgInfo.Height) }) +
+
+ +
+ } +

+ @templ.Raw(util.EnrichPost(post.Body)) +

+
} templ Posts(posts []database.Post, thread database.Thread) { -@PostOriginal(posts[0], &thread) -
- for _, post := range (posts[1:]) { - @PostReply(post) -
- } -
+ @PostOriginal(posts[0], &thread) +
+ for _, post := range (posts[1:]) { + @PostReply(post) +
+ } +
} templ Thread(board database.Board, thread database.Thread, posts []database.Post) { -@shared.Layout() { - -@BoardHeader(board) -
- - + @BoardHeader(board) +
+ + -
- - - - - - - - - - -
comment
File
- -
- -
-
- [Catalog] - [Refresh] -
-
-
- @Posts(posts, thread) -
-} + then put event.detail.xhr.responseText into #newPostWarning + end" + > + + + + + + + + + + + +
comment
File
+ + + +
+
+ [Catalog] + [Refresh] +
+
+
+ @Posts(posts, thread) +
+ } }