Complete guide to integrating Pixel Patrol API into your application
site_xxxxxxxxxxxxxxxxxxxx
)# .env file
PIXELPATROL_API_KEY=site_xxxxxxxxxxxxxxxxxxxx
PIXELPATROL_API_URL=https://your-supabase-url.supabase.co/functions/v1
class PixelPatrolClient {
constructor(apiKey, baseUrl) {
this.apiKey = apiKey;
this.baseUrl = baseUrl || process.env.PIXELPATROL_API_URL;
}
async submitMedia(data) {
const response = await fetch(`${this.baseUrl}/submit-media`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
api_key: this.apiKey,
...data
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || `HTTP ${response.status}`);
}
return response.json();
}
async getMediaStatus(mediaId) {
const response = await fetch(`${this.baseUrl}/get-media-status`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
api_key: this.apiKey,
media_id: mediaId
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || `HTTP ${response.status}`);
}
return response.json();
}
}
// Initialize client
const pixelPatrol = new PixelPatrolClient(process.env.PIXELPATROL_API_KEY);
// When user uploads content
async function handleUserUpload(userId, imageUrl, context) {
try {
// Submit for moderation
const result = await pixelPatrol.submitMedia({
content_url: imageUrl,
app_media_id: `user-${userId}-${Date.now()}`,
submitted_by: userId,
metadata: {
context: context, // 'profile_photo', 'post_image', etc.
upload_timestamp: new Date().toISOString()
}
});
// Store media ID for tracking
await database.save({
userId,
mediaId: result.id,
pixelPatrolId: result.id,
status: 'pending_moderation'
});
return {
success: true,
message: 'Content submitted for review',
mediaId: result.id
};
} catch (error) {
console.error('Moderation submission failed:', error);
// Decide whether to block upload or allow with manual review
return {
success: false,
error: 'Moderation service unavailable'
};
}
}
async function moderateTextContent(text, userId, context) {
try {
const result = await pixelPatrol.submitMedia({
body: text,
app_media_id: `text-${context}-${Date.now()}`,
submitted_by: userId,
metadata: {
content_type: 'text',
context: context, // 'comment', 'post', 'message'
character_count: text.length
}
});
// For text, you might want to wait for quick moderation
if (context === 'comment') {
// Poll for quick result (with timeout)
const status = await waitForModeration(result.id, 5000); // 5 second timeout
if (status.action === 'block') {
return {
allowed: false,
reason: 'Content violates community guidelines'
};
}
}
return {
allowed: true,
mediaId: result.id
};
} catch (error) {
console.error('Text moderation failed:', error);
// Fallback strategy
return {
allowed: true, // or false, depending on your policy
requiresReview: true
};
}
}
async function processBatchContent(items) {
const results = [];
const batchSize = 10; // Process in batches to avoid rate limits
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
// Submit batch concurrently
const promises = batch.map(item =>
pixelPatrol.submitMedia({
content_url: item.url,
app_media_id: item.id,
metadata: item.metadata
}).catch(error => ({
error: error.message,
item: item
}))
);
const batchResults = await Promise.all(promises);
results.push(...batchResults);
// Rate limiting - wait between batches
if (i + batchSize < items.length) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay
}
}
return results;
}
import crypto from 'crypto';
// Webhook handler
async function handlePixelPatrolWebhook(req, res) {
// Verify signature
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', process.env.PIXELPATROL_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event
const event = req.body;
switch (event.event) {
case 'media.moderated':
await handleModerationComplete(event.data);
break;
case 'media.created':
await handleMediaCreated(event.data);
break;
default:
console.log('Unhandled event type:', event.event);
}
// Always respond quickly
res.status(200).json({ received: true });
}
// Handle moderation completion
async function handleModerationComplete(data) {
const { media_id, status, ai_results, applied_rules } = data;
// Update your database
await database.updateMedia({
pixelPatrolId: media_id,
moderationStatus: status,
aiResults: ai_results,
appliedRules: applied_rules,
moderatedAt: new Date()
});
// Take action based on status
if (status === 'rejected' || status === 'flagged') {
await handleRejectedContent(media_id);
} else if (status === 'approved') {
await publishContent(media_id);
}
}
class PixelPatrolError extends Error {
constructor(message, code, details) {
super(message);
this.code = code;
this.details = details;
}
}
async function submitWithErrorHandling(data) {
try {
return await pixelPatrol.submitMedia(data);
} catch (error) {
// Parse error response
if (error.message.includes('Invalid API Key')) {
throw new PixelPatrolError(
'Authentication failed',
'AUTH_FAILED',
{ originalError: error.message }
);
}
if (error.message.includes('Moderation limit exceeded')) {
throw new PixelPatrolError(
'Moderation limit reached',
'LIMIT_EXCEEDED',
{
type: 'moderation_limit',
suggestion: 'Upgrade your plan or wait for limit reset'
}
);
}
if (error.message.includes('Team has no active subscription')) {
throw new PixelPatrolError(
'Subscription required',
'NO_SUBSCRIPTION',
{ suggestion: 'Please activate a subscription' }
);
}
// Generic error
throw new PixelPatrolError(
'Moderation service error',
'SERVICE_ERROR',
{ originalError: error.message }
);
}
}
async function submitWithRetry(data, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await pixelPatrol.submitMedia(data);
} catch (error) {
lastError = error;
// Don't retry on client errors
if (error.message.includes('Invalid') ||
error.message.includes('limit exceeded')) {
throw error;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
const moderationCache = new Map();
async function getCachedModeration(contentHash) {
// Check cache first
const cached = moderationCache.get(contentHash);
if (cached && cached.timestamp > Date.now() - 3600000) { // 1 hour cache
return cached.result;
}
// If not cached, submit for moderation
const result = await pixelPatrol.submitMedia({
content_url: contentUrl,
app_media_id: contentHash,
metadata: {
content_hash: contentHash
}
});
// Cache the result
moderationCache.set(contentHash, {
result: result,
timestamp: Date.now()
});
return result;
}
// Use connection pooling for better performance
import { Agent } from 'https';
const httpsAgent = new Agent({
keepAlive: true,
maxSockets: 10
});
// Use with fetch
const response = await fetch(url, {
agent: httpsAgent,
// ... other options
});
import { jest } from '@jest/globals';
// Mock the API client
jest.mock('./pixelPatrolClient');
describe('Content Moderation', () => {
it('should handle approved content', async () => {
// Mock successful moderation
pixelPatrol.submitMedia.mockResolvedValue({
id: 'media_123',
status: 'pending'
});
const result = await handleUserUpload('user123', 'https://example.com/image.jpg', 'profile');
expect(result.success).toBe(true);
expect(result.mediaId).toBe('media_123');
});
it('should handle rate limits gracefully', async () => {
// Mock rate limit error
pixelPatrol.submitMedia.mockRejectedValue(
new Error('Moderation limit exceeded')
);
const result = await handleUserUpload('user123', 'https://example.com/image.jpg', 'profile');
expect(result.success).toBe(false);
expect(result.error).toContain('limit');
});
});
// Test webhook handling
describe('Webhook Handler', () => {
it('should verify webhook signatures', async () => {
const payload = {
event: 'media.moderated',
data: { media_id: 'test_123' }
};
const signature = crypto
.createHmac('sha256', 'test_secret')
.update(JSON.stringify(payload))
.digest('hex');
const req = {
headers: { 'x-pixelpatrol-signature': signature },
body: payload
};
const res = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
};
await handlePixelPatrolWebhook(req, res);
expect(res.status).toHaveBeenCalledWith(200);
});
});
import winston from 'winston';
const logger = winston.createLogger({
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'pixelpatrol.log' })
]
});
// Log all API interactions
async function submitMediaWithLogging(data) {
const startTime = Date.now();
try {
const result = await pixelPatrol.submitMedia(data);
logger.info('Media submitted successfully', {
mediaId: result.id,
appMediaId: data.app_media_id,
duration: Date.now() - startTime
});
return result;
} catch (error) {
logger.error('Media submission failed', {
error: error.message,
appMediaId: data.app_media_id,
duration: Date.now() - startTime
});
throw error;
}
}