From 8ff699e776d1148b24fc5731014fec26d58f0835 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Wed, 18 Mar 2026 22:17:42 +0000 Subject: [PATCH] fix: resolve DBAL frontend client-side errors and deployment issues - Fix DBAL overview page: basePath doubled in NavTabs and Link hrefs - Fix client-side fetch URLs: prepend basePath for /api/status and /api/query - Remove unused workspace deps (api-clients, core-hooks, redux) from DBAL frontend - Simplify DBAL Dockerfile to standalone build (no monorepo workspace deps needed) - Add null guards for health array in ServerStatusPanel - Fix Prometheus nginx proxy: don't strip prefix (web.external-url handles it) - Fix caproverforge portal: remove onMouse handlers from Server Component Co-Authored-By: Claude Opus 4.6 (1M context) --- deployment/config/nginx/production.conf | 2 +- deployment/docker-compose.stack.yml | 4 +- frontends/dbal/Dockerfile | 57 +++++++++--------------- frontends/dbal/next.config.ts | 6 --- frontends/dbal/package.json | 6 +-- frontends/dbal/src/DaemonPage.tsx | 4 +- frontends/dbal/src/NavTabs.tsx | 4 +- frontends/dbal/src/QueryConsole.tsx | 3 +- frontends/dbal/src/ServerStatusPanel.tsx | 13 +++--- 9 files changed, 38 insertions(+), 61 deletions(-) diff --git a/deployment/config/nginx/production.conf b/deployment/config/nginx/production.conf index 771641ab6..b40f3e11e 100644 --- a/deployment/config/nginx/production.conf +++ b/deployment/config/nginx/production.conf @@ -345,9 +345,9 @@ http { } # Prometheus - Metrics scraping + # --web.external-url=/prometheus/ means it expects the prefix in requests location /prometheus/ { set $upstream_prometheus prometheus; - rewrite ^/prometheus/(.*)$ /$1 break; proxy_pass http://$upstream_prometheus:9090; proxy_http_version 1.1; proxy_set_header Host $host; diff --git a/deployment/docker-compose.stack.yml b/deployment/docker-compose.stack.yml index dd6042e55..83f44ef71 100644 --- a/deployment/docker-compose.stack.yml +++ b/deployment/docker-compose.stack.yml @@ -749,8 +749,8 @@ services: # DBAL Frontend - Daemon overview + query console dbal-frontend: build: - context: .. - dockerfile: frontends/dbal/Dockerfile + context: ../frontends/dbal + dockerfile: Dockerfile args: DBAL_DAEMON_URL: http://dbal:8080 container_name: metabuilder-dbal-frontend diff --git a/frontends/dbal/Dockerfile b/frontends/dbal/Dockerfile index defbe7b51..008c297ca 100644 --- a/frontends/dbal/Dockerfile +++ b/frontends/dbal/Dockerfile @@ -1,50 +1,35 @@ -# Multi-stage build: node_modules from base image, source built fresh -# Context: monorepo root (..) -# Requires: docker build -f deployment/base-images/Dockerfile.node-deps \ -# -t metabuilder/base-node-deps:latest . +FROM node:24-alpine AS base -# --- Build stage --- -ARG BASE_REGISTRY=metabuilder -FROM ${BASE_REGISTRY}/base-node-deps:latest AS builder +FROM base AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY package.json ./ +RUN npm install --legacy-peer-deps + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . ARG DBAL_DAEMON_URL=http://dbal:8080 ENV DBAL_DAEMON_URL=$DBAL_DAEMON_URL -# ── Shared packages (resolved by transpilePackages) ───────────────────────── -# Only the workspace deps that dbal-ui actually imports. -# -# redux/api-clients → hooks-async → redux-slices (transitive chain) -COPY redux/api-clients/ ./redux/api-clients/ -COPY redux/hooks-async/ ./redux/hooks-async/ -COPY redux/slices/ ./redux/slices/ -COPY redux/core-hooks/ ./redux/core-hooks/ +RUN mkdir -p public && npm run build -# ── DBAL frontend (the app itself) ────────────────────────────────────────── -COPY frontends/dbal/ ./frontends/dbal/ - -# Build workspace library packages -RUN for ws in redux/slices redux/hooks-async redux/api-clients redux/core-hooks; do \ - npm run build -w "$ws" --if-present 2>&1 || echo "WARN: $ws build failed (non-critical)"; \ -done - -# Build the app -RUN cd frontends/dbal && npx next build - -# --- Runtime stage --- -FROM node:24-alpine +FROM base AS runner WORKDIR /app - -COPY --from=builder /app/frontends/dbal/.next/standalone ./ -COPY --from=builder /app/frontends/dbal/.next/static ./frontends/dbal/.next/static - ENV NODE_ENV=production -ENV PORT=3000 -ENV HOSTNAME="0.0.0.0" - +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +COPY --from=builder /app/public ./public +RUN mkdir .next && chown nextjs:nodejs .next +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +USER nextjs EXPOSE 3000 +ENV PORT=3000 HOSTNAME="0.0.0.0" HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \ CMD wget --quiet --tries=1 --spider http://127.0.0.1:3000/dbal || exit 1 -WORKDIR /app/frontends/dbal CMD ["node", "server.js"] diff --git a/frontends/dbal/next.config.ts b/frontends/dbal/next.config.ts index 52ab32a20..9a1e917b1 100644 --- a/frontends/dbal/next.config.ts +++ b/frontends/dbal/next.config.ts @@ -4,12 +4,6 @@ const nextConfig: NextConfig = { reactStrictMode: true, output: 'standalone', basePath: '/dbal', - transpilePackages: [ - '@metabuilder/api-clients', - '@metabuilder/core-hooks', - '@metabuilder/hooks-async', - '@metabuilder/redux-slices', - ], } export default nextConfig diff --git a/frontends/dbal/package.json b/frontends/dbal/package.json index 0ca6d515c..3c0fa41b8 100644 --- a/frontends/dbal/package.json +++ b/frontends/dbal/package.json @@ -14,13 +14,9 @@ "test:e2e:ui": "playwright test --ui" }, "dependencies": { - "@metabuilder/api-clients": "*", - "@metabuilder/core-hooks": "*", "next": "^16.1.6", "react": "^19.2.4", - "react-dom": "^19.2.4", - "react-redux": "^9.2.0", - "redux": "^5.0.1" + "react-dom": "^19.2.4" }, "overrides": { "react": "^19.2.4", diff --git a/frontends/dbal/src/DaemonPage.tsx b/frontends/dbal/src/DaemonPage.tsx index dd2a339d4..1151ae21d 100644 --- a/frontends/dbal/src/DaemonPage.tsx +++ b/frontends/dbal/src/DaemonPage.tsx @@ -44,10 +44,10 @@ export function DBALDaemonPage() { A hardened, sandboxed C++ daemon serves all database operations via REST API. It validates every request, enforces ACLs, and executes SQL through safe adapters.

