Files
sub2api-cn-relay-manager/internal/access/closure.go

97 lines
3.2 KiB
Go

package access
import (
"context"
"fmt"
"strings"
"sub2api-cn-relay-manager/internal/host/sub2api"
)
const (
ModeSubscription = "subscription"
ModeSelfService = "self_service"
)
type SubscriptionTarget struct {
UserID string
DurationDays int
}
type ClosureRequest struct {
Mode string
ProbeAPIKey string
Subscriptions []SubscriptionTarget
GroupID string
ExpectedModel string
}
type Host interface {
EnsureSubscriptionAccess(ctx context.Context, req sub2api.EnsureSubscriptionAccessRequest) (sub2api.SubscriptionAccessRef, error)
AssignSubscription(ctx context.Context, req sub2api.AssignSubscriptionRequest) (sub2api.SubscriptionRef, error)
CheckGatewayAccess(ctx context.Context, req sub2api.GatewayAccessCheckRequest) (sub2api.GatewayAccessResult, error)
}
type Service struct {
host Host
}
func NewService(host Host) *Service {
return &Service{host: host}
}
func Validate(req ClosureRequest) error {
switch strings.TrimSpace(req.Mode) {
case ModeSubscription:
if len(req.Subscriptions) == 0 {
return fmt.Errorf("subscription access requires at least one subscription target")
}
case ModeSelfService:
if strings.TrimSpace(req.ProbeAPIKey) == "" {
return fmt.Errorf("self_service access requires probe api key")
}
default:
return fmt.Errorf("unsupported access mode %q", req.Mode)
}
if strings.TrimSpace(req.Mode) != ModeSubscription && strings.TrimSpace(req.ProbeAPIKey) == "" {
return fmt.Errorf("access probe api key is required to verify gateway closure")
}
return nil
}
func (s *Service) Close(ctx context.Context, req ClosureRequest) (sub2api.GatewayAccessResult, error) {
if s == nil || s.host == nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("access host is required")
}
if err := Validate(req); err != nil {
return sub2api.GatewayAccessResult{}, err
}
probeAPIKey := strings.TrimSpace(req.ProbeAPIKey)
if strings.TrimSpace(req.Mode) == ModeSubscription {
for _, target := range req.Subscriptions {
resolvedTarget := target.UserID
accessRef, err := s.host.EnsureSubscriptionAccess(ctx, sub2api.EnsureSubscriptionAccessRequest{UserSelector: target.UserID, GroupID: req.GroupID})
if err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("ensure subscription access for %s: %w", target.UserID, err)
}
if strings.TrimSpace(accessRef.UserID) != "" {
resolvedTarget = accessRef.UserID
}
if strings.TrimSpace(accessRef.APIKey) != "" {
probeAPIKey = strings.TrimSpace(accessRef.APIKey)
}
if _, err := s.host.AssignSubscription(ctx, sub2api.AssignSubscriptionRequest{UserID: resolvedTarget, GroupID: req.GroupID, DurationDays: target.DurationDays}); err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("assign subscription for %s: %w", target.UserID, err)
}
}
}
if probeAPIKey == "" {
return sub2api.GatewayAccessResult{}, fmt.Errorf("access probe api key is required to verify gateway closure")
}
result, err := s.host.CheckGatewayAccess(ctx, sub2api.GatewayAccessCheckRequest{APIKey: probeAPIKey, ExpectedModel: req.ExpectedModel})
if err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("check gateway access: %w", err)
}
return result, nil
}