arcadiasuite/server/api-central/routes.ts

338 lines
10 KiB
TypeScript

import { Router } from "express";
import { db } from "../../db";
import { sql } from "drizzle-orm";
const router = Router();
const requireAuth = (req: any, res: any, next: any) => {
if (!req.isAuthenticated()) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
};
// Tabela de conexões de API (criar se não existir)
async function ensureApiConnectionsTable() {
try {
await db.execute(sql`
CREATE TABLE IF NOT EXISTS api_connections (
id SERIAL PRIMARY KEY,
tenant_id INTEGER,
name VARCHAR(200) NOT NULL,
type VARCHAR(50) NOT NULL,
base_url TEXT NOT NULL,
api_key TEXT,
api_secret TEXT,
headers JSONB DEFAULT '{}',
status VARCHAR(50) DEFAULT 'disconnected',
last_sync_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
await db.execute(sql`
CREATE TABLE IF NOT EXISTS api_endpoints (
id SERIAL PRIMARY KEY,
connection_id INTEGER REFERENCES api_connections(id) ON DELETE CASCADE,
name VARCHAR(200),
method VARCHAR(10) NOT NULL,
path TEXT NOT NULL,
description TEXT,
headers JSONB DEFAULT '{}',
body_template TEXT,
params JSONB DEFAULT '[]',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
await db.execute(sql`
CREATE TABLE IF NOT EXISTS api_logs (
id SERIAL PRIMARY KEY,
connection_id INTEGER REFERENCES api_connections(id) ON DELETE CASCADE,
endpoint_id INTEGER,
method VARCHAR(10),
url TEXT,
request_headers JSONB,
request_body TEXT,
response_status INTEGER,
response_body TEXT,
latency_ms INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
} catch (e) {
console.log("[API Central] Tables already exist or error:", e);
}
}
ensureApiConnectionsTable();
// Listar conexões
router.get("/connections", requireAuth, async (req, res) => {
try {
const result = await db.execute(sql`
SELECT * FROM api_connections ORDER BY created_at DESC
`);
res.json(result.rows);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Criar conexão
router.post("/connections", requireAuth, async (req, res) => {
try {
const { name, type, baseUrl, apiKey, apiSecret, headers } = req.body;
if (!name || !baseUrl) {
return res.status(400).json({ error: "Nome e URL são obrigatórios" });
}
const result = await db.execute(sql`
INSERT INTO api_connections (name, type, base_url, api_key, api_secret, headers, status)
VALUES (${name}, ${type || 'rest'}, ${baseUrl}, ${apiKey}, ${apiSecret}, ${JSON.stringify(headers || {})}, 'disconnected')
RETURNING *
`);
res.json(result.rows[0]);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Atualizar conexão
router.put("/connections/:id", requireAuth, async (req, res) => {
try {
const { id } = req.params;
const { name, type, baseUrl, apiKey, apiSecret, headers, status } = req.body;
const result = await db.execute(sql`
UPDATE api_connections
SET name = COALESCE(${name}, name),
type = COALESCE(${type}, type),
base_url = COALESCE(${baseUrl}, base_url),
api_key = COALESCE(${apiKey}, api_key),
api_secret = COALESCE(${apiSecret}, api_secret),
headers = COALESCE(${headers ? JSON.stringify(headers) : null}, headers),
status = COALESCE(${status}, status),
updated_at = CURRENT_TIMESTAMP
WHERE id = ${parseInt(id)}
RETURNING *
`);
res.json(result.rows[0]);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Deletar conexão
router.delete("/connections/:id", requireAuth, async (req, res) => {
try {
const { id } = req.params;
await db.execute(sql`DELETE FROM api_connections WHERE id = ${parseInt(id)}`);
res.json({ success: true });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Listar endpoints de uma conexão
router.get("/connections/:id/endpoints", requireAuth, async (req, res) => {
try {
const { id } = req.params;
const result = await db.execute(sql`
SELECT * FROM api_endpoints WHERE connection_id = ${parseInt(id)} ORDER BY created_at
`);
res.json(result.rows);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Criar endpoint
router.post("/connections/:id/endpoints", requireAuth, async (req, res) => {
try {
const { id } = req.params;
const { name, method, path, description, headers, bodyTemplate, params } = req.body;
const result = await db.execute(sql`
INSERT INTO api_endpoints (connection_id, name, method, path, description, headers, body_template, params)
VALUES (${parseInt(id)}, ${name}, ${method}, ${path}, ${description}, ${JSON.stringify(headers || {})}, ${bodyTemplate}, ${JSON.stringify(params || [])})
RETURNING *
`);
res.json(result.rows[0]);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Atualizar endpoint
router.put("/endpoints/:id", requireAuth, async (req, res) => {
try {
const { id } = req.params;
const { name, method, path, description, headers, bodyTemplate, params } = req.body;
const result = await db.execute(sql`
UPDATE api_endpoints
SET name = COALESCE(${name}, name),
method = COALESCE(${method}, method),
path = COALESCE(${path}, path),
description = COALESCE(${description}, description),
headers = COALESCE(${headers ? JSON.stringify(headers) : null}, headers),
body_template = COALESCE(${bodyTemplate}, body_template),
params = COALESCE(${params ? JSON.stringify(params) : null}, params)
WHERE id = ${parseInt(id)}
RETURNING *
`);
res.json(result.rows[0]);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Deletar endpoint
router.delete("/endpoints/:id", requireAuth, async (req, res) => {
try {
const { id } = req.params;
await db.execute(sql`DELETE FROM api_endpoints WHERE id = ${parseInt(id)}`);
res.json({ success: true });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Executar chamada de API
router.post("/execute", requireAuth, async (req, res) => {
try {
const { connectionId, endpointId, method, url, headers, body } = req.body;
const startTime = Date.now();
const fetchOptions: RequestInit = {
method: method || "GET",
headers: {
"Content-Type": "application/json",
...headers,
},
};
if (body && method !== "GET") {
fetchOptions.body = typeof body === "string" ? body : JSON.stringify(body);
}
const response = await fetch(url, fetchOptions);
const latency = Date.now() - startTime;
let responseBody: string;
const contentType = response.headers.get("content-type");
if (contentType?.includes("application/json")) {
responseBody = JSON.stringify(await response.json(), null, 2);
} else {
responseBody = await response.text();
}
// Salvar log
await db.execute(sql`
INSERT INTO api_logs (connection_id, endpoint_id, method, url, request_headers, request_body, response_status, response_body, latency_ms)
VALUES (${connectionId || null}, ${endpointId || null}, ${method}, ${url}, ${JSON.stringify(headers || {})}, ${typeof body === 'string' ? body : JSON.stringify(body)}, ${response.status}, ${responseBody}, ${latency})
`);
res.json({
success: response.ok,
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
body: responseBody,
latency,
});
} catch (error: any) {
res.status(500).json({
success: false,
error: error.message,
body: `Erro ao executar requisição: ${error.message}`
});
}
});
// Listar logs
router.get("/logs", requireAuth, async (req, res) => {
try {
const { connectionId, limit = 50 } = req.query;
let query;
if (connectionId) {
query = sql`
SELECT * FROM api_logs
WHERE connection_id = ${parseInt(connectionId as string)}
ORDER BY created_at DESC
LIMIT ${parseInt(limit as string)}
`;
} else {
query = sql`
SELECT * FROM api_logs
ORDER BY created_at DESC
LIMIT ${parseInt(limit as string)}
`;
}
const result = await db.execute(query);
res.json(result.rows);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// Testar conexão
router.post("/connections/:id/test", requireAuth, async (req, res) => {
try {
const { id } = req.params;
const connResult = await db.execute(sql`
SELECT * FROM api_connections WHERE id = ${parseInt(id)}
`);
if (connResult.rows.length === 0) {
return res.status(404).json({ error: "Conexão não encontrada" });
}
const conn = connResult.rows[0] as any;
const startTime = Date.now();
const headers: Record<string, string> = conn.headers || {};
if (conn.api_key) {
headers["Authorization"] = `Bearer ${conn.api_key}`;
}
const response = await fetch(conn.base_url, {
method: "GET",
headers,
});
const latency = Date.now() - startTime;
const status = response.ok ? "connected" : "error";
await db.execute(sql`
UPDATE api_connections
SET status = ${status}, last_sync_at = CURRENT_TIMESTAMP
WHERE id = ${parseInt(id)}
`);
res.json({
success: response.ok,
status,
latency,
httpStatus: response.status,
});
} catch (error: any) {
await db.execute(sql`
UPDATE api_connections
SET status = 'error'
WHERE id = ${parseInt(req.params.id)}
`);
res.json({
success: false,
status: "error",
error: error.message,
});
}
});
export default router;