diff --git a/handlers/messages.go b/handlers/messages.go new file mode 100644 index 0000000..378fd49 --- /dev/null +++ b/handlers/messages.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "database/sql" + "log" + "net/http" + + "synlotto-website/helpers" + "synlotto-website/storage" +) + +func MessagesInboxHandler(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + data := BuildTemplateData(db, w, r) + context := helpers.TemplateContext(w, r, data) + + tmpl := helpers.LoadTemplateFiles("messages.html", "templates/account/messages/index.html") + + err := tmpl.ExecuteTemplate(w, "layout", context) + if err != nil { + helpers.RenderError(w, r, 500) + } + } +} + +func ReadMessageHandler(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + idStr := r.URL.Query().Get("id") + messageID := helpers.Atoi(idStr) + + session, _ := helpers.GetSession(w, r) + userID, ok := session.Values["user_id"].(int) + if !ok { + helpers.RenderError(w, r, 403) + return + } + + message, err := storage.GetMessageByID(db, userID, messageID) + if err != nil { + log.Printf("❌ Message not found: %v", err) + message = nil + } else if !message.IsRead { + _ = storage.MarkMessageAsRead(db, messageID, userID) + } + + data := BuildTemplateData(db, w, r) + context := helpers.TemplateContext(w, r, data) + context["Message"] = message + + tmpl := helpers.LoadTemplateFiles("read-message.html", "templates/account/messages/read.html") + + tmpl.ExecuteTemplate(w, "layout", context) + } +} diff --git a/helpers/template.go b/helpers/template.go index ec8025c..ffff078 100644 --- a/helpers/template.go +++ b/helpers/template.go @@ -60,6 +60,12 @@ func TemplateFuncs() template.FuncMap { "inSlice": InSlice, "lower": lower, "rangeClass": rangeClass, + "truncate": func(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max] + "..." + }, } } diff --git a/main.go b/main.go index 479ac6b..eaf3915 100644 --- a/main.go +++ b/main.go @@ -68,6 +68,8 @@ func setupAccountRoutes(mux *http.ServeMux, db *sql.DB) { mux.HandleFunc("/signup", middleware.Auth(false)(handlers.Signup)) mux.HandleFunc("/account/tickets/add_ticket", handlers.AddTicket(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/notifications", middleware.Auth(true)(handlers.NotificationsHandler(db))) mux.HandleFunc("/account/notifications/read", middleware.Auth(true)(handlers.MarkNotificationReadHandler(db))) } diff --git a/models/user.go b/models/user.go index daff8c9..2ffaf9d 100644 --- a/models/user.go +++ b/models/user.go @@ -23,12 +23,13 @@ type Notification struct { } type Message struct { - ID int - Sender string - Subject string - Message string - IsRead bool - CreatedAt time.Time + ID int + SenderId int + RecipientId int + Subject string + Message string + IsRead bool + CreatedAt time.Time } var db *sql.DB diff --git a/storage/messages.go b/storage/messages.go index 1854953..5168065 100644 --- a/storage/messages.go +++ b/storage/messages.go @@ -2,24 +2,25 @@ package storage import ( "database/sql" + "fmt" "synlotto-website/models" ) func GetMessageCount(db *sql.DB, userID int) (int, error) { var count int err := db.QueryRow(` - SELECT COUNT(*) FROM users_messages - WHERE user_id = ? AND is_read = FALSE + SELECT COUNT(*) FROM user_messages + WHERE recipient_id = ? AND is_read = FALSE `, userID).Scan(&count) return count, err } func GetRecentMessages(db *sql.DB, userID int, limit int) []models.Message { rows, err := db.Query(` - SELECT id, title, message, is_read - FROM users_messages - WHERE user_id = ? - ORDER BY created_at DESC + SELECT id, sender_id, recipient_id, subject, body, is_read, created_at + FROM user_messages + WHERE recipient_id = ? + ORDER BY created_at DESC LIMIT ? `, userID, limit) if err != nil { @@ -30,9 +31,53 @@ func GetRecentMessages(db *sql.DB, userID int, limit int) []models.Message { var messages []models.Message for rows.Next() { var m models.Message - rows.Scan(&m.ID, &m.Subject, &m.Message, &m.IsRead) - messages = append(messages, m) + err := rows.Scan( + &m.ID, + &m.SenderId, + &m.RecipientId, + &m.Subject, + &m.Message, + &m.IsRead, + &m.CreatedAt, + ) + if err == nil { + messages = append(messages, m) + } } - return messages } + +func GetMessageByID(db *sql.DB, userID, messageID int) (*models.Message, error) { + row := db.QueryRow(` + SELECT id, sender_id, recipient_id, subject, body, is_read, created_at + FROM user_messages + WHERE id = ? AND recipient_id = ? + `, messageID, userID) + + var m models.Message + err := row.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Message, &m.IsRead, &m.CreatedAt) + if err != nil { + return nil, err + } + return &m, nil +} + +func MarkMessageAsRead(db *sql.DB, messageID, userID int) error { + result, err := db.Exec(` + UPDATE user_messages + SET is_read = TRUE + WHERE id = ? AND recipient_id = ? + `, messageID, userID) + if err != nil { + return err + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + return err + } + if rowsAffected == 0 { + return fmt.Errorf("no matching message found for user_id=%d and message_id=%d", userID, messageID) + } + return nil +} diff --git a/storage/schema.go b/storage/schema.go index efdb29b..993f137 100644 --- a/storage/schema.go +++ b/storage/schema.go @@ -114,8 +114,9 @@ CREATE TABLE IF NOT EXISTS my_tickets ( const SchemaUsersMessages = ` CREATE TABLE IF NOT EXISTS users_messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL REFERENCES users(id), - title TEXT NOT NULL, + senderId INTEGER NOT NULL REFERENCES users(id), + recipientId int, + subject TEXT NOT NULL, message TEXT, is_read BOOLEAN DEFAULT FALSE, type VARCHAR(50), diff --git a/templates/account/messages/index.html b/templates/account/messages/index.html new file mode 100644 index 0000000..ff21f25 --- /dev/null +++ b/templates/account/messages/index.html @@ -0,0 +1,22 @@ +{{ define "content" }} +
+

Your Messages

+ +
+{{ end }} diff --git a/templates/account/messages/read.html b/templates/account/messages/read.html new file mode 100644 index 0000000..8d92980 --- /dev/null +++ b/templates/account/messages/read.html @@ -0,0 +1,15 @@ +{{ define "content" }} +
+ {{ if .Message }} +

{{ .Message.Subject }}

+

Received: {{ .Message.CreatedAt.Format "02 Jan 2006 15:04" }}

+
+

{{ .Message.Body }}

+ Back to Inbox + {{ else }} +
+ Message not found or access denied. +
+ {{ end }} +
+{{ end }} diff --git a/templates/topbar.html b/templates/topbar.html index 3ed641e..5921a9c 100644 --- a/templates/topbar.html +++ b/templates/topbar.html @@ -82,33 +82,35 @@