mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 22:49:03 +08:00
chore: add billing api resources (#3704)
This commit is contained in:
parent
f3c00e1a57
commit
1a855582a7
@ -8,6 +8,7 @@ import (
|
|||||||
"go.signoz.io/signoz/ee/query-service/dao"
|
"go.signoz.io/signoz/ee/query-service/dao"
|
||||||
"go.signoz.io/signoz/ee/query-service/interfaces"
|
"go.signoz.io/signoz/ee/query-service/interfaces"
|
||||||
"go.signoz.io/signoz/ee/query-service/license"
|
"go.signoz.io/signoz/ee/query-service/license"
|
||||||
|
"go.signoz.io/signoz/ee/query-service/usage"
|
||||||
baseapp "go.signoz.io/signoz/pkg/query-service/app"
|
baseapp "go.signoz.io/signoz/pkg/query-service/app"
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline"
|
"go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline"
|
||||||
"go.signoz.io/signoz/pkg/query-service/cache"
|
"go.signoz.io/signoz/pkg/query-service/cache"
|
||||||
@ -27,6 +28,7 @@ type APIHandlerOptions struct {
|
|||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
AppDao dao.ModelDao
|
AppDao dao.ModelDao
|
||||||
RulesManager *rules.Manager
|
RulesManager *rules.Manager
|
||||||
|
UsageManager *usage.Manager
|
||||||
FeatureFlags baseint.FeatureLookup
|
FeatureFlags baseint.FeatureLookup
|
||||||
LicenseManager *license.Manager
|
LicenseManager *license.Manager
|
||||||
LogsParsingPipelineController *logparsingpipeline.LogParsingPipelineController
|
LogsParsingPipelineController *logparsingpipeline.LogParsingPipelineController
|
||||||
@ -82,6 +84,10 @@ func (ah *APIHandler) LM() *license.Manager {
|
|||||||
return ah.opts.LicenseManager
|
return ah.opts.LicenseManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) UM() *usage.Manager {
|
||||||
|
return ah.opts.UsageManager
|
||||||
|
}
|
||||||
|
|
||||||
func (ah *APIHandler) AppDao() dao.ModelDao {
|
func (ah *APIHandler) AppDao() dao.ModelDao {
|
||||||
return ah.opts.AppDao
|
return ah.opts.AppDao
|
||||||
}
|
}
|
||||||
@ -150,6 +156,13 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew
|
|||||||
router.HandleFunc("/api/v1/pat", am.OpenAccess(ah.getPATs)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/pat", am.OpenAccess(ah.getPATs)).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/pat/{id}", am.OpenAccess(ah.deletePAT)).Methods(http.MethodDelete)
|
router.HandleFunc("/api/v1/pat/{id}", am.OpenAccess(ah.deletePAT)).Methods(http.MethodDelete)
|
||||||
|
|
||||||
|
router.HandleFunc("/api/v1/checkout", am.AdminAccess(ah.checkout)).Methods(http.MethodPost)
|
||||||
|
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
||||||
|
|
||||||
|
router.HandleFunc("/api/v2/licenses",
|
||||||
|
am.ViewAccess(ah.listLicensesV2)).
|
||||||
|
Methods(http.MethodGet)
|
||||||
|
|
||||||
ah.APIHandler.RegisterRoutes(router, am)
|
ah.APIHandler.RegisterRoutes(router, am)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,44 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.signoz.io/signoz/ee/query-service/model"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"go.signoz.io/signoz/ee/query-service/constants"
|
||||||
|
"go.signoz.io/signoz/ee/query-service/model"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type tierBreakdown struct {
|
||||||
|
UnitPrice float64 `json:"unitPrice"`
|
||||||
|
Quantity int64 `json:"quantity"`
|
||||||
|
TierStart int64 `json:"tierStart"`
|
||||||
|
TierEnd int64 `json:"tierEnd"`
|
||||||
|
TierCost float64 `json:"tierCost"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type usageResponse struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Unit string `json:"unit"`
|
||||||
|
Tiers []tierBreakdown `json:"tiers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type details struct {
|
||||||
|
Total float64 `json:"total"`
|
||||||
|
Breakdown []usageResponse `json:"breakdown"`
|
||||||
|
BaseFee float64 `json:"baseFee"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type billingDetails struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data struct {
|
||||||
|
BillingPeriodStart int64 `json:"billingPeriodStart"`
|
||||||
|
BillingPeriodEnd int64 `json:"billingPeriodEnd"`
|
||||||
|
Details details `json:"details"`
|
||||||
|
Discount float64 `json:"discount"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
func (ah *APIHandler) listLicenses(w http.ResponseWriter, r *http.Request) {
|
func (ah *APIHandler) listLicenses(w http.ResponseWriter, r *http.Request) {
|
||||||
licenses, apiError := ah.LM().GetLicenses(context.Background())
|
licenses, apiError := ah.LM().GetLicenses(context.Background())
|
||||||
if apiError != nil {
|
if apiError != nil {
|
||||||
@ -38,3 +72,150 @@ func (ah *APIHandler) applyLicense(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ah.Respond(w, license)
|
ah.Respond(w, license)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) checkout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
type checkoutResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data struct {
|
||||||
|
RedirectURL string `json:"redirectURL"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
hClient := &http.Client{}
|
||||||
|
req, err := http.NewRequest("POST", constants.LicenseSignozIo+"/checkout", r.Body)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey)
|
||||||
|
licenseResp, err := hClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode response body
|
||||||
|
var resp checkoutResponse
|
||||||
|
if err := json.NewDecoder(licenseResp.Body).Decode(&resp); err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ah.Respond(w, resp.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) getBilling(w http.ResponseWriter, r *http.Request) {
|
||||||
|
licenseKey := r.URL.Query().Get("licenseKey")
|
||||||
|
|
||||||
|
if licenseKey == "" {
|
||||||
|
RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
billingURL := fmt.Sprintf("%s/usage?licenseKey=%s", constants.LicenseSignozIo, licenseKey)
|
||||||
|
|
||||||
|
hClient := &http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", billingURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey)
|
||||||
|
billingResp, err := hClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode response body
|
||||||
|
var billingResponse billingDetails
|
||||||
|
if err := json.NewDecoder(billingResp.Body).Decode(&billingResponse); err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(srikanthccv):Fetch the current day usage and add it to the response
|
||||||
|
ah.Respond(w, billingResponse.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
licenses, apiError := ah.LM().GetLicenses(context.Background())
|
||||||
|
if apiError != nil {
|
||||||
|
RespondError(w, apiError, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := model.Licenses{
|
||||||
|
TrialStart: -1,
|
||||||
|
TrialEnd: -1,
|
||||||
|
OnTrial: false,
|
||||||
|
WorkSpaceBlock: false,
|
||||||
|
Licenses: licenses,
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentActiveLicenseKey string
|
||||||
|
|
||||||
|
for _, license := range licenses {
|
||||||
|
if license.IsCurrent {
|
||||||
|
currentActiveLicenseKey = license.Key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the case when no license is applied i.e community edition
|
||||||
|
// There will be no trial details or license details
|
||||||
|
if currentActiveLicenseKey == "" {
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch trial details
|
||||||
|
hClient := &http.Client{}
|
||||||
|
url := fmt.Sprintf("%s/trial?licenseKey=%s", constants.LicenseSignozIo, currentActiveLicenseKey)
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Error("Error while creating request for trial details", err)
|
||||||
|
// If there is an error in fetching trial details, we will still return the license details
|
||||||
|
// to avoid blocking the UI
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey)
|
||||||
|
trialResp, err := hClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Error("Error while fetching trial details", err)
|
||||||
|
// If there is an error in fetching trial details, we will still return the license details
|
||||||
|
// to avoid incorrectly blocking the UI
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer trialResp.Body.Close()
|
||||||
|
|
||||||
|
trialRespBody, err := io.ReadAll(trialResp.Body)
|
||||||
|
|
||||||
|
if err != nil || trialResp.StatusCode != http.StatusOK {
|
||||||
|
zap.S().Error("Error while fetching trial details", err)
|
||||||
|
// If there is an error in fetching trial details, we will still return the license details
|
||||||
|
// to avoid incorrectly blocking the UI
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode response body
|
||||||
|
var trialRespData model.SubscriptionServerResp
|
||||||
|
|
||||||
|
if err := json.Unmarshal(trialRespBody, &trialRespData); err != nil {
|
||||||
|
zap.S().Error("Error while decoding trial details", err)
|
||||||
|
// If there is an error in fetching trial details, we will still return the license details
|
||||||
|
// to avoid incorrectly blocking the UI
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.TrialStart = trialRespData.Data.TrialStart
|
||||||
|
resp.TrialEnd = trialRespData.Data.TrialEnd
|
||||||
|
resp.OnTrial = trialRespData.Data.OnTrial
|
||||||
|
resp.WorkSpaceBlock = trialRespData.Data.WorkSpaceBlock
|
||||||
|
|
||||||
|
ah.Respond(w, resp)
|
||||||
|
}
|
||||||
|
@ -217,6 +217,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
DialTimeout: serverOptions.DialTimeout,
|
DialTimeout: serverOptions.DialTimeout,
|
||||||
AppDao: modelDao,
|
AppDao: modelDao,
|
||||||
RulesManager: rm,
|
RulesManager: rm,
|
||||||
|
UsageManager: usageManager,
|
||||||
FeatureFlags: lm,
|
FeatureFlags: lm,
|
||||||
LicenseManager: lm,
|
LicenseManager: lm,
|
||||||
LogsParsingPipelineController: logParsingPipelineController,
|
LogsParsingPipelineController: logParsingPipelineController,
|
||||||
|
@ -9,6 +9,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var LicenseSignozIo = "https://license.signoz.io/api/v1"
|
var LicenseSignozIo = "https://license.signoz.io/api/v1"
|
||||||
|
var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
|
||||||
|
|
||||||
var SpanLimitStr = GetOrDefaultEnv("SPAN_LIMIT", "5000")
|
var SpanLimitStr = GetOrDefaultEnv("SPAN_LIMIT", "5000")
|
||||||
|
|
||||||
|
@ -89,3 +89,16 @@ func (l *License) ParseFeatures() {
|
|||||||
l.FeatureSet = BasicPlan
|
l.FeatureSet = BasicPlan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Licenses struct {
|
||||||
|
TrialStart int64 `json:"trialStart"`
|
||||||
|
TrialEnd int64 `json:"trialEnd"`
|
||||||
|
OnTrial bool `json:"onTrial"`
|
||||||
|
WorkSpaceBlock bool `json:"workSpaceBlock"`
|
||||||
|
Licenses []License `json:"licenses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscriptionServerResp struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data Licenses `json:"data"`
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user