mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-03 16:02:02 +00:00
138 lines
4.2 KiB
Go
138 lines
4.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
)
|
|
|
|
// adminManagedPlanRequest is a grant. A reason is required for the same reason
|
|
// the risk override requires one: an unexplained paid workspace is the thing
|
|
// this feature exists to avoid creating.
|
|
type adminManagedPlanRequest struct {
|
|
PlanID string `json:"plan_id"`
|
|
Reason string `json:"reason"`
|
|
Until *time.Time `json:"until,omitempty"`
|
|
}
|
|
|
|
// AdminListPlans returns every plan, private ones included. The customer
|
|
// endpoint returns only public plans, and a grant is most often onto a private
|
|
// one (an internal or partner plan), so the picker needs the full list.
|
|
func (h *Handler) AdminListPlans(c *gin.Context) {
|
|
if h.SubscriptionService == nil {
|
|
errx.JSON(c, errx.NewPublic(errx.ServiceUnavailable, "Plans are not available on this instance."))
|
|
return
|
|
}
|
|
plans, xerr := h.SubscriptionService.ListPlans(c.Request.Context(), false)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"plans": plans})
|
|
}
|
|
|
|
// AdminGetOrgManagedPlan reports whether a workspace is paid because an
|
|
// operator said so, and why.
|
|
func (h *Handler) AdminGetOrgManagedPlan(c *gin.Context) {
|
|
orgID, ok := adminOrgParam(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if h.OrganizationService == nil {
|
|
errx.JSON(c, errx.NewPublic(errx.ServiceUnavailable, "Organizations are not available on this instance."))
|
|
return
|
|
}
|
|
managed, xerr := h.OrganizationService.GetManagedPlan(c.Request.Context(), orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, managed)
|
|
}
|
|
|
|
// AdminGrantOrgManagedPlan puts a workspace on a plan without Stripe.
|
|
func (h *Handler) AdminGrantOrgManagedPlan(c *gin.Context) {
|
|
adminID := middleware.GetAdminUserID(c)
|
|
if adminID == nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
orgID, ok := adminOrgParam(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if h.OrganizationService == nil {
|
|
errx.JSON(c, errx.NewPublic(errx.ServiceUnavailable, "Organizations are not available on this instance."))
|
|
return
|
|
}
|
|
|
|
var req adminManagedPlanRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "a plan and a reason are required"))
|
|
return
|
|
}
|
|
planID, err := uuid.Parse(strings.TrimSpace(req.PlanID))
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "plan_id must be a plan uuid"))
|
|
return
|
|
}
|
|
reason := strings.TrimSpace(req.Reason)
|
|
if reason == "" {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "a reason is required, so the grant is answerable later"))
|
|
return
|
|
}
|
|
|
|
managed, xerr := h.OrganizationService.GrantManagedPlan(c.Request.Context(), orgID, planID, *adminID, reason, req.Until)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
details := map[string]any{"plan_id": planID.String(), "reason": reason}
|
|
if req.Until != nil {
|
|
details["until"] = req.Until.UTC().Format(time.RFC3339)
|
|
}
|
|
h.logOrgPlanAction(c, *adminID, orgID, "grant_managed_plan", details)
|
|
c.JSON(http.StatusOK, managed)
|
|
}
|
|
|
|
// AdminRevokeOrgManagedPlan ends a grant. The workspace keeps the plan row it
|
|
// was on and stops counting as paid.
|
|
func (h *Handler) AdminRevokeOrgManagedPlan(c *gin.Context) {
|
|
adminID := middleware.GetAdminUserID(c)
|
|
if adminID == nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
orgID, ok := adminOrgParam(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if h.OrganizationService == nil {
|
|
errx.JSON(c, errx.NewPublic(errx.ServiceUnavailable, "Organizations are not available on this instance."))
|
|
return
|
|
}
|
|
|
|
managed, xerr := h.OrganizationService.RevokeManagedPlan(c.Request.Context(), orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.logOrgPlanAction(c, *adminID, orgID, "revoke_managed_plan", nil)
|
|
c.JSON(http.StatusOK, managed)
|
|
}
|
|
|
|
// logOrgPlanAction records the operator action on the platform-admin trail,
|
|
// which is who-did-what across tenants rather than within one.
|
|
func (h *Handler) logOrgPlanAction(c *gin.Context, adminID, orgID uuid.UUID, action string, details map[string]any) {
|
|
if h.AdminService == nil {
|
|
return
|
|
}
|
|
h.AdminService.LogAdminAction(c.Request.Context(), adminID, action, "organization", &orgID,
|
|
details, c.ClientIP(), c.Request.UserAgent())
|
|
}
|