anor

package module
v0.0.0-...-3bb7120 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2024 License: MIT Imports: 14 Imported by: 0

README

Anor

anor

Anor is a full-stack e-commerce application inspired by the functionality and design of Uzum, eBay, and Zalando. It uses Golang for the backend, HTMX and _hyperscript with Bootstrap for the frontend, and PostgreSQL, Redis, and Typesense for data storage, caching, and search capabilities.

Visit our live demo: https://anor.alisherm.dev/

Quick Start

For quick setup instructions, see DEVELOPMENT.md.

Features

For a comprehensive list of features, see FEATURES.md.

Documentation

Contributing

Contributions to the project are welcome! See CONTRIBUTING.md for details.

Support the Project

If you find Anor useful, please consider giving it a star on GitHub. Your support helps to increase the visibility of the project and encourages further development.

Star on GitHub

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const DefaultCurrency = "USD"

Variables

View Source
var (
	ErrNotFound     = errors.New("not found")
	ErrUserNotFound = errors.New("user not found")

	ErrPaymentNotFound               = errors.New("payment not found")
	ErrUserExists                    = errors.New("user exists")
	ErrProductNotFound               = errors.New("product not found")
	ErrProductPricingNotFound        = errors.New("product pricing not found")
	ErrCartNotFound                  = errors.New("cart not found")
	ErrCartItemNotFound              = errors.New("cart item not found")
	ErrProductVariantNotFound        = errors.New("product variant not found")
	ErrProductVariantPricingNotFound = errors.New("product variant pricing not found")
	ErrAtLeastOneOptionRequired      = errors.New("at least one option required")
	ErrAddressNotFound               = errors.New("address not found")
)
View Source
var ErrBinding = errors.New("binding error")

ErrBinding indicates that binding *http.Request data to the receiver struct failed.

View Source
var ErrValidation = errors.New("validation error")

ErrValidation indicates that the bound data did not pass validation.

Functions

func BindValid

func BindValid[T BindValidator](r *http.Request, v T) error

BindValid binds HTTP request data to v and validates it. Returns ErrBinding or ErrValidation wrapped with details if either step fails.

func Bool

func Bool(v bool) *bool

Bool returns a pointer to the bool value passed in.

func BoolValue

func BoolValue(v *bool) bool

BoolValue returns the value of the bool pointer passed in or false if the pointer is nil.

func ClientError

func ClientError(logger *slog.Logger, w http.ResponseWriter, err error, statusCode int)

func Float64

func Float64(v float64) *float64

Float64 returns a pointer to the float64 value passed in.

func Float64Value

func Float64Value(v *float64) float64

Float64Value returns the value of the float64 pointer passed in or 0.0 if the pointer is nil.

func Int

func Int(v int) *int

Int returns a pointer to the int value passed in.

func Int32

func Int32(v int32) *int32

Int32 returns a pointer to the int32 value passed in.

func Int32Value

func Int32Value(v *int32) int32

Int32Value returns the value of the int32 pointer passed in or 0 if the pointer is nil.

func Int64

func Int64(v int64) *int64

Int64 returns a pointer to the int64 value passed in.

func Int64Value

func Int64Value(v *int64) int64

Int64Value returns the value of the int64 pointer passed in or 0 if the pointer is nil.

func IntValue

func IntValue(v *int) int

IntValue returns the value of the int pointer passed in or 0 if the pointer is nil.

func JSONClientError

func JSONClientError(logger *slog.Logger, w http.ResponseWriter, err error, statusCode int)

func JSONServerInternalError

func JSONServerInternalError(logger *slog.Logger, w http.ResponseWriter, err error)

JSONServerInternalError writes a JSON error response to the http.ResponseWriter

func LogError

func LogError(logger *slog.Logger, err error)

func ServerInternalError

func ServerInternalError(logger *slog.Logger, w http.ResponseWriter, err error)

func String

func String(v string) *string

String returns a pointer to the string value passed in.

func StringValue

func StringValue(v *string) string

StringValue returns the value of the string pointer passed in or an empty string if the pointer is nil.

func Time

func Time(v time.Time) *time.Time

Time returns a pointer to the time.Time value passed in.

func TimeValue

func TimeValue(v *time.Time) time.Time

TimeValue returns the value of the time.Time pointer passed in or the zero value of time.Time if the pointer is nil.

Types

type Address

