From 23aacb450947f0f4ecef5ca10c5d8634f4741406 Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Tue, 22 Jul 2025 19:10:54 -0700 Subject: [PATCH] Finish hackathon.go and make dbclient a singleton --- handlers/hackathon.go | 212 ++++++++++++++++++++++++++++++++- internals/database/dbclient.go | 30 +++-- 2 files changed, 232 insertions(+), 10 deletions(-) diff --git a/handlers/hackathon.go b/handlers/hackathon.go index 3665e83..1208146 100644 --- a/handlers/hackathon.go +++ b/handlers/hackathon.go @@ -1,14 +1,224 @@ package handlers import ( + "codegrillathon/internals/database" "fmt" "net/http" + + "github.com/markbates/goth/gothic" ) +type HackathonForm struct { + HackathonName string + Description string +} + +type Hackathon struct { + Id int + HackathonName string + OwnerId int + StartDate string + EndDate string + Description string + Provider string +} + func (h *Handler) Hackathon(w http.ResponseWriter, r *http.Request) { - fmt.Println("I hit the hackathon route...") err := h.Template.ExecuteTemplate(w, "hackathon.html", nil) if err != nil { http.Error(w, "Template rendering error", http.StatusInternalServerError) } } + +func (h *Handler) CreateHackathon(w http.ResponseWriter, r *http.Request) { + err := h.Template.ExecuteTemplate(w, "create-hackathon.html", nil) + if err != nil { + http.Error(w, "Template rendering error", http.StatusInternalServerError) + } +} + +func (h *Handler) ParseHackthonForm(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } + + err := r.ParseForm() + if err != nil { + http.Error(w, "Error parsing form", http.StatusBadRequest) + return + } + + hackathonName := r.FormValue("hackathonName") + description := r.FormValue("description") + startDate := r.FormValue("startDate") + endDate := r.FormValue("endDate") + + if hackathonName == "" || description == "" || startDate == "" || endDate == "" { + http.Error(w, "All fields are required", http.StatusBadRequest) + } + + session, err := gothic.Store.Get(r, "user-session") + if err != nil { + http.Error(w, "Error retrieving session for welcome page", http.StatusInternalServerError) + } + + owner_id, ok := session.Values["user_id"].(int) + if !ok { + http.Error(w, "Error retrieving session", http.StatusInternalServerError) + return + } + + provider, ok := session.Values["provider"].(string) + if !ok { + http.Error(w, "Error retrieving session", http.StatusInternalServerError) + return + } + + dbClient, err := database.GetDbClientInstance() + + if err != nil { + http.Error(w, "DB connection error", http.StatusInternalServerError) + return + } + + _, err = dbClient.Exec( + `INSERT INTO hackathons (hackathon_name, owner_id, start_date, end_date, description, provider) VALUES (?, ?, ?, ?, ?, ?)`, + hackathonName, + owner_id, + startDate, + endDate, + description, + provider, + ) + + if err != nil { + http.Error(w, "Database insert error", http.StatusInternalServerError) + return + } + + err = h.Template.ExecuteTemplate(w, "hackathon.html", nil) + if err != nil { + http.Error(w, "Error executing template", http.StatusInternalServerError) + } +} + +func (h *Handler) ListHackathonsByProvider(w http.ResponseWriter, r *http.Request) { + provider := r.PathValue("provider") + + dbClient, err := database.GetDbClientInstance() + if err != nil { + http.Error(w, "Error connecting to database", http.StatusInternalServerError) + } + + rows, err := dbClient.Query( + `SELECT id, hackathon_name, owner_id, start_date, end_date, description, provider FROM hackathons WHERE provider = ?`, + provider, + ) + + if err != nil { + http.Error(w, "Error fetching from database", http.StatusInternalServerError) + return + } + + defer rows.Close() + + var hackathons []Hackathon + + for rows.Next() { + var hackathon Hackathon + err := rows.Scan( + &hackathon.Id, + &hackathon.HackathonName, + &hackathon.OwnerId, + &hackathon.StartDate, + &hackathon.EndDate, + &hackathon.Description, + &hackathon.Provider, + ) + if err != nil { + http.Error(w, fmt.Sprintf("Error scanning row: %v", err), http.StatusInternalServerError) + return + } + hackathons = append(hackathons, hackathon) + } + + if err = rows.Err(); err != nil { + http.Error(w, fmt.Sprintf("Error iterating rows: %v", err), http.StatusInternalServerError) + return + } + + data := struct { + Provider string + Hackathons []Hackathon + }{ + Provider: provider, + Hackathons: hackathons, + } + + err = h.Template.ExecuteTemplate(w, "hackathons.html", data) + if err != nil { + http.Error(w, "Template rendering error", http.StatusInternalServerError) + } +} + +func (h *Handler) ListHackathonsByUser(w http.ResponseWriter, r *http.Request) { + provider := r.PathValue("provider") + username := r.PathValue("user") + + dbClient, err := database.GetDbClientInstance() + if err != nil { + fmt.Println("error using dbclient") + } + + rows, err := dbClient.Query( + "SELECT h.*, u.username FROM hackathon h INNER JOIN users u ON h.owner_id = u.id WHERE h.provider = ? AND h.owner_id = ? ", + provider, + username) + + // render the page + if err != nil { + http.Error(w, "Error fetching from database", http.StatusInternalServerError) + return + } + + defer rows.Close() + + var hackathons []Hackathon + + for rows.Next() { + var hackathon Hackathon + err := rows.Scan( + &hackathon.Id, + &hackathon.HackathonName, + &hackathon.OwnerId, + &hackathon.StartDate, + &hackathon.EndDate, + &hackathon.Description, + &hackathon.Provider, + ) + if err != nil { + http.Error(w, fmt.Sprintf("Error scanning row: %v", err), http.StatusInternalServerError) + return + } + hackathons = append(hackathons, hackathon) + } + + if err = rows.Err(); err != nil { + http.Error(w, fmt.Sprintf("Error iterating rows: %v", err), http.StatusInternalServerError) + return + } + + data := struct { + Provider string + Hackathons []Hackathon + }{ + Provider: provider, + Hackathons: hackathons, + } + + err = h.Template.ExecuteTemplate(w, "hackathons.html", data) + if err != nil { + http.Error(w, "Template rendering error", http.StatusInternalServerError) + } + +} diff --git a/internals/database/dbclient.go b/internals/database/dbclient.go index 7be70f6..a1a9838 100644 --- a/internals/database/dbclient.go +++ b/internals/database/dbclient.go @@ -3,9 +3,11 @@ package database import ( "database/sql" "fmt" + "os" + "sync" + "github.com/jmoiron/sqlx" _ "github.com/tursodatabase/libsql-client-go/libsql" - "os" ) type Database interface { @@ -18,15 +20,25 @@ type DBClient struct { db *sqlx.DB } -func NewDbClient() (*DBClient, error) { - dsn := os.Getenv("DATABASE_URL") + "?authToken=" + os.Getenv("DATABASE_TOKEN") - db, err := sqlx.Open("libsql", dsn) - if err != nil { - fmt.Printf("error connecting to database: %v", err) - return nil, fmt.Errorf("failed to open db: %w", err) - } +var ( + instance *DBClient + once sync.Once + initErr error +) + +func GetDbClientInstance() (*DBClient, error) { + once.Do(func() { + dsn := os.Getenv("DATABASE_URL") + "?authToken=" + os.Getenv("DATABASE_TOKEN") + db, err := sqlx.Open("libsql", dsn) + if err != nil { + initErr = fmt.Errorf("failed to open db: %w", err) + return + } + + instance = &DBClient{db: db} + }) - return &DBClient{db: db}, nil + return instance, initErr } func (c *DBClient) Query(query string, args ...interface{}) (*sqlx.Rows, error) {