mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
</p>
|
||||
<div className={styles.buttonGroup}>
|
||||
<Link href="/dbal" className={styles.btnPrimary}>
|
||||
<Link href="/" className={styles.btnPrimary}>
|
||||
Visit daemon docs
|
||||
</Link>
|
||||
<Link href="/dbal#status" className={styles.btnOutline}>
|
||||
<Link href="/#status" className={styles.btnOutline}>
|
||||
Check status
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ServerHealth, StatusResponse } from './status'
|
||||
import styles from './ServerStatusPanel.module.scss'
|
||||
|
||||
export function ServerStatusPanel() {
|
||||
const [health, setHealth] = useState<ServerHealth[]>([])
|
||||
const [health, setHealth] = useState<ServerHealth[]>(() => [])
|
||||
const [lastUpdated, setLastUpdated] = useState<string>('')
|
||||
const [error, setError] = useState<string | null>(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() {
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
{loading && health.length === 0 ? (
|
||||
{loading && (!health || health.length === 0) ? (
|
||||
<div className={styles.placeholder}>Loading status...</div>
|
||||
) : error ? (
|
||||
<div className={styles.placeholder}>
|
||||
@@ -73,7 +74,7 @@ export function ServerStatusPanel() {
|
||||
<p className={styles.caption}>Try refreshing the page in a few moments.</p>
|
||||
</div>
|
||||
) : (
|
||||
health.map(item => (
|
||||
(health ?? []).map(item => (
|
||||
<article key={item.name} className={styles.card}>
|
||||
<div className={styles.cardRow}>
|
||||
<div className={styles.cardTitleGroup}>
|
||||
|
||||
Reference in New Issue
Block a user