Files
telecom-platform/apps/api-server/internal/services/amf_client.go
T
nutcas3 6e11d40f5e feat: Add AMF client for session termination and subscriber status notifications
- Add AMFClient struct with base URL and HTTP client
- Add NewAMFClient with 30s timeout
- Add TerminateSession for subscriber session termination with reason and timestamp
- Add NotifySubscriberUpdate for subscriber status change notifications
- Add SessionTerminationRequest and SessionTerminationResponse models
- Add generateRequestID helper for request tracking
2026-04-14 16:00:24 +03:00

127 lines
3.4 KiB
Go

package services
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/nutcas3/telecom-platform/apps/api-server/internal/models"
)
// AMFClient represents the Access and Mobility Management Function client
type AMFClient struct {
baseURL string
httpClient *http.Client
}
// NewAMFClient creates a new AMF client
func NewAMFClient(baseURL string) *AMFClient {
return &AMFClient{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
// SessionTerminationRequest represents a session termination request to AMF
type SessionTerminationRequest struct {
IMSI models.IMSI `json:"imsi"`
Reason string `json:"reason"`
Timestamp time.Time `json:"timestamp"`
}
// SessionTerminationResponse represents the response from AMF
type SessionTerminationResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
SessionID string `json:"session_id,omitempty"`
}
// TerminateSession terminates a subscriber session in the AMF
func (a *AMFClient) TerminateSession(ctx context.Context, imsi models.IMSI, reason string) error {
req := SessionTerminationRequest{
IMSI: imsi,
Reason: reason,
Timestamp: time.Now(),
}
jsonData, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("failed to marshal session termination request: %w", err)
}
url := fmt.Sprintf("%s/api/v1/sessions/terminate", a.baseURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create HTTP request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("X-Request-ID", generateRequestID())
resp, err := a.httpClient.Do(httpReq)
if err != nil {
return fmt.Errorf("failed to send session termination request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("AMF returned status %d", resp.StatusCode)
}
var response SessionTerminationResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return fmt.Errorf("failed to decode AMF response: %w", err)
}
if !response.Success {
return fmt.Errorf("AMF session termination failed: %s", response.Message)
}
return nil
}
// NotifySubscriberUpdate notifies AMF of subscriber status changes
func (a *AMFClient) NotifySubscriberUpdate(ctx context.Context, imsi models.IMSI, status models.SubscriberStatus) error {
req := map[string]interface{}{
"imsi": imsi,
"status": status,
"timestamp": time.Now(),
}
jsonData, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("failed to marshal subscriber update request: %w", err)
}
url := fmt.Sprintf("%s/api/v1/subscribers/update", a.baseURL)
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create HTTP request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("X-Request-ID", generateRequestID())
resp, err := a.httpClient.Do(httpReq)
if err != nil {
return fmt.Errorf("failed to send subscriber update request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("AMF returned status %d", resp.StatusCode)
}
return nil
}
// generateRequestID generates a unique request ID
func generateRequestID() string {
return fmt.Sprintf("%d", time.Now().UnixNano())
}