diff --git a/.gitignore b/.gitignore index f626921..942a34a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .env .idea/ -.exe \ No newline at end of file +*.exe \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 58e2ff8..02a1b36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ # Use the official Go image to compile the application FROM golang:1.25-alpine AS builder -LABEL authors="avner" # Set the working directory for building WORKDIR /app diff --git a/Makefile b/Makefile index 11759be..919a4bd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -BINARY_NAME=motzklist-api-gateway +BINARY_NAME=motzklist-backend BUILD_DIR=./build # Default command: runs the server (using '.' to include all files in package) diff --git a/db_api.go b/db_api.go index 7fdb17b..937cbae 100644 --- a/db_api.go +++ b/db_api.go @@ -2,8 +2,10 @@ package main import ( "database/sql" + "encoding/json" "fmt" "log" + "net/http" "os" "strconv" "time" @@ -153,7 +155,7 @@ func getCartByUserID(userID string) []CartEntry { var cart []CartEntry queryEntry := ` SELECT ce.ceid, g.gid, g.gname, s.sid, s.sname - FROM cartEntry ce + FROM cart_entry ce JOIN grade g ON ce.gid = g.gid JOIN school s ON g.sid = s.sid WHERE ce.uid = $1 @@ -186,10 +188,10 @@ func getCartItemsFromApply(ceidStr string) []Equipment { ceid, _ := strconv.Atoi(ceidStr) query := ` - SELECT e.eid, e.ename, e.price, COUNT(a.eid) as qty - FROM apply a - JOIN equipment e ON a.eid = e.eid - WHERE a.ceid = $1 + SELECT e.eid, e.ename, e.price, COUNT(ci.eid) as qty + FROM cart_item ci + JOIN equipment e ON ci.eid = e.eid + WHERE ci.ceid = $1 GROUP BY e.eid, e.ename, e.price ` rows, err := DB.Query(query, ceid) @@ -219,7 +221,7 @@ func saveCart(userID string, cart []CartEntry) error { return fmt.Errorf("starting transaction: %w", err) } - _, err = tx.Exec("DELETE FROM cartEntry WHERE uid = $1", uid) + _, err = tx.Exec("DELETE FROM cart_entry WHERE uid = $1", uid) if err != nil { tx.Rollback() log.Println("Error clearing old cart:", err) @@ -230,22 +232,22 @@ func saveCart(userID string, cart []CartEntry) error { var newCeid int gid, _ := strconv.Atoi(entry.Grade.ID) - err := tx.QueryRow("INSERT INTO cartEntry (gid, uid) VALUES ($1, $2) RETURNING ceid", gid, uid).Scan(&newCeid) + err := tx.QueryRow("INSERT INTO cart_entry (gid, uid) VALUES ($1, $2) RETURNING ceid", gid, uid).Scan(&newCeid) if err != nil { tx.Rollback() - log.Println("Error inserting cartEntry:", err) - return fmt.Errorf("inserting cartEntry: %w", err) + log.Println("Error inserting cart_entry:", err) + return fmt.Errorf("inserting cart_entry: %w", err) } for _, item := range entry.Items { eid, _ := strconv.Atoi(item.ID) for i := 0; i < item.Quantity; i++ { - _, err := tx.Exec("INSERT INTO apply (ceid, eid) VALUES ($1, $2)", newCeid, eid) + _, err := tx.Exec("INSERT INTO cart_item (ceid, eid) VALUES ($1, $2)", newCeid, eid) if err != nil { tx.Rollback() - log.Println("Error inserting to apply:", err) - return fmt.Errorf("inserting to apply: %w", err) + log.Println("Error inserting to cart_item:", err) + return fmt.Errorf("inserting to cart_item: %w", err) } } } @@ -257,3 +259,101 @@ func saveCart(userID string, cart []CartEntry) error { } return nil } + +// NEW - adding purchase history +func getUserOrderHistory(userID string) []Order { + log.Println("Got to getUserOrderHistory function") + uid, _ := strconv.Atoi(userID) + var orders []Order + + queryOrders := ` + SELECT oid, gid, purchase_date, total_amount + FROM orders + WHERE uid = $1 + ORDER BY purchase_date DESC; + ` + rows, err := DB.Query(queryOrders, uid) + if err != nil { + log.Println("Error getting orders history:", err) + return []Order{} + } + defer rows.Close() + + for rows.Next() { + var o Order + var t time.Time + + if err := rows.Scan(&o.ID, &o.GradeID, &t, &o.TotalAmount); err != nil { + log.Println("Error scanning order row:", err) + continue + } + + o.PurchaseDate = t.Format("2006-01-02 15:04:05") + + o.Items = getOrderItems(o.ID) + + orders = append(orders, o) + } + + return orders +} + +func getOrderItems(orderID string) []OrderItem { + oid, _ := strconv.Atoi(orderID) + var items []OrderItem + + queryItems := ` + SELECT + e.ename, + oi.quantity, + oi.price_at_purchase, + (oi.quantity * oi.price_at_purchase) as total_item_cost + FROM order_item oi + JOIN equipment e ON oi.eid = e.eid + WHERE oi.oid = $1; + ` + rows, err := DB.Query(queryItems, oid) + if err != nil { + log.Println("Error getting order items:", err) + return []OrderItem{} + } + defer rows.Close() + + for rows.Next() { + var item OrderItem + + if err := rows.Scan(&item.EquipmentName, &item.Quantity, &item.Price, &item.TotalPrice); err != nil { + log.Println("Error scanning order item row:", err) + continue + } + items = append(items, item) + } + + return items +} + +func getOrderHistoryHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + JSONError(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + cookie, err := r.Cookie("sessionid") + if err != nil { + JSONError(w, "Unauthorized", http.StatusUnauthorized) + return + } + + userID, exists := sessions[cookie.Value] + if !exists { + JSONError(w, "Unauthorized", http.StatusUnauthorized) + return + } + + history := getUserOrderHistory(userID) + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(history); err != nil { + log.Printf("Failed to encode history response: %v", err) + } +} diff --git a/go.mod b/go.mod index 40f1717..5c5e57c 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module api-gateway-avner +module motzklist-backend go 1.25.4 diff --git a/main.go b/main.go index d59ac67..8d6e1fe 100644 --- a/main.go +++ b/main.go @@ -53,14 +53,28 @@ func JSONError(w http.ResponseWriter, err string, code int) { } } -// =====NEW===== -// login type User struct { UserID string `json:"userid"` Username string `json:"username"` Password string `json:"password"` } +// NEW - adding purchase history +type OrderItem struct { + EquipmentName string `json:"equipment_name"` + Quantity int `json:"quantity"` + Price float64 `json:"price"` + TotalPrice float64 `json:"total_price"` +} + +type Order struct { + ID string `json:"order_id"` + GradeID string `json:"grade_id"` + PurchaseDate string `json:"purchase_date"` + TotalAmount float64 `json:"total_amount"` + Items []OrderItem `json:"items"` +} + func enableCORS(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") @@ -108,6 +122,7 @@ func main() { http.HandleFunc("/api/logout", enableCORS(logoutHandler)) http.HandleFunc("/api/cart", enableCORS(getPostCartHandler)) http.HandleFunc("/api/create-checkout-session", enableCORS(CreateCheckoutSession)) + http.HandleFunc("/api/history", enableCORS(getOrderHistoryHandler)) // Start the API Gateway server port := "8080" // Changed port to string without colon for easier fmt use @@ -122,4 +137,4 @@ func main() { // Use the formatted address to listen log.Fatal(http.ListenAndServe(serverAddr, nil)) -} \ No newline at end of file +} diff --git a/mock_db.go b/mock_db.go index 8ca2253..268df9f 100644 --- a/mock_db.go +++ b/mock_db.go @@ -84,21 +84,19 @@ var MockUsers = []User{ // CartEntry structure for frontend compatibility // (matches what the frontend expects) type CartEntry struct { - ID string `json:"id"` - Timestamp int64 `json:"timestamp"` - School School `json:"school"` - Grade Grade `json:"grade"` - Items []Equipment `json:"items"` + ID string `json:"id"` + School School `json:"school"` + Grade Grade `json:"grade"` + Items []Equipment `json:"items"` } // data for cart var MockCarts = map[string][]CartEntry{ "1": { { - ID: "cart-1", - Timestamp: 1700000000, - School: School{ID: "1", Name: "Ben Gurion"}, - Grade: Grade{ID: "9", Name: "9th Grade"}, + ID: "cart-1", + School: School{ID: "1", Name: "Ben Gurion"}, + Grade: Grade{ID: "9", Name: "9th Grade"}, Items: []Equipment{ {ID: "101", Name: "Notebook", Quantity: 2}, {ID: "102", Name: "Engineering Calculator", Quantity: 1}, @@ -108,10 +106,9 @@ var MockCarts = map[string][]CartEntry{ }, "2": { { - ID: "cart-2", - Timestamp: 1700000001, - School: School{ID: "2", Name: "ORT"}, - Grade: Grade{ID: "12", Name: "12th Grade"}, + ID: "cart-2", + School: School{ID: "2", Name: "ORT"}, + Grade: Grade{ID: "12", Name: "12th Grade"}, Items: []Equipment{ {ID: "201", Name: "Laptop (Required)", Quantity: 1}, {ID: "202", Name: "Engineering Calculator", Quantity: 1}, @@ -119,4 +116,4 @@ var MockCarts = map[string][]CartEntry{ }, }, }, -} \ No newline at end of file +}