Refactor and remove sqlite and replace with MySQL
This commit is contained in:
203
internal/handlers/lottery/syndicate/syndicate.go
Normal file
203
internal/handlers/lottery/syndicate/syndicate.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
templateHandlers "synlotto-website/handlers/template"
|
||||
securityHelpers "synlotto-website/helpers/security"
|
||||
templateHelpers "synlotto-website/helpers/template"
|
||||
|
||||
"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 := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
tmpl := templateHelpers.LoadTemplateFiles("create-syndicate.html", "templates/syndicate/create.html")
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
|
||||
case http.MethodPost:
|
||||
name := r.FormValue("name")
|
||||
description := r.FormValue("description")
|
||||
|
||||
userId, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok || name == "" {
|
||||
templateHelpers.SetFlash(w, r, "Invalid data submitted")
|
||||
http.Redirect(w, r, "/syndicate/create", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := storage.CreateSyndicate(db, userId, name, description)
|
||||
if err != nil {
|
||||
log.Printf("❌ CreateSyndicate failed: %v", err)
|
||||
templateHelpers.SetFlash(w, r, "Failed to create syndicate")
|
||||
} else {
|
||||
templateHelpers.SetFlash(w, r, "Syndicate created successfully")
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||
default:
|
||||
templateHelpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ListSyndicatesHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.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)
|
||||
|
||||
managedMap := make(map[int]bool)
|
||||
for _, s := range managed {
|
||||
managedMap[s.ID] = true
|
||||
}
|
||||
|
||||
var filteredJoined []models.Syndicate
|
||||
for _, s := range member {
|
||||
if !managedMap[s.ID] {
|
||||
filteredJoined = append(filteredJoined, s)
|
||||
}
|
||||
}
|
||||
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["ManagedSyndicates"] = managed
|
||||
context["JoinedSyndicates"] = filteredJoined
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("syndicates.html", "templates/syndicate/index.html")
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
}
|
||||
}
|
||||
|
||||
func ViewSyndicateHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
syndicate, err := storage.GetSyndicateByID(db, syndicateID)
|
||||
if err != nil || syndicate == nil {
|
||||
templateHelpers.RenderError(w, r, 404)
|
||||
return
|
||||
}
|
||||
|
||||
isManager := userID == syndicate.OwnerID
|
||||
isMember := storage.IsSyndicateMember(db, syndicateID, userID)
|
||||
|
||||
if !isManager && !isMember {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
members := storage.GetSyndicateMembers(db, syndicateID)
|
||||
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["Syndicate"] = syndicate
|
||||
context["Members"] = members
|
||||
context["IsManager"] = isManager
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("syndicate-view.html", "templates/syndicate/view.html")
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
}
|
||||
}
|
||||
|
||||
func SyndicateLogTicketHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.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 {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["Syndicate"] = syndicate
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("syndicate-log-ticket.html", "templates/syndicate/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")
|
||||
|
||||
err := storage.InsertTicket(db, models.Ticket{
|
||||
UserId: userID,
|
||||
GameType: gameType,
|
||||
DrawDate: drawDate,
|
||||
PurchaseMethod: method,
|
||||
SyndicateId: &syndicateId,
|
||||
// ToDo image path
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
templateHelpers.SetFlash(w, r, "Failed to add ticket.")
|
||||
} else {
|
||||
templateHelpers.SetFlash(w, r, "Ticket added for syndicate.")
|
||||
}
|
||||
|
||||
http.Redirect(w, r, fmt.Sprintf("/syndicate/view?id=%d", syndicateId), http.StatusSeeOther)
|
||||
|
||||
default:
|
||||
templateHelpers.RenderError(w, r, 405)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SyndicateTicketsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
if syndicateID == 0 {
|
||||
templateHelpers.RenderError(w, r, 400)
|
||||
return
|
||||
}
|
||||
|
||||
if !storage.IsSyndicateMember(db, syndicateID, userID) {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
tickets := storage.GetSyndicateTickets(db, syndicateID)
|
||||
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["SyndicateID"] = syndicateID
|
||||
context["Tickets"] = tickets
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("syndicate-tickets.html", "templates/syndicate/tickets.html")
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
}
|
||||
}
|
||||
226
internal/handlers/lottery/syndicate/syndicate_invites.go
Normal file
226
internal/handlers/lottery/syndicate/syndicate_invites.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
templateHandlers "synlotto-website/handlers/template"
|
||||
securityHelpers "synlotto-website/helpers/security"
|
||||
templateHelpers "synlotto-website/helpers/template"
|
||||
|
||||
"synlotto-website/helpers"
|
||||
"synlotto-website/storage"
|
||||
)
|
||||
|
||||
func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["SyndicateID"] = syndicateID
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("invite-syndicate.html", "templates/syndicate/invite.html")
|
||||
err := tmpl.ExecuteTemplate(w, "layout", context)
|
||||
if err != nil {
|
||||
templateHelpers.RenderError(w, r, 500)
|
||||
}
|
||||
case http.MethodPost:
|
||||
syndicateID := helpers.Atoi(r.FormValue("syndicate_id"))
|
||||
username := r.FormValue("username")
|
||||
|
||||
err := storage.InviteToSyndicate(db, userID, syndicateID, username)
|
||||
if err != nil {
|
||||
templateHelpers.SetFlash(w, r, "Failed to send invite: "+err.Error())
|
||||
} else {
|
||||
templateHelpers.SetFlash(w, r, "Invite sent successfully.")
|
||||
}
|
||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||
|
||||
default:
|
||||
templateHelpers.RenderError(w, r, http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ViewInvitesHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
invites := storage.GetPendingInvites(db, userID)
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["Invites"] = invites
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("invites.html", "templates/syndicate/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 := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
err := storage.AcceptInvite(db, inviteID, userID)
|
||||
if err != nil {
|
||||
templateHelpers.SetFlash(w, r, "Failed to accept invite")
|
||||
} else {
|
||||
templateHelpers.SetFlash(w, r, "You have joined the syndicate")
|
||||
}
|
||||
http.Redirect(w, r, "/syndicate", 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, "/syndicate/invites", http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateInviteToken(db *sql.DB, syndicateID, invitedByID int, ttlHours int) (string, error) {
|
||||
token, err := securityHelpers.GenerateSecureToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
expires := time.Now().Add(time.Duration(ttlHours) * time.Hour)
|
||||
|
||||
_, err = db.Exec(`
|
||||
INSERT INTO syndicate_invite_tokens (syndicate_id, token, invited_by_user_id, expires_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`, syndicateID, token, invitedByID, expires)
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
func AcceptInviteToken(db *sql.DB, token string, userID int) error {
|
||||
var syndicateID int
|
||||
var expiresAt, acceptedAt sql.NullTime
|
||||
err := db.QueryRow(`
|
||||
SELECT syndicate_id, expires_at, accepted_at
|
||||
FROM syndicate_invite_tokens
|
||||
WHERE token = ?
|
||||
`, token).Scan(&syndicateID, &expiresAt, &acceptedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid or expired token")
|
||||
}
|
||||
if acceptedAt.Valid || expiresAt.Time.Before(time.Now()) {
|
||||
return fmt.Errorf("token already used or expired")
|
||||
}
|
||||
|
||||
_, err = db.Exec(`
|
||||
INSERT INTO syndicate_members (syndicate_id, user_id, role, status, joined_at)
|
||||
VALUES (?, ?, 'member', 'active', CURRENT_TIMESTAMP)
|
||||
`, syndicateID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.Exec(`
|
||||
UPDATE syndicate_invite_tokens
|
||||
SET accepted_by_user_id = ?, accepted_at = CURRENT_TIMESTAMP
|
||||
WHERE token = ?
|
||||
`, userID, token)
|
||||
return err
|
||||
}
|
||||
|
||||
func GenerateInviteLinkHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
token, err := CreateInviteToken(db, syndicateID, userID, 48)
|
||||
if err != nil {
|
||||
templateHelpers.SetFlash(w, r, "Failed to generate invite link.")
|
||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
origin := r.Host
|
||||
if r.TLS != nil {
|
||||
origin = "https://" + origin
|
||||
} else {
|
||||
origin = "http://" + origin
|
||||
}
|
||||
inviteLink := fmt.Sprintf("%s/syndicate/join?token=%s", origin, token)
|
||||
|
||||
templateHelpers.SetFlash(w, r, "Invite link created: "+inviteLink)
|
||||
http.Redirect(w, r, "/syndicate/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func JoinSyndicateWithTokenHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
token := r.URL.Query().Get("token")
|
||||
if token == "" {
|
||||
templateHelpers.SetFlash(w, r, "Invalid or missing invite token.")
|
||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
err := AcceptInviteToken(db, token, userID)
|
||||
if err != nil {
|
||||
templateHelpers.SetFlash(w, r, "Failed to join syndicate: "+err.Error())
|
||||
} else {
|
||||
templateHelpers.SetFlash(w, r, "You have joined the syndicate!")
|
||||
}
|
||||
http.Redirect(w, r, "/syndicate", http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func ManageInviteTokensHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, ok := securityHelpers.GetCurrentUserID(r)
|
||||
if !ok {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
|
||||
|
||||
if !storage.IsSyndicateManager(db, syndicateID, userID) {
|
||||
templateHelpers.RenderError(w, r, 403)
|
||||
return
|
||||
}
|
||||
|
||||
tokens := storage.GetInviteTokensForSyndicate(db, syndicateID)
|
||||
|
||||
data := templateHandlers.BuildTemplateData(db, w, r)
|
||||
context := templateHelpers.TemplateContext(w, r, data)
|
||||
context["Tokens"] = tokens
|
||||
context["SyndicateID"] = syndicateID
|
||||
|
||||
tmpl := templateHelpers.LoadTemplateFiles("invite-links.html", "templates/syndicate/invite_links.html")
|
||||
tmpl.ExecuteTemplate(w, "layout", context)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user