- + Visit daemon docs - + Check status
diff --git a/frontends/dbal/src/NavTabs.tsx b/frontends/dbal/src/NavTabs.tsx index b36be5949..edbed0dea 100644 --- a/frontends/dbal/src/NavTabs.tsx +++ b/frontends/dbal/src/NavTabs.tsx @@ -4,8 +4,8 @@ import { usePathname } from 'next/navigation' import styles from './NavTabs.module.scss' const tabs = [ - { href: '/dbal', label: 'Overview' }, - { href: '/dbal/query', label: 'Query Console' }, + { href: '/', label: 'Overview' }, + { href: '/query', label: 'Query Console' }, ] export function NavTabs() { diff --git a/frontends/dbal/src/QueryConsole.tsx b/frontends/dbal/src/QueryConsole.tsx index c3290d5b2..66e134495 100644 --- a/frontends/dbal/src/QueryConsole.tsx +++ b/frontends/dbal/src/QueryConsole.tsx @@ -119,7 +119,8 @@ export function QueryConsole() { } try { - const res = await fetch('/api/query', { + const basePath = process.env.__NEXT_ROUTER_BASEPATH || '/dbal' + const res = await fetch(`${basePath}/api/query`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ method, path, body: parsedBody }), diff --git a/frontends/dbal/src/ServerStatusPanel.tsx b/frontends/dbal/src/ServerStatusPanel.tsx index 9f65b1b25..4365772f3 100644 --- a/frontends/dbal/src/ServerStatusPanel.tsx +++ b/frontends/dbal/src/ServerStatusPanel.tsx @@ -5,7 +5,7 @@ import type { ServerHealth, StatusResponse } from './status' import styles from './ServerStatusPanel.module.scss' export function ServerStatusPanel() { - const [health, setHealth] = useState([]) + const [health, setHealth] = useState(() => []) const [lastUpdated, setLastUpdated] = useState('') const [error, setError] = useState(null) const [loading, setLoading] = useState(true) @@ -15,7 +15,8 @@ export function ServerStatusPanel() { const fetchStatus = async () => { try { - const response = await fetch('/api/status', { cache: 'no-store' }) + const basePath = process.env.__NEXT_ROUTER_BASEPATH || '/dbal' + const response = await fetch(`${basePath}/api/status`, { cache: 'no-store' }) if (!response.ok) { throw new Error('Status endpoint failed') } @@ -48,11 +49,11 @@ export function ServerStatusPanel() { return 'Status unavailable right now' } - if (health.length === 0) { + if (!health || health.length === 0) { return 'Initializing status feed...' } - const degraded = health.some(item => item.status !== 'online') + const degraded = health?.some(item => item.status !== 'online') return degraded ? 'Some systems need attention' : 'All systems nominal' }, [health, error]) @@ -65,7 +66,7 @@ export function ServerStatusPanel() {
- {loading && health.length === 0 ? ( + {loading && (!health || health.length === 0) ? (
Loading status...
) : error ? (
@@ -73,7 +74,7 @@ export function ServerStatusPanel() {

Try refreshing the page in a few moments.

) : ( - health.map(item => ( + (health ?? []).map(item => (