package main import ( "context" "encoding/json" "fmt" "log" "net/http" "os" "strconv" "strings" "github.com/jackc/pgx/v5/pgxpool" ) type CompetitionPostResults struct { SplatfestID uint32 `json:"splatfest_id"` Score uint32 `json:"score"` TeamID uint8 `json:"team_id"` TeamWin uint8 `json:"team_win"` User uint32 `json:"user"` } var db *pgxpool.Pool func main() { var err error connStr := os.Getenv("DATABASE_URL") db, err = pgxpool.New(context.Background(), connStr) if err != nil { log.Fatalf("Unable to connect to database: %v\n", err) } defer db.Close() http.HandleFunc("/results/get", handleGetResults) http.HandleFunc("/results/votes", handleGetVotes) http.HandleFunc("/results/post", handlePostResults) log.Println("Server starting on :8000...") log.Fatal(http.ListenAndServe(":8000", nil)) } // GET /results/get?splatfest_id= func handleGetResults(w http.ResponseWriter, r *http.Request) { festID := r.URL.Query().Get("splatfest_id") rows, err := db.Query(context.Background(), "SELECT splatfest_id, score, team_id, team_win, user_id FROM results WHERE splatfest_id = $1", festID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() var results []CompetitionPostResults for rows.Next() { var res CompetitionPostResults err := rows.Scan(&res.SplatfestID, &res.Score, &res.TeamID, &res.TeamWin, &res.User) if err != nil { continue } results = append(results, res) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(results) } // GET /results/votes?splatfest_id= func handleGetVotes(w http.ResponseWriter, r *http.Request) { festID := r.URL.Query().Get("splatfest_id") rows, err := db.Query(context.Background(), "SELECT team_id, COUNT(user_id) FROM user_votes WHERE splatfest_id = $1 GROUP BY team_id ORDER BY team_id ASC", festID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() // genuinely i can't be assed anymore voteCounts := make(map[uint8]uint32) maxID := 0 for rows.Next() { var tid uint8 var count uint32 if err := rows.Scan(&tid, &count); err == nil { voteCounts[tid] = count if int(tid) > maxID { maxID = int(tid) } } } resultArr := make([]string, maxID+1) for i := 0; i <= maxID; i++ { resultArr[i] = strconv.FormatUint(uint64(voteCounts[uint8(i)]), 10) } fmt.Fprintf(w, "[%s]", strings.Join(resultArr, ",")) } // POST /results/post func handlePostResults(w http.ResponseWriter, r *http.Request) { var res CompetitionPostResults if err := json.NewDecoder(r.Body).Decode(&res); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } ctx := context.Background() tx, err := db.Begin(ctx) if err != nil { http.Error(w, "Transaction error", http.StatusInternalServerError) return } defer tx.Rollback(ctx) _, err = tx.Exec(ctx, "INSERT INTO results (splatfest_id, score, team_id, team_win, user_id) VALUES ($1, $2, $3, $4, $5)", res.SplatfestID, res.Score, res.TeamID, res.TeamWin, res.User) if err != nil { log.Printf("DB Error results: %v", err) return } _, err = tx.Exec(ctx, "INSERT INTO user_votes (user_id, splatfest_id, team_id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", res.User, res.SplatfestID, res.TeamID) if err != nil { log.Printf("DB Error votes: %v", err) return } tx.Commit(ctx) w.WriteHeader(http.StatusOK) }