Add full shop application: Go backend, PostgreSQL, Docker, Caddy and admin panel.

This commit is contained in:
shop
2026-06-25 16:39:33 +03:00
parent a150e4f8c6
commit ee5688f722
24 changed files with 1797 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
package repository
import (
"context"
"errors"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"golang.org/x/crypto/bcrypt"
)
type AdminRepository struct {
pool *pgxpool.Pool
}
func NewAdminRepository(pool *pgxpool.Pool) *AdminRepository {
return &AdminRepository{pool: pool}
}
func (r *AdminRepository) Upsert(ctx context.Context, email, password string) error {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
_, err = r.pool.Exec(ctx, `
INSERT INTO admins (email, password_hash)
VALUES ($1, $2)
ON CONFLICT (email) DO UPDATE SET password_hash = EXCLUDED.password_hash
`, email, string(hash))
return err
}
func (r *AdminRepository) Authenticate(ctx context.Context, email, password string) (bool, error) {
var hash string
err := r.pool.QueryRow(ctx, `
SELECT password_hash FROM admins WHERE email = $1
`, email).Scan(&hash)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return false, nil
}
return false, err
}
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil, nil
}
+64
View File
@@ -0,0 +1,64 @@
package repository
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"shop/internal/models"
)
type ProductRepository struct {
pool *pgxpool.Pool
}
func NewProductRepository(pool *pgxpool.Pool) *ProductRepository {
return &ProductRepository{pool: pool}
}
func (r *ProductRepository) Featured(ctx context.Context, limit int) ([]models.Product, error) {
rows, err := r.pool.Query(ctx, `
SELECT id, name, description, price, image_url, category, created_at
FROM products
ORDER BY created_at DESC
LIMIT $1
`, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var products []models.Product
for rows.Next() {
var p models.Product
if err := rows.Scan(&p.ID, &p.Name, &p.Description, &p.Price, &p.ImageURL, &p.Category, &p.CreatedAt); err != nil {
return nil, err
}
products = append(products, p)
}
return products, rows.Err()
}
func (r *ProductRepository) Categories(ctx context.Context) ([]models.Category, error) {
rows, err := r.pool.Query(ctx, `
SELECT category, COUNT(*) AS cnt
FROM products
GROUP BY category
ORDER BY cnt DESC
`)
if err != nil {
return nil, err
}
defer rows.Close()
var categories []models.Category
for rows.Next() {
var c models.Category
if err := rows.Scan(&c.Name, &c.Count); err != nil {
return nil, err
}
c.Slug = c.Name
categories = append(categories, c)
}
return categories, rows.Err()
}