feat: integração Metaset BI via proxy interno

- Adiciona server/bi/metaset-proxy.ts para proxy do serviço Metaset
- Registra proxy /metaset no servidor (setupMetasetProxy)
- Atualiza página Metaset.tsx para usar proxy local /metaset
- Remove referência externa bi.onboardbi.com.br
- Agora usa serviço interno http://metaset:8100
- Mantém navegação suave com navbar sempre visível
This commit is contained in:
Jonas Pacheco 2026-04-08 14:39:28 -03:00
parent bdb92c4135
commit 5554e9d613
3 changed files with 98 additions and 66 deletions

View File

@ -1,34 +1,52 @@
import { useState } from "react";
import { Loader2, AlertCircle, ExternalLink } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useState, useEffect } from "react";
import { Loader2, AlertCircle } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
export default function Metaset() {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
useEffect(() => {
// Verificar se o Metaset está online
fetch("/api/bi/metaset/health", { credentials: "include" })
.then(r => r.json())
.then(data => {
if (!data.online) {
setHasError(true);
setErrorMessage("O Metaset está offline ou iniciando.");
}
})
.catch(() => {
setHasError(true);
setErrorMessage("Não foi possível conectar ao Metaset.");
})
.finally(() => {
setIsLoading(false);
});
}, []);
if (hasError) {
return (
<div className="h-[calc(100vh-40px)] w-full flex flex-col bg-background">
{/* Header do módulo */}
<div className="h-10 border-b flex items-center px-4 justify-between bg-muted/30">
<div className="flex items-center gap-2">
<span className="font-semibold text-sm">Metaset BI</span>
</div>
<Button variant="ghost" size="sm" asChild>
<a
href="https://bi.onboardbi.com.br/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1"
>
<ExternalLink className="w-3 h-3" />
Abrir em nova aba
</a>
<div className="h-[calc(100vh-40px)] w-full flex items-center justify-center p-4">
<Alert variant="destructive" className="max-w-md">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
{errorMessage}
<div className="mt-4">
<Button onClick={() => window.location.reload()}>
Tentar novamente
</Button>
</div>
</AlertDescription>
</Alert>
</div>
);
}
{/* Container do iframe */}
<div className="flex-1 relative">
return (
<div className="h-[calc(100vh-40px)] w-full relative">
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-background z-10">
<div className="flex flex-col items-center gap-2">
@ -37,41 +55,12 @@ export default function Metaset() {
</div>
</div>
)}
{hasError ? (
<div className="absolute inset-0 flex items-center justify-center p-4">
<Alert variant="destructive" className="max-w-md">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Não foi possível carregar o Metaset. O site pode não permitir embed em iframe.
<div className="mt-4">
<Button asChild>
<a
href="https://bi.onboardbi.com.br/"
target="_blank"
rel="noopener noreferrer"
>
Abrir Metaset em nova aba
</a>
</Button>
</div>
</AlertDescription>
</Alert>
</div>
) : (
<iframe
src="https://bi.onboardbi.com.br/"
src="/metaset"
className="w-full h-full border-0"
onLoad={() => setIsLoading(false)}
onError={() => {
setIsLoading(false);
setHasError(true);
}}
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
title="Metaset BI"
/>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,39 @@
import { Express, Response } from "express";
import { createProxyMiddleware } from "http-proxy-middleware";
const METASET_HOST = process.env.METASET_HOST || "metaset";
const METASET_PORT = parseInt(process.env.METASET_PORT || "8100", 10);
const METASET_TIMEOUT = 60000;
export function setupMetasetProxy(app: Express): void {
const target = `http://${METASET_HOST}:${METASET_PORT}`;
const metasetProxy = createProxyMiddleware({
target,
changeOrigin: true,
timeout: METASET_TIMEOUT,
proxyTimeout: METASET_TIMEOUT,
pathRewrite: { "^/metaset": "" },
on: {
error: (err, _req, res) => {
console.error("[Metaset Proxy] Error:", err.message);
if (res && typeof (res as Response).status === "function") {
(res as Response).status(502).json({
error: "Metaset indisponível",
message: "O Metaset BI está iniciando. Tente novamente em alguns segundos.",
target,
});
}
},
proxyRes: (proxyRes) => {
const location = proxyRes.headers["location"];
if (location && typeof location === "string" && location.startsWith("/")) {
proxyRes.headers["location"] = `/metaset${location}`;
}
},
},
});
app.use("/metaset", metasetProxy);
console.log(`[Metaset Proxy] Configurado -> /metaset/* => ${target}`);
}

View File

@ -51,6 +51,7 @@ import governanceRoutes from "./governance/routes";
import { setupPlusProxy } from "./plus/proxy";
import { setupSupersetProxy } from "./superset/proxy";
import { registerSupersetRoutes } from "./superset/routes";
import { setupMetasetProxy } from "./bi/metaset-proxy";
import { setupErpNextProxy } from "./erpnext/proxy";
import { importErpNextRulesForTenant } from "./soe/erpnext-rule-importer";
import { registerEngineRoomRoutes } from "./engine-room/routes";
@ -89,6 +90,9 @@ export async function registerRoutes(
setupSupersetProxy(app);
registerSupersetRoutes(app);
// MetaSet - BI alternativo (porta 8100)
setupMetasetProxy(app);
// ERPNext container proxy (ativo apenas em DOCKER_MODE ou ERPNEXT_PROXY_ENABLED=true)
setupErpNextProxy(app);