From ec683dd0317cae67d8bc00f4c9a0ba7eb97a5b4e Mon Sep 17 00:00:00 2001 From: VibhavSetlur Date: Wed, 20 May 2026 11:48:36 -0500 Subject: [PATCH 1/3] refactor: simplify maintenance banner to use NEXT_PUBLIC env var directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed /api/maintenance endpoint — it was blocked by nginx routing. OutageBanner now checks NEXT_PUBLIC_MAINTENANCE_MODE at build time. Sam just sets NEXT_PUBLIC_MAINTENANCE_MODE=true in the env, rebuilds, and the banner appears — no nginx config changes needed. --- app/api/maintenance/route.ts | 57 ---------------------------------- components/ui/OutageBanner.tsx | 23 +++----------- 2 files changed, 4 insertions(+), 76 deletions(-) delete mode 100644 app/api/maintenance/route.ts diff --git a/app/api/maintenance/route.ts b/app/api/maintenance/route.ts deleted file mode 100644 index ee153a71..00000000 --- a/app/api/maintenance/route.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Maintenance mode status endpoint. - * - * Returns whether the site is in maintenance mode and an optional message. - * Operators can toggle this by: - * 1. Creating /tmp/maintenance.json with {"enabled":true,"message":"..."} - * (file is checked at runtime, no restart needed) - * 2. Setting MAINTENANCE_MODE env var (requires container restart) - */ -import { NextResponse } from 'next/server'; -import fs from 'fs'; - -const NO_CACHE_HEADERS = { 'Cache-Control': 'no-store, max-age=0' }; - -interface MaintenanceStatus { - enabled: boolean; - message: string; -} - -function readFileStatus(): MaintenanceStatus | null { - try { - const raw = fs.readFileSync('/tmp/maintenance.json', 'utf-8'); - const data = JSON.parse(raw); - if (data && typeof data.enabled === 'boolean') { - return { - enabled: data.enabled, - message: typeof data.message === 'string' ? data.message : '', - }; - } - } catch { - // File doesn't exist or invalid — ignore - } - return null; -} - -export async function GET(): Promise { - // 1. Check file-based toggle first (runtime toggle, no restart needed) - const fileStatus = readFileStatus(); - if (fileStatus) { - return NextResponse.json(fileStatus, { headers: NO_CACHE_HEADERS }); - } - - // 2. Fall back to env var (requires container restart) - const envEnabled = process.env.MAINTENANCE_MODE === 'true' || process.env.MAINTENANCE_MODE === '1'; - if (envEnabled) { - return NextResponse.json({ - enabled: true, - message: process.env.MAINTENANCE_MESSAGE || 'Site is undergoing maintenance. Please check back shortly.', - }, { headers: NO_CACHE_HEADERS }); - } - - // 3. Default: not in maintenance - return NextResponse.json( - { enabled: false, message: '' }, - { headers: NO_CACHE_HEADERS }, - ); -} diff --git a/components/ui/OutageBanner.tsx b/components/ui/OutageBanner.tsx index 113c65ff..3e4b9df5 100644 --- a/components/ui/OutageBanner.tsx +++ b/components/ui/OutageBanner.tsx @@ -1,29 +1,14 @@ 'use client'; -import { useEffect, useState } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Alert from '@mui/material/Alert'; -interface MaintenanceStatus { - enabled: boolean; - message: string; -} +const MAINTENANCE_ENABLED = process.env.NEXT_PUBLIC_MAINTENANCE_MODE === 'true'; +const MAINTENANCE_MESSAGE = process.env.NEXT_PUBLIC_MAINTENANCE_MESSAGE || 'Site is undergoing maintenance. Please check back shortly.'; export default function OutageBanner() { - const [status, setStatus] = useState(null); - - useEffect(() => { - fetch('/api/maintenance', { cache: 'no-store' }) - .then((res) => { - if (!res.ok) throw new Error(`HTTP ${res.status}`); - return res.json(); - }) - .then((data: MaintenanceStatus) => setStatus(data)) - .catch(() => setStatus({ enabled: false, message: '' })); - }, []); - - if (!status || !status.enabled) return null; + if (!MAINTENANCE_ENABLED) return null; return ( @@ -37,7 +22,7 @@ export default function OutageBanner() { }} > - {status.message || 'Site is undergoing maintenance. Please check back shortly.'} + {MAINTENANCE_MESSAGE} From 5443e528ec101d4e209a29865a5814cabcba465c Mon Sep 17 00:00:00 2001 From: VibhavSetlur Date: Wed, 20 May 2026 11:50:32 -0500 Subject: [PATCH 2/3] docs: add NEXT_PUBLIC_MAINTENANCE_MODE vars to .env.example --- .env.example | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 57b040e8..d9d95662 100644 --- a/.env.example +++ b/.env.example @@ -202,4 +202,11 @@ NEXT_PUBLIC_DEPLOY_DATE= # Get a token from https://p3.theseed.org/user/authenticate # # Optional. Not required for application functionality. -PATRIC_TOKEN= \ No newline at end of file +PATRIC_TOKEN= + +# Maintenance mode banner +# Set to 'true' and rebuild to display a yellow outage banner on every page. +# Optional. Defaults to disabled. +NEXT_PUBLIC_MAINTENANCE_MODE=false +# Optional custom message shown in the banner when maintenance mode is enabled. +NEXT_PUBLIC_MAINTENANCE_MESSAGE= \ No newline at end of file From 6e92508050c568e49847304993f586dcaa947ec8 Mon Sep 17 00:00:00 2001 From: VibhavSetlur Date: Wed, 20 May 2026 11:54:38 -0500 Subject: [PATCH 3/3] fix: comment out unused comment state variables to clear lint warnings --- app/(reference-data)/biochem/reactions/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/(reference-data)/biochem/reactions/page.tsx b/app/(reference-data)/biochem/reactions/page.tsx index f93a997e..1da08638 100644 --- a/app/(reference-data)/biochem/reactions/page.tsx +++ b/app/(reference-data)/biochem/reactions/page.tsx @@ -211,8 +211,8 @@ export default function ReactionsPage() { }, [handleFilterModelChange]); // Modal state - const [commentModalOpen, setCommentModalOpen] = useState(false); - const [commentReactionId, setCommentReactionId] = useState(null); + /* const [commentModalOpen, setCommentModalOpen] = useState(false); */ + /* const [commentReactionId, setCommentReactionId] = useState(null); */ const [exportModalOpen, setExportModalOpen] = useState(false); const [pathwaysModalOpen, setPathwaysModalOpen] = useState(false); const [pathwaysModalReactionId, setPathwaysModalReactionId] = useState(null); @@ -250,10 +250,10 @@ export default function ReactionsPage() { })); }, [filterModel]); - const handleOpenComment = useCallback((id: string) => { + /* const handleOpenComment = useCallback((id: string) => { setCommentReactionId(id); setCommentModalOpen(true); - }, []); + }, []); */ const handleOpenPathwaysModal = useCallback((reaction: Reaction) => { setPathwaysModalReactionId(reaction.id); @@ -375,7 +375,7 @@ export default function ReactionsPage() { return row.ontology; }, }, - ], [handleOpenComment, handleOpenPathwaysModal]); + ], [handleOpenPathwaysModal]); const queryOpts = useMemo(() => ({ limit: paginationModel.pageSize,