Compare commits
1 Commits
fe57aab3b4
...
f5d9fe5223
| Author | SHA1 | Date | |
|---|---|---|---|
| f5d9fe5223 |
@ -79,6 +79,14 @@ func (c *Config) GetCustomerJWTExpiresTTL() int {
|
|||||||
return c.Jwt.Customer.ExpiresTTL
|
return c.Jwt.Customer.ExpiresTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetSelfOrderJWTSecret() string {
|
||||||
|
return c.Jwt.SelfOrder.Secret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetSelfOrderJWTExpiresTTL() int {
|
||||||
|
return c.Jwt.SelfOrder.ExpiresTTL
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) LogLevel() string {
|
func (c *Config) LogLevel() string {
|
||||||
return c.Log.LogLevel
|
return c.Log.LogLevel
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,12 @@ type Jwt struct {
|
|||||||
Token Token `mapstructure:"token"`
|
Token Token `mapstructure:"token"`
|
||||||
RefreshToken RefreshToken `mapstructure:"refresh_token"`
|
RefreshToken RefreshToken `mapstructure:"refresh_token"`
|
||||||
Customer Customer `mapstructure:"customer"`
|
Customer Customer `mapstructure:"customer"`
|
||||||
|
SelfOrder SelfOrder `mapstructure:"self_order"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelfOrder struct {
|
||||||
|
ExpiresTTL int `mapstructure:"expires-ttl"`
|
||||||
|
Secret string `mapstructure:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
|
|||||||
@ -13,6 +13,9 @@ jwt:
|
|||||||
customer:
|
customer:
|
||||||
expires-ttl: 7776000
|
expires-ttl: 7776000
|
||||||
secret: "z8d5TlFCT58Q$i0%S^2M&3WtE$PMgd"
|
secret: "z8d5TlFCT58Q$i0%S^2M&3WtE$PMgd"
|
||||||
|
self_order:
|
||||||
|
expires-ttl: 120
|
||||||
|
secret: "S3lf0rd3r_S3ss10n_S3cr3t_K3y_2024"
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
host: 62.72.45.250
|
host: 62.72.45.250
|
||||||
|
|||||||
@ -51,6 +51,8 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
repos.tableRepo,
|
repos.tableRepo,
|
||||||
repos.outletRepo,
|
repos.outletRepo,
|
||||||
repos.userRepo,
|
repos.userRepo,
|
||||||
|
cfg.GetSelfOrderJWTSecret(),
|
||||||
|
cfg.GetSelfOrderJWTExpiresTTL(),
|
||||||
)
|
)
|
||||||
|
|
||||||
a.router = router.NewRouter(
|
a.router = router.NewRouter(
|
||||||
@ -114,6 +116,7 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
services.spinGameService,
|
services.spinGameService,
|
||||||
middleware.customerAuthMiddleware,
|
middleware.customerAuthMiddleware,
|
||||||
selfOrderHandler,
|
selfOrderHandler,
|
||||||
|
middleware.selfOrderAuthMiddleware,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -448,14 +451,16 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
}
|
}
|
||||||
|
|
||||||
type middlewares struct {
|
type middlewares struct {
|
||||||
authMiddleware *middleware.AuthMiddleware
|
authMiddleware *middleware.AuthMiddleware
|
||||||
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
||||||
|
selfOrderAuthMiddleware *middleware.SelfOrderAuthMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
|
func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
|
||||||
return &middlewares{
|
return &middlewares{
|
||||||
authMiddleware: middleware.NewAuthMiddleware(services.authService),
|
authMiddleware: middleware.NewAuthMiddleware(services.authService),
|
||||||
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
|
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
|
||||||
|
selfOrderAuthMiddleware: middleware.NewSelfOrderAuthMiddleware(cfg.GetSelfOrderJWTSecret()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,18 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SelfOrderMenuRequest struct {
|
type SelfOrderSessionRequest struct {
|
||||||
TableID uuid.UUID `json:"table_id" validate:"required"`
|
TableID uuid.UUID `json:"table_id" validate:"required"`
|
||||||
CustomerName string `json:"customer_name" validate:"required"`
|
CustomerName string `json:"customer_name" validate:"required"`
|
||||||
Phone *string `json:"phone,omitempty"`
|
Phone *string `json:"phone,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SelfOrderSessionResponse struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
TableID uuid.UUID `json:"table_id"`
|
||||||
|
}
|
||||||
|
|
||||||
type SelfOrderMenuResponse struct {
|
type SelfOrderMenuResponse struct {
|
||||||
OutletName string `json:"outlet_name"`
|
OutletName string `json:"outlet_name"`
|
||||||
TableName string `json:"table_name"`
|
TableName string `json:"table_name"`
|
||||||
@ -40,10 +46,8 @@ type SelfOrderMenuVariant struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderCreateOrderRequest struct {
|
type SelfOrderCreateOrderRequest struct {
|
||||||
TableID uuid.UUID `json:"table_id" validate:"required"`
|
Phone *string `json:"phone,omitempty"`
|
||||||
CustomerName string `json:"customer_name" validate:"required"`
|
OrderItems []SelfOrderCreateOrderItem `json:"order_items" validate:"required,min=1,dive"`
|
||||||
Phone *string `json:"phone,omitempty"`
|
|
||||||
OrderItems []SelfOrderCreateOrderItem `json:"order_items" validate:"required,min=1,dive"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderCreateOrderItem struct {
|
type SelfOrderCreateOrderItem struct {
|
||||||
@ -53,10 +57,6 @@ type SelfOrderCreateOrderItem struct {
|
|||||||
Notes *string `json:"notes,omitempty"`
|
Notes *string `json:"notes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelfOrderListCategoriesRequest struct {
|
|
||||||
TableID uuid.UUID `form:"table_id" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelfOrderCategoryItem struct {
|
type SelfOrderCategoryItem struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
@ -19,12 +19,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SelfOrderHandler struct {
|
type SelfOrderHandler struct {
|
||||||
orderService service.OrderService
|
orderService service.OrderService
|
||||||
categoryService service.CategoryService
|
categoryService service.CategoryService
|
||||||
productService service.ProductService
|
productService service.ProductService
|
||||||
tableRepo repository.TableRepositoryInterface
|
tableRepo repository.TableRepositoryInterface
|
||||||
outletRepo processor.OutletRepository
|
outletRepo processor.OutletRepository
|
||||||
userRepo processor.UserRepository
|
userRepo processor.UserRepository
|
||||||
|
selfOrderJWTSecret string
|
||||||
|
selfOrderJWTTTL int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSelfOrderHandler(
|
func NewSelfOrderHandler(
|
||||||
@ -34,44 +36,120 @@ func NewSelfOrderHandler(
|
|||||||
tableRepo repository.TableRepositoryInterface,
|
tableRepo repository.TableRepositoryInterface,
|
||||||
outletRepo processor.OutletRepository,
|
outletRepo processor.OutletRepository,
|
||||||
userRepo processor.UserRepository,
|
userRepo processor.UserRepository,
|
||||||
|
selfOrderJWTSecret string,
|
||||||
|
selfOrderJWTTTL int,
|
||||||
) *SelfOrderHandler {
|
) *SelfOrderHandler {
|
||||||
return &SelfOrderHandler{
|
return &SelfOrderHandler{
|
||||||
orderService: orderService,
|
orderService: orderService,
|
||||||
categoryService: categoryService,
|
categoryService: categoryService,
|
||||||
productService: productService,
|
productService: productService,
|
||||||
tableRepo: tableRepo,
|
tableRepo: tableRepo,
|
||||||
outletRepo: outletRepo,
|
outletRepo: outletRepo,
|
||||||
userRepo: userRepo,
|
userRepo: userRepo,
|
||||||
|
selfOrderJWTSecret: selfOrderJWTSecret,
|
||||||
|
selfOrderJWTTTL: selfOrderJWTTTL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
|
func (h *SelfOrderHandler) getSelfOrderContext(c *gin.Context) (uuid.UUID, string, string, error) {
|
||||||
|
tableIDStr, _ := c.Get("self_order_table_id")
|
||||||
|
customerName, _ := c.Get("self_order_customer_name")
|
||||||
|
phoneStr, _ := c.Get("self_order_phone")
|
||||||
|
|
||||||
|
tableIDStrTyped, ok := tableIDStr.(string)
|
||||||
|
if !ok || tableIDStrTyped == "" {
|
||||||
|
return uuid.Nil, "", "", fmt.Errorf("table_id not found in context")
|
||||||
|
}
|
||||||
|
tableID, err := uuid.Parse(tableIDStrTyped)
|
||||||
|
if err != nil {
|
||||||
|
return uuid.Nil, "", "", fmt.Errorf("invalid table_id in token")
|
||||||
|
}
|
||||||
|
|
||||||
|
nameTyped, ok := customerName.(string)
|
||||||
|
if !ok || nameTyped == "" {
|
||||||
|
return uuid.Nil, "", "", fmt.Errorf("customer_name not found in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
phone, _ := phoneStr.(string)
|
||||||
|
|
||||||
|
return tableID, nameTyped, phone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SelfOrderHandler) CreateSession(c *gin.Context) {
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
var req contract.SelfOrderMenuRequest
|
var req contract.SelfOrderSessionRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> request binding failed")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateSession -> request binding failed")
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
||||||
}), "SelfOrderHandler::GetMenu")
|
}), "SelfOrderHandler::CreateSession")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.TableID == uuid.Nil {
|
if req.TableID == uuid.Nil {
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "table_id is required"),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "table_id is required"),
|
||||||
}), "SelfOrderHandler::GetMenu")
|
}), "SelfOrderHandler::CreateSession")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.CustomerName == "" {
|
if req.CustomerName == "" {
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "customer_name is required"),
|
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "customer_name is required"),
|
||||||
}), "SelfOrderHandler::GetMenu")
|
}), "SelfOrderHandler::CreateSession")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateSession -> table not found")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.NotFoundErrorCode, constants.TableEntity, "table not found"),
|
||||||
|
}), "SelfOrderHandler::CreateSession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !table.IsActive {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.TableEntity, "table is not active"),
|
||||||
|
}), "SelfOrderHandler::CreateSession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
phone := ""
|
||||||
|
if req.Phone != nil {
|
||||||
|
phone = *req.Phone
|
||||||
|
}
|
||||||
|
|
||||||
|
token, expiresAt, err := util.GenerateSelfOrderSessionToken(req.TableID, req.CustomerName, phone, h.selfOrderJWTSecret, h.selfOrderJWTTTL)
|
||||||
|
if err != nil {
|
||||||
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateSession -> failed to generate token")
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.InternalServerErrorCode, constants.OrderServiceEntity, "failed to create session"),
|
||||||
|
}), "SelfOrderHandler::CreateSession")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(&contract.SelfOrderSessionResponse{
|
||||||
|
Token: token,
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
|
TableID: req.TableID,
|
||||||
|
}), "SelfOrderHandler::CreateSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
tableID, customerName, _, err := h.getSelfOrderContext(c)
|
||||||
|
if err != nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, err.Error()),
|
||||||
|
}), "SelfOrderHandler::GetMenu")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
table, err := h.tableRepo.GetByID(ctx, tableID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> table not found")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> table not found")
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
@ -96,6 +174,8 @@ func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = customerName
|
||||||
|
|
||||||
isActive := true
|
isActive := true
|
||||||
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
|
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
|
||||||
OrganizationID: &table.OrganizationID,
|
OrganizationID: &table.OrganizationID,
|
||||||
@ -192,6 +272,14 @@ func (h *SelfOrderHandler) buildMenuResponse(
|
|||||||
func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
tableID, customerName, phone, err := h.getSelfOrderContext(c)
|
||||||
|
if err != nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, err.Error()),
|
||||||
|
}), "SelfOrderHandler::CreateOrder")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var req contract.SelfOrderCreateOrderRequest
|
var req contract.SelfOrderCreateOrderRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> request binding failed")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> request binding failed")
|
||||||
@ -208,7 +296,7 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
table, err := h.tableRepo.GetByID(ctx, tableID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> table not found")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> table not found")
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
@ -243,22 +331,27 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := make(map[string]interface{})
|
customerPhone := phone
|
||||||
metadata["self_order"] = true
|
|
||||||
metadata["customer_name"] = req.CustomerName
|
|
||||||
if req.Phone != nil {
|
if req.Phone != nil {
|
||||||
metadata["customer_phone"] = *req.Phone
|
customerPhone = *req.Phone
|
||||||
}
|
}
|
||||||
|
|
||||||
tableID := req.TableID
|
metadata := make(map[string]interface{})
|
||||||
|
metadata["self_order"] = true
|
||||||
|
metadata["customer_name"] = customerName
|
||||||
|
if customerPhone != "" {
|
||||||
|
metadata["customer_phone"] = customerPhone
|
||||||
|
}
|
||||||
|
|
||||||
|
tableIDPtr := tableID
|
||||||
modelReq := &models.CreateOrderRequest{
|
modelReq := &models.CreateOrderRequest{
|
||||||
OutletID: table.OutletID,
|
OutletID: table.OutletID,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
TableID: &tableID,
|
TableID: &tableIDPtr,
|
||||||
TableNumber: &table.TableName,
|
TableNumber: &table.TableName,
|
||||||
OrderType: constants.OrderTypeDineIn,
|
OrderType: constants.OrderTypeDineIn,
|
||||||
OrderItems: orderItems,
|
OrderItems: orderItems,
|
||||||
CustomerName: &req.CustomerName,
|
CustomerName: &customerName,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,12 +369,6 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCreateOrderRequest) error {
|
func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCreateOrderRequest) error {
|
||||||
if req.TableID == uuid.Nil {
|
|
||||||
return fmt.Errorf("table_id is required")
|
|
||||||
}
|
|
||||||
if req.CustomerName == "" {
|
|
||||||
return fmt.Errorf("customer_name is required")
|
|
||||||
}
|
|
||||||
if len(req.OrderItems) == 0 {
|
if len(req.OrderItems) == 0 {
|
||||||
return fmt.Errorf("at least one order item is required")
|
return fmt.Errorf("at least one order item is required")
|
||||||
}
|
}
|
||||||
@ -299,23 +386,15 @@ func (h *SelfOrderHandler) validateCreateOrderRequest(req *contract.SelfOrderCre
|
|||||||
func (h *SelfOrderHandler) ListCategories(c *gin.Context) {
|
func (h *SelfOrderHandler) ListCategories(c *gin.Context) {
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
var req contract.SelfOrderListCategoriesRequest
|
tableID, _, _, err := h.getSelfOrderContext(c)
|
||||||
if err := c.ShouldBindQuery(&req); err != nil {
|
if err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::ListCategories -> query binding failed")
|
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
|
contract.NewResponseError(constants.ValidationErrorCode, constants.RequestEntity, err.Error()),
|
||||||
}), "SelfOrderHandler::ListCategories")
|
}), "SelfOrderHandler::ListCategories")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.TableID == uuid.Nil {
|
table, err := h.tableRepo.GetByID(ctx, tableID)
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
|
||||||
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "table_id is required"),
|
|
||||||
}), "SelfOrderHandler::ListCategories")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
table, err := h.tableRepo.GetByID(ctx, req.TableID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::ListCategories -> table not found")
|
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::ListCategories -> table not found")
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
|||||||
86
internal/middleware/self_order_auth_middleware.go
Normal file
86
internal/middleware/self_order_auth_middleware.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"apskel-pos-be/internal/constants"
|
||||||
|
"apskel-pos-be/internal/contract"
|
||||||
|
"apskel-pos-be/internal/util"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SelfOrderAuthMiddleware struct {
|
||||||
|
selfOrderJWTSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSelfOrderAuthMiddleware(selfOrderJWTSecret string) *SelfOrderAuthMiddleware {
|
||||||
|
return &SelfOrderAuthMiddleware{
|
||||||
|
selfOrderJWTSecret: selfOrderJWTSecret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SelfOrderAuthMiddleware) ValidateSelfOrderToken() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
authHeader := c.GetHeader("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Authorization header is required"),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Invalid authorization header format"),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
||||||
|
if tokenString == "" {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Token is required"),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := util.ValidateSelfOrderToken(tokenString, m.selfOrderJWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Invalid token: "+err.Error()),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tableID, ok := claims["table_id"].(string)
|
||||||
|
if !ok || tableID == "" {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "table_id not found in token"),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customerName, ok := claims["customer_name"].(string)
|
||||||
|
if !ok || customerName == "" {
|
||||||
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
|
||||||
|
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "customer_name not found in token"),
|
||||||
|
}), "SelfOrderAuthMiddleware::ValidateSelfOrderToken")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
phone, _ := claims["phone"].(string)
|
||||||
|
|
||||||
|
c.Set("self_order_table_id", tableID)
|
||||||
|
c.Set("self_order_customer_name", customerName)
|
||||||
|
c.Set("self_order_phone", phone)
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -49,9 +49,10 @@ type Router struct {
|
|||||||
selfOrderHandler *handler.SelfOrderHandler
|
selfOrderHandler *handler.SelfOrderHandler
|
||||||
authMiddleware *middleware.AuthMiddleware
|
authMiddleware *middleware.AuthMiddleware
|
||||||
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
||||||
|
selfOrderAuthMiddleware *middleware.SelfOrderAuthMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, selfOrderHandler *handler.SelfOrderHandler) *Router {
|
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, selfOrderHandler *handler.SelfOrderHandler, selfOrderAuthMiddleware *middleware.SelfOrderAuthMiddleware) *Router {
|
||||||
|
|
||||||
return &Router{
|
return &Router{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
@ -91,6 +92,7 @@ func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authSer
|
|||||||
customerAuthMiddleware: customerAuthMiddleware,
|
customerAuthMiddleware: customerAuthMiddleware,
|
||||||
productVariantHandler: handler.NewProductVariantHandler(productVariantService, productVariantValidator),
|
productVariantHandler: handler.NewProductVariantHandler(productVariantService, productVariantValidator),
|
||||||
selfOrderHandler: selfOrderHandler,
|
selfOrderHandler: selfOrderHandler,
|
||||||
|
selfOrderAuthMiddleware: selfOrderAuthMiddleware,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,9 +151,15 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
|
|
||||||
selfOrder := v1.Group("/self-order")
|
selfOrder := v1.Group("/self-order")
|
||||||
{
|
{
|
||||||
selfOrder.GET("/categories", r.selfOrderHandler.ListCategories)
|
selfOrder.POST("/session", r.selfOrderHandler.CreateSession)
|
||||||
selfOrder.POST("/menu", r.selfOrderHandler.GetMenu)
|
}
|
||||||
selfOrder.POST("/order", r.selfOrderHandler.CreateOrder)
|
|
||||||
|
selfOrderProtected := v1.Group("/self-order")
|
||||||
|
selfOrderProtected.Use(r.selfOrderAuthMiddleware.ValidateSelfOrderToken())
|
||||||
|
{
|
||||||
|
selfOrderProtected.GET("/menu", r.selfOrderHandler.GetMenu)
|
||||||
|
selfOrderProtected.GET("/categories", r.selfOrderHandler.ListCategories)
|
||||||
|
selfOrderProtected.POST("/order", r.selfOrderHandler.CreateOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
organizations := v1.Group("/organizations")
|
organizations := v1.Group("/organizations")
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"apskel-pos-be/internal/entities"
|
"apskel-pos-be/internal/entities"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateCustomerTokens generates access and refresh tokens for customer
|
// GenerateCustomerTokens generates access and refresh tokens for customer
|
||||||
@ -85,3 +86,55 @@ func ExtractCustomerIDFromToken(token *jwt.Token) (string, error) {
|
|||||||
|
|
||||||
return customerID, nil
|
return customerID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateSelfOrderSessionToken(tableID uuid.UUID, customerName string, phone string, secret string, ttlMinutes int) (string, int64, error) {
|
||||||
|
now := time.Now()
|
||||||
|
expiresAt := now.Add(time.Duration(ttlMinutes) * time.Minute)
|
||||||
|
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"table_id": tableID.String(),
|
||||||
|
"customer_name": customerName,
|
||||||
|
"phone": phone,
|
||||||
|
"session_id": uuid.New().String(),
|
||||||
|
"type": "self_order_access",
|
||||||
|
"iat": now.Unix(),
|
||||||
|
"exp": expiresAt.Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString([]byte(secret))
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, fmt.Errorf("failed to generate self-order session token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenString, expiresAt.Unix(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateSelfOrderToken(tokenString string, secret string) (jwt.MapClaims, error) {
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
return []byte(secret), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse self-order token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid {
|
||||||
|
return nil, fmt.Errorf("invalid self-order token")
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid self-order token claims")
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenType, ok := claims["type"].(string)
|
||||||
|
if !ok || tokenType != "self_order_access" {
|
||||||
|
return nil, fmt.Errorf("invalid self-order token type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user