Yimaru-BackEnd/internal/web_server/handlers/subscription_content_gate.go
Yared Yemane 26cf7d2908 feat: scope subscriptions by content type and make Duolingo plans lifetime
LEARN_ENGLISH plans unlock LMS only; IELTS and DUOLINGO unlock matching exam-prep catalog courses. Enable category subscription gating, restrict programs to Learn English, and treat Duolingo subscriptions as non-expiring one-time purchases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 03:59:26 -07:00

85 lines
2.5 KiB
Go

package handlers
import (
"Yimaru-Backend/internal/domain"
"fmt"
"github.com/gofiber/fiber/v2"
)
func (h *Handler) learnerHasSubscriptionCategory(c *fiber.Ctx, category domain.SubscriptionCategory) (bool, error) {
userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 {
return false, fiber.NewError(fiber.StatusUnauthorized, "Unauthorized")
}
return h.subscriptionsSvc.HasActiveSubscriptionByCategory(c.Context(), userID, category)
}
func (h *Handler) ensureLearnerExamPrepContentAccess(c *fiber.Ctx, contentCategory string) error {
role, _ := c.Locals("role").(domain.Role)
if !role.IsCustomerLearnerRole() {
return nil
}
if !domain.IsExamPrepContentCategory(contentCategory) {
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
Message: "Catalog course not found",
})
}
active, err := h.learnerHasSubscriptionCategory(c, domain.SubscriptionCategory(contentCategory))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: fmt.Sprintf("Failed to verify %s subscription", contentCategory),
Error: err.Error(),
})
}
if !active {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: fmt.Sprintf("An active %s subscription is required", humanizeSubscriptionCategory(domain.SubscriptionCategory(contentCategory))),
})
}
return nil
}
func (h *Handler) blockLearnerIfNotLMSProgram(c *fiber.Ctx, program domain.Program) error {
role, _ := c.Locals("role").(domain.Role)
if !role.IsCustomerLearnerRole() {
return nil
}
if !domain.IsLMSContentCategory(program.Category) {
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
Message: "Program not found",
})
}
return nil
}
func humanizeSubscriptionCategory(category domain.SubscriptionCategory) string {
switch category {
case domain.SubscriptionCategoryLearnEnglish:
return "learn english"
case domain.SubscriptionCategoryIELTS:
return "IELTS"
case domain.SubscriptionCategoryDuolingo:
return "Duolingo"
default:
return string(category)
}
}
func filterExamPrepCatalogCoursesForLearner(items []domain.ExamPrepCatalogCourse, hasIELTS, hasDuolingo bool) []domain.ExamPrepCatalogCourse {
filtered := make([]domain.ExamPrepCatalogCourse, 0, len(items))
for _, item := range items {
switch domain.SubscriptionCategory(item.Category) {
case domain.SubscriptionCategoryIELTS:
if hasIELTS {
filtered = append(filtered, item)
}
case domain.SubscriptionCategoryDuolingo:
if hasDuolingo {
filtered = append(filtered, item)
}
}
}
return filtered
}