type Address struct {
	ID            int64
	UserID        int64
	DefaultFor    AddressDefaultType
	Name          string
	AddressLine1  string
	AddressLine2  string
	City          string
	StateProvince string
	PostalCode    string
	Country       string
	Phone         string
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

func (Address) Equals

func (a Address) Equals(b Address) bool

func (Address) IsZero

func (a Address) IsZero() bool

type AddressCreateParams

type AddressCreateParams struct {
	UserID        int64
	DefaultFor    AddressDefaultType
	Name          string
	AddressLine1  string
	AddressLine2  string
	City          string
	StateProvince string
	PostalCode    string
	Country       string
}

type AddressDefaultType

type AddressDefaultType string
const (
	AddressDefaultTypeShipping AddressDefaultType = "Shipping"
	AddressDefaultTypeBilling  AddressDefaultType = "Billing"
)

type AddressListParams

type AddressListParams struct {
	UserID   int64
	Page     int
	PageSize int
}

type AddressService

type AddressService interface {
	Create(ctx context.Context, params AddressCreateParams) (Address, error)
	Get(ctx context.Context, id int64) (Address, error)
	GetDefault(ctx context.Context, userID int64, defaultFor AddressDefaultType) (Address, error)
	List(ctx context.Context, params AddressListParams) ([]Address, error)
}

type AuthProvider

type AuthProvider string
const (
	ProviderBuiltin AuthProvider = "builtin"
	ProviderGoogle  AuthProvider = "google"
)

type AuthService

type AuthService interface {
	Signup(ctx context.Context, name, email, password string) (User, error)
	SignupConfirm(ctx context.Context, otp, email string) error
	Signin(ctx context.Context, email, password string) (User, error)
	SigninWithGoogle(ctx context.Context, email, name string) (User, error)
	ResendOTP(ctx context.Context, email string) error
	SendResetPasswordLink(ctx context.Context, email string) error
	VerifyResetPasswordToken(ctx context.Context, token string) (bool, error)
	ResetPassword(ctx context.Context, token string, password string) error
	GetUser(ctx context.Context, id int64) (*User, error)
}

type BaseProduct

type BaseProduct struct {
	ID               int64
	StoreID          int32
	CategoryID       int32
	Name             string
	Brand            string
	Handle           string
	ShortInformation []string
	ImageUrls        map[int]string
	Specifications   map[string]string
	Status           ProductStatus
	CreatedAt        time.Time
	UpdatedAt        time.Time
}

type BindValidator

type BindValidator interface {
	Binder
	Validator
}

BindValidator is an interface that groups the basic Bind and Validate methods

type Binder

type Binder interface {
	Bind(r *http.Request) error
}

Binder is the interface that wraps the basic Bind method.

Bind populates the receiver with data from the HTTP request.

type Cart

type Cart struct {
	ID           int64
	UserID       int64
	Status       CartStatus
	LastActivity time.Time
	ExpiresAt    time.Time
	CreatedAt    time.Time
	UpdatedAt    time.Time

	CartItems []*CartItem

	TotalAmount  decimal.Decimal
	CurrencyCode string
}

type CartCreateParams

type CartCreateParams struct {
	UserID    int64
	ExpiresAt time.Time
}

type CartItem

type CartItem struct {
	ID                int64
	CartID            int64
	VariantID         int64
	Qty               int32
	Price             decimal.Decimal
	Currency          string
	Thumbnail         string
	ProductName       string
	ProductPath       string
	VariantAttributes map[string]string
	IsRemoved         bool
	CreatedAt         time.Time
	UpdatedAt         time.Time

	AvailableQty int32
}

type CartItemAddParams

type CartItemAddParams struct {
	CartID    int64
	VariantID int64
	Qty       int
}

type CartItemListParams

type CartItemListParams struct {
	CartID   int64
	Page     int64
	PageSize int64
}

type CartItemUpdateParams

type CartItemUpdateParams struct {
	Qty int
}

type CartMergeParams

type CartMergeParams struct {
	GuestCartID int64
	UserID      int64
}

type CartService

type CartService interface {
	Create(ctx context.Context, params CartCreateParams) (Cart, error)
	Get(ctx context.Context, id int64, includeItems bool) (Cart, error)
	GetByUserID(ctx context.Context, userID int64, includeItems bool) (Cart, error)
	Update(ctx context.Context, id int64, params CartUpdateParams) error
	Merge(ctx context.Context, params CartMergeParams) (Cart, error)

	ListItems(ctx context.Context, params CartItemListParams) ([]CartItem, error)
	AddItem(ctx context.Context, params CartItemAddParams) (CartItem, error)
	UpdateItem(ctx context.Context, itemID int64, params CartItemUpdateParams) error
	DeleteItem(ctx context.Context, itemID int64) error

	CountItems(ctx context.Context, cartID int64) (int64, error)
	IsMyItem(ctx context.Context, params IsMyCartItemParams) (bool, error)
}

type CartStatus

type CartStatus string
const (
	CartStatusOpen      CartStatus = "Open"
	CartStatusCompleted CartStatus = "Completed"
	CartStatusMerged    CartStatus = "Merged"
	CartStatusExpired   CartStatus = "Expired"
	CartStatusAbandoned CartStatus = "Abandoned"
)

type CartUpdateParams

type CartUpdateParams struct {
	Status CartStatus
}

type Category

type Category struct {
	ID       int32
	Category string
	Handle   string
	ParentID int32

	IsLeafCategory bool
}

func (Category) IsLeaf

func (c Category) IsLeaf() bool

func (Category) IsRoot

func (c Category) IsRoot() bool

type CategoryHierarchy

type CategoryHierarchy struct {
	AncestorCategories []Category `json:"ac,omitempty"`
	SiblingCategories  []Category `json:"sc,omitempty"`
	ChildCategories    []Category `json:"cc,omitempty"`
}

type CategoryService

type CategoryService interface {
	GetCategory(ctx context.Context, id int32) (Category, error)
	GetAncestorCategories(ctx context.Context, id int32) ([]Category, error)
	GetSiblingCategories(ctx context.Context, id int32) ([]Category, error)
	GetChildCategories(ctx context.Context, id int32) ([]Category, error)
	GetRootCategories(ctx context.Context) ([]Category, error)
	GetCategoryHierarchy(ctx context.Context, category Category) (CategoryHierarchy, error)
	GetCategoryWithAncestors(ctx context.Context, categoryID int32) (CategoryWithAncestors, error)
}

type CategoryWithAncestors

type CategoryWithAncestors struct {
	Category           Category   `json:"c"`
	AncestorCategories []Category `json:"ac"`
}

type FeaturedSelection

type FeaturedSelection struct {
	ID           int64
	ResourcePath string
	BannerInfo   map[string]string
	ImageUrl     string
	QueryParams  map[string]string
	StartDate    time.Time
	EndDate      time.Time
	DisplayOrder int
	CreatedAt    pgtype.Timestamptz
	UpdatedAt    pgtype.Timestamptz

	Products []Product
}

FeaturedSelection represents a highlighted item or group of items. It can be used in various contexts, such as: - Banner carousel items on the home page - Highlighted categories of products - Curated search results with specific filters and sorting - Single product promotions - External links to campaigns or special offers

FeaturedSelections are versatile and can represent either a group of products, a single product, or an external link, depending on the context and configuration.

type FeaturedSelectionService

type FeaturedSelectionService interface {
	ListAllActive(ctx context.Context) ([]FeaturedSelection, error)
}

type FilterParam

type FilterParam struct {
	Rating    int
	PriceFrom decimal.Decimal
	PriceTo   decimal.Decimal
	Brands    []string
	Colors    []string
	Sizes     []string
}

func (FilterParam) IsZero

func (p FilterParam) IsZero() bool

type IsMyCartItemParams

type IsMyCartItemParams struct {
	ItemID int64
	CartID int64
}

type JSONErrorResponse

type JSONErrorResponse struct {
	Error      string `json:"error"`
	StatusCode int    `json:"status_code,omitempty"`
}

JSONErrorResponse represents a standardized JSON error response

func NewJSONErrorResponse

func NewJSONErrorResponse(err error, statusCode int) JSONErrorResponse

NewJSONErrorResponse creates a new JSONErrorResponse

type ListByCategoryParams

type ListByCategoryParams struct {
	With   relation.Set
	Filter FilterParam
	Sort   SortParam
	Paging Paging
}

type MiddlewareFunc

type MiddlewareFunc func(http.Handler) http.Handler

type Order

type Order struct {
	ID                int64
	UserID            int64
	CartID            int64
	PaymentMethod     PaymentMethod
	PaymentStatus     PaymentStatus
	Status            OrderStatus
	ShippingAddressID int64
	IsPickup          bool
	Amount            decimal.Decimal
	Currency          string
	CreatedAt         time.Time
	UpdatedAt         time.Time

	// Related structs that can be eagerly loaded
	OrderItems        []*OrderItem
	ShippingAddress   Address
	StripeCardPayment StripeCardPayment
	DeliveryDate      time.Time
}

type OrderCreateParams

type OrderCreateParams struct {
	Cart              Cart
	PaymentMethod     PaymentMethod
	PaymentStatus     PaymentStatus
	ShippingAddressID int64
	IsPickup          bool
	Amount            decimal.Decimal
	Currency          string
}

type OrderItem

type OrderItem struct {
	ID                int64
	OrderID           int64
	VariantID         int64
	Qty               int32
	Price             decimal.Decimal
	Thumbnail         string
	ProductName       string
	VariantAttributes map[string]string
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

type OrderListParams

type OrderListParams struct {
	UserID        int64
	WithRelations relation.Set
	Page          int
	PageSize      int
}

type OrderService

type OrderService interface {
	Create(ctx context.Context, params OrderCreateParams) (int64, error)
	Get(ctx context.Context, orderID int64, withItems bool) (Order, error)
	List(ctx context.Context, params OrderListParams) ([]Order, error)
	ListActive(ctx context.Context, params OrderListParams) ([]Order, error)
	ListUnpaid(ctx context.Context, params OrderListParams) ([]Order, error)
}

type OrderStatus

type OrderStatus string
const (
	OrderStatusPending    OrderStatus = "Pending"
	OrderStatusProcessing OrderStatus = "Processing"
	OrderStatusShipped    OrderStatus = "Shipped"
	OrderStatusDelivered  OrderStatus = "Delivered"
	OrderStatusCanceled   OrderStatus = "Canceled"
)

type Paging

type Paging struct {
	Page     int
	PageSize int
}

type PaymentMethod

type PaymentMethod string
const (
	PaymentMethodStripeCard      PaymentMethod = "StripeCard"
	PaymentMethodPaypal          PaymentMethod = "Paypal"
	PaymentMethodAnorInstallment PaymentMethod = "AnorInstallment"
	PaymentMethodPayOnDelivery   PaymentMethod = "PayOnDelivery"
)

type PaymentStatus

type PaymentStatus string
const (
	PaymentStatusPending PaymentStatus = "Pending"
	PaymentStatusPaid    PaymentStatus = "Paid"
)

type PopularProductListParams

type PopularProductListParams struct {
	Page     int
	PageSize int
}

type Product

type Product struct {
	ID               int64
	StoreID          int32
	CategoryID       int32
	Name             string
	Brand            string
	Handle           string
	ShortInformation []string
	ImageUrls        map[int]string
	Specifications   map[string]string
	Status           ProductStatus
	CreatedAt        time.Time
	UpdatedAt        time.Time

	Store           Store
	Category        Category
	ProductVariants []ProductVariant
	Attributes      []ProductAttribute
	Pricing         ProductPricing
	Rating          Rating
	Reviews         []ProductRating

	SoldCount int
	LeftCount int
}

type ProductAttribute

type ProductAttribute struct {
	Attribute string
	Values    []string
}

type ProductPricing

type ProductPricing struct {
	ProductID       int64
	BasePrice       decimal.Decimal
	CurrencyCode    string
	Discount        decimal.Decimal
	DiscountedPrice decimal.Decimal
	IsOnSale        bool
}

type ProductRating

type ProductRating struct {
	ID        int64
	ProductID int64
	UserID    int64
	Rating    int16
	Review    string
	ImageUrls []string
	CreatedAt time.Time
}

type ProductService

type ProductService interface {
	Get(ctx context.Context, id int64, with relation.Set) (*Product, error)
	ListByCategory(ctx context.Context, category Category, params ListByCategoryParams) ([]Product, int64, error)
	ListAllBrandsByCategory(ctx context.Context, category Category) ([]string, error)
	GetMinMaxPricesByCategory(ctx context.Context, category Category) ([2]decimal.Decimal, error)

	ListPopularProducts(ctx context.Context, params PopularProductListParams) ([]Product, error)
}

type ProductStatus

type ProductStatus string
const (
	ProductStatusDraft           ProductStatus = "Draft"
	ProductStatusPendingApproval ProductStatus = "PendingApproval"
	ProductStatusPublished       ProductStatus = "Published"
)

type ProductVariant

type ProductVariant struct {
	ID               int64                 `json:"id"`
	SKU              string                `json:"sku"`
	Qty              int32                 `json:"quantity"`
	IsCustomPriced   bool                  `json:"-"` // TODO: implement
	ImageIdentifiers []int16               `json:"-"` // TODO: implement
	Pricing          ProductVariantPricing `json:"-"` // TODO: implement
	CreatedAt        time.Time             `json:"-"`
	UpdatedAt        time.Time             `json:"-"`

	Attributes map[string]string `json:"attributes"`
}

type ProductVariantPricing

type ProductVariantPricing struct {
	VariantID       int64
	BasePrice       decimal.Decimal
	CurrencyCode    string
	Discount        decimal.Decimal
	DiscountedPrice decimal.Decimal
	IsOnSale        bool
}

type Rating

type Rating struct {
	Rating      float32
	RatingCount int
}

type Router

type Router struct {
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter() *Router

func (*Router) Group

func (router *Router) Group(prefix string, middlewares ...MiddlewareFunc) *Router

func (*Router) Handle

func (router *Router) Handle(pattern string, handler http.Handler, middlewares ...MiddlewareFunc)

func (*Router) HandleFunc

func (router *Router) HandleFunc(pattern string, handler http.HandlerFunc, middlewares ...MiddlewareFunc)

HandleFunc registers a new route with the given pattern, handler function, and optional middleware functions.

func (*Router) ServeHTTP

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface.

func (*Router) Use

func (router *Router) Use(mw ...MiddlewareFunc)

type SortParam

type SortParam string
const (
	SortParamPopular        SortParam = "popular"
	SortParamPriceHighToLow SortParam = "price_high_to_low"
	SortParamPriceLowToHigh SortParam = "price_low_to_high"
	SortParamHighestRated   SortParam = "highest_rated"
	SortParamNewArrivals    SortParam = "new_arrivals"
	SortParamBestSellers    SortParam = "best_sellers"
)

type Store

type Store struct {
	ID          int32
	Handle      string
	UserID      int64
	Name        string
	Description string
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

type StripeCardPayment

type StripeCardPayment struct {
	ID               int64
	OrderID          int64
	UserID           int64
	BillingAddressID int64
	PaymentIntentID  string
	PaymentMethodID  string
	Amount           decimal.Decimal
	Currency         string
	Status           string
	ClientSecret     string
	LastError        string
	CardLast4        string
	CardBrand        string
	CreatedAt        time.Time
	UpdatedAt        time.Time
}

type StripePaymentCreateParams

type StripePaymentCreateParams struct {
	OrderID          int64
	UserID           int64
	BillingAddressID int64
	PaymentIntentID  string
	PaymentMethodID  string
	Amount           decimal.Decimal
	Currency         string
	Status           string
	ClientSecret     string
	LastError        string
	CardLast4        string
	CardBrand        string
	CreatedAt        time.Time
}

type StripePaymentService

type StripePaymentService interface {
	GetByOrderID(ctx context.Context, orderID int64) (StripeCardPayment, error)
	Create(ctx context.Context, params StripePaymentCreateParams) error
}

type User

type User struct {
	ID          int64
	Email       string
	Password    string
	PhoneNumber string
	FullName    string
	Status      UserStatus
	CreatedAt   time.Time
	UpdatedAt   time.Time

	Roles []UserRole
	Cart  *Cart
	//Wishlist     Wishlist
	Orders         []Order
	ActivityCounts UserActivityCounts
}

func (User) GetFirstname

func (u User) GetFirstname() string

type UserActivityCounts

type UserActivityCounts struct {
	CartItemsCount     int
	WishlistItemsCount int
	ActiveOrdersCount  int
}

type UserCreateParams

type UserCreateParams struct {
	Name        string
	Email       string
	Password    string
	Provider    AuthProvider
	PhoneNumber string
	Status      UserStatus
}

type UserRole

type UserRole string
const (
	RoleCustomer UserRole = "customer"
	RoleSeller   UserRole = "seller"
	RoleAdmin    UserRole = "admin"
)

type UserService

type UserService interface {
	Create(ctx context.Context, params UserCreateParams) (User, error)
	Get(ctx context.Context, id int64) (User, error)
	GetByEmail(ctx context.Context, email string) (User, error)
	UpdateStatusByEmail(ctx context.Context, status UserStatus, email string) error // switch status and email
	UpdatePassword(ctx context.Context, id int64, password string) error
	GetActivityCounts(ctx context.Context, id int64) (UserActivityCounts, error)
}

type UserStatus

type UserStatus string
const (
	UserStatusBlocked             UserStatus = "Blocked"
	UserStatusRegistrationPending UserStatus = "RegistrationPending"
	UserStatusActive              UserStatus = "Active"
)

type Validator

type Validator interface {
	Validate() error
}

Validator is an interface that wraps the basic Validate method.

Validate checks the receiver's data for validity.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL