Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
package httpapi
import (
"errors"
"net/http"
"github.com/descrybe/descrybe-v2/apps/api/internal/billing"
)
// writePlanGate writes a 402 plan_gate payload when err is a known billing gate.
// Returns true when the response was written.
func writePlanGate(w http.ResponseWriter, err error) bool {
if err == nil {
return false
}
if !(errors.Is(err, billing.ErrInsufficientCredits) ||
errors.Is(err, billing.ErrProductLimitExceeded) ||
errors.Is(err, billing.ErrAIRequiresUpgrade) ||
errors.Is(err, billing.ErrEPRELRequiresUpgrade) ||
errors.Is(err, billing.ErrFeatureDisabled)) {
return false
}
body := map[string]any{
"error": err.Error(),
"code": planGateCode(err),
"upgrade_url": "/pricing",
}
if errors.Is(err, billing.ErrFeatureDisabled) {
body["error"] = "feature_disabled"
if key := billing.FeatureKeyFromError(err); key != "" {
body["feature"] = key
}
}
JSON(w, http.StatusPaymentRequired, body)
return true
}
// requireFeatures rejects with 402 when any key is not effective for the company.
// Billing nil: pass-through only outside production; production fails closed with 503.
// Returns false when the response was already written.
func (s *Server) requireFeatures(w http.ResponseWriter, r *http.Request, keys ...string) bool {
if len(keys) == 0 {
return true
}
if s.testAssertFeatures != nil {
if err := s.testAssertFeatures(r.Context(), keys...); err != nil {
if writePlanGate(w, err) {
return false
}
Error(w, http.StatusInternalServerError, "feature check failed")
return false
}
return true
}
if s.Billing == nil {
if s.Config.IsProduction() {
Error(w, http.StatusServiceUnavailable, "billing unavailable")
return false
}
return true
}
cid, ok := CompanyIDFromContext(r.Context())
if !ok {
Error(w, http.StatusUnauthorized, "company required")
return false
}
if err := s.Billing.AssertFeatures(r.Context(), cid, keys...); err != nil {
if writePlanGate(w, err) {
return false
}
Error(w, http.StatusInternalServerError, "feature check failed")
return false
}
return true
}
// RequireFeature rejects the request with 402 when the company's effective
// features do not include key. Billing nil: pass-through only outside production;
// production fails closed with 503 (never silently allow all features).
func (s *Server) RequireFeature(key string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !s.requireFeatures(w, r, key) {
return
}
next.ServeHTTP(w, r)
})
}
}