Stack of changes to get gin, scs, nosurf running.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// internal/handlers/lottery/tickets/ticket_handler.go
|
||||
package handlers
|
||||
|
||||
import (
|
||||
@@ -10,21 +11,23 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
httpHelpers "synlotto-website/internal/helpers/http"
|
||||
templateHandlers "synlotto-website/internal/handlers/template"
|
||||
securityHelpers "synlotto-website/internal/helpers/security"
|
||||
templateHelpers "synlotto-website/internal/helpers/template"
|
||||
draws "synlotto-website/internal/services/draws"
|
||||
|
||||
"synlotto-website/internal/helpers"
|
||||
"synlotto-website/internal/models"
|
||||
"synlotto-website/internal/platform/bootstrap"
|
||||
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
return httpHelpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||
// AddTicket renders the add-ticket form (GET) and handles multi-line ticket submission (POST).
|
||||
func AddTicket(app *bootstrap.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
rows, err := db.Query(`
|
||||
rows, err := app.DB.Query(`
|
||||
SELECT DISTINCT draw_date
|
||||
FROM results_thunderball
|
||||
ORDER BY draw_date DESC
|
||||
@@ -44,29 +47,27 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
data := models.TemplateData{}
|
||||
// Use shared template data builder (expects *bootstrap.App)
|
||||
data := templateHandlers.BuildTemplateData(app, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["CSRFToken"] = nosurf.Token(r)
|
||||
context["DrawDates"] = drawDates
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("add_ticket.html", "templates/account/tickets/add_ticket.html")
|
||||
|
||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||
if err != nil {
|
||||
tmpl := templateHelpers.LoadTemplateFiles("add_ticket.html", "web/templates/account/tickets/add_ticket.html")
|
||||
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
||||
log.Println("❌ Template render error:", err)
|
||||
http.Error(w, "Error rendering form", http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err := r.ParseMultipartForm(10 << 20)
|
||||
if err != nil {
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil {
|
||||
http.Error(w, "Invalid form", http.StatusBadRequest)
|
||||
log.Println("❌ Failed to parse form:", err)
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
userID, ok := securityHelpers.GetCurrentUserID(app.SessionManager, r)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/account/login", http.StatusSeeOther)
|
||||
return
|
||||
@@ -77,7 +78,6 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
purchaseMethod := r.FormValue("purchase_method")
|
||||
purchaseDate := r.FormValue("purchase_date")
|
||||
purchaseTime := r.FormValue("purchase_time")
|
||||
|
||||
if purchaseTime != "" {
|
||||
purchaseDate += "T" + purchaseTime
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
out, err := os.Create(filename)
|
||||
if err == nil {
|
||||
defer out.Close()
|
||||
io.Copy(out, file)
|
||||
_, _ = io.Copy(out, file)
|
||||
imagePath = filename
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := db.Exec(`
|
||||
if _, err := app.DB.Exec(`
|
||||
INSERT INTO my_tickets (
|
||||
userId, game_type, draw_date,
|
||||
ball1, ball2, ball3, ball4, ball5, ball6,
|
||||
@@ -169,27 +169,26 @@ func AddTicket(db *sql.DB) http.HandlerFunc {
|
||||
b[0], b[1], b[2], b[3], b[4], b[5],
|
||||
bo[0], bo[1],
|
||||
purchaseMethod, purchaseDate, imagePath,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
log.Println("❌ Failed to insert ticket line:", err)
|
||||
} else {
|
||||
log.Printf("✅ Ticket line %d saved", i+1) // ToDo create audit
|
||||
log.Printf("✅ Ticket line %d saved", i+1)
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/tickets", http.StatusSeeOther)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
||||
return httpHelpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(10 << 20)
|
||||
if err != nil {
|
||||
// SubmitTicket handles alternate multipart ticket submission (POST-only).
|
||||
func SubmitTicket(app *bootstrap.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil {
|
||||
http.Error(w, "Invalid form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
userID, ok := securityHelpers.GetCurrentUserID(app.SessionManager, r)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/account/login", http.StatusSeeOther)
|
||||
return
|
||||
@@ -200,7 +199,6 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
||||
purchaseMethod := r.FormValue("purchase_method")
|
||||
purchaseDate := r.FormValue("purchase_date")
|
||||
purchaseTime := r.FormValue("purchase_time")
|
||||
|
||||
if purchaseTime != "" {
|
||||
purchaseDate += "T" + purchaseTime
|
||||
}
|
||||
@@ -213,13 +211,13 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
||||
out, err := os.Create(filename)
|
||||
if err == nil {
|
||||
defer out.Close()
|
||||
io.Copy(out, file)
|
||||
_, _ = io.Copy(out, file)
|
||||
imagePath = filename
|
||||
}
|
||||
}
|
||||
|
||||
ballCount := 6
|
||||
bonusCount := 2
|
||||
const ballCount = 6
|
||||
const bonusCount = 2
|
||||
|
||||
balls := make([][]int, ballCount)
|
||||
bonuses := make([][]int, bonusCount)
|
||||
@@ -247,7 +245,7 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
_, err := db.Exec(`
|
||||
if _, err := app.DB.Exec(`
|
||||
INSERT INTO my_tickets (
|
||||
user_id, game_type, draw_date,
|
||||
ball1, ball2, ball3, ball4, ball5, ball6,
|
||||
@@ -259,30 +257,30 @@ func SubmitTicket(db *sql.DB) http.HandlerFunc {
|
||||
b[0], b[1], b[2], b[3], b[4], b[5],
|
||||
bo[0], bo[1],
|
||||
purchaseMethod, purchaseDate, imagePath,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
log.Println("❌ Insert failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/tickets", http.StatusSeeOther)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||
return httpHelpers.AuthMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||
data := models.TemplateData{}
|
||||
var tickets []models.Ticket
|
||||
// GetMyTickets lists the current user's tickets.
|
||||
func GetMyTickets(app *bootstrap.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Use shared template data builder (ensures user/flash/notifications present)
|
||||
data := templateHandlers.BuildTemplateData(app, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["Tickets"] = tickets
|
||||
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
userID, ok := securityHelpers.GetCurrentUserID(app.SessionManager, r)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/account/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := db.Query(`
|
||||
var tickets []models.Ticket
|
||||
rows, err := app.DB.Query(`
|
||||
SELECT id, game_type, draw_date,
|
||||
ball1, ball2, ball3, ball4, ball5, ball6,
|
||||
bonus1, bonus2,
|
||||
@@ -308,19 +306,18 @@ func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||
var prizeLabel sql.NullString
|
||||
var prizeAmount sql.NullFloat64
|
||||
|
||||
err := rows.Scan(
|
||||
if err := rows.Scan(
|
||||
&t.Id, &t.GameType, &t.DrawDate,
|
||||
&b1, &b2, &b3, &b4, &b5, &b6,
|
||||
&bo1, &bo2,
|
||||
&t.PurchaseMethod, &t.PurchaseDate, &t.ImagePath, &t.Duplicate,
|
||||
&matchedMain, &matchedBonus, &prizeTier, &isWinner, &prizeLabel, &prizeAmount,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
log.Println("⚠️ Failed to scan ticket row:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Build primary number + bonus fields
|
||||
// Normalize fields
|
||||
t.Ball1 = int(b1.Int64)
|
||||
t.Ball2 = int(b2.Int64)
|
||||
t.Ball3 = int(b3.Int64)
|
||||
@@ -348,28 +345,55 @@ func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||
if prizeAmount.Valid {
|
||||
t.PrizeAmount = prizeAmount.Float64
|
||||
}
|
||||
// Build balls slices (for template use)
|
||||
|
||||
// Derived fields for templates
|
||||
t.Balls = helpers.BuildBallsSlice(t)
|
||||
t.BonusBalls = helpers.BuildBonusSlice(t)
|
||||
|
||||
// 🎯 Get the actual draw info (used to show which numbers matched)
|
||||
draw := draws.GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
||||
// Fetch matching draw info
|
||||
draw := draws.GetDrawResultForTicket(app.DB, t.GameType, t.DrawDate)
|
||||
t.MatchedDraw = draw
|
||||
|
||||
// ✅ DEBUG
|
||||
log.Printf("✅ Ticket #%d", t.Id)
|
||||
log.Printf("Balls: %v", t.Balls)
|
||||
log.Printf("DrawResult: %+v", draw)
|
||||
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("my_tickets.html", "templates/account/tickets/my_tickets.html")
|
||||
context["Tickets"] = tickets
|
||||
|
||||
err = tmpl.ExecuteTemplate(w, "layout", context)
|
||||
if err != nil {
|
||||
tmpl := templateHelpers.LoadTemplateFiles("my_tickets.html", "web/templates/account/tickets/my_tickets.html")
|
||||
if err := tmpl.ExecuteTemplate(w, "layout", context); err != nil {
|
||||
log.Println("❌ Template error:", err)
|
||||
http.Error(w, "Error rendering page", http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ToDo
|
||||
// http: superfluous response.WriteHeader call (from SCS)
|
||||
|
||||
//This happens when headers are written twice in a request. With SCS, it sets cookies in WriteHeader. If something else already wrote the headers (or wrote them again), you see this warning.
|
||||
|
||||
//Common culprits & fixes:
|
||||
|
||||
//Use Gin’s redirect instead of the stdlib one:
|
||||
|
||||
// Replace:
|
||||
//http.Redirect(w, r, "/account/login", http.StatusSeeOther)
|
||||
|
||||
// With:
|
||||
//c.Redirect(http.StatusSeeOther, "/account/login")
|
||||
//c.Abort() // stop further handlers writing
|
||||
|
||||
//Do this everywhere you redirect (signup, login, logout).
|
||||
|
||||
//Don’t call two status-writes. For template GETs, this is fine:
|
||||
|
||||
//c.Status(http.StatusOK)
|
||||
//_ = tmpl.ExecuteTemplate(c.Writer, "layout", ctx) // writes body once
|
||||
|
||||
//Just make sure you never write another header after that.
|
||||
|
||||
//Keep your wrapping order as you have it (it’s correct):
|
||||
|
||||
//Gin → SCS.LoadAndSave → NoSurf → http.Server
|
||||
|
||||
//If you still get the warning after switching to c.Redirect + c.Abort(), tell me which handler it’s coming from and I’ll point to the exact double-write.
|
||||
|
||||
Reference in New Issue
Block a user