From 262536135d8df4453d54416cfe7271b4f1bc6cbc Mon Sep 17 00:00:00 2001 From: H3ALY Date: Thu, 30 Oct 2025 17:22:52 +0000 Subject: [PATCH] Still working through messages and notifications. --- internal/domain/messages/domain.go | 4 +- internal/handlers/account/messages/archive.go | 11 ++- internal/handlers/account/messages/read.go | 10 ++- internal/handlers/account/messages/send.go | 18 ++--- internal/handlers/account/tickets/list.go | 4 +- internal/http/routes/accountroutes.go | 4 +- internal/models/message.go | 15 ++++ internal/models/notification.go | 12 +++ internal/models/user.go | 23 ------ .../platform/services/messages/service.go | 27 +++---- .../services/notifications/service.go | 4 +- internal/storage/messages/read.go | 8 +- internal/storage/notifications/read.go | 8 +- web/templates/account/messages/archived.html | 25 +++--- web/templates/account/messages/index.html | 76 +++++++++---------- web/templates/account/messages/read.html | 11 ++- web/templates/account/messages/send.html | 20 +++-- web/templates/main/footer.html | 2 +- 18 files changed, 154 insertions(+), 128 deletions(-) create mode 100644 internal/models/message.go create mode 100644 internal/models/notification.go diff --git a/internal/domain/messages/domain.go b/internal/domain/messages/domain.go index e2d052d..db2c436 100644 --- a/internal/domain/messages/domain.go +++ b/internal/domain/messages/domain.go @@ -7,8 +7,8 @@ import ( type Message = models.Message type CreateMessageInput struct { - RecipientID int64 `form:"to" binding:"required,username"` - Subject string `form:"subject" binding:"required,max=200"` + RecipientID int64 `form:"to" binding:"required,numeric"` + Subject string `form:"recipient_id" binding:"required,max=200"` Body string `form:"body" binding:"required"` } diff --git a/internal/handlers/account/messages/archive.go b/internal/handlers/account/messages/archive.go index 2c9e1b1..b1d507e 100644 --- a/internal/handlers/account/messages/archive.go +++ b/internal/handlers/account/messages/archive.go @@ -7,10 +7,10 @@ package accountMessageHandler import ( "net/http" + templateHandlers "synlotto-website/internal/handlers/template" templateHelpers "synlotto-website/internal/helpers/template" "synlotto-website/internal/logging" - "synlotto-website/internal/models" "synlotto-website/internal/platform/bootstrap" "github.com/gin-gonic/gin" @@ -31,7 +31,9 @@ func (h *AccountMessageHandlers) ArchivedList(c *gin.Context) { return } - ctx := templateHelpers.TemplateContext(c.Writer, c.Request, models.TemplateData{}) + data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request) + ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data) + if f := sm.PopString(c.Request.Context(), "flash"); f != "" { ctx["Flash"] = f } @@ -39,10 +41,7 @@ func (h *AccountMessageHandlers) ArchivedList(c *gin.Context) { ctx["Title"] = "Archived Messages" ctx["Messages"] = msgs - tmpl := templateHelpers.LoadTemplateFiles( - "layout.html", - "web/templates/account/messages/archived.html", - ) + tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/archived.html") c.Status(http.StatusOK) if err := tmpl.ExecuteTemplate(c.Writer, "layout", ctx); err != nil { diff --git a/internal/handlers/account/messages/read.go b/internal/handlers/account/messages/read.go index 953a4c9..f039acb 100644 --- a/internal/handlers/account/messages/read.go +++ b/internal/handlers/account/messages/read.go @@ -7,10 +7,10 @@ package accountMessageHandler import ( "net/http" + templateHandlers "synlotto-website/internal/handlers/template" templateHelpers "synlotto-website/internal/helpers/template" "synlotto-website/internal/logging" - "synlotto-website/internal/models" "synlotto-website/internal/platform/bootstrap" "github.com/gin-gonic/gin" @@ -33,8 +33,8 @@ func (h *AccountMessageHandlers) List(c *gin.Context) { return } - // Build template context just like LoginGet - ctx := templateHelpers.TemplateContext(c.Writer, c.Request, models.TemplateData{}) + data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request) + ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data) if f := sm.PopString(c.Request.Context(), "flash"); f != "" { ctx["Flash"] = f @@ -73,7 +73,9 @@ func (h *AccountMessageHandlers) ReadGet(c *gin.Context) { return } - ctx := templateHelpers.TemplateContext(c.Writer, c.Request, models.TemplateData{}) + data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request) + ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data) + if f := sm.PopString(c.Request.Context(), "flash"); f != "" { ctx["Flash"] = f } diff --git a/internal/handlers/account/messages/send.go b/internal/handlers/account/messages/send.go index d2d8e93..4e54431 100644 --- a/internal/handlers/account/messages/send.go +++ b/internal/handlers/account/messages/send.go @@ -8,6 +8,7 @@ import ( "net/http" domain "synlotto-website/internal/domain/messages" + templateHandlers "synlotto-website/internal/handlers/template" templateHelpers "synlotto-website/internal/helpers/template" "synlotto-website/internal/logging" @@ -18,23 +19,22 @@ import ( "github.com/justinas/nosurf" ) -// GET /account/messages/add +// GET /account/messages/send // Renders: web/templates/account/messages/send.html -func (h *AccountMessageHandlers) AddGet(c *gin.Context) { +func (h *AccountMessageHandlers) SendGet(c *gin.Context) { app := c.MustGet("app").(*bootstrap.App) sm := app.SessionManager - ctx := templateHelpers.TemplateContext(c.Writer, c.Request, models.TemplateData{}) + data := templateHandlers.BuildTemplateData(app, c.Writer, c.Request) + ctx := templateHelpers.TemplateContext(c.Writer, c.Request, data) + if f := sm.PopString(c.Request.Context(), "flash"); f != "" { ctx["Flash"] = f } ctx["CSRFToken"] = nosurf.Token(c.Request) ctx["Title"] = "Send Message" - tmpl := templateHelpers.LoadTemplateFiles( - "layout.html", - "web/templates/account/messages/send.html", - ) + tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/messages/send.html") c.Status(http.StatusOK) if err := tmpl.ExecuteTemplate(c.Writer, "layout", ctx); err != nil { @@ -43,8 +43,8 @@ func (h *AccountMessageHandlers) AddGet(c *gin.Context) { } } -// POST /account/messages/add -func (h *AccountMessageHandlers) AddPost(c *gin.Context) { +// POST /account/messages/send +func (h *AccountMessageHandlers) SendPost(c *gin.Context) { app := c.MustGet("app").(*bootstrap.App) sm := app.SessionManager diff --git a/internal/handlers/account/tickets/list.go b/internal/handlers/account/tickets/list.go index 506fd5e..8b403e4 100644 --- a/internal/handlers/account/tickets/list.go +++ b/internal/handlers/account/tickets/list.go @@ -38,8 +38,8 @@ func List(c *gin.Context) { rows, err := app.DB.QueryContext(c.Request.Context(), ` SELECT id, numbers, game, price, purchased_at, created_at - FROM tickets - WHERE user_id = ? + FROM my_tickets + WHERE userId = ? ORDER BY purchased_at DESC, id DESC `, userID) if err != nil { diff --git a/internal/http/routes/accountroutes.go b/internal/http/routes/accountroutes.go index 28bfbb8..5d0bac3 100644 --- a/internal/http/routes/accountroutes.go +++ b/internal/http/routes/accountroutes.go @@ -66,8 +66,8 @@ func RegisterAccountRoutes(app *bootstrap.App) { messages.Use(middleware.AuthMiddleware(), middleware.RequireAuth()) { messages.GET("/", msgH.List) - messages.GET("/add", msgH.AddGet) - messages.POST("/add", msgH.AddPost) + messages.GET("/send", msgH.SendGet) + messages.POST("/send", msgH.SendPost) messages.GET("/archived", msgH.ArchivedList) // renders archived.html messages.GET("/:id", msgH.ReadGet) // renders read.html } diff --git a/internal/models/message.go b/internal/models/message.go new file mode 100644 index 0000000..18f4442 --- /dev/null +++ b/internal/models/message.go @@ -0,0 +1,15 @@ +package models + +import "time" + +type Message struct { + ID int + SenderId int + RecipientId int + Subject string + Body string + IsRead bool + IsArchived bool + CreatedAt time.Time + ArchivedAt *time.Time +} diff --git a/internal/models/notification.go b/internal/models/notification.go new file mode 100644 index 0000000..feec0bd --- /dev/null +++ b/internal/models/notification.go @@ -0,0 +1,12 @@ +package models + +import "time" + +type Notification struct { + ID int + UserId int + Title string + Body string + IsRead bool + CreatedAt time.Time +} diff --git a/internal/models/user.go b/internal/models/user.go index f7d362d..4c71e73 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -13,26 +13,3 @@ type User struct { CreatedAt time.Time UpdatedAt time.Time } - -// ToDo: should be in a notification model? -type Notification struct { - ID int - UserId int - Title string - Body string - IsRead bool - CreatedAt time.Time -} - -// ToDo: should be in a message model? -type Message struct { - ID int - SenderId int - RecipientId int - Subject string - Body string - IsRead bool - IsArchived bool - CreatedAt time.Time - ArchivedAt *time.Time -} diff --git a/internal/platform/services/messages/service.go b/internal/platform/services/messages/service.go index 2226a7d..b85c94f 100644 --- a/internal/platform/services/messages/service.go +++ b/internal/platform/services/messages/service.go @@ -37,14 +37,15 @@ func New(db *sql.DB, opts ...func(*Service)) *Service { // Ensure *Service satisfies the domain interface. var _ domain.MessageService = (*Service)(nil) +// ToDo: Needs a userId on table or rename the recipiant id.. but then again dont want to expose userids to users for sending. func (s *Service) ListInbox(userID int64) ([]domain.Message, error) { ctx, cancel := context.WithTimeout(context.Background(), s.Timeout) defer cancel() q := ` - SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at + SELECT id, senderId, recipientId, subject, message, is_read, is_archived, created_at FROM users_messages - WHERE user_id = ? AND is_archived = FALSE + WHERE recipientId = ? AND is_archived = FALSE ORDER BY created_at DESC` q = s.bind(q) @@ -57,7 +58,7 @@ func (s *Service) ListInbox(userID int64) ([]domain.Message, error) { var out []domain.Message for rows.Next() { var m domain.Message - if err := rows.Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil { + if err := rows.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil { return nil, err } out = append(out, m) @@ -71,9 +72,9 @@ func (s *Service) ListArchived(userID int64) ([]domain.Message, error) { defer cancel() q := ` - SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at + SELECT id, senderId, recipientId, subject, message, is_read, is_archived, created_at FROM users_messages - WHERE user_id = ? AND is_archived = TRUE + WHERE recipientId = ? AND is_archived = TRUE ORDER BY created_at DESC` q = s.bind(q) @@ -86,7 +87,7 @@ func (s *Service) ListArchived(userID int64) ([]domain.Message, error) { var out []domain.Message for rows.Next() { var m domain.Message - if err := rows.Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil { + if err := rows.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil { return nil, err } out = append(out, m) @@ -99,14 +100,14 @@ func (s *Service) GetByID(userID, id int64) (*domain.Message, error) { defer cancel() q := ` - SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at + SELECT id, senderId, recipientId, subject, message, is_read, is_archived, created_at FROM users_messages - WHERE user_id = ? AND id = ?` + WHERE recipientId = ? AND id = ?` q = s.bind(q) var m domain.Message err := s.DB.QueryRowContext(ctx, q, userID, id). - Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt) + Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt) if errors.Is(err, sql.ErrNoRows) { return nil, nil } @@ -123,19 +124,19 @@ func (s *Service) Create(userID int64, in domain.CreateMessageInput) (int64, err switch s.Dialect { case "postgres": const q = ` - INSERT INTO messages (user_id, from_email, to_email, subject, body, is_read, is_archived, created_at) + INSERT INTO messages (id, senderId, recipientId, subject, message, is_read, is_archived, created_at) VALUES ($1, $2, $3, $4, $5, FALSE, FALSE, NOW()) RETURNING id` var id int64 - if err := s.DB.QueryRowContext(ctx, q, userID, "", in.To, in.Subject, in.Body).Scan(&id); err != nil { + if err := s.DB.QueryRowContext(ctx, q, userID, "", in.RecipientID, in.Subject, in.Body).Scan(&id); err != nil { return 0, err } return id, nil default: // mysql/sqlite const q = ` - INSERT INTO messages (user_id, from_email, to_email, subject, body, is_read, is_archived, created_at) + INSERT INTO messages (id, senderId, recipientId, subject, message is_read, is_archived, created_at) VALUES (?, ?, ?, ?, ?, FALSE, FALSE, CURRENT_TIMESTAMP)` - res, err := s.DB.ExecContext(ctx, q, userID, "", in.To, in.Subject, in.Body) + res, err := s.DB.ExecContext(ctx, q, userID, "", in.RecipientID, in.Subject, in.Body) if err != nil { return 0, err } diff --git a/internal/platform/services/notifications/service.go b/internal/platform/services/notifications/service.go index f9014f1..b0130c3 100644 --- a/internal/platform/services/notifications/service.go +++ b/internal/platform/services/notifications/service.go @@ -42,7 +42,7 @@ func (s *Service) List(userID int64) ([]domain.Notification, error) { const q = ` SELECT id, title, body, is_read, created_at FROM notifications -WHERE user_id = ? +WHERE userId = ? ORDER BY created_at DESC` rows, err := s.DB.QueryContext(ctx, q, userID) @@ -69,7 +69,7 @@ func (s *Service) GetByID(userID, id int64) (*domain.Notification, error) { const q = ` SELECT id, title, body, is_read, created_at FROM notifications -WHERE user_id = ? AND id = ?` +WHERE userId = ? AND id = ?` var n domain.Notification err := s.DB.QueryRowContext(ctx, q, userID, id). diff --git a/internal/storage/messages/read.go b/internal/storage/messages/read.go index 3040b30..1fb544e 100644 --- a/internal/storage/messages/read.go +++ b/internal/storage/messages/read.go @@ -36,7 +36,7 @@ func GetRecentMessages(db *sql.DB, userID int, limit int) []models.Message { &m.SenderId, &m.RecipientId, &m.Subject, - &m.Message, + &m.Body, &m.IsRead, &m.CreatedAt, ) @@ -55,7 +55,7 @@ func GetMessageByID(db *sql.DB, userID, messageID int) (*models.Message, error) `, messageID, userID) var m models.Message - err := row.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Message, &m.IsRead, &m.CreatedAt) + err := row.Scan(&m.ID, &m.SenderId, &m.RecipientId, &m.Subject, &m.Body, &m.IsRead, &m.CreatedAt) if err != nil { return nil, err } @@ -81,7 +81,7 @@ func GetArchivedMessages(db *sql.DB, userID int, page, perPage int) []models.Mes var m models.Message err := rows.Scan( &m.ID, &m.SenderId, &m.RecipientId, - &m.Subject, &m.Message, &m.IsRead, + &m.Subject, &m.Body, &m.IsRead, &m.CreatedAt, &m.ArchivedAt, ) if err == nil { @@ -110,7 +110,7 @@ func GetInboxMessages(db *sql.DB, userID int, page, perPage int) []models.Messag var m models.Message err := rows.Scan( &m.ID, &m.SenderId, &m.RecipientId, - &m.Subject, &m.Message, &m.IsRead, &m.CreatedAt, + &m.Subject, &m.Body, &m.IsRead, &m.CreatedAt, ) if err == nil { messages = append(messages, m) diff --git a/internal/storage/notifications/read.go b/internal/storage/notifications/read.go index 74ec21e..98ac87d 100644 --- a/internal/storage/notifications/read.go +++ b/internal/storage/notifications/read.go @@ -16,7 +16,7 @@ func GetNotificationByID(db *sql.DB, userID, notificationID int) (*models.Notifi `, notificationID, userID) var n models.Notification - err := row.Scan(&n.ID, &n.UserId, &n.Subject, &n.Body, &n.IsRead) + err := row.Scan(&n.ID, &n.UserId, &n.Title, &n.Body, &n.IsRead) if err != nil { return nil, err } @@ -27,7 +27,7 @@ func GetNotificationCount(db *sql.DB, userID int) int { var count int err := db.QueryRow(` SELECT COUNT(*) FROM users_notification - WHERE user_id = ? AND is_read = FALSE`, userID).Scan(&count) + WHERE userId = ? AND is_read = FALSE`, userID).Scan(&count) if err != nil { log.Println("⚠️ Failed to count notifications:", err) @@ -41,7 +41,7 @@ func GetRecentNotifications(db *sql.DB, userID int, limit int) []models.Notifica rows, err := db.Query(` SELECT id, subject, body, is_read, created_at FROM users_notification - WHERE user_id = ? + WHERE userId = ? ORDER BY created_at DESC LIMIT ?`, userID, limit) if err != nil { @@ -54,7 +54,7 @@ func GetRecentNotifications(db *sql.DB, userID int, limit int) []models.Notifica for rows.Next() { var n models.Notification - if err := rows.Scan(&n.ID, &n.Subject, &n.Body, &n.IsRead, &n.CreatedAt); err == nil { + if err := rows.Scan(&n.ID, &n.Title, &n.Body, &n.IsRead, &n.CreatedAt); err == nil { notifications = append(notifications, n) } } diff --git a/web/templates/account/messages/archived.html b/web/templates/account/messages/archived.html index ece8187..2daa685 100644 --- a/web/templates/account/messages/archived.html +++ b/web/templates/account/messages/archived.html @@ -7,20 +7,27 @@
{{ .Subject }}
-

{{ .Message }}

+

{{ .Body }}

- Archived: {{ .ArchivedAt.Format "02 Jan 2006 15:04" }} + + Archived: + {{ if .ArchivedAt.Valid }} + {{ .ArchivedAt.Time.Format "02 Jan 2006 15:04" }} + {{ else }} + — + {{ end }} +

+
+ + + +
-
- {{ $.CSRFField }} - - -
{{ end }} - +