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 }} /> )} -