Adding page to view currently uploaded members, as well as verifying admin is logged in
This commit is contained in:
@@ -100,6 +100,49 @@ func adminIDHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func adminLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
var loginReq services.LoginRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&loginReq); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Invalid request payload"})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate admin credentials
|
||||
isValid, err := services.ValidateAdminLogin(loginReq.Username, loginReq.Password)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Invalid username or password"})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
token, err := services.GenerateAuthToken(loginReq.Username)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Failed to generate token"})
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(services.LoginResponse{
|
||||
Success: true,
|
||||
Token: token,
|
||||
})
|
||||
}
|
||||
|
||||
func adminMembersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
@@ -126,13 +169,15 @@ func adminMembersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if file, _, err := r.FormFile("members.csv"); err != nil {
|
||||
file, _, err := r.FormFile("members.csv")
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to upload file", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if fileBytes, err := ioutil.ReadAll(file); err != nil {
|
||||
fileBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to read file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -147,6 +192,36 @@ func adminMembersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(map[string]bool{"success": true})
|
||||
}
|
||||
|
||||
func adminMembersView(w http.ResponseWriter, r *http.Request) {
|
||||
yearStr := r.URL.Query().Get("year")
|
||||
if yearStr == "" {
|
||||
http.Error(w, "Year is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
year, err := strconv.Atoi(yearStr)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid year", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
members, err := services.GetMembersByYear(year)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"members": members,
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
@@ -157,7 +232,9 @@ func main() {
|
||||
http.HandleFunc("/api/stats/{id}", statsIDHandler)
|
||||
http.HandleFunc("/api/admin", adminHandler)
|
||||
http.HandleFunc("/api/admin/{id}", adminIDHandler)
|
||||
http.HandleFunc("/api/admin/login", adminLoginHandler)
|
||||
http.HandleFunc("/api/admin/members", adminMembersHandler)
|
||||
http.HandleFunc("/api/admin/members/view", adminMembersView)
|
||||
|
||||
buildPath := filepath.Join(".", "client", "build")
|
||||
fs := http.FileServer(http.Dir(buildPath))
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"go-sjles-pta-vote/server/db"
|
||||
)
|
||||
|
||||
type Member struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
const BATCH_SIZE = 100
|
||||
|
||||
func ParseMembersFromBytes(year int, fileBytes []byte) error {
|
||||
reader := csv.NewReader(strings.NewReader(string(fileBytes)))
|
||||
reader.FieldsPerRecord = -1 // Allow variable number of fields per record
|
||||
records, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read CSV from bytes")
|
||||
}
|
||||
|
||||
var members []Member
|
||||
|
||||
for i, record := range records {
|
||||
if i == 0 {
|
||||
continue // Skip the first line (column headers)
|
||||
}
|
||||
if len(record) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
firstName := strings.TrimSpace(record[1])
|
||||
lastName := strings.TrimSpace(record[2])
|
||||
email := strings.TrimSpace(record[3])
|
||||
|
||||
members = append(members, Member{
|
||||
Name: fmt.Sprintf("%s %s", firstName, lastName),
|
||||
Email: email,
|
||||
})
|
||||
|
||||
if len(record) < 30 {
|
||||
continue
|
||||
}
|
||||
|
||||
email2 := strings.TrimSpace(record[27])
|
||||
if email2 != "" {
|
||||
firstName2 := strings.TrimSpace(record[29])
|
||||
lastName2 := strings.TrimSpace(record[28])
|
||||
|
||||
members = append(members, Member{
|
||||
Name: fmt.Sprintf("%s %s", firstName2, lastName2),
|
||||
Email: email2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return saveMember(year, members)
|
||||
}
|
||||
|
||||
func saveMember(year int, members []Member) error {
|
||||
insertMembersQuery := `
|
||||
INSERT OR REPLACE INTO members (email, member_name, school_year)
|
||||
VALUES ($1, $2, $3)
|
||||
`
|
||||
log.Printf("Starting to save %d members for year %d", len(members), year)
|
||||
|
||||
db_conn, err := db.Connect()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to connect to database")
|
||||
}
|
||||
defer db_conn.Close()
|
||||
|
||||
tx, err := db_conn.Begin()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(insertMembersQuery)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to prepare statement")
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
for index, member := range members {
|
||||
_, err = stmt.Exec(member.Email, member.Name, year)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to execute insert")
|
||||
}
|
||||
|
||||
if (index+1) % BATCH_SIZE == 0 {
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
tx, err = db_conn.Begin()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to begin new transaction")
|
||||
}
|
||||
|
||||
stmt, err = tx.Prepare(insertMembersQuery)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.Wrap(err, "failed to prepare new statement")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
Reference in New Issue
Block a user