Add Addigy API integration and Docker deployment with Redis caching
- Implemented complete Addigy API v2 client with authentication via x-api-key - Added device and policy endpoints with automatic org ID resolution - Created field mapping from snake_case to Title Case for UI compatibility - Handles nested 'facts' response structure from Addigy devices API - Added comprehensive API documentation in ADDIGY_API_GUIDE.md - Multi-stage Dockerfile with optimized production build - Custom ports: App on 3100, Redis on 6380 (avoids conflicts) - Docker Compose orchestration with health checks - Standalone Next.js output for smaller container images - Non-root user execution for security - Implemented Redis caching layer for API responses - 5-minute TTL with graceful fallback if Redis unavailable - Cache key structure: service:entity:filter1:filter2 - Applied to Addigy devices endpoint with cache hit/miss logging - Fixed TypeScript strict mode errors for production builds - Added null safety checks with optional chaining throughout API routes - Wrapped useSearchParams in Suspense boundary for Next.js 15+ compatibility - Fixed type assertions for dynamic API responses - Corrected Set<string> type mismatches in device comparison logic - Created DOCKER_README.md with complete deployment guide - Updated ADDIGY_API_GUIDE.md with real-world API patterns - Documented response structures, field mappings, and troubleshooting - Next.js 16.0.0 with Turbopack - Redis 7 with AOF persistence - Podman/Docker compatible - TypeScript strict mode compliant
This commit is contained in:
parent
cf5fabe306
commit
f429f3af54
23 changed files with 1931 additions and 36 deletions
345
ADDIGY_API_GUIDE.md
Normal file
345
ADDIGY_API_GUIDE.md
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
# Addigy API Integration Guide
|
||||
|
||||
This guide explains how to use the Addigy API integration in the PSA-Utils application.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Get Your Addigy API Token
|
||||
|
||||
1. Log into your Addigy account
|
||||
2. Navigate to **Account > Integrations**
|
||||
3. Click on the **V2** tab under "Addigy API"
|
||||
4. Click **New API Token**
|
||||
5. Enter a name for your token (e.g., "PSA-Utils Integration")
|
||||
6. Select the appropriate permissions:
|
||||
- **View Devices** - Required to fetch device information
|
||||
- **View Policies** - Required to fetch policy information
|
||||
- **View Organizations** - Optional, for organization data
|
||||
- Additional permissions as needed for your use case
|
||||
7. Click **Save**
|
||||
8. **Copy the API token immediately** - you won't be able to see it again!
|
||||
|
||||
**Note**: The Addigy v2 API uses the `x-api-key` header for authentication, not Bearer tokens.
|
||||
|
||||
### 2. Configure Environment Variables
|
||||
|
||||
Add your Addigy API token to `.env.local`:
|
||||
|
||||
```bash
|
||||
# Addigy API Configuration
|
||||
ADDIGY_API_URL=https://api.addigy.com/api/v2
|
||||
ADDIGY_API_TOKEN=your_actual_token_here
|
||||
|
||||
# Optional: Parent Organization ID (if needed)
|
||||
# ADDIGY_ORG_ID=your_org_id_here
|
||||
```
|
||||
|
||||
### 3. Restart Your Development Server
|
||||
|
||||
After updating the `.env.local` file, restart your Next.js development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Import the Addigy Client
|
||||
|
||||
```typescript
|
||||
import { getAddigyClient } from '@/lib/services/addigy-factory';
|
||||
|
||||
const addigyClient = getAddigyClient();
|
||||
```
|
||||
|
||||
### Common Operations
|
||||
|
||||
#### Get All Devices
|
||||
|
||||
```typescript
|
||||
const devices = await addigyClient.getAllDevices();
|
||||
console.log(`Found ${devices.length} devices`);
|
||||
```
|
||||
|
||||
#### Get Online Devices Only
|
||||
|
||||
```typescript
|
||||
const onlineDevices = await addigyClient.getOnlineDevices();
|
||||
```
|
||||
|
||||
#### Get Devices by Policy
|
||||
|
||||
```typescript
|
||||
const policyId = '003f01fa-9bb3-421a-be29-409adf7a1xxx';
|
||||
const devices = await addigyClient.getDevicesByPolicy(policyId);
|
||||
```
|
||||
|
||||
#### Get Device Applications
|
||||
|
||||
```typescript
|
||||
const deviceId = '114x11c3-b5dy-4n38-92fb-34th6ju782se';
|
||||
const applications = await addigyClient.getDeviceApplications(deviceId);
|
||||
```
|
||||
|
||||
#### Get All Policies
|
||||
|
||||
```typescript
|
||||
const policies = await addigyClient.getAllPolicies();
|
||||
```
|
||||
|
||||
#### Get Policy by ID
|
||||
|
||||
```typescript
|
||||
const policyId = '76yh84t0-ju74-bh45-jd6b-ok87y4gc83gt';
|
||||
const policy = await addigyClient.getPolicyById(policyId);
|
||||
```
|
||||
|
||||
#### Get Active Alerts
|
||||
|
||||
```typescript
|
||||
const alerts = await addigyClient.getActiveAlerts();
|
||||
```
|
||||
|
||||
#### Get Maintenance Items
|
||||
|
||||
```typescript
|
||||
const maintenanceItems = await addigyClient.getMaintenanceItems();
|
||||
```
|
||||
|
||||
#### Get Custom Facts for a Device
|
||||
|
||||
```typescript
|
||||
const deviceId = '114x11c3-b5dy-4n38-92fb-34th6ju782se';
|
||||
const facts = await addigyClient.getCustomFacts(deviceId);
|
||||
```
|
||||
|
||||
#### Assign Device to Policy
|
||||
|
||||
```typescript
|
||||
const deviceId = '114x11c3-b5dy-4n38-92fb-34th6ju782se';
|
||||
const newPolicyId = '76yh84t0-ju74-bh45-jd6b-ok87y4gc83gt';
|
||||
await addigyClient.assignDeviceToPolicy(deviceId, newPolicyId);
|
||||
```
|
||||
|
||||
### Advanced Queries with Filters
|
||||
|
||||
```typescript
|
||||
// Get devices with custom filters
|
||||
const devices = await addigyClient.getAllDevices({
|
||||
filters: [
|
||||
{
|
||||
audit_field: 'Free Disk Percentage',
|
||||
type: 'number',
|
||||
operation: 'less_than',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
audit_field: 'Firewall Enabled',
|
||||
type: 'boolean',
|
||||
operation: 'equals',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
limit: 100,
|
||||
page: 1,
|
||||
});
|
||||
```
|
||||
|
||||
### Pagination for Large Datasets
|
||||
|
||||
```typescript
|
||||
// Get all devices with automatic pagination
|
||||
const allDevices = await addigyClient.getAllDevicesPaginated(100);
|
||||
console.log(`Total devices: ${allDevices.length}`);
|
||||
```
|
||||
|
||||
### Test Connection
|
||||
|
||||
```typescript
|
||||
const isConnected = await addigyClient.testConnection();
|
||||
if (isConnected) {
|
||||
console.log('Successfully connected to Addigy API');
|
||||
} else {
|
||||
console.error('Failed to connect to Addigy API');
|
||||
}
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
The integration includes the following pre-built API endpoints:
|
||||
|
||||
### GET `/api/addigy-devices`
|
||||
|
||||
Fetch devices from Addigy. Automatically retrieves the organization ID from policies if not configured.
|
||||
|
||||
**Query Parameters:**
|
||||
- `policyId` (optional) - Filter devices by policy ID
|
||||
- `online` (optional) - Set to 'true' to get only online devices
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
# Get all devices
|
||||
curl http://localhost:3000/api/addigy-devices
|
||||
|
||||
# Get devices for a specific policy
|
||||
curl http://localhost:3000/api/addigy-devices?policyId=76yh84t0-ju74-bh45-jd6b-ok87y4gc83gt
|
||||
|
||||
# Get only online devices
|
||||
curl http://localhost:3000/api/addigy-devices?online=true
|
||||
```
|
||||
|
||||
### GET `/api/addigy-policies`
|
||||
|
||||
Fetch all policies from Addigy.
|
||||
|
||||
## Important API Details
|
||||
|
||||
### Authentication
|
||||
- Uses `x-api-key` header (not `Authorization: Bearer`)
|
||||
- Single API token for authentication
|
||||
|
||||
### Endpoint Structure
|
||||
- **Devices**: `POST /o/{orgid}/devices` - Requires organization ID
|
||||
- **Policies**: `POST /oa/policies/query` - Returns flat array
|
||||
- All endpoints use POST with JSON body, not GET
|
||||
|
||||
### Request Body Format
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"per_page": 500,
|
||||
"query": {
|
||||
"filters": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response Structure
|
||||
|
||||
#### Devices Response
|
||||
Devices are returned with nested `facts` structure:
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"facts": {
|
||||
"device_name": { "value": "MacBook-Pro", "type": "string" },
|
||||
"serial_number": { "value": "C02XX...", "type": "string" },
|
||||
"online": { "value": true, "type": "boolean" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Policies Response
|
||||
Policies return as a flat array:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"policyId": "69ae51e4-...",
|
||||
"orgid": "4200f347-...",
|
||||
"name": "Policy Name"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl http://localhost:3000/api/addigy-policies
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
The Addigy API has a rate limit of **1,000 requests per 10 seconds**. The client includes built-in rate limiting to prevent exceeding this limit. If you exceed the rate limit, further requests will be rejected for 24 hours.
|
||||
|
||||
## Type Definitions
|
||||
|
||||
All Addigy types are defined in `/lib/types/addigy.ts`. Key types include:
|
||||
|
||||
- `AddigyDevice` - Device information
|
||||
- `AddigyPolicy` - Policy information
|
||||
- `AddigyApplication` - Installed application details
|
||||
- `AddigyAlert` - Alert information
|
||||
- `AddigyOrganization` - Organization details
|
||||
- `AddigyMaintenanceItem` - Maintenance task information
|
||||
- `AddigyMonitoringItem` - Monitoring rule information
|
||||
- `AddigyCustomFact` - Custom facts/variables
|
||||
- `AddigySoftwareItem` - Software catalog items
|
||||
|
||||
## Error Handling
|
||||
|
||||
Always wrap API calls in try-catch blocks:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const devices = await addigyClient.getAllDevices();
|
||||
// Process devices
|
||||
} catch (error) {
|
||||
console.error('Error fetching devices:', error);
|
||||
// Handle error appropriately
|
||||
}
|
||||
```
|
||||
|
||||
## Common Device Fields
|
||||
|
||||
Addigy returns device fields in snake_case format. The integration automatically maps these to Title Case for UI compatibility:
|
||||
|
||||
| Addigy Field | Mapped To | Description |
|
||||
|--------------|-----------|-------------|
|
||||
| `agentid` | `agentid` | Unique device identifier |
|
||||
| `device_name` | `Device Name` | Device hostname |
|
||||
| `device_model_name` | `Device Model Name` | Device model (e.g., "MacBook Pro") |
|
||||
| `mac_os_x_version` | `MAC OS X Version` | macOS version |
|
||||
| `ios_version` | `iOS Version` | iOS version |
|
||||
| `online` | `online` | Boolean indicating if device is online |
|
||||
| `policy_id` | `policy_id` | Current policy ID |
|
||||
| `current_user` | `Current User` | Currently logged in user |
|
||||
| `serial_number` | `Serial Number` | Device serial number |
|
||||
| `free_disk_percentage` | `Free Disk Percentage` | Available disk space percentage |
|
||||
| `battery_percentage` | `Battery Percentage` | Current battery level |
|
||||
| `firewall_enabled` | `Firewall Enabled` | Boolean |
|
||||
| `filevault_enabled` | `FileVault Enabled` | Boolean |
|
||||
| `agent_version` | `Agent Version` | Addigy agent version |
|
||||
|
||||
## Resources
|
||||
|
||||
- [Addigy API v2 Documentation](https://support.addigy.com/hc/en-us/articles/16938210315411-API-Documentation-v2)
|
||||
- [Addigy API v2 Interactive Docs](https://api.addigy.com/api/v2/documentation/)
|
||||
- [Addigy Support Center](https://support.addigy.com/)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "ADDIGY_API_TOKEN environment variable is required"
|
||||
|
||||
Make sure you've added your API token to the `.env.local` file and restarted your development server.
|
||||
|
||||
### "API Error: Unauthorized" or 401 Error
|
||||
|
||||
Check that your API token is valid and has the necessary permissions. You may need to create a new token with the correct permissions in Addigy.
|
||||
|
||||
### "404 page not found" Error
|
||||
|
||||
This usually means:
|
||||
1. Wrong endpoint path - check you're using `/o/{orgid}/devices` for devices
|
||||
2. Missing organization ID - the integration will auto-fetch from policies if not configured
|
||||
3. Using GET instead of POST - all Addigy v2 endpoints require POST
|
||||
|
||||
### Empty Device Names
|
||||
|
||||
Addigy uses snake_case field names (e.g., `device_name`). The integration automatically maps these to the UI-expected format (e.g., `Device Name`).
|
||||
|
||||
### Rate Limit Exceeded
|
||||
|
||||
If you receive a rate limit error, wait 24 hours before making more API requests. Consider implementing caching or reducing the frequency of API calls.
|
||||
|
||||
### "Invalid JSON response from Addigy API"
|
||||
|
||||
This may indicate an issue with the API endpoint or your token permissions. Check the Addigy API documentation for the specific endpoint you're trying to access.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore the Addigy API documentation for additional endpoints
|
||||
- Create custom API routes for your specific use cases
|
||||
- Integrate Addigy data with your Autotask or other systems
|
||||
- Build dashboards to visualize device status, alerts, and compliance
|
||||
Loading…
Add table
Add a link
Reference in a new issue