Bridge Architecture
Deep dive into the Bridge API architecture, the central orchestration layer.
Overview
Container Details
| Property | Value |
|---|---|
| Container | mediamagic-bridge |
| Port | 3100 |
| Runtime | Node.js 20 |
| Framework | Express.js |
Directory Structure
bridge/
├── server.js # Entry point
├── package.json # Dependencies
├── Dockerfile # Container build
│
├── config/
│ └── platforms.js # Platform configuration
│
├── helpers/
│ ├── ayrshare/ # Social media
│ │ ├── analytics.js
│ │ ├── comments.js
│ │ ├── history.js
│ │ ├── schedule.js
│ │ └── sync.js
│ │
│ ├── assemblyai/ # Transcription
│ │ └── transcribe.js
│ │
│ ├── boxcast/ # Boxcast sync
│ │ └── sync.js
│ │
│ ├── bunny/ # CDN
│ │ ├── storage.js
│ │ └── upload.js
│ │
│ ├── captions/ # Caption generation
│ │ ├── burnCaptions.js
│ │ ├── captionPipeline.js
│ │ └── generateASS.js
│ │
│ ├── claude/ # AI analysis
│ │ ├── analyze-clips.js
│ │ ├── prompts.js
│ │ └── transcript-analysis.js
│ │
│ ├── cron/ # Scheduled jobs
│ │ └── collectAnalytics.js
│ │
│ ├── deepgram/ # Transcription
│ │ └── transcribe.js
│ │
│ ├── espocrm/ # CRM client
│ │ └── client.js
│ │
│ ├── ffmpeg/ # Video processing
│ │ ├── compile.js
│ │ ├── extract.js
│ │ ├── filmstrip.js
│ │ └── waveform.js
│ │
│ ├── n8n/ # Workflows
│ │ └── triggerWorkflow.js
│ │
│ ├── postgres/ # Analytics DB
│ │ ├── client.js
│ │ └── queries.js
│ │
│ ├── stockmedia/ # Stock media
│ │ ├── runway.js
│ │ ├── shutterstock.js
│ │ └── storyblocks.js
│ │
│ └── xserver/ # Platform automation
│ └── client.js
│
└── routes/
├── index.js # Route registration
├── analytics.js
├── ayrshare.js
├── boxcast.js
├── broll.js
├── cdn.js
├── clips.js
├── comments.js
├── webhooks.js
└── xserver.jsRequest Flow
Key Components
server.js (Entry Point)
javascript
import express from 'express';
import routes from './routes/index.js';
const app = express();
// Middleware
app.use(express.json());
app.use(cors());
// Routes
app.use('/', routes);
// Health check
app.get('/health', (req, res) => {
res.json({ ok: true, service: 'mediamagic-bridge' });
});
// Start server
app.listen(3100);Route Registration
javascript
// routes/index.js
import { Router } from 'express';
import webhooks from './webhooks.js';
import analytics from './analytics.js';
import ayrshare from './ayrshare.js';
const router = Router();
router.use('/webhooks', webhooks);
router.use('/analytics', analytics);
router.use('/ayrshare', ayrshare);
// ... more routes
export default router;Webhook Handler Pattern
javascript
// routes/webhooks.js
router.post('/platform-updated', async (req, res) => {
const entity = req.body;
try {
// Check if queued for publishing
if (entity.status === 'Queued') {
// Determine platform endpoint
const route = PLATFORM_ROUTES[entity.platform];
// Forward to X Server
const result = await xServerClient.post(route, entity);
// Update EspoCRM
await espoClient.update('PlatformPublish', entity.id, {
status: 'Published',
platformUrl: result.url,
});
}
res.json({ success: true });
} catch (error) {
// Update with error
await espoClient.update('PlatformPublish', entity.id, {
status: 'Failed',
apiResponse: error.message,
});
res.status(500).json({ error: error.message });
}
});Helper Pattern
javascript
// helpers/ayrshare/schedule.js
export async function schedulePost(data) {
const response = await axios.post(
'https://app.ayrshare.com/api/post',
{
post: data.content,
platforms: data.platforms,
scheduleDate: data.scheduledAt,
mediaUrls: data.mediaUrls,
},
{
headers: {
'Authorization': `Bearer ${process.env.AYRSHARE_API_KEY}`,
},
}
);
return response.data;
}Cron Jobs
Scheduled tasks run inside the Bridge container:
javascript
// helpers/cron/collectAnalytics.js
import cron from 'node-cron';
// Run every 6 hours
cron.schedule('0 */6 * * *', async () => {
console.log('Collecting analytics...');
// Fetch from Ayrshare
const analytics = await fetchAyrshareAnalytics();
// Store in PostgreSQL
await storeMetrics(analytics);
console.log('Analytics collection complete');
});Platform Routes Configuration
javascript
// routes/webhooks.js
const PLATFORM_ROUTES = {
'Rumble': {
livestream: '/rumble-create',
upload: '/rumble-episode'
},
'YouTube': {
livestream: '/youtube-create',
upload: null
},
'Boxcast': {
livestream: '/boxcast-livestream',
upload: '/boxcast-media-upload'
},
// ... more platforms
};Error Handling
Error Response Format
json
{
"error": "Error message",
"code": "ERROR_CODE",
"details": {}
}Database Connections
PostgreSQL Pool
javascript
// helpers/postgres/client.js
import pg from 'pg';
const pool = new pg.Pool({
host: process.env.POSTGRES_HOST || 'postgres',
database: process.env.POSTGRES_DB || 'mediamagic_analytics',
user: process.env.POSTGRES_USER || 'mediamagic',
password: process.env.POSTGRES_PASSWORD,
});
export async function query(text, params) {
const result = await pool.query(text, params);
return result.rows;
}EspoCRM Client
javascript
// helpers/espocrm/client.js
export async function update(entity, id, data) {
return axios.put(
`${ESPOCRM_URL}/api/v1/${entity}/${id}`,
data,
{
headers: {
'X-Api-Key': process.env.ESPOCRM_API_KEY,
'Content-Type': 'application/json',
},
}
);
}Extending the Bridge
Adding a New Route
- Create route file:
javascript
// routes/myfeature.js
import { Router } from 'express';
const router = Router();
router.post('/action', async (req, res) => {
// Implementation
});
export default router;- Register in index:
javascript
// routes/index.js
import myfeature from './myfeature.js';
router.use('/myfeature', myfeature);Adding a New Helper
- Create helper directory and file:
javascript
// helpers/myservice/client.js
export async function doSomething(params) {
// Implementation
}- Use in routes:
javascript
import { doSomething } from '../helpers/myservice/client.js';