Repost
This commit is contained in:
@@ -30,6 +30,8 @@ type Entry struct {
|
||||
ReplyCount int64 `json:"reply_count"`
|
||||
LastActivity int64 `json:"last_activity"`
|
||||
Deleted int64 `json:"deleted"`
|
||||
BumpCount int64 `json:"bump_count"`
|
||||
LastBump int64 `json:"last_bump"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
@@ -39,6 +41,7 @@ type Entry struct {
|
||||
const entrySelect = `
|
||||
SELECT e.pid, e.uid, e.created_at, e.content, e.filepath,
|
||||
e.reply_to, e.reply_count, e.last_activity, e.deleted,
|
||||
e.bump_count, e.last_bump,
|
||||
COALESCE(u.username, ''), COALESCE(u.avatar, '')
|
||||
FROM entry e LEFT JOIN user u ON u.uid = e.uid`
|
||||
|
||||
@@ -49,7 +52,8 @@ func scanEntries(rows *sql.Rows) ([]Entry, error) {
|
||||
for rows.Next() {
|
||||
var e Entry
|
||||
if err := rows.Scan(&e.PID, &e.UID, &e.CreatedAt, &e.Content, &e.Filepath,
|
||||
&e.ReplyTo, &e.ReplyCount, &e.LastActivity, &e.Deleted, &e.Username, &e.Avatar); err != nil {
|
||||
&e.ReplyTo, &e.ReplyCount, &e.LastActivity, &e.Deleted,
|
||||
&e.BumpCount, &e.LastBump, &e.Username, &e.Avatar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, e)
|
||||
@@ -62,7 +66,8 @@ func entryByPID(pid int64) (Entry, error) {
|
||||
var e Entry
|
||||
err := db.QueryRow(entrySelect+` WHERE e.pid = ?`, pid).Scan(
|
||||
&e.PID, &e.UID, &e.CreatedAt, &e.Content, &e.Filepath,
|
||||
&e.ReplyTo, &e.ReplyCount, &e.LastActivity, &e.Deleted, &e.Username, &e.Avatar)
|
||||
&e.ReplyTo, &e.ReplyCount, &e.LastActivity, &e.Deleted,
|
||||
&e.BumpCount, &e.LastBump, &e.Username, &e.Avatar)
|
||||
return e, err
|
||||
}
|
||||
|
||||
@@ -341,6 +346,66 @@ func handleDeleteEntry(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
||||
}
|
||||
|
||||
// bumpStep ist der Cooldown-Zuwachs je bereits erfolgtem Bump: nach dem 1. Bump
|
||||
// ein Tag Sperre, nach dem 10. Bump zehn Tage. So wird Hochspülen mit jedem Mal
|
||||
// teurer.
|
||||
const bumpStep = 24 * 60 * 60
|
||||
|
||||
// handleBump hebt last_activity eines Beitrags auf jetzt -> er steigt im Feed
|
||||
// (Roots, sortiert nach last_activity) bzw. in der Antwortliste eines Threads
|
||||
// wieder nach oben. Tritt an die Stelle der früheren "Bump"-Kommentare. Gegen
|
||||
// Spam wächst der Cooldown linear mit der Zahl bisheriger Bumps (siehe bumpStep);
|
||||
// er sitzt am Beitrag selbst (global), nicht am bumpenden Nutzer. Jeder
|
||||
// Eingeloggte darf bumpen (Route in der requireAuth-Gruppe).
|
||||
func handleBump(w http.ResponseWriter, r *http.Request) {
|
||||
pid, err := strconv.ParseInt(chi.URLParam(r, "pid"), 10, 64)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Ungültige pid")
|
||||
return
|
||||
}
|
||||
|
||||
var bumpCount, lastBump, deleted int64
|
||||
if err := db.QueryRow(`SELECT bump_count, last_bump, deleted FROM entry WHERE pid = ?`, pid).
|
||||
Scan(&bumpCount, &lastBump, &deleted); err != nil {
|
||||
writeError(w, http.StatusNotFound, "Beitrag nicht gefunden")
|
||||
return
|
||||
}
|
||||
if deleted != 0 {
|
||||
writeError(w, http.StatusNotFound, "Beitrag nicht gefunden")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
// Cooldown = bisherige Bumps in Tagen. Beim ersten Bump (bumpCount 0) ist die
|
||||
// Sperre 0 -> sofort erlaubt; danach wächst sie mit jedem Bump.
|
||||
if remaining := lastBump + bumpCount*bumpStep - now; remaining > 0 {
|
||||
w.Header().Set("Retry-After", strconv.FormatInt(remaining, 10))
|
||||
writeJSON(w, http.StatusTooManyRequests, map[string]any{
|
||||
"error": "Dieser Beitrag wurde erst kürzlich repostet.",
|
||||
"retry_after": remaining,
|
||||
"bump_count": bumpCount,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := db.Exec(
|
||||
`UPDATE entry SET last_activity = ?, bump_count = bump_count + 1, last_bump = ? WHERE pid = ?`,
|
||||
now, now, pid,
|
||||
); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Bumpen fehlgeschlagen")
|
||||
return
|
||||
}
|
||||
|
||||
bumpCount++
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"pid": pid,
|
||||
"bump_count": bumpCount,
|
||||
"last_bump": now,
|
||||
"last_activity": now,
|
||||
"retry_after": bumpCount * bumpStep, // bis zum nächsten erlaubten Bump
|
||||
})
|
||||
}
|
||||
|
||||
// voteTally liefert die Zähler und die eigene Auswahl (selected) für einen Beitrag.
|
||||
// Ist uid == 0 (nicht eingeloggt), ist selected immer "none".
|
||||
func voteTally(pid, uid int64) (left, right int, selected string) {
|
||||
|
||||
Reference in New Issue
Block a user