Fleshing out some routes from notifications and messages
This commit is contained in:
128
internal/handlers/account/messages/list.go
Normal file
128
internal/handlers/account/messages/list.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package accountMessageHandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET /account/messages
|
||||||
|
// Renders: web/templates/account/messages/index.html
|
||||||
|
func (h *AccountMessageHandlers) List(c *gin.Context) {
|
||||||
|
userID := mustUserID(c) // replace with your auth/user extraction
|
||||||
|
msgs, err := h.Svc.ListInbox(userID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load messages"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "account/messages/index.html", gin.H{
|
||||||
|
"title": "Messages",
|
||||||
|
"messages": msgs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /account/messages/add
|
||||||
|
// Renders: web/templates/account/messages/send.html
|
||||||
|
func (h *AccountMessageHandlers) AddGet(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "account/messages/send.html", gin.H{
|
||||||
|
"title": "Send Message",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /account/messages/add
|
||||||
|
func (h *AccountMessageHandlers) AddPost(c *gin.Context) {
|
||||||
|
userID := mustUserID(c)
|
||||||
|
var in CreateMessageInput
|
||||||
|
if err := c.ShouldBind(&in); err != nil {
|
||||||
|
// Re-render form with validation errors
|
||||||
|
c.HTML(http.StatusBadRequest, "account/messages/send.html", gin.H{
|
||||||
|
"title": "Send Message",
|
||||||
|
"error": "Please correct the errors below.",
|
||||||
|
"form": in,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := h.Svc.Create(userID, in); err != nil {
|
||||||
|
c.HTML(http.StatusInternalServerError, "account/messages/send.html", gin.H{
|
||||||
|
"title": "Send Message",
|
||||||
|
"error": "Could not send message.",
|
||||||
|
"form": in,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Redirect back to inbox
|
||||||
|
c.Redirect(http.StatusSeeOther, "/account/messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Optional extras since you have read.html and archived.html ---
|
||||||
|
|
||||||
|
// GET /account/messages/:id
|
||||||
|
// Renders: web/templates/account/messages/read.html
|
||||||
|
func (h *AccountMessageHandlers) ReadGet(c *gin.Context) {
|
||||||
|
userID := mustUserID(c)
|
||||||
|
id, err := parseIDParam(c, "id")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg, err := h.Svc.GetByID(userID, id)
|
||||||
|
if err != nil || msg == nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "account/messages/read.html", gin.H{
|
||||||
|
"title": msg.Subject,
|
||||||
|
"message": msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /account/messages/archived
|
||||||
|
// Renders: web/templates/account/messages/archived.html
|
||||||
|
func (h *AccountMessageHandlers) ArchivedList(c *gin.Context) {
|
||||||
|
userID := mustUserID(c)
|
||||||
|
msgs, err := h.Svc.ListArchived(userID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load archived messages"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "account/messages/archived.html", gin.H{
|
||||||
|
"title": "Archived Messages",
|
||||||
|
"messages": msgs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- helpers ---
|
||||||
|
|
||||||
|
func mustUserID(c *gin.Context) int64 {
|
||||||
|
// Pull from your auth middleware/session. Panic-unsafe alternative:
|
||||||
|
if v, ok := c.Get("userID"); ok {
|
||||||
|
if id, ok2 := v.(int64); ok2 {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback for stubs:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIDParam(c *gin.Context, name string) (int64, error) {
|
||||||
|
// typical atoi wrapper
|
||||||
|
// (implement: strconv.ParseInt(c.Param(name), 10, 64))
|
||||||
|
return atoi64(c.Param(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func atoi64(s string) (int64, error) {
|
||||||
|
// small helper to keep imports focused
|
||||||
|
// replace with strconv.ParseInt in real code
|
||||||
|
var n int64
|
||||||
|
for _, ch := range []byte(s) {
|
||||||
|
if ch < '0' || ch > '9' {
|
||||||
|
return 0, &strconvNumErr{}
|
||||||
|
}
|
||||||
|
n = n*10 + int64(ch-'0')
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type strconvNumErr struct{}
|
||||||
|
|
||||||
|
func (e *strconvNumErr) Error() string { return "invalid number" }
|
||||||
32
internal/handlers/account/messages/types.go
Normal file
32
internal/handlers/account/messages/types.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package accountMessageHandlers
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type AccountMessageHandlers struct {
|
||||||
|
Svc MessageService
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateMessageInput struct {
|
||||||
|
To string `form:"to" binding:"required,email"`
|
||||||
|
Subject string `form:"subject" binding:"required,max=200"`
|
||||||
|
Body string `form:"body" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
ID int64
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
Subject string
|
||||||
|
Body string
|
||||||
|
IsRead bool
|
||||||
|
IsArchived bool
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: Should interfaces be else where?
|
||||||
|
type MessageService interface {
|
||||||
|
ListInbox(userID int64) ([]Message, error)
|
||||||
|
ListArchived(userID int64) ([]Message, error)
|
||||||
|
GetByID(userID, id int64) (*Message, error)
|
||||||
|
Create(userID int64, in CreateMessageInput) (int64, error)
|
||||||
|
}
|
||||||
78
internal/handlers/account/notifications/list.go
Normal file
78
internal/handlers/account/notifications/list.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package accountNotificationHandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAccountNotificationHandlers(svc NotificationService) *AccountNotificationHandlers {
|
||||||
|
return &AccountNotificationHandlers{Svc: svc}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /account/notifications
|
||||||
|
// Renders: web/templates/account/notifications/index.html
|
||||||
|
func (h *AccountNotificationHandlers) List(c *gin.Context) {
|
||||||
|
userID := mustUserID(c)
|
||||||
|
items, err := h.Svc.List(userID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to load notifications"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "account/notifications/index.html", gin.H{
|
||||||
|
"title": "Notifications",
|
||||||
|
"notifications": items,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Optional since you have read.html ---
|
||||||
|
|
||||||
|
// GET /account/notifications/:id
|
||||||
|
// Renders: web/templates/account/notifications/read.html
|
||||||
|
func (h *AccountNotificationHandlers) ReadGet(c *gin.Context) {
|
||||||
|
userID := mustUserID(c)
|
||||||
|
id, err := parseIDParam(c, "id")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nt, err := h.Svc.GetByID(userID, id)
|
||||||
|
if err != nil || nt == nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "account/notifications/read.html", gin.H{
|
||||||
|
"title": nt.Title,
|
||||||
|
"notification": nt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- helpers (same as in messages; consider sharing in a pkg) ---
|
||||||
|
|
||||||
|
func mustUserID(c *gin.Context) int64 {
|
||||||
|
if v, ok := c.Get("userID"); ok {
|
||||||
|
if id, ok2 := v.(int64); ok2 {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIDParam(c *gin.Context, name string) (int64, error) {
|
||||||
|
return atoi64(c.Param(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func atoi64(s string) (int64, error) {
|
||||||
|
var n int64
|
||||||
|
for _, ch := range []byte(s) {
|
||||||
|
if ch < '0' || ch > '9' {
|
||||||
|
return 0, &strconvNumErr{}
|
||||||
|
}
|
||||||
|
n = n*10 + int64(ch-'0')
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type strconvNumErr struct{}
|
||||||
|
|
||||||
|
func (e *strconvNumErr) Error() string { return "invalid number" }
|
||||||
21
internal/handlers/account/notifications/types.go
Normal file
21
internal/handlers/account/notifications/types.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package accountNotificationHandler
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
ID int64
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
IsRead bool
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountNotificationHandlers struct {
|
||||||
|
Svc NotificationService
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: Should interfaces be else where?
|
||||||
|
type NotificationService interface {
|
||||||
|
List(userID int64) ([]Notification, error)
|
||||||
|
GetByID(userID, id int64) (*Notification, error)
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
accountHandlers "synlotto-website/internal/handlers/account"
|
accountHandlers "synlotto-website/internal/handlers/account"
|
||||||
|
accountMessageHandlers "synlotto-website/internal/handlers/account/messages"
|
||||||
|
accountNotificationHandlers "synlotto-website/internal/handlers/account/notifications"
|
||||||
accountTicketHandlers "synlotto-website/internal/handlers/account/tickets"
|
accountTicketHandlers "synlotto-website/internal/handlers/account/tickets"
|
||||||
|
|
||||||
"synlotto-website/internal/http/middleware"
|
"synlotto-website/internal/http/middleware"
|
||||||
@@ -49,6 +51,25 @@ func RegisterAccountRoutes(app *bootstrap.App) {
|
|||||||
accAuth.GET("/logout", accountHandlers.Logout) // optional
|
accAuth.GET("/logout", accountHandlers.Logout) // optional
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Messages (auth-required)
|
||||||
|
messages := r.Group("/account/messages")
|
||||||
|
messages.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
||||||
|
{
|
||||||
|
messages.GET("/", accountMessageHandlers.List)
|
||||||
|
messages.GET("/add", accountMessageHandlers.AddGet)
|
||||||
|
messages.POST("/add", accountMessageHandlers.AddPost)
|
||||||
|
messages.GET("/archived", accountMessageHandlers.ArchivedList) // renders archived.html
|
||||||
|
messages.GET("/:id", accountMessageHandlers.ReadGet) // renders read.html
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications (auth-required)
|
||||||
|
notifications := r.Group("/account/notifications")
|
||||||
|
notifications.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
||||||
|
{
|
||||||
|
notifications.GET("/", accountNotificationHandlers.List)
|
||||||
|
notifications.GET("/:id", accountNotificationHandlers.ReadGet) // renders read.html
|
||||||
|
}
|
||||||
|
|
||||||
// Tickets (auth-required)
|
// Tickets (auth-required)
|
||||||
tickets := r.Group("/account/tickets")
|
tickets := r.Group("/account/tickets")
|
||||||
tickets.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
tickets.Use(middleware.AuthMiddleware(), middleware.RequireAuth())
|
||||||
|
|||||||
Reference in New Issue
Block a user