# Real-time Monitoring

STA Connector provides real-time monitoring capabilities through the Admin API and React Admin Portal.

> **Note:** The Go implementation uses REST API polling rather than WebSockets. For live updates, the Admin Portal uses periodic polling of the Admin API endpoints.

## Admin Portal Dashboard

The React Admin Portal provides real-time monitoring through automatic polling of the Admin API.

### Features

- **Health Status** - Live system health indicator
- **Metrics Dashboard** - Real-time file processing metrics
- **File Monitoring** - Live file status updates
- **Log Viewer** - Recent log entries

### Configuration

The Admin Portal polling interval can be configured:

```typescript
// Default: 5 seconds for metrics, 10 seconds for files
const METRICS_POLL_INTERVAL = 5000;
const FILES_POLL_INTERVAL = 10000;
```

## REST API Polling

For custom integrations, poll the Admin API endpoints:

### Metrics Polling

```javascript
async function pollMetrics(interval = 5000) {
  const auth = btoa('admin:password');

  while (true) {
    try {
      const response = await fetch('http://localhost:8081/admin/metrics', {
        headers: { 'Authorization': `Basic ${auth}` }
      });

      const metrics = await response.json();
      updateDashboard(metrics);
    } catch (error) {
      console.error('Metrics poll failed:', error);
    }

    await new Promise(resolve => setTimeout(resolve, interval));
  }
}
```

### File Status Polling

```javascript
async function pollFileStatus(fileId, onUpdate) {
  const auth = btoa('admin:password');

  while (true) {
    const response = await fetch(
      `http://localhost:8081/admin/files?id=${fileId}`,
      { headers: { 'Authorization': `Basic ${auth}` } }
    );

    const data = await response.json();
    const file = data.files[0];

    onUpdate(file);

    // Stop polling when file reaches terminal status
    if (['completed', 'failed'].includes(file.status)) {
      break;
    }

    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}
```

### Health Check Polling

```javascript
async function monitorHealth(interval = 30000) {
  const auth = btoa('admin:password');

  while (true) {
    try {
      const response = await fetch('http://localhost:8081/admin/health', {
        headers: { 'Authorization': `Basic ${auth}` }
      });

      const health = await response.json();

      if (health.status !== 'healthy') {
        sendAlert(`System unhealthy: ${JSON.stringify(health.checks)}`);
      }
    } catch (error) {
      sendAlert(`Health check failed: ${error.message}`);
    }

    await new Promise(resolve => setTimeout(resolve, interval));
  }
}
```

## Event-Driven Integration

For event-driven architectures, consider these patterns:

### Webhook Callbacks

Configure webhook callbacks when submitting files:

```bash
curl -X POST http://localhost:8080/api/v1/files \
  -H "Authorization: Bearer ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "system": "CCS",
    "file_type": "ARQC001",
    "data": {
      "content": "base64-content"
    },
    "destination": {
      "ispb": "00000000"
    },
    "callback": {
      "url": "https://your-app.com/webhooks/sta",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }'
```

### External Message Queue

Integrate with message queues for asynchronous processing:

```go
// Example: Publishing file events to RabbitMQ
func publishFileEvent(event FileEvent) error {
    body, _ := json.Marshal(event)
    return channel.Publish(
        "sta-events",     // exchange
        event.Status,     // routing key
        false,            // mandatory
        false,            // immediate
        amqp.Publishing{
            ContentType: "application/json",
            Body:        body,
        },
    )
}
```

## Python Monitoring Client

```python
import requests
import time
from requests.auth import HTTPBasicAuth
from typing import Callable, Optional

class STAMonitor:
    def __init__(self, admin_url: str, username: str, password: str):
        self.admin_url = admin_url.rstrip('/')
        self.auth = HTTPBasicAuth(username, password)

    def get_metrics(self) -> dict:
        """Get current metrics."""
        response = requests.get(
            f'{self.admin_url}/admin/metrics',
            auth=self.auth
        )
        response.raise_for_status()
        return response.json()

    def get_health(self) -> dict:
        """Get health status."""
        response = requests.get(
            f'{self.admin_url}/admin/health',
            auth=self.auth
        )
        response.raise_for_status()
        return response.json()

    def list_files(
        self,
        status: Optional[str] = None,
        system: Optional[str] = None,
        limit: int = 100
    ) -> dict:
        """List files with optional filters."""
        params = {'limit': limit}
        if status:
            params['status'] = status
        if system:
            params['system'] = system

        response = requests.get(
            f'{self.admin_url}/admin/files',
            auth=self.auth,
            params=params
        )
        response.raise_for_status()
        return response.json()

    def watch_metrics(
        self,
        callback: Callable[[dict], None],
        interval: int = 5
    ):
        """Continuously poll metrics."""
        while True:
            try:
                metrics = self.get_metrics()
                callback(metrics)
            except Exception as e:
                print(f'Metrics poll error: {e}')

            time.sleep(interval)

    def watch_file(
        self,
        file_id: str,
        callback: Callable[[dict], None],
        interval: int = 2
    ) -> dict:
        """Watch a file until it reaches terminal status."""
        while True:
            files = self.list_files()
            file = next(
                (f for f in files['files'] if f['id'] == file_id),
                None
            )

            if file:
                callback(file)
                if file['status'] in ('completed', 'failed'):
                    return file

            time.sleep(interval)


