**Untested! ** Add restore functionality for archived messages

- Added `RestoreMessageHandler` and route at `/account/messages/restore`
- Updated `users_messages` table to support `archived_at` reset
- Added restore button to archived messages template
- Ensures archived messages can be moved back into inbox
This commit is contained in:
2025-04-02 23:53:29 +01:00
parent db5352bc9c
commit 053ccf3845
13 changed files with 756 additions and 0 deletions

236
handlers/syndicate.go Normal file
View File

@@ -0,0 +1,236 @@
package handlers
import (
"database/sql"
"fmt"
"net/http"
"synlotto-website/helpers"
"synlotto-website/models"
"synlotto-website/storage"
)
func CreateSyndicateHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
tmpl := helpers.LoadTemplateFiles("create-syndicate.html", "templates/account/syndicates/create.html")
tmpl.ExecuteTemplate(w, "layout", context)
case http.MethodPost:
name := r.FormValue("name")
description := r.FormValue("description")
userID, ok := helpers.GetCurrentUserID(r)
if !ok || name == "" {
helpers.SetFlash(w, r, "Invalid data submitted")
http.Redirect(w, r, "/account/syndicates/create", http.StatusSeeOther)
return
}
err := storage.CreateSyndicate(db, name, description, userID)
if err != nil {
helpers.SetFlash(w, r, "Failed to create syndicate")
} else {
helpers.SetFlash(w, r, "Syndicate created successfully")
}
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther)
default:
helpers.RenderError(w, r, http.StatusMethodNotAllowed)
}
}
}
func ListSyndicatesHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403) // ToDo need to make this use the handler so i dont need to define errors.
return
}
managed := storage.GetSyndicatesByOwner(db, userID)
member := storage.GetSyndicatesByMember(db, userID)
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["ManagedSyndicates"] = managed
context["JoinedSyndicates"] = member
tmpl := helpers.LoadTemplateFiles("syndicates.html", "templates/account/syndicates/index.html")
tmpl.ExecuteTemplate(w, "layout", context)
}
}
func ViewSyndicateHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
syndicate, err := storage.GetSyndicateByID(db, syndicateID)
if err != nil || syndicate == nil {
helpers.RenderError(w, r, 404)
return
}
isManager := userID == syndicate.OwnerID
isMember := storage.IsSyndicateMember(db, syndicateID, userID)
if !isManager && !isMember {
helpers.RenderError(w, r, 403)
return
}
members := storage.GetSyndicateMembers(db, syndicateID)
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["Syndicate"] = syndicate
context["Members"] = members
context["IsManager"] = isManager
tmpl := helpers.LoadTemplateFiles("syndicate-view.html", "templates/syndicates/view.html")
tmpl.ExecuteTemplate(w, "layout", context)
}
}
func InviteMemberHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
syndicate, err := storage.GetSyndicateByID(db, syndicateID)
if err != nil || syndicate == nil || syndicate.OwnerID != userID {
helpers.RenderError(w, r, 403)
return
}
switch r.Method {
case http.MethodGet:
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["Syndicate"] = syndicate
tmpl := helpers.LoadTemplateFiles("invite.html", "templates/syndicates/invite.html")
tmpl.ExecuteTemplate(w, "layout", context)
case http.MethodPost:
username := r.FormValue("username")
invitee := storage.GetUserByUsername(db, username)
if invitee == nil {
helpers.SetFlash(w, r, "User not found.")
} else if storage.IsSyndicateMember(db, syndicateID, invitee.Id) {
helpers.SetFlash(w, r, "User is already a member.")
} else {
err := storage.AddMemberToSyndicate(db, syndicateID, invitee.Id)
if err != nil {
helpers.SetFlash(w, r, "Failed to invite user.")
} else {
helpers.SetFlash(w, r, "User successfully invited.")
}
}
http.Redirect(w, r, fmt.Sprintf("/account/syndicates/view?id=%d", syndicateID), http.StatusSeeOther)
default:
helpers.RenderError(w, r, 405)
}
}
}
func SyndicateLogTicketHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
syndicateId := helpers.Atoi(r.URL.Query().Get("id"))
syndicate, err := storage.GetSyndicateByID(db, syndicateId)
if err != nil || syndicate.OwnerID != userID {
helpers.RenderError(w, r, 403)
return
}
switch r.Method {
case http.MethodGet:
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["Syndicate"] = syndicate
tmpl := helpers.LoadTemplateFiles("syndicate-log-ticket.html", "templates/syndicates/log_ticket.html")
tmpl.ExecuteTemplate(w, "layout", context)
case http.MethodPost:
gameType := r.FormValue("game_type")
drawDate := r.FormValue("draw_date")
method := r.FormValue("purchase_method")
balls := helpers.ParseBallFields(r, gameType)
err := storage.InsertTicket(db, models.Ticket{
UserId: userID,
GameType: gameType,
DrawDate: drawDate,
PurchaseMethod: method,
SyndicateId: &syndicateId,
// ToDo image path
})
if err != nil {
helpers.SetFlash(w, r, "Failed to add ticket.")
} else {
helpers.SetFlash(w, r, "Ticket added for syndicate.")
}
http.Redirect(w, r, fmt.Sprintf("/account/syndicates/view?id=%d", syndicateId), http.StatusSeeOther)
default:
helpers.RenderError(w, r, 405)
}
}
}
func SyndicateTicketsHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
if syndicateID == 0 {
helpers.RenderError(w, r, 400)
return
}
// Check membership
if !storage.IsSyndicateMember(db, syndicateID, userID) {
helpers.RenderError(w, r, 403)
return
}
tickets := storage.GetSyndicateTickets(db, syndicateID)
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["SyndicateID"] = syndicateID
context["Tickets"] = tickets
tmpl := helpers.LoadTemplateFiles("syndicate-tickets.html", "templates/syndicates/tickets.html")
tmpl.ExecuteTemplate(w, "layout", context)
}
}

