navidocs/INTEGRATION_QUICK_REFERENCE.md
Danny Stocker 58b344aa31 FINAL: P0 blockers fixed + Joe Trader + ignore binaries
Fixed:
- Price: €800K-€1.5M, Sunseeker added
- Agent 1: Joe Trader persona + actual sale ads research
- Ignored meilisearch binary + data/ (too large for GitHub)
- SESSION_DEBUG_BLOCKERS.md created

Ready for Session 1 launch.

🤖 Generated with Claude Code
2025-11-13 01:29:59 +01:00

418 lines
10 KiB
Markdown

# NaviDocs Integration Quick Reference
**Quick lookup for developers implementing new integrations**
---
## Database Key Tables for Integrations
### 1. Metadata Storage (Extensible JSON Fields)
**entities table** - Boat/asset data
```javascript
// Example metadata
{
"hull_cert_date": "2020-01-15",
"survey_status": "valid",
"survey_provider": "HagsEye",
"mls_id": "yacht-123456",
"listing_price": 450000,
"condition_notes": "Excellent, fully maintained",
"ha_entity_id": "yacht.name_docs"
}
```
**documents table** - File metadata
```javascript
{
"sale_list_price": 450000,
"condition_notes": "excellent",
"buyer_name": "John Doe",
"notify_on_expiry": true,
"expiry_date": "2025-01-15",
"signature_required": true
}
```
**organizations table** - Broker/agency config
```javascript
{
"broker_id": "broker-123",
"region": "SE",
"crm_provider": "zillow",
"webhook_urls": {
"homeassistant": "http://ha.local:8123/api/webhook/...",
"mqtt": "mqtt://broker.local:1883"
}
}
```
### 2. Settings Table for Integration Config
```sql
SELECT * FROM system_settings WHERE category = 'integrations';
```
**Example rows:**
- `key='ha.webhook_url', value='http://...', category='integrations'`
- `key='mqtt.broker', value='mqtt://...', category='integrations'`
- `key='crm.api_key', value='...', category='integrations', encrypted=1`
---
## API Route Templates
### Webhook Receiver Pattern
```javascript
// /server/routes/webhooks.js
import express from 'express';
const router = express.Router();
router.post('/homeassistant', async (req, res) => {
const { event, timestamp, data } = req.body;
// Validate webhook token
if (req.headers['x-webhook-token'] !== process.env.HA_WEBHOOK_TOKEN) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Log event
db.prepare(`
INSERT INTO integration_events (id, event_type, payload, created_at)
VALUES (?, ?, ?, ?)
`).run(uuidv4(), 'ha.' + event, JSON.stringify(data), now);
res.json({ success: true });
});
export default router;
```
### Event Publisher Pattern
```javascript
// /server/services/event-bus.js
export async function publishEvent(event, data) {
// 1. Log to database
db.prepare(`
INSERT INTO events (id, type, payload, created_at)
VALUES (?, ?, ?, ?)
`).run(uuidv4(), event, JSON.stringify(data), now);
// 2. Publish to registered webhooks
const integrations = db.prepare(`
SELECT * FROM integrations
WHERE active = 1 AND type = 'webhook'
`).all();
for (const webhook of integrations) {
// Queue webhook delivery
await queueWebhookDelivery(webhook.id, event, data);
}
// 3. Publish to MQTT if configured
if (config.mqtt.enabled) {
await mqttClient.publish(`navidocs/events/${event}`, JSON.stringify(data));
}
}
```
---
## Service Layer Extension Points
### 1. Add to Search Service (search.js)
```javascript
// Add new filterable attribute
export function addSearchAttribute(attributeName, type = 'string') {
// Update Meilisearch index config
// type: 'string', 'number', 'date', 'array'
}
// Index document with new fields
export async function indexDocument(doc) {
const searchDoc = {
id: doc.id,
title: doc.title,
text: doc.ocr_text,
documentType: doc.document_type,
// Add custom fields from metadata
...JSON.parse(doc.metadata || '{}')
};
await meiliIndex.addDocuments([searchDoc]);
}
```
### 2. Add to Queue Service (queue.js)
```javascript
// Create new job type
export async function addWebhookJob(integrationId, event, payload) {
return await queue.add('webhook-delivery', {
integrationId,
event,
payload,
timestamp: Date.now()
});
}
// Create worker processor
export function registerJobProcessor(jobType, handler) {
queue.process(jobType, handler);
}
```
### 3. Add to Authorization (authorization.service.js)
```javascript
// Check if organization has integration enabled
export function canUseIntegration(userId, orgId, integrationType) {
const org = db.prepare(`
SELECT * FROM organizations WHERE id = ?
`).get(orgId);
const config = JSON.parse(org.metadata || '{}');
return config.integrations?.[integrationType]?.enabled === true;
}
```
---
## Common Integration Patterns
### Pattern 1: Document Upload Trigger
```javascript
// When document upload completes:
// 1. Save to database
// 2. Trigger OCR job
// 3. Publish event
// 4. Notify integrations
import { publishEvent } from '../services/event-bus.js';
await publishEvent('document.uploaded', {
documentId: doc.id,
title: doc.title,
entityId: doc.entity_id,
organizationId: doc.organization_id,
uploadedBy: doc.uploaded_by,
timestamp: Date.now()
});
```
### Pattern 2: OCR Completion Notification
```javascript
// In ocr-worker.js, after indexing:
await publishEvent('document.indexed', {
documentId: doc.id,
pageCount: doc.page_count,
ocrLanguage: doc.language,
processingTimeMs: Date.now() - startTime,
confidence: avgConfidence
});
```
### Pattern 3: Permission Change Audit
```javascript
// In permission routes:
await publishEvent('permission.granted', {
resourceType: permission.resource_type,
resourceId: permission.resource_id,
userId: permission.user_id,
permission: permission.permission,
grantedBy: req.user.userId,
timestamp: Date.now()
});
```
---
## File References
### Database
- **Schema:** `/home/setup/navidocs/server/db/schema.sql`
- **Migrations:** `/home/setup/navidocs/server/db/migrations/*.sql`
### API Routes
- **Auth:** `/home/setup/navidocs/server/routes/auth.routes.js`
- **Documents:** `/home/setup/navidocs/server/routes/documents.js`
- **Upload:** `/home/setup/navidocs/server/routes/upload.js`
- **Search:** `/home/setup/navidocs/server/routes/search.js`
- **Settings:** `/home/setup/navidocs/server/routes/settings.routes.js`
### Services
- **Auth:** `/home/setup/navidocs/server/services/auth.service.js`
- **Authorization:** `/home/setup/navidocs/server/services/authorization.service.js`
- **Queue:** `/home/setup/navidocs/server/services/queue.js`
- **Search:** `/home/setup/navidocs/server/services/search.js`
- **Settings:** `/home/setup/navidocs/server/services/settings.service.js`
- **Audit:** `/home/setup/navidocs/server/services/audit.service.js`
### Workers
- **OCR:** `/home/setup/navidocs/server/workers/ocr-worker.js`
- **Image Extraction:** `/home/setup/navidocs/server/workers/image-extractor.js`
---
## Environment Variables for Integrations
```bash
# Home Assistant Webhook
HA_WEBHOOK_URL=http://homeassistant.local:8123/api/webhook/navidocs
HA_WEBHOOK_TOKEN=your-secure-token
# MQTT Broker
MQTT_BROKER=mqtt://broker.local:1883
MQTT_USERNAME=navidocs
MQTT_PASSWORD=secure-password
# Cloud Storage (S3)
AWS_S3_BUCKET=navidocs-documents
AWS_S3_REGION=us-east-1
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
# External APIs
CRM_API_KEY=...
SURVEY_PROVIDER_API_KEY=...
# Webhook Delivery
WEBHOOK_RETRY_MAX_ATTEMPTS=3
WEBHOOK_TIMEOUT_MS=30000
```
---
## Testing Webhook Integration
### 1. Test Webhook Receiver
```bash
curl -X POST http://localhost:3001/api/webhooks/homeassistant \
-H "Content-Type: application/json" \
-H "X-Webhook-Token: your-token" \
-d '{
"event": "document.indexed",
"timestamp": 1699868400,
"data": { "documentId": "...", "pageCount": 42 }
}'
```
### 2. Monitor Integration Events
```sql
SELECT * FROM integration_events
ORDER BY created_at DESC
LIMIT 20;
```
### 3. Check Integration Status
```bash
curl http://localhost:3001/api/admin/settings/category/integrations
```
---
## Yacht Sales Specific Integrations
### Broker CRM Sync
**Data Flow:** MLS → NaviDocs
```javascript
// entities.metadata fields to sync
{
"mls_id": "Y-12345",
"listing_agent": "Jane Smith",
"listing_office": "Broker LLC",
"list_date": "2024-11-01",
"sale_price": 450000,
"status": "pending" // listed, pending, sold, withdrawn
}
// Trigger update on document upload
publishEvent('boat.documentation_added', {
entityId, mls_id, documentType
});
```
### Expiration Warnings
```javascript
// documents.metadata
{
"expires_at": 1735689600, // Unix timestamp
"reminder_days": [7, 1], // Send reminder 7 days before, 1 day before
"type": "survey", // survey, haul_out, inspection
"renewal_required": true
}
// Worker job to check expiration
async function checkDocumentExpiry() {
const expiring = db.prepare(`
SELECT * FROM documents
WHERE JSON_EXTRACT(metadata, '$.expires_at') < ?
`).all(Date.now() + 7*24*3600*1000);
for (const doc of expiring) {
publishEvent('document.expiring_soon', { documentId: doc.id });
}
}
```
### As-Built Package Generator
```javascript
// On sale completion, collect all documents
export async function generateAsBuiltPackage(boatId) {
const docs = db.prepare(`
SELECT * FROM documents
WHERE entity_id = ? AND status = 'indexed'
`).all(boatId);
// Create PDF with index
// Upload to Google Drive
// Share with buyer
publishEvent('as_built_package.generated', { boatId });
}
```
---
## Security Considerations
1. **Encrypt Integration Credentials**
- Store API keys, tokens in encrypted columns
- Use `sodium_crypto_secretbox` for encryption
- Decrypt only when needed
2. **Validate Webhook Payloads**
- HMAC-SHA256 signature verification
- Check timestamp (prevent replay attacks)
- Rate limit by source IP
3. **Audit All Integration Events**
- Log who configured integrations
- Log all data flows
- Keep audit trail for compliance
4. **Scope Integration Permissions**
- Each integration tied to organization
- Can't access other orgs' data
- Minimal required permissions
---
## Performance Tips
1. **Webhook Delivery**
- Queue webhooks, don't send synchronously
- Implement exponential backoff for retries
- Batch events if possible
2. **Search Indexing**
- Index in background worker, not in upload route
- Use batch indexing for large datasets
- Delete old pages before reindexing
3. **Large File Handling**
- Stream uploads, don't load into memory
- Process OCR page-by-page
- Cache frequently accessed PDFs
---
**Last Updated:** 2025-11-13
**For full details:** See ARCHITECTURE_INTEGRATION_ANALYSIS.md