167 lines
5.9 KiB
Go
167 lines
5.9 KiB
Go
package v1
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gochat/gochat/internal/service"
|
|
"github.com/gochat/gochat/pkg/response"
|
|
)
|
|
|
|
// EnterpriseAccountHandler implements Chatwoot enterprise account billing and
|
|
// limit endpoints consumed by the reused dashboard EnterpriseAccountAPI client.
|
|
type EnterpriseAccountHandler struct {
|
|
svc *service.AccountService
|
|
}
|
|
|
|
func NewEnterpriseAccountHandler(svc *service.AccountService) *EnterpriseAccountHandler {
|
|
return &EnterpriseAccountHandler{svc: svc}
|
|
}
|
|
|
|
func enterpriseAccountID(c *gin.Context) uint {
|
|
if id := parseAccountIDParam(c); id != 0 {
|
|
return id
|
|
}
|
|
return getAccountID(c)
|
|
}
|
|
|
|
// Limits returns account usage limits in Chatwoot's enterprise payload shape.
|
|
// GET /enterprise/api/v1/accounts/:account_id/limits
|
|
func (h *EnterpriseAccountHandler) Limits(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
|
|
payload, err := h.svc.EnterpriseLimits(c.Request.Context(), accountID, getUserID(c))
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrAccountNotFound, "account not found")
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, payload)
|
|
}
|
|
|
|
// ToggleDeletion marks or unmarks an account for scheduled deletion.
|
|
// POST /enterprise/api/v1/accounts/:account_id/toggle_deletion
|
|
func (h *EnterpriseAccountHandler) ToggleDeletion(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
ActionType string `json:"action_type" form:"action_type"`
|
|
}
|
|
_ = c.ShouldBind(&req)
|
|
|
|
switch req.ActionType {
|
|
case "delete":
|
|
if _, err := h.svc.MarkForDeletion(c.Request.Context(), accountID, getUserID(c), "manual_deletion"); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusUnprocessableEntity, response.ErrValidation, err.Error())
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "Account marked for deletion"})
|
|
case "undelete":
|
|
if _, err := h.svc.UnmarkForDeletion(c.Request.Context(), accountID, getUserID(c)); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusUnprocessableEntity, response.ErrValidation, err.Error())
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "Account unmarked for deletion"})
|
|
default:
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Invalid action_type. Must be either \"delete\" or \"undelete\""})
|
|
}
|
|
}
|
|
|
|
// Subscription mirrors the Cloud customer-creation guard and returns no content.
|
|
// POST /enterprise/api/v1/accounts/:account_id/subscription
|
|
func (h *EnterpriseAccountHandler) Subscription(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
payload, selectionRequired, err := h.svc.EnterpriseSubscription(c.Request.Context(), getUserID(c), accountID)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrAccountNotFound, "account not found")
|
|
return
|
|
}
|
|
if selectionRequired {
|
|
c.JSON(http.StatusOK, payload)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// Checkout returns Chatwoot's billing-details error when no Stripe session can be created locally.
|
|
// POST /enterprise/api/v1/accounts/:account_id/checkout
|
|
func (h *EnterpriseAccountHandler) Checkout(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
if _, err := h.svc.GetByUserAndID(c.Request.Context(), getUserID(c), accountID); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrAccountNotFound, "account not found")
|
|
return
|
|
}
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Please subscribe to a plan before viewing the billing details"})
|
|
}
|
|
|
|
// TopupCheckout validates credits and exposes a provider-unavailable boundary for local installs.
|
|
// POST /enterprise/api/v1/accounts/:account_id/topup_checkout
|
|
func (h *EnterpriseAccountHandler) TopupCheckout(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
if _, err := h.svc.GetByUserAndID(c.Request.Context(), getUserID(c), accountID); err != nil {
|
|
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrAccountNotFound, "account not found")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Credits int `json:"credits" form:"credits"`
|
|
}
|
|
_ = c.ShouldBind(&req)
|
|
if req.Credits <= 0 {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Credits are required"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Top-up checkout provider is not configured"})
|
|
}
|
|
|
|
func (h *EnterpriseAccountHandler) SelectBillingCurrency(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
var req struct {
|
|
Currency string `json:"currency" form:"currency"`
|
|
}
|
|
_ = c.ShouldBind(&req)
|
|
if accountID == 0 || strings.TrimSpace(req.Currency) == "" {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "Invalid billing currency"})
|
|
return
|
|
}
|
|
if err := h.svc.SelectBillingCurrency(c.Request.Context(), getUserID(c), accountID, req.Currency); err != nil {
|
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *EnterpriseAccountHandler) TopupOptions(c *gin.Context) {
|
|
accountID := enterpriseAccountID(c)
|
|
if accountID == 0 {
|
|
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
|
return
|
|
}
|
|
payload, err := h.svc.EnterpriseTopupOptions(c.Request.Context(), getUserID(c), accountID)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrAccountNotFound, "account not found")
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, payload)
|
|
}
|