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
This commit is contained in:
parent
0a8e1fe46b
commit
36b709b911
@ -222,6 +222,8 @@ GITEA_URL=http://gitea-changemaker:3000
|
|||||||
GITEA_PORT=3030
|
GITEA_PORT=3030
|
||||||
GITEA_WEB_PORT=3030
|
GITEA_WEB_PORT=3030
|
||||||
GITEA_SSH_PORT=2222
|
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_TYPE=mysql
|
||||||
GITEA_DB_HOST=gitea-db:3306
|
GITEA_DB_HOST=gitea-db:3306
|
||||||
GITEA_DB_NAME=gitea
|
GITEA_DB_NAME=gitea
|
||||||
|
|||||||
@ -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 { useOutletContext } from 'react-router-dom';
|
||||||
import { Button, Space, Badge, Spin, Grid, Result, Alert } from 'antd';
|
import { Button, Space, Badge, Spin, Grid, Result, Alert } from 'antd';
|
||||||
import { ReloadOutlined, LinkOutlined, DatabaseOutlined } from '@ant-design/icons';
|
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 type { ServicesStatus, ServicesConfig } from '@/types/api';
|
||||||
import { buildServiceUrl } from '@/lib/service-url';
|
import { buildServiceUrl } from '@/lib/service-url';
|
||||||
|
|
||||||
const BANNER_DISMISSED_KEY = 'nocodb-auth-banner-dismissed';
|
|
||||||
|
|
||||||
export default function NocoDBPage() {
|
export default function NocoDBPage() {
|
||||||
const { setPageHeader } = useOutletContext<AppOutletContext>();
|
const { setPageHeader } = useOutletContext<AppOutletContext>();
|
||||||
const screens = Grid.useBreakpoint();
|
const screens = Grid.useBreakpoint();
|
||||||
@ -17,9 +15,9 @@ export default function NocoDBPage() {
|
|||||||
const [online, setOnline] = useState<boolean | null>(null);
|
const [online, setOnline] = useState<boolean | null>(null);
|
||||||
const [config, setConfig] = useState<ServicesConfig | null>(null);
|
const [config, setConfig] = useState<ServicesConfig | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [bannerDismissed, setBannerDismissed] = useState(
|
const [iframeSrc, setIframeSrc] = useState<string | null>(null);
|
||||||
() => localStorage.getItem(BANNER_DISMISSED_KEY) === 'true'
|
const [authFailed, setAuthFailed] = useState(false);
|
||||||
);
|
const authAttempted = useRef(false);
|
||||||
|
|
||||||
const fetchStatus = useCallback(async () => {
|
const fetchStatus = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -44,7 +42,31 @@ export default function NocoDBPage() {
|
|||||||
? buildServiceUrl(config.nocodbSubdomain, config.domain, config.nocodbPort)
|
? buildServiceUrl(config.nocodbSubdomain, config.domain, config.nocodbPort)
|
||||||
: null;
|
: 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(() => {
|
const handleRefresh = useCallback(() => {
|
||||||
|
authAttempted.current = false;
|
||||||
|
setIframeSrc(null);
|
||||||
|
setAuthFailed(false);
|
||||||
fetchStatus();
|
fetchStatus();
|
||||||
}, [fetchStatus]);
|
}, [fetchStatus]);
|
||||||
|
|
||||||
@ -115,11 +137,11 @@ export default function NocoDBPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column' }}>
|
<div style={{ height: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column' }}>
|
||||||
{!bannerDismissed && (
|
{authFailed && (
|
||||||
<Alert
|
<Alert
|
||||||
message={
|
message={
|
||||||
<>
|
<>
|
||||||
If the database browser appears blank, you may need to{' '}
|
Auto sign-in unavailable. You may need to{' '}
|
||||||
<a href={serviceUrl} target="_blank" rel="noopener noreferrer">
|
<a href={serviceUrl} target="_blank" rel="noopener noreferrer">
|
||||||
sign in to NocoDB in a new tab
|
sign in to NocoDB in a new tab
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
@ -129,23 +151,26 @@ export default function NocoDBPage() {
|
|||||||
type="info"
|
type="info"
|
||||||
showIcon
|
showIcon
|
||||||
closable
|
closable
|
||||||
onClose={() => {
|
onClose={() => setAuthFailed(false)}
|
||||||
setBannerDismissed(true);
|
|
||||||
localStorage.setItem(BANNER_DISMISSED_KEY, 'true');
|
|
||||||
}}
|
|
||||||
style={{ borderRadius: 0, flexShrink: 0 }}
|
style={{ borderRadius: 0, flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<iframe
|
{iframeSrc ? (
|
||||||
src={serviceUrl}
|
<iframe
|
||||||
style={{
|
src={iframeSrc}
|
||||||
width: '100%',
|
style={{
|
||||||
flex: 1,
|
width: '100%',
|
||||||
border: 'none',
|
flex: 1,
|
||||||
display: 'block',
|
border: 'none',
|
||||||
}}
|
display: 'block',
|
||||||
title="NocoDB"
|
}}
|
||||||
/>
|
title="NocoDB"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', padding: 80 }}>
|
||||||
|
<Spin size="large" tip="Signing in to NocoDB..." />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,8 @@ const envSchema = z.object({
|
|||||||
|
|
||||||
// Platform Services (NocoDB, n8n, Gitea)
|
// Platform Services (NocoDB, n8n, Gitea)
|
||||||
NOCODB_URL: z.string().default('http://changemaker-v2-nocodb:8080'),
|
NOCODB_URL: z.string().default('http://changemaker-v2-nocodb:8080'),
|
||||||
|
NC_ADMIN_EMAIL: z.string().default(''),
|
||||||
|
NC_ADMIN_PASSWORD: z.string().default(''),
|
||||||
NOCODB_PORT: z.coerce.number().default(8091),
|
NOCODB_PORT: z.coerce.number().default(8091),
|
||||||
NOCODB_EMBED_PORT: z.coerce.number().default(8881),
|
NOCODB_EMBED_PORT: z.coerce.number().default(8881),
|
||||||
N8N_URL: z.string().default('http://n8n-changemaker:5678'),
|
N8N_URL: z.string().default('http://n8n-changemaker:5678'),
|
||||||
|
|||||||
@ -393,17 +393,26 @@ async function autoSetupIfNeeded(): Promise<{ alreadyComplete: boolean; success:
|
|||||||
// DB might not be ready yet
|
// DB might not be ready yet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for Gitea to be available (up to 3 retries, 15s apart)
|
// Wait for Gitea to be available and admin user to exist (up to 6 retries, 10s apart).
|
||||||
|
// The gitea-init.sh script creates the admin user after migrations,
|
||||||
|
// so we need to wait for both Gitea web AND admin auth to be ready.
|
||||||
let giteaReady = false;
|
let giteaReady = false;
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 6; i++) {
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||||
try {
|
try {
|
||||||
|
// Check if Gitea is online
|
||||||
const res = await fetch(`${env.GITEA_URL}/api/v1/version`, { signal: controller.signal });
|
const res = await fetch(`${env.GITEA_URL}/api/v1/version`, { signal: controller.signal });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
giteaReady = true;
|
// Also verify admin auth works (user may not exist yet if init script is still running)
|
||||||
break;
|
try {
|
||||||
|
await giteaBasicRequest<{ login: string }>('GET', '/user', 'admin', password);
|
||||||
|
giteaReady = true;
|
||||||
|
break;
|
||||||
|
} catch {
|
||||||
|
// Admin user not ready yet — gitea-init.sh may still be running
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
@ -411,14 +420,14 @@ async function autoSetupIfNeeded(): Promise<{ alreadyComplete: boolean; success:
|
|||||||
} catch {
|
} catch {
|
||||||
// Not ready yet
|
// Not ready yet
|
||||||
}
|
}
|
||||||
if (i < 2) {
|
if (i < 5) {
|
||||||
logger.info(`Gitea auto-setup: waiting for Gitea to be ready (attempt ${i + 1}/3)...`);
|
logger.info(`Gitea auto-setup: waiting for Gitea + admin user (attempt ${i + 1}/6)...`);
|
||||||
await new Promise(r => setTimeout(r, 15000));
|
await new Promise(r => setTimeout(r, 10000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!giteaReady) {
|
if (!giteaReady) {
|
||||||
return { alreadyComplete: false, success: false, error: 'Gitea not reachable after 3 attempts' };
|
return { alreadyComplete: false, success: false, error: 'Gitea not reachable or admin user not ready after 6 attempts' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run setup
|
// Run setup
|
||||||
|
|||||||
@ -64,6 +64,48 @@ router.get(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// GET /api/services/nocodb-auth — proxy NocoDB signin to get an auth token for iframe auto-login
|
||||||
|
router.get(
|
||||||
|
'/nocodb-auth',
|
||||||
|
async (_req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (!env.NC_ADMIN_EMAIL || !env.NC_ADMIN_PASSWORD) {
|
||||||
|
res.status(503).json({ error: 'NocoDB admin credentials not configured' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${env.NOCODB_URL}/api/v1/auth/user/signin`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ email: env.NC_ADMIN_EMAIL, password: env.NC_ADMIN_PASSWORD }),
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
res.status(502).json({ error: 'NocoDB authentication failed' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as { token?: string };
|
||||||
|
if (!data.token) {
|
||||||
|
res.status(502).json({ error: 'No token in NocoDB response' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ token: data.token });
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('NocoDB auth proxy failed', err);
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// GET /api/services/config — return public-facing port numbers + subdomain info for iframe URLs
|
// GET /api/services/config — return public-facing port numbers + subdomain info for iframe URLs
|
||||||
router.get(
|
router.get(
|
||||||
'/config',
|
'/config',
|
||||||
|
|||||||
16
config.sh
16
config.sh
@ -852,19 +852,29 @@ configure_features() {
|
|||||||
|
|
||||||
if [[ "$NON_INTERACTIVE" == "false" ]]; then
|
if [[ "$NON_INTERACTIVE" == "false" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
info "Gitea auto-setup will create the API token, repos, and OAuth app automatically."
|
info "Gitea will be auto-initialized on first boot (no manual install wizard)."
|
||||||
info "You need to provide the Gitea admin password (set during Gitea's first-run install)."
|
info "The admin user and docs comment system will be configured automatically."
|
||||||
|
info "Provide a password for the Gitea admin account:"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
read -srp " Gitea admin password [leave blank to set up later via admin GUI]: " gitea_admin_pw
|
read -srp " Gitea admin password [leave blank to set up later via admin GUI]: " gitea_admin_pw
|
||||||
echo ""
|
echo ""
|
||||||
if [[ -n "$gitea_admin_pw" ]]; then
|
if [[ -n "$gitea_admin_pw" ]]; then
|
||||||
|
update_env_var "GITEA_ADMIN_USER" "admin"
|
||||||
update_env_var "GITEA_ADMIN_PASSWORD" "$gitea_admin_pw"
|
update_env_var "GITEA_ADMIN_PASSWORD" "$gitea_admin_pw"
|
||||||
update_env_var "GITEA_COMMENTS_REPO_OWNER" "admin"
|
update_env_var "GITEA_COMMENTS_REPO_OWNER" "admin"
|
||||||
success "Gitea admin password saved — auto-setup will run on next start"
|
success "Gitea admin user + docs comment auto-setup configured"
|
||||||
else
|
else
|
||||||
info "No password provided. Run Gitea Setup from the admin GUI after first start."
|
info "No password provided. Run Gitea Setup from the admin GUI after first start."
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
# Non-interactive: reuse admin password for Gitea
|
||||||
|
local gitea_pw="${NI_ADMIN_PASSWORD:-}"
|
||||||
|
if [[ -n "$gitea_pw" ]]; then
|
||||||
|
update_env_var "GITEA_ADMIN_USER" "admin"
|
||||||
|
update_env_var "GITEA_ADMIN_PASSWORD" "$gitea_pw"
|
||||||
|
update_env_var "GITEA_COMMENTS_REPO_OWNER" "admin"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
DOCS_COMMENTS_ENABLED="no"
|
DOCS_COMMENTS_ENABLED="no"
|
||||||
|
|||||||
@ -128,6 +128,16 @@ services:
|
|||||||
- GITEA_REGISTRY=${GITEA_REGISTRY:-gitea.bnkops.com/admin}
|
- GITEA_REGISTRY=${GITEA_REGISTRY:-gitea.bnkops.com/admin}
|
||||||
- GITEA_REGISTRY_USER=${GITEA_REGISTRY_USER:-}
|
- GITEA_REGISTRY_USER=${GITEA_REGISTRY_USER:-}
|
||||||
- GITEA_REGISTRY_PASS=${GITEA_REGISTRY_PASS:-}
|
- GITEA_REGISTRY_PASS=${GITEA_REGISTRY_PASS:-}
|
||||||
|
# Gitea (docs comments, version history, auto-setup)
|
||||||
|
- GITEA_URL=${GITEA_URL:-http://gitea-changemaker:3000}
|
||||||
|
- GITEA_API_TOKEN=${GITEA_API_TOKEN:-}
|
||||||
|
- GITEA_ADMIN_PASSWORD=${GITEA_ADMIN_PASSWORD:-}
|
||||||
|
- GITEA_DOCS_REPO=${GITEA_DOCS_REPO:-admin/changemaker.lite}
|
||||||
|
- GITEA_DOCS_PREFIX=${GITEA_DOCS_PREFIX:-mkdocs/docs}
|
||||||
|
- GITEA_DOCS_BRANCH=${GITEA_DOCS_BRANCH:-v2}
|
||||||
|
# NocoDB credentials (for auto sign-in proxy)
|
||||||
|
- NC_ADMIN_EMAIL=${NC_ADMIN_EMAIL:-admin@cmlite.org}
|
||||||
|
- NC_ADMIN_PASSWORD=${NC_ADMIN_PASSWORD:-}
|
||||||
# GeoIP (MaxMind GeoLite2)
|
# GeoIP (MaxMind GeoLite2)
|
||||||
- MAXMIND_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID:-}
|
- MAXMIND_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID:-}
|
||||||
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-}
|
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-}
|
||||||
@ -688,11 +698,19 @@ services:
|
|||||||
- GITEA__service__ENABLE_REVERSE_PROXY_EMAIL=false
|
- GITEA__service__ENABLE_REVERSE_PROXY_EMAIL=false
|
||||||
- GITEA__service__REVERSE_PROXY_AUTHENTICATION_HEADER=X-WEBAUTH-USER
|
- GITEA__service__REVERSE_PROXY_AUTHENTICATION_HEADER=X-WEBAUTH-USER
|
||||||
- GITEA__service__REQUIRE_SIGNIN_VIEW=true
|
- GITEA__service__REQUIRE_SIGNIN_VIEW=true
|
||||||
|
# Skip installation wizard — admin user created by gitea-init.sh
|
||||||
|
- GITEA__security__INSTALL_LOCK=true
|
||||||
|
# Admin user creation (used by gitea-init.sh on first boot)
|
||||||
|
- GITEA_ADMIN_USER=${GITEA_ADMIN_USER:-admin}
|
||||||
|
- GITEA_ADMIN_PASSWORD=${GITEA_ADMIN_PASSWORD:-}
|
||||||
|
- GITEA_ADMIN_EMAIL=${INITIAL_ADMIN_EMAIL:-admin@cmlite.org}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- gitea-data:/data
|
- gitea-data:/data
|
||||||
|
- ./scripts/gitea-init.sh:/custom/gitea-init.sh:ro
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
command: ["/bin/sh", "/custom/gitea-init.sh"]
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${GITEA_WEB_PORT:-3030}:3000"
|
- "127.0.0.1:${GITEA_WEB_PORT:-3030}:3000"
|
||||||
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
||||||
|
|||||||
@ -136,6 +136,9 @@ services:
|
|||||||
- GITEA_DOCS_REPO=${GITEA_DOCS_REPO:-admin/changemaker.lite}
|
- GITEA_DOCS_REPO=${GITEA_DOCS_REPO:-admin/changemaker.lite}
|
||||||
- GITEA_DOCS_PREFIX=${GITEA_DOCS_PREFIX:-mkdocs/docs}
|
- GITEA_DOCS_PREFIX=${GITEA_DOCS_PREFIX:-mkdocs/docs}
|
||||||
- GITEA_DOCS_BRANCH=${GITEA_DOCS_BRANCH:-v2}
|
- GITEA_DOCS_BRANCH=${GITEA_DOCS_BRANCH:-v2}
|
||||||
|
# NocoDB credentials (for auto sign-in proxy)
|
||||||
|
- NC_ADMIN_EMAIL=${NC_ADMIN_EMAIL:-admin@cmlite.org}
|
||||||
|
- NC_ADMIN_PASSWORD=${NC_ADMIN_PASSWORD:-}
|
||||||
# GeoIP (MaxMind GeoLite2)
|
# GeoIP (MaxMind GeoLite2)
|
||||||
- MAXMIND_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID:-}
|
- MAXMIND_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID:-}
|
||||||
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-}
|
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-}
|
||||||
@ -714,11 +717,19 @@ services:
|
|||||||
- GITEA__service__ENABLE_REVERSE_PROXY_EMAIL=false
|
- GITEA__service__ENABLE_REVERSE_PROXY_EMAIL=false
|
||||||
- GITEA__service__REVERSE_PROXY_AUTHENTICATION_HEADER=X-WEBAUTH-USER
|
- GITEA__service__REVERSE_PROXY_AUTHENTICATION_HEADER=X-WEBAUTH-USER
|
||||||
- GITEA__service__REQUIRE_SIGNIN_VIEW=true
|
- GITEA__service__REQUIRE_SIGNIN_VIEW=true
|
||||||
|
# Skip installation wizard — admin user created by gitea-init.sh
|
||||||
|
- GITEA__security__INSTALL_LOCK=true
|
||||||
|
# Admin user creation (used by gitea-init.sh on first boot)
|
||||||
|
- GITEA_ADMIN_USER=${GITEA_ADMIN_USER:-admin}
|
||||||
|
- GITEA_ADMIN_PASSWORD=${GITEA_ADMIN_PASSWORD:-}
|
||||||
|
- GITEA_ADMIN_EMAIL=${INITIAL_ADMIN_EMAIL:-admin@cmlite.org}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- gitea-data:/data
|
- gitea-data:/data
|
||||||
|
- ./scripts/gitea-init.sh:/custom/gitea-init.sh:ro
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
command: ["/bin/sh", "/custom/gitea-init.sh"]
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${GITEA_WEB_PORT:-3030}:3000"
|
- "127.0.0.1:${GITEA_WEB_PORT:-3030}:3000"
|
||||||
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
||||||
|
|||||||
@ -78,6 +78,14 @@ server {
|
|||||||
server_name db.${DOMAIN};
|
server_name db.${DOMAIN};
|
||||||
add_header Content-Security-Policy "frame-ancestors 'self' app.${DOMAIN}" always;
|
add_header Content-Security-Policy "frame-ancestors 'self' app.${DOMAIN}" always;
|
||||||
|
|
||||||
|
# Auth bridge for iframe auto-sign-in (token passed via URL hash, never sent to server)
|
||||||
|
location = /auth-bridge {
|
||||||
|
default_type text/html;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
add_header Content-Security-Policy "frame-ancestors 'self' app.${DOMAIN}" always;
|
||||||
|
return 200 '<!DOCTYPE html><html><head><meta charset="utf-8"><script>(function(){var t=location.hash.substring(1);if(!t){document.body.innerText="No token";return;}document.cookie="nocodb-token="+encodeURIComponent(t)+";path=/;SameSite=Lax;max-age=86400";try{localStorage.setItem("nocodb-token",JSON.stringify(t));}catch(e){}window.location.replace("/dashboard/");})()</script></head><body>Signing in...</body></html>';
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
set $upstream_nocodb http://changemaker-v2-nocodb:8080;
|
set $upstream_nocodb http://changemaker-v2-nocodb:8080;
|
||||||
proxy_pass $upstream_nocodb;
|
proxy_pass $upstream_nocodb;
|
||||||
@ -287,6 +295,14 @@ server {
|
|||||||
|
|
||||||
server {
|
server {
|
||||||
listen ${NOCODB_EMBED_PORT};
|
listen ${NOCODB_EMBED_PORT};
|
||||||
|
|
||||||
|
# Auth bridge for iframe auto-sign-in (localhost/dev variant)
|
||||||
|
location = /auth-bridge {
|
||||||
|
default_type text/html;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
return 200 '<!DOCTYPE html><html><head><meta charset="utf-8"><script>(function(){var t=location.hash.substring(1);if(!t){document.body.innerText="No token";return;}document.cookie="nocodb-token="+encodeURIComponent(t)+";path=/;SameSite=Lax;max-age=86400";try{localStorage.setItem("nocodb-token",JSON.stringify(t));}catch(e){}window.location.replace("/dashboard/");})()</script></head><body>Signing in...</body></html>';
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
set $upstream_nocodb http://changemaker-v2-nocodb:8080;
|
set $upstream_nocodb http://changemaker-v2-nocodb:8080;
|
||||||
proxy_pass $upstream_nocodb;
|
proxy_pass $upstream_nocodb;
|
||||||
|
|||||||
@ -103,7 +103,7 @@ cp "$PROJECT_DIR/api/prisma/init-nocodb-db.sh" "$STAGE_DIR/scripts/"
|
|||||||
cp "$PROJECT_DIR/api/prisma/init-gancio-db.sh" "$STAGE_DIR/scripts/"
|
cp "$PROJECT_DIR/api/prisma/init-gancio-db.sh" "$STAGE_DIR/scripts/"
|
||||||
|
|
||||||
# Runtime scripts
|
# Runtime scripts
|
||||||
for script in nocodb-init.sh mkdocs-entrypoint.sh backup.sh \
|
for script in nocodb-init.sh gitea-init.sh mkdocs-entrypoint.sh backup.sh \
|
||||||
upgrade.sh upgrade-check.sh upgrade-watcher.sh \
|
upgrade.sh upgrade-check.sh upgrade-watcher.sh \
|
||||||
uninstall.sh test-deployment.sh; do
|
uninstall.sh test-deployment.sh; do
|
||||||
if [[ -f "$PROJECT_DIR/scripts/$script" ]]; then
|
if [[ -f "$PROJECT_DIR/scripts/$script" ]]; then
|
||||||
|
|||||||
54
scripts/gitea-init.sh
Executable file
54
scripts/gitea-init.sh
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# =============================================================================
|
||||||
|
# Gitea Initialization Script
|
||||||
|
# =============================================================================
|
||||||
|
# Replaces the default CMD in the Gitea Docker container.
|
||||||
|
# Runs database migrations, creates the admin user (if credentials are provided
|
||||||
|
# and the user doesn't already exist), then starts the Gitea web server.
|
||||||
|
#
|
||||||
|
# This script is exec'd by /usr/bin/entrypoint, which has already:
|
||||||
|
# - Set up UID/GID
|
||||||
|
# - Created directories with correct permissions
|
||||||
|
# - Converted GITEA__* env vars into /data/gitea/conf/app.ini
|
||||||
|
# =============================================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PREFIX="[gitea-init]"
|
||||||
|
log() { echo "$PREFIX $1"; }
|
||||||
|
|
||||||
|
# --- Step 1: Run database migrations ---
|
||||||
|
log "Running database migrations..."
|
||||||
|
MIGRATE_OK=false
|
||||||
|
for i in $(seq 1 10); do
|
||||||
|
if gitea migrate 2>&1; then
|
||||||
|
MIGRATE_OK=true
|
||||||
|
log "Migrations complete"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
log "Waiting for database... (attempt $i/10)"
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$MIGRATE_OK" = false ]; then
|
||||||
|
log "WARNING: Migrations may not have completed — starting anyway"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 2: Create admin user if credentials provided ---
|
||||||
|
if [ -n "$GITEA_ADMIN_USER" ] && [ -n "$GITEA_ADMIN_PASSWORD" ] && [ -n "$GITEA_ADMIN_EMAIL" ]; then
|
||||||
|
log "Creating admin user '${GITEA_ADMIN_USER}'..."
|
||||||
|
if gitea admin user create --admin \
|
||||||
|
--username "$GITEA_ADMIN_USER" \
|
||||||
|
--password "$GITEA_ADMIN_PASSWORD" \
|
||||||
|
--email "$GITEA_ADMIN_EMAIL" \
|
||||||
|
--must-change-password false 2>&1; then
|
||||||
|
log "Admin user created successfully"
|
||||||
|
else
|
||||||
|
log "Admin user already exists (or creation skipped)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "No GITEA_ADMIN_USER/PASSWORD/EMAIL set — skipping admin creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 3: Start Gitea web server ---
|
||||||
|
log "Starting Gitea web server..."
|
||||||
|
exec gitea web
|
||||||
Loading…
x
Reference in New Issue
Block a user