Messages: Add archive (soft-delete) support + dropdown UI polish

- Implemented `/account/messages/archive` route for soft-archiving messages
- Added `is_archived` flag to `users_messages` schema and model
- Topbar dropdown now reflects accurate unread message count
- Fixed missing route registration for archive handler
- Improved message visibility checks to prevent access violations
- Placeholder for rate-limit (429) error page rendering identified
This commit is contained in:
2025-04-02 17:15:57 +01:00
parent 2fd053777d
commit e3428911b9
5 changed files with 34 additions and 3 deletions

View File

@@ -52,3 +52,23 @@ func ReadMessageHandler(db *sql.DB) http.HandlerFunc {
tmpl.ExecuteTemplate(w, "layout", context)
}
}
func ArchiveMessageHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := helpers.Atoi(r.URL.Query().Get("id"))
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
err := storage.ArchiveMessage(db, userID, id)
if err != nil {
helpers.SetFlash(w, r, "Failed to archive message.")
} else {
helpers.SetFlash(w, r, "Message archived.")
}
http.Redirect(w, r, "/account/messages", http.StatusSeeOther)
}
}

View File

@@ -70,6 +70,7 @@ func setupAccountRoutes(mux *http.ServeMux, db *sql.DB) {
mux.HandleFunc("/account/tickets/my_tickets", handlers.GetMyTickets(db))
mux.HandleFunc("/account/messages", middleware.Auth(true)(handlers.MessagesInboxHandler(db)))
mux.HandleFunc("/account/messages/read", middleware.Auth(true)(handlers.ReadMessageHandler(db)))
mux.HandleFunc("/account/messages/archive", middleware.Auth(true)(handlers.ArchiveMessageHandler(db)))
mux.HandleFunc("/account/notifications", middleware.Auth(true)(handlers.NotificationsHandler(db)))
mux.HandleFunc("/account/notifications/read", middleware.Auth(true)(handlers.MarkNotificationReadHandler(db)))
}

View File

@@ -10,7 +10,7 @@ func GetMessageCount(db *sql.DB, userID int) (int, error) {
var count int
err := db.QueryRow(`
SELECT COUNT(*) FROM users_messages
WHERE recipientId = ? AND is_read = FALSE
WHERE recipientId = ? AND is_read = FALSE AND is_archived = FALSE
`, userID).Scan(&count)
return count, err
}
@@ -19,7 +19,7 @@ func GetRecentMessages(db *sql.DB, userID int, limit int) []models.Message {
rows, err := db.Query(`
SELECT id, senderId, recipientId, subject, message, is_read, created_at
FROM users_messages
WHERE recipientId = ?
WHERE recipientId = ? AND is_archived = FALSE
ORDER BY created_at DESC
LIMIT ?
`, userID, limit)
@@ -81,3 +81,12 @@ func MarkMessageAsRead(db *sql.DB, messageID, userID int) error {
}
return nil
}
func ArchiveMessage(db *sql.DB, userID, messageID int) error {
_, err := db.Exec(`
UPDATE users_messages
SET is_archived = TRUE
WHERE id = ? AND recipientId = ?
`, messageID, userID)
return err
}

View File

@@ -119,6 +119,7 @@ CREATE TABLE IF NOT EXISTS users_messages (
subject TEXT NOT NULL,
message TEXT,
is_read BOOLEAN DEFAULT FALSE,
is_archived BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`

View File

@@ -5,7 +5,7 @@
<p class="text-muted">Received: {{ .Message.CreatedAt.Format "02 Jan 2006 15:04" }}</p>
<hr>
<p>{{ .Message.Message }}</p>
<a href="/account/messages" class="btn btn-secondary mt-4">Back to Inbox</a>
<a href="/account/messages" class="btn btn-secondary mt-4">Back to Inbox</a> <a href="/account/messages/archive?id={{ .Message.ID }}" class="btn btn-outline-danger mt-3">Archive</a>
{{ else }}
<div class="alert alert-danger text-center">
Message not found or access denied.