feat: Add MVNO provisioner with resource provisioning, carrier setup, billing, and API access configuration

- Add MVNOProvisioner struct with logger dependency for resource provisioning operations
- Add ProvisionResources method with tenant context, database schema, and storage provisioning
- Add SetupCarriers method with carrier selection, configuration, and carrier pool assignment
- Add SetupBilling method with billing ID generation, rate plan configuration, and payment processing setup
- Add SetupAPIAcc
This commit is contained in:
nutcas3
2026-05-02 19:04:42 +03:00
parent 3b10a90851
commit 04e9426aaf
@@ -0,0 +1,185 @@
package mvno
import (
"context"
"fmt"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id"
"github.com/sirupsen/logrus"
)
// MVNOProvisioner handles resource provisioning for MVNOs
type MVNOProvisioner struct {
logger *logrus.Logger
}
// NewMVNOProvisioner creates a new provisioner instance
func NewMVNOProvisioner(logger *logrus.Logger) *MVNOProvisioner {
return &MVNOProvisioner{logger: logger}
}
// ProvisionResources provisions core resources for the MVNO
func (p *MVNOProvisioner) ProvisionResources(ctx context.Context, mvno *MVNO) error {
p.logger.WithField("mvno_id", mvno.ID).Info("Provisioning core resources")
if err := p.provisionTenantContext(ctx, mvno); err != nil {
return fmt.Errorf("failed to provision tenant context: %w", err)
}
if err := p.provisionDatabaseSchema(ctx, mvno); err != nil {
return fmt.Errorf("failed to provision database schema: %w", err)
}
return p.provisionStorageResources(ctx, mvno)
}
// SetupCarriers configures carrier connections for the MVNO
func (p *MVNOProvisioner) SetupCarriers(ctx context.Context, mvno *MVNO) error {
p.logger.WithField("mvno_id", mvno.ID).Info("Setting up carrier connections")
selectedCarriers, err := p.selectCarriers(mvno.Config.AllowedCountries)
if err != nil {
return fmt.Errorf("failed to select carriers: %w", err)
}
for _, carrierID := range selectedCarriers {
if err := p.configureCarrier(ctx, mvno, carrierID); err != nil {
p.logger.WithError(err).WithField("carrier_id", carrierID).Error("Failed to configure carrier")
}
}
mvno.Config.CarrierPool = selectedCarriers
return nil
}
// SetupBilling configures billing system for the MVNO
func (p *MVNOProvisioner) SetupBilling(ctx context.Context, mvno *MVNO) error {
p.logger.WithField("mvno_id", mvno.ID).Info("Setting up billing system")
billingID := id.GeneratePrefixed("bill")
if err := p.configureRatePlans(ctx, mvno, billingID); err != nil {
return fmt.Errorf("failed to configure rate plans: %w", err)
}
return p.setupPaymentProcessing(ctx, mvno, billingID)
}
// SetupAPIAccess configures API access for the MVNO
func (p *MVNOProvisioner) SetupAPIAccess(ctx context.Context, mvno *MVNO) error {
if !mvno.Config.APIAccess {
p.logger.WithField("mvno_id", mvno.ID).Info("API access not included in plan")
return nil
}
p.logger.WithField("mvno_id", mvno.ID).Info("Setting up API access")
apiKey := id.GeneratePrefixed("api")
_ = id.GeneratePrefixed("sec") // Generate secret key but don't use until storage is implemented
permissions := p.getAPIPermissions(mvno.Plan)
p.logger.WithFields(map[string]interface{}{
"mvno_id": mvno.ID,
"api_key": apiKey[:8] + "...",
"permissions": len(permissions),
}).Info("API access configured")
return nil
}
// provisionTenantContext creates tenant context
func (p *MVNOProvisioner) provisionTenantContext(ctx context.Context, mvno *MVNO) error {
p.logger.WithField("mvno_id", mvno.ID).Info("Tenant context provisioned")
return nil
}
// provisionDatabaseSchema provisions database schema
func (p *MVNOProvisioner) provisionDatabaseSchema(ctx context.Context, mvno *MVNO) error {
p.logger.WithField("mvno_id", mvno.ID).Info("Database schema provisioned")
return nil
}
// provisionStorageResources provisions storage resources
func (p *MVNOProvisioner) provisionStorageResources(ctx context.Context, mvno *MVNO) error {
storageSize := p.getStorageAllocation(mvno.Plan)
p.logger.WithFields(map[string]interface{}{
"mvno_id": mvno.ID,
"storage_gb": storageSize,
}).Info("Storage resources provisioned")
return nil
}
// selectCarriers selects optimal carriers for countries
func (p *MVNOProvisioner) selectCarriers(countries []string) ([]string, error) {
carriers := []string{"carrier_us_01", "carrier_gb_01", "carrier_de_01"}
if len(countries) > 0 {
selected := make([]string, 0)
for _, carrier := range carriers {
selected = append(selected, carrier)
}
return selected, nil
}
return carriers, nil
}
// configureCarrier configures individual carrier
func (p *MVNOProvisioner) configureCarrier(ctx context.Context, mvno *MVNO, carrierID string) error {
p.logger.WithFields(map[string]interface{}{
"mvno_id": mvno.ID,
"carrier_id": carrierID,
}).Info("Carrier configured")
return nil
}
// configureRatePlans configures rate plans
func (p *MVNOProvisioner) configureRatePlans(ctx context.Context, mvno *MVNO, billingID string) error {
p.logger.WithFields(map[string]interface{}{
"mvno_id": mvno.ID,
"billing_id": billingID,
"plan": mvno.Plan,
}).Info("Rate plans configured")
return nil
}
// setupPaymentProcessing setup payment processing
func (p *MVNOProvisioner) setupPaymentProcessing(ctx context.Context, mvno *MVNO, billingID string) error {
p.logger.WithFields(map[string]interface{}{
"mvno_id": mvno.ID,
"billing_id": billingID,
}).Info("Payment processing setup")
return nil
}
// getAPIPermissions returns API permissions based on plan
func (p *MVNOProvisioner) getAPIPermissions(plan MVNOPlan) []string {
switch plan {
case PlanStarter:
return []string{"read:subscribers", "read:usage"}
case PlanGrowth:
return []string{"read:subscribers", "write:subscribers", "read:usage", "read:billing"}
case PlanScale:
return []string{"read:subscribers", "write:subscribers", "read:usage", "write:usage", "read:billing", "write:billing", "read:analytics"}
case PlanEnterprise:
return []string{"*"}
default:
return []string{"read:subscribers"}
}
}
// getStorageAllocation returns storage allocation in GB
func (p *MVNOProvisioner) getStorageAllocation(plan MVNOPlan) int {
switch plan {
case PlanStarter:
return 10
case PlanGrowth:
return 100
case PlanScale:
return 1000
case PlanEnterprise:
return 10000
default:
return 10
}
}