fase(16): integrations module

This commit is contained in:
Claude
2026-03-06 07:22:00 -05:00
committed by debian
parent cffa1aeea9
commit 1f1678af17
49 changed files with 2558 additions and 13 deletions

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createIntegrationsRouter = createIntegrationsRouter;
const express_1 = require("express");
const Integration_1 = require("../../domain/entities/Integration");
const WebhookEndpoint_1 = require("../../domain/entities/WebhookEndpoint");
const IntegrationType_1 = require("../../domain/value-objects/IntegrationType");
function createIntegrationsRouter(deps) {
const router = (0, express_1.Router)();
const { integrationRepo, webhookRepo } = deps;
// ─── Integrations CRUD ──────────────────────────────────────────────────────
router.get('/', async (_req, res) => {
const items = await integrationRepo.findAll();
res.json(items.map(serializeIntegration));
});
router.post('/', async (req, res) => {
const { name, type, config } = req.body;
if (!name || !type) {
res.status(400).json({ error: 'name and type are required' });
return;
}
let intType;
try {
intType = IntegrationType_1.IntegrationType.fromString(type);
}
catch {
res.status(400).json({ error: `Invalid integration type: ${type}` });
return;
}
const integration = Integration_1.Integration.create({ name, type: intType, config: config ?? {} });
await integrationRepo.save(integration);
res.status(201).json(serializeIntegration(integration));
});
router.get('/:id', async (req, res) => {
const item = await integrationRepo.findById(req.params['id']);
if (!item) {
res.status(404).json({ error: 'Integration not found' });
return;
}
res.json(serializeIntegration(item));
});
router.patch('/:id', async (req, res) => {
const item = await integrationRepo.findById(req.params['id']);
if (!item) {
res.status(404).json({ error: 'Integration not found' });
return;
}
const { enabled, config } = req.body;
if (enabled === true)
item.enable();
else if (enabled === false)
item.disable();
if (config)
item.updateConfig(config);
await integrationRepo.update(item);
res.json(serializeIntegration(item));
});
router.delete('/:id', async (req, res) => {
await integrationRepo.delete(req.params['id']);
res.status(204).end();
});
// ─── Webhook Endpoints ───────────────────────────────────────────────────────
router.get('/webhooks/endpoints', async (_req, res) => {
const endpoints = await webhookRepo.findAll();
res.json(endpoints.map(serializeWebhook));
});
router.post('/webhooks/endpoints', async (req, res) => {
const { url } = req.body;
if (!url) {
res.status(400).json({ error: 'url is required' });
return;
}
const endpoint = WebhookEndpoint_1.WebhookEndpoint.create({ url });
await webhookRepo.save(endpoint);
res.status(201).json(serializeWebhook(endpoint));
});
router.delete('/webhooks/endpoints/:id', async (req, res) => {
await webhookRepo.delete(req.params['id']);
res.status(204).end();
});
return router;
}
function serializeIntegration(i) {
return {
id: i.id.toString(),
name: i.name,
type: i.type.value,
enabled: i.enabled,
config: maskSecrets(i.config),
createdAt: i.createdAt.toISOString(),
};
}
function serializeWebhook(ep) {
return {
id: ep.id.toString(),
url: ep.url,
enabled: ep.enabled,
createdAt: ep.createdAt.toISOString(),
lastDeliveredAt: ep.lastDeliveredAt?.toISOString() ?? null,
lastStatus: ep.lastStatus ?? null,
// Return the secret only once at creation (caller can see it from the first POST)
};
}
function maskSecrets(config) {
const masked = { ...config };
for (const key of ['token', 'secret', 'password', 'apiKey']) {
if (masked[key])
masked[key] = '***';
}
return masked;
}