package server import ( "context" "errors" "fmt" "time" "sultan/pkg/license" "sultan/pkg/policy" "sultan/pkg/registry" "sultan/pkg/store" ) // directory liefert der Registry Mandant, Lizenz und Drain-Status eines // Knotens aus dem Store. // // Die Lizenz wird aus dem Mandanten-Datensatz abgeleitet (inkl. Features); // die Mahnstufe ergibt sich aus der offenen Zahlung (DunningSchedule). type directory struct { store *store.Store } func (d *directory) NodeContext(ctx context.Context, nodeID string) (registry.NodeContext, error) { node, err := d.store.GetNode(ctx, nodeID) if errors.Is(err, store.ErrNotFound) { return registry.NodeContext{}, fmt.Errorf("%w: %s", registry.ErrUnknownNode, nodeID) } if err != nil { return registry.NodeContext{}, err } t, err := d.store.GetTenant(ctx, node.TenantID) if errors.Is(err, store.ErrNotFound) { return registry.NodeContext{}, fmt.Errorf("%w: Mandant von %s fehlt", registry.ErrUnknownNode, nodeID) } if err != nil { return registry.NodeContext{}, err } return registry.NodeContext{ Tenant: policy.Tenant{ ID: t.ID, Status: t.Status, DunningLevel: policy.DunningSchedule{}.Level(t.PaymentOverdueSince, time.Now()), }, License: licenseOf(t), Drained: node.Status == store.NodeDrained, Maintenance: maintenanceOf(node), Disconnected: node.Status == store.NodeOffline, }, nil } // licenseOf leitet die (unsignierte) Lizenz aus dem Mandanten-Datensatz ab. func licenseOf(t store.Tenant) license.License { features := make([]license.Feature, 0, len(t.Features)) for _, f := range t.Features { features = append(features, license.Feature(f)) } return license.License{ TenantID: t.ID, Tier: license.Tier(t.Tier), MaxRoutes: t.MaxRoutes, AllowedDomains: t.AllowedDomains, Features: features, ValidUntil: t.ValidUntil, } } func maintenanceOf(n store.Node) *policy.Maintenance { if n.Maintenance == nil { return nil } return &policy.Maintenance{Until: n.Maintenance.Until, PublicReason: n.Maintenance.PublicReason} }