# Usage example
if __name__ == '__main__':
    monitor = STAMonitor(
        admin_url='http://localhost:8081',
        username='admin',
        password='password'
    )

    def on_metrics(metrics):
        print(f"Processed: {metrics['inbound']['files_processed']}")
        print(f"Pending: {metrics['inbound']['files_pending']}")

    def on_file_update(file):
        print(f"File {file['id']}: {file['status']}")

    # Watch metrics
    # monitor.watch_metrics(on_metrics, interval=5)

    # Watch specific file
    # final = monitor.watch_file('file-id', on_file_update)
```

## Node.js Monitoring Client

```javascript
const axios = require('axios');

class STAMonitor {
  constructor(adminUrl, username, password) {
    this.client = axios.create({
      baseURL: adminUrl,
      auth: { username, password }
    });
  }

  async getMetrics() {
    const response = await this.client.get('/admin/metrics');
    return response.data;
  }

  async getHealth() {
    const response = await this.client.get('/admin/health');
    return response.data;
  }

  async listFiles({ status, system, limit = 100 } = {}) {
    const response = await this.client.get('/admin/files', {
      params: { status, system, limit }
    });
    return response.data;
  }

  async watchMetrics(callback, interval = 5000) {
    const poll = async () => {
      try {
        const metrics = await this.getMetrics();
        callback(null, metrics);
      } catch (error) {
        callback(error, null);
      }
    };

    await poll();
    return setInterval(poll, interval);
  }

  async watchFile(fileId, callback, interval = 2000) {
    return new Promise((resolve, reject) => {
      const poll = async () => {
        try {
          const data = await this.listFiles();
          const file = data.files.find(f => f.id === fileId);

          if (file) {
            callback(null, file);

            if (['completed', 'failed'].includes(file.status)) {
              clearInterval(timer);
              resolve(file);
            }
          }
        } catch (error) {
          callback(error, null);
        }
      };

      poll();
      const timer = setInterval(poll, interval);
    });
  }
}

// Usage example
async function main() {
  const monitor = new STAMonitor(
    'http://localhost:8081',
    'admin',
    'password'
  );

  // Get current metrics
  const metrics = await monitor.getMetrics();
  console.log('Processed:', metrics.inbound.files_processed);

  // Watch metrics
  const metricsTimer = await monitor.watchMetrics((err, metrics) => {
    if (err) {
      console.error('Metrics error:', err);
      return;
    }
    console.log('Pending files:', metrics.inbound.files_pending);
  });

  // Stop watching after 1 minute
  setTimeout(() => clearInterval(metricsTimer), 60000);
}

main().catch(console.error);
```

## Alerting

Set up alerts based on metrics thresholds:

```python
def check_alerts(metrics: dict) -> list:
    alerts = []

    # High pending file count
    if metrics['inbound']['files_pending'] > 100:
        alerts.append({
            'severity': 'warning',
            'metric': 'inbound.files_pending',
            'value': metrics['inbound']['files_pending'],
            'threshold': 100,
            'message': 'High pending file count'
        })

    # High failure rate
    total = metrics['inbound']['files_processed']
    failed = metrics['inbound']['files_failed']
    if total > 0 and (failed / total) > 0.05:
        alerts.append({
            'severity': 'critical',
            'metric': 'inbound.failure_rate',
            'value': failed / total,
            'threshold': 0.05,
            'message': 'High failure rate (>5%)'
        })

    # STA rate limit low
    if metrics['sta_client']['rate_limit_remaining'] < 10:
        alerts.append({
            'severity': 'warning',
            'metric': 'sta_client.rate_limit_remaining',
            'value': metrics['sta_client']['rate_limit_remaining'],
            'threshold': 10,
            'message': 'STA rate limit nearly exhausted'
        })

    return alerts
```

## Next Steps

- [Admin API](/api/admin) - Full API reference
- [Real-time Guide](/guide/realtime) - Integration patterns
- [Troubleshooting](/troubleshooting) - Common issues
