Implement JWT session and token

This commit is contained in:
2026-05-07 15:59:58 +07:00
parent 3721fb3cd7
commit f5d9fe5223
9 changed files with 309 additions and 61 deletions
+123 -44
View File
@@ -19,12 +19,14 @@ import (
)
type SelfOrderHandler struct {
orderService service.OrderService
categoryService service.CategoryService
productService service.ProductService
tableRepo repository.TableRepositoryInterface
outletRepo processor.OutletRepository
userRepo processor.UserRepository
orderService service.OrderService
categoryService service.CategoryService
productService service.ProductService
tableRepo repository.TableRepositoryInterface
outletRepo processor.OutletRepository
userRepo processor.UserRepository
selfOrderJWTSecret string
selfOrderJWTTTL int
}
func NewSelfOrderHandler(
@@ -34,44 +36,120 @@ func NewSelfOrderHandler(
tableRepo repository.TableRepositoryInterface,
outletRepo processor.OutletRepository,
userRepo processor.UserRepository,
selfOrderJWTSecret string,
selfOrderJWTTTL int,
) *SelfOrderHandler {
return &SelfOrderHandler{
orderService: orderService,
categoryService: categoryService,
productService: productService,
tableRepo: tableRepo,
outletRepo: outletRepo,
userRepo: userRepo,
orderService: orderService,
categoryService: categoryService,
productService: productService,
tableRepo: tableRepo,
outletRepo: outletRepo,
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()
var req contract.SelfOrderMenuRequest
var req contract.SelfOrderSessionRequest
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{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
}), "SelfOrderHandler::GetMenu")
}), "SelfOrderHandler::CreateSession")
return
}
if req.TableID == uuid.Nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "table_id is required"),
}), "SelfOrderHandler::GetMenu")
}), "SelfOrderHandler::CreateSession")
return
}
if req.CustomerName == "" {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "customer_name is required"),
}), "SelfOrderHandler::GetMenu")
}), "SelfOrderHandler::CreateSession")
return
}
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 {
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::GetMenu -> table not found")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
@@ -96,6 +174,8 @@ func (h *SelfOrderHandler) GetMenu(c *gin.Context) {
return
}
_ = customerName
isActive := true
catResp := h.categoryService.ListCategories(ctx, &contract.ListCategoriesRequest{
OrganizationID: &table.OrganizationID,
@@ -192,6 +272,14 @@ func (h *SelfOrderHandler) buildMenuResponse(
func (h *SelfOrderHandler) CreateOrder(c *gin.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
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> request binding failed")
@@ -208,7 +296,7 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
return
}
table, err := h.tableRepo.GetByID(ctx, req.TableID)
table, err := h.tableRepo.GetByID(ctx, tableID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::CreateOrder -> table not found")
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{})
metadata["self_order"] = true
metadata["customer_name"] = req.CustomerName
customerPhone := phone
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{
OutletID: table.OutletID,
UserID: userID,
TableID: &tableID,
TableID: &tableIDPtr,
TableNumber: &table.TableName,
OrderType: constants.OrderTypeDineIn,
OrderItems: orderItems,
CustomerName: &req.CustomerName,
CustomerName: &customerName,
Metadata: metadata,
}
@@ -276,12 +369,6 @@ func (h *SelfOrderHandler) CreateOrder(c *gin.Context) {
}
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 {
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) {
ctx := c.Request.Context()
var req contract.SelfOrderListCategoriesRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::ListCategories -> query binding failed")
tableID, _, _, err := h.getSelfOrderContext(c)
if err != nil {
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")
return
}
if req.TableID == uuid.Nil {
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)
table, err := h.tableRepo.GetByID(ctx, tableID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("SelfOrderHandler::ListCategories -> table not found")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{