Added syndicate creation and invite logic

- Implemented `CreateSyndicate` with transaction support
- Added `InviteToSyndicate` with membership check and invite insert
- Created syndicate invite model and DB logic for accepting/declining
- Ensured consistent error handling with `fmt.Errorf`
- Cleaned up unused invite handlers/routes
This commit is contained in:
2025-04-04 10:26:24 +01:00
parent df6608dda5
commit ef4478e8a6
2 changed files with 58 additions and 17 deletions

View File

@@ -3,35 +3,46 @@ package handlers
import ( import (
"database/sql" "database/sql"
"net/http" "net/http"
"strconv"
"synlotto-website/helpers" "synlotto-website/helpers"
"synlotto-website/models"
"synlotto-website/storage" "synlotto-website/storage"
) )
func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc { func SyndicateInviteHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
syndicateID := helpers.Atoi(r.FormValue("syndicate_id")) userID, ok := helpers.GetCurrentUserID(r)
recipientUsername := r.FormValue("username")
senderID, ok := helpers.GetCurrentUserID(r)
if !ok { if !ok {
helpers.RenderError(w, r, 403) helpers.RenderError(w, r, http.StatusForbidden)
return return
} }
recipient := models.GetUserByUsername(recipientUsername) switch r.Method {
if recipient == nil { case http.MethodGet:
helpers.SetFlash(w, r, "User not found") syndicateID := helpers.Atoi(r.URL.Query().Get("id"))
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther) data := BuildTemplateData(db, w, r)
return context := helpers.TemplateContext(w, r, data)
} context["SyndicateID"] = syndicateID
err := storage.InviteUserToSyndicate(db, syndicateID, recipient.Id, senderID) tmpl := helpers.LoadTemplateFiles("invite-syndicate.html", "templates/syndicates/invite.html")
if err != nil { err := tmpl.ExecuteTemplate(w, "layout", context)
helpers.SetFlash(w, r, "Failed to invite user") if err != nil {
} else { helpers.RenderError(w, r, 500)
helpers.SetFlash(w, r, "Invitation sent to "+recipientUsername) }
case http.MethodPost:
syndicateID := helpers.Atoi(r.FormValue("syndicate_id"))
username := r.FormValue("username")
err := storage.InviteToSyndicate(db, userID, syndicateID, username)
if err != nil {
helpers.SetFlash(w, r, "Failed to send invite: "+err.Error())
} else {
helpers.SetFlash(w, r, "Invite sent successfully.")
}
http.Redirect(w, r, "/account/syndicates/view?id="+strconv.Itoa(syndicateID), http.StatusSeeOther)
default:
helpers.RenderError(w, r, http.StatusMethodNotAllowed)
} }
http.Redirect(w, r, "/account/syndicates", http.StatusSeeOther)
} }
} }

View File

@@ -228,3 +228,33 @@ func CreateSyndicate(db *sql.DB, ownerID int, name, description string) (int64,
return syndicateID, nil return syndicateID, nil
} }
func InviteToSyndicate(db *sql.DB, inviterID, syndicateID int, username string) error {
var inviteeID int
err := db.QueryRow(`
SELECT id FROM users WHERE username = ?
`, username).Scan(&inviteeID)
if err == sql.ErrNoRows {
return fmt.Errorf("user not found")
} else if err != nil {
return err
}
var count int
err = db.QueryRow(`
SELECT COUNT(*) FROM syndicate_members
WHERE syndicate_id = ? AND user_id = ?
`, syndicateID, inviteeID).Scan(&count)
if err != nil {
return err
}
if count > 0 {
return fmt.Errorf("user already a member or invited")
}
_, err = db.Exec(`
INSERT INTO syndicate_members (syndicate_id, user_id, is_manager, status)
VALUES (?, ?, 0, 'invited')
`, syndicateID, inviteeID)
return err
}