330 lines
8.8 KiB
Go
330 lines
8.8 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"html/template"
|
||
"log"
|
||
"net/http"
|
||
"strconv"
|
||
"sync"
|
||
)
|
||
|
||
type Product struct {
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Price float64 `json:"price"`
|
||
}
|
||
|
||
var (
|
||
products []Product
|
||
mu sync.Mutex // Для безопасного доступа к списку продуктов
|
||
nextID int = 1 // Для автоматической генерации ID
|
||
)
|
||
|
||
// Структура для передачи данных в шаблон
|
||
type PageData struct {
|
||
Products []Product
|
||
Cart []CartItem
|
||
}
|
||
|
||
type CartItem struct {
|
||
ProductID int `json:"product_id"`
|
||
UserID int `json:"user_id"`
|
||
Quantity int `json:"quantity"`
|
||
CreatedID int `json:"created_id"`
|
||
}
|
||
|
||
// Получение корзины из cookies
|
||
func GetCartFromCookie(r *http.Request) ([]CartItem, error) {
|
||
// Read the cookie
|
||
cookie, err := r.Cookie("cart")
|
||
if err != nil {
|
||
if err == http.ErrNoCookie {
|
||
// If no cookie, return an empty cart
|
||
return []CartItem{}, nil
|
||
}
|
||
return nil, fmt.Errorf("failed to read cart cookie: %w", err)
|
||
}
|
||
|
||
// Check if the cookie value looks like JSON
|
||
var cart []CartItem
|
||
err = json.Unmarshal([]byte(cookie.Value), &cart)
|
||
if err != nil {
|
||
log.Println("Invalid cart cookie value, resetting cart.")
|
||
return []CartItem{}, nil // Reset to an empty cart on error
|
||
}
|
||
|
||
return cart, nil
|
||
}
|
||
|
||
// Сохранение корзины в cookies
|
||
func SetCartCookie(w http.ResponseWriter, cart []CartItem) error {
|
||
cartJSON, err := json.Marshal(cart)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to serialize cart: %w", err)
|
||
}
|
||
|
||
// Устанавливаем cookie с корзиной
|
||
http.SetCookie(w, &http.Cookie{
|
||
Name: "cart",
|
||
Value: string(cartJSON),
|
||
Path: "/",
|
||
})
|
||
return nil
|
||
}
|
||
func renderTemplate(w http.ResponseWriter, r *http.Request, cart []CartItem) {
|
||
tmpl, err := template.New("index").Parse(`
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Products</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; }
|
||
table { width: 50%; border-collapse: collapse; margin: 20px 0; }
|
||
th, td { padding: 8px 12px; text-align: left; border: 1px solid #ddd; }
|
||
h1 { text-align: center; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>Products</h1>
|
||
<table>
|
||
<tr>
|
||
<th>Product ID</th>
|
||
<th>Name</th>
|
||
<th>Price</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
{{range .Products}}
|
||
<tr>
|
||
<td>{{.ID}}</td>
|
||
<td>{{.Name}}</td>
|
||
<td>{{.Price}}</td>
|
||
<td>
|
||
<a href="/edit/{{.ID}}">Edit</a> |
|
||
<a href="/delete/{{.ID}}">Delete</a>
|
||
</td>
|
||
</tr>
|
||
{{end}}
|
||
</table>
|
||
|
||
<h2>Add a New Product</h2>
|
||
<form action="/add" method="POST">
|
||
<label for="name">Product Name:</label>
|
||
<input type="text" name="name" required><br><br>
|
||
|
||
<label for="price">Price:</label>
|
||
<input type="number" step="0.01" name="price" required><br><br>
|
||
|
||
<button type="submit">Add Product</button>
|
||
</form>
|
||
|
||
<h2>Cart</h2>
|
||
<table>
|
||
<tr>
|
||
<th>Product ID</th>
|
||
<th>Quantity</th>
|
||
</tr>
|
||
{{range .Cart}}
|
||
<tr>
|
||
<td>{{.ProductID}}</td>
|
||
<td>{{.Quantity}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</table>
|
||
</body>
|
||
</html>
|
||
`)
|
||
if err != nil {
|
||
log.Println("Error parsing template:", err)
|
||
http.Error(w, "Error parsing template", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Передаем данные продукта и корзины в шаблон
|
||
data := PageData{
|
||
Products: products,
|
||
Cart: cart,
|
||
}
|
||
|
||
err = tmpl.Execute(w, data)
|
||
if err != nil {
|
||
log.Println("Error executing template:", err)
|
||
http.Error(w, "Error executing template", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
func AddProduct(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == http.MethodPost {
|
||
// Получаем данные из формы
|
||
name := r.FormValue("name")
|
||
price := r.FormValue("price")
|
||
|
||
// Преобразуем цену в float
|
||
var priceFloat float64
|
||
_, err := fmt.Sscanf(price, "%f", &priceFloat)
|
||
if err != nil {
|
||
http.Error(w, "Invalid price", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// Создаем новый продукт
|
||
mu.Lock()
|
||
product := Product{
|
||
ID: nextID,
|
||
Name: name,
|
||
Price: priceFloat,
|
||
}
|
||
nextID++
|
||
products = append(products, product)
|
||
mu.Unlock()
|
||
|
||
// Перенаправляем пользователя на главную страницу после добавления продукта
|
||
http.Redirect(w, r, "/", http.StatusFound)
|
||
return
|
||
}
|
||
|
||
// Получаем корзину из cookies
|
||
cart, err := GetCartFromCookie(r)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("Error reading cart: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Отображаем шаблон с обновленным списком продуктов и корзиной
|
||
renderTemplate(w, r, cart)
|
||
}
|
||
|
||
func EditProduct(w http.ResponseWriter, r *http.Request) {
|
||
idStr := r.URL.Path[len("/edit/"):]
|
||
|
||
id, err := strconv.Atoi(idStr)
|
||
if err != nil {
|
||
http.Error(w, "Invalid product ID", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// Ищем продукт по ID
|
||
var product *Product
|
||
for i := range products {
|
||
if products[i].ID == id {
|
||
product = &products[i]
|
||
break
|
||
}
|
||
}
|
||
|
||
if product == nil {
|
||
http.Error(w, "Product not found", http.StatusNotFound)
|
||
return
|
||
}
|
||
|
||
if r.Method == http.MethodPost {
|
||
// Обновляем детали продукта
|
||
product.Name = r.FormValue("name")
|
||
price := r.FormValue("price")
|
||
var priceFloat float64
|
||
_, err := fmt.Sscanf(price, "%f", &priceFloat)
|
||
if err != nil {
|
||
http.Error(w, "Invalid price", http.StatusBadRequest)
|
||
return
|
||
}
|
||
product.Price = priceFloat
|
||
|
||
// Перенаправляем на главную страницу
|
||
http.Redirect(w, r, "/", http.StatusFound)
|
||
return
|
||
}
|
||
|
||
// Получаем корзину из cookies
|
||
// cart, err := GetCartFromCookie(r)
|
||
// if err != nil {
|
||
// http.Error(w, fmt.Sprintf("Error reading cart: %v", err), http.StatusInternalServerError)
|
||
// return
|
||
// }
|
||
|
||
// Отображаем форму редактирования продукта
|
||
tmpl := `<html><body>
|
||
<h1>Edit Product</h1>
|
||
<form action="/edit/{{.ID}}" method="POST">
|
||
<label for="name">Product Name:</label>
|
||
<input type="text" name="name" value="{{.Name}}" required><br><br>
|
||
|
||
<label for="price">Price:</label>
|
||
<input type="text" name="price" value="{{.Price}}" required><br><br>
|
||
|
||
<button type="submit">Save Changes</button>
|
||
</form>
|
||
</body></html>`
|
||
|
||
tmplParsed, err := template.New("edit").Parse(tmpl)
|
||
if err != nil {
|
||
http.Error(w, "Error parsing template", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Передаем данные продукта в шаблон
|
||
tmplParsed.Execute(w, product)
|
||
}
|
||
|
||
func DeleteProduct(w http.ResponseWriter, r *http.Request) {
|
||
idStr := r.URL.Path[len("/delete/"):]
|
||
|
||
id, err := strconv.Atoi(idStr)
|
||
if err != nil {
|
||
http.Error(w, "Invalid product ID", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// Находим индекс продукта для удаления
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
for i, product := range products {
|
||
if product.ID == id {
|
||
// Удаляем продукт из списка
|
||
products = append(products[:i], products[i+1:]...)
|
||
break
|
||
}
|
||
}
|
||
|
||
// Получаем корзину из cookies
|
||
cart, err := GetCartFromCookie(r)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("Error reading cart: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Сохраняем обновленную корзину в cookies (если необходимо)
|
||
err = SetCartCookie(w, cart)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("Error setting cart cookie: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Перенаправляем на главную страницу
|
||
http.Redirect(w, r, "/", http.StatusFound)
|
||
}
|
||
|
||
func main() {
|
||
// Обработчики маршрутов
|
||
http.HandleFunc("/add", AddProduct)
|
||
http.HandleFunc("/edit/", EditProduct)
|
||
http.HandleFunc("/delete/", DeleteProduct)
|
||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
// Получаем корзину из cookies
|
||
cart, err := GetCartFromCookie(r)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("Error reading cart: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// Отображаем шаблон с корзиной
|
||
renderTemplate(w, r, cart)
|
||
})
|
||
|
||
log.Println("Server is running on http://localhost:8080")
|
||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||
}
|