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:
Lorentz Hinrichsen 2025-10-28 22:49:08 -04:00
parent cf5fabe306
commit f429f3af54
23 changed files with 1931 additions and 36 deletions

345
ADDIGY_API_GUIDE.md Normal file
View 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