View File

@@ -0,0 +1,80 @@
package handlers
import (
"database/sql"
"net/http"
"synlotto-website/helpers"
"synlotto-website/models"
"synlotto-website/storage"
)
func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
syndicateID := helpers.Atoi(r.FormValue("syndicate_id"))
recipientUsername := r.FormValue("username")
senderID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
recipient := models.GetUserByUsername(recipientUsername)
if recipient == nil {
helpers.SetFlash(w, r, "User not found")
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther)
return
}
err := storage.InviteUserToSyndicate(db, syndicateID, recipient.Id, senderID)
if err != nil {
helpers.SetFlash(w, r, "Failed to invite user")
} else {
helpers.SetFlash(w, r, "Invitation sent to "+recipientUsername)
}
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther)
}
}
func ViewInvitesHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
invites := storage.GetPendingInvites(db, userID)
data := BuildTemplateData(db, w, r)
context := helpers.TemplateContext(w, r, data)
context["Invites"] = invites
tmpl := helpers.LoadTemplateFiles("invites.html", "templates/syndicates/invites.html")
tmpl.ExecuteTemplate(w, "layout", context)
}
}
func AcceptInviteHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
inviteID := helpers.Atoi(r.URL.Query().Get("id"))
userID, ok := helpers.GetCurrentUserID(r)
if !ok {
helpers.RenderError(w, r, 403)
return
}
err := storage.AcceptInvite(db, inviteID, userID)
if err != nil {
helpers.SetFlash(w, r, "Failed to accept invite")
} else {
helpers.SetFlash(w, r, "You have joined the syndicate")
}
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther)
}
}
func DeclineInviteHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
inviteID := helpers.Atoi(r.URL.Query().Get("id"))
_ = storage.UpdateInviteStatus(db, inviteID, "declined")
http.Redirect(w, r, "/account/syndicates/invites", http.StatusSeeOther)
}
}