You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next+Express全栈应用中实现可后台配置的站点维护模式

站点维护模式实现方案(Next.js + Express)

一、维护状态的存储方案

1. 数据库持久化 + Redis缓存(最推荐)

你的顾虑完全合理:.env没法动态修改,直接读数据库又怕拖慢速度。用数据库存持久化状态、Redis存实时缓存是完美解决思路:

  • 后台面板修改维护状态时,先更新数据库(确保服务重启后状态不丢失),同时把最新状态同步到Redis(内存读写速度极快)
  • Express中间件和Next端都从Redis读取状态,既避免数据库频繁查询,又支持后台动态修改

代码示例:

  • Express后台更新接口(需加管理员鉴权):
// Express 后台设置维护状态接口
app.post('/api/admin/maintenance', async (req, res) => {
  const { enabled, message } = req.body;
  // 持久化到数据库(以Sequelize ORM为例)
  await MaintenanceConfig.upsert({ id: 1, enabled, message });
  // 同步到Redis,可设置过期时间防止脏数据
  await redis.set('maintenance_mode', JSON.stringify({ enabled, message }), 'EX', 86400);
  res.json({ success: true });
});
  • Express维护模式全局中间件:
// Express 全局维护中间件(跳过后台管理接口)
app.use(async (req, res, next) => {
  if (req.path.startsWith('/api/admin')) return next();
  
  const maintenanceStr = await redis.get('maintenance_mode');
  if (maintenanceStr) {
    const { enabled, message } = JSON.parse(maintenanceStr);
    if (enabled) {
      return res.status(503).json({ message: message || '站点维护中,请稍后再试' });
    }
  }
  next();
});

2. 仅数据库(小流量备选)

如果不想引入Redis,也可以直接用数据库,但要加内存缓存优化:

  • Express启动时读取一次维护状态到内存,后台修改时主动更新内存值
  • Next端用SWR/React Query做客户端缓存,避免每次页面加载都请求数据库

二、Next.js 端检查维护状态的性能优化

直接在_app.ts里同步调用API肯定会阻塞页面渲染,推荐两种优化方式:

1. 服务器端提前检查(getServerSideProps)

对需要SSR的页面,在getServerSideProps里提前检查维护状态,直接返回维护页面,不渲染正常内容:

// 页面组件的getServerSideProps
export async function getServerSideProps() {
  // 调用Express的状态接口(或直接连Redis读取)
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/maintenance/status`);
  const { enabled, message } = await res.json();
  
  if (enabled) {
    return {
      props: { maintenance: { enabled, message } }
    };
  }
  
  return { props: {} };
}

然后在_app.ts里判断:

function MyApp({ Component, pageProps }) {
  if (pageProps.maintenance?.enabled) {
    return <MaintenancePage message={pageProps.maintenance.message} />;
  }
  return <Component {...pageProps} />;
}

这种方式不会让用户看到正常页面的加载过程,体验更流畅。

2. 客户端异步检查 + 骨架屏

如果用SSG/ISR页面,没法用getServerSideProps,就在_app.ts里用useEffect异步请求,同时显示骨架屏避免空白:

import { useState, useEffect } from 'react';
import Skeleton from '../components/Skeleton';
import MaintenancePage from '../components/MaintenancePage';

function MyApp({ Component, pageProps }) {
  const [isMaintenance, setIsMaintenance] = useState(false);
  const [maintenanceMsg, setMaintenanceMsg] = useState('');
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    async function checkMaintenanceStatus() {
      try {
        // 用Next.js API路由做代理,避免跨域问题
        const res = await fetch('/api/maintenance/status');
        const data = await res.json();
        setIsMaintenance(data.enabled);
        setMaintenanceMsg(data.message || '站点维护中');
      } catch (err) {
        // 请求失败默认不进入维护模式
        setIsMaintenance(false);
      } finally {
        setIsLoading(false);
      }
    }
    checkMaintenanceStatus();
  }, []);

  if (isLoading) return <Skeleton />;
  if (isMaintenance) return <MaintenancePage message={maintenanceMsg} />;
  return <Component {...pageProps} />;
}

配合SWR设置30秒缓存过期时间,既能保证实时性,又能减少重复请求。

三、适配生产与开发环境

  • 开发环境:可以在.env里加默认值(比如MAINTENANCE_ENABLED=false),同时允许后台面板动态修改;另外给本地IP加白名单,避免开发时被自己设置的维护模式挡住
  • 生产环境:完全依赖Redis/数据库的动态配置,禁止用.env硬编码
  • 环境区分代码示例:
// Express 开发环境本地IP白名单
if (process.env.NODE_ENV === 'development') {
  app.use(async (req, res, next) => {
    // 本地IP跳过维护检查
    if (req.ip === '127.0.0.1' && !req.path.includes('/api/admin/maintenance')) {
      return next();
    }
    // 正常维护检查逻辑
    next();
  });
}

四、额外注意点

  • 后台管理接口必须跳过维护模式检查,不然管理员也没法修改状态
  • 维护页面的静态资源(CSS/JS/图片)要确保能正常访问,别把静态资源也拦截了
  • 可以给管理员IP加白名单,允许维护模式下正常访问站点调试

内容的提问来源于stack exchange,提问作者Aman Khubani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:57:06