From 36b709b911dc4575a2666b7c3dd99534d3c412a8 Mon Sep 17 00:00:00 2001 From: bunker-admin Date: Thu, 9 Apr 2026 12:49:33 -0600 Subject: [PATCH] Automate Gitea init, NocoDB auto sign-in, and fix prod compose - Add scripts/gitea-init.sh: runs migrations + creates admin user on first boot, replacing the manual installation wizard - Set GITEA__security__INSTALL_LOCK=true in both compose files - Add NocoDB auth bridge (nginx) + /api/services/nocodb-auth proxy endpoint so the admin iframe auto-authenticates - Update NocoDBPage.tsx to fetch token and use auth bridge flow - Fix docker-compose.prod.yml missing Gitea env vars for API container (GITEA_URL, GITEA_API_TOKEN, GITEA_ADMIN_PASSWORD, etc.) - Pass NC_ADMIN_EMAIL/PASSWORD to API for NocoDB auth proxy - Increase Gitea auto-setup retries from 3 to 6 with admin auth check - Update config.sh non-interactive mode to set GITEA_ADMIN_USER - Include gitea-init.sh in release tarball (build-release.sh) Bunker Admin --- .env.example | 2 + admin/src/pages/NocoDBPage.tsx | 69 +++++++++++++------ api/src/config/env.ts | 2 + .../gitea-setup/gitea-setup.service.ts | 25 ++++--- api/src/modules/services/services.routes.ts | 42 +++++++++++ config.sh | 16 ++++- docker-compose.prod.yml | 18 +++++ docker-compose.yml | 11 +++ nginx/conf.d/services.conf.template | 16 +++++ scripts/build-release.sh | 2 +- scripts/gitea-init.sh | 54 +++++++++++++++ 11 files changed, 223 insertions(+), 34 deletions(-) create mode 100755 scripts/gitea-init.sh diff --git a/.env.example b/.env.example index 3d29ab5a..0e814ba8 100644 --- a/.env.example +++ b/.env.example @@ -222,6 +222,8 @@ GITEA_URL=http://gitea-changemaker:3000 GITEA_PORT=3030 GITEA_WEB_PORT=3030 GITEA_SSH_PORT=2222 +# Admin user (auto-created on first boot by gitea-init.sh) +GITEA_ADMIN_USER=admin GITEA_DB_TYPE=mysql GITEA_DB_HOST=gitea-db:3306 GITEA_DB_NAME=gitea diff --git a/admin/src/pages/NocoDBPage.tsx b/admin/src/pages/NocoDBPage.tsx index f867e0a6..4373b7ed 100644 --- a/admin/src/pages/NocoDBPage.tsx +++ b/admin/src/pages/NocoDBPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useMemo } from 'react'; +import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { useOutletContext } from 'react-router-dom'; import { Button, Space, Badge, Spin, Grid, Result, Alert } from 'antd'; import { ReloadOutlined, LinkOutlined, DatabaseOutlined } from '@ant-design/icons'; @@ -7,8 +7,6 @@ import type { AppOutletContext } from '@/components/AppLayout'; import type { ServicesStatus, ServicesConfig } from '@/types/api'; import { buildServiceUrl } from '@/lib/service-url'; -const BANNER_DISMISSED_KEY = 'nocodb-auth-banner-dismissed'; - export default function NocoDBPage() { const { setPageHeader } = useOutletContext(); const screens = Grid.useBreakpoint(); @@ -17,9 +15,9 @@ export default function NocoDBPage() { const [online, setOnline] = useState(null); const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); - const [bannerDismissed, setBannerDismissed] = useState( - () => localStorage.getItem(BANNER_DISMISSED_KEY) === 'true' - ); + const [iframeSrc, setIframeSrc] = useState(null); + const [authFailed, setAuthFailed] = useState(false); + const authAttempted = useRef(false); const fetchStatus = useCallback(async () => { try { @@ -44,7 +42,31 @@ export default function NocoDBPage() { ? buildServiceUrl(config.nocodbSubdomain, config.domain, config.nocodbPort) : null; + // Auto sign-in: fetch NocoDB auth token and navigate iframe to auth bridge + useEffect(() => { + if (!serviceUrl || !online || authAttempted.current) return; + authAttempted.current = true; + + (async () => { + try { + const res = await api.get<{ token: string }>('/services/nocodb-auth'); + if (res.data.token) { + // Navigate iframe to auth bridge (same origin as NocoDB) which sets the cookie + setIframeSrc(`${serviceUrl}/auth-bridge#${res.data.token}`); + return; + } + } catch { + // Auth endpoint unavailable — fall back to direct URL + } + setAuthFailed(true); + setIframeSrc(serviceUrl); + })(); + }, [serviceUrl, online]); + const handleRefresh = useCallback(() => { + authAttempted.current = false; + setIframeSrc(null); + setAuthFailed(false); fetchStatus(); }, [fetchStatus]); @@ -115,11 +137,11 @@ export default function NocoDBPage() { return (
- {!bannerDismissed && ( + {authFailed && ( - If the database browser appears blank, you may need to{' '} + Auto sign-in unavailable. You may need to{' '} sign in to NocoDB in a new tab {' '} @@ -129,23 +151,26 @@ export default function NocoDBPage() { type="info" showIcon closable - onClose={() => { - setBannerDismissed(true); - localStorage.setItem(BANNER_DISMISSED_KEY, 'true'); - }} + onClose={() => setAuthFailed(false)} style={{ borderRadius: 0, flexShrink: 0 }} /> )} -