From a7b431823e4fe5019287863e17ee57dc779c8a1e Mon Sep 17 00:00:00 2001 From: shinya Date: Tue, 1 Jul 2025 01:11:19 +0800 Subject: [PATCH] feat: dynamic site name --- Dockerfile | 20 +++++++++++--------- next.config.js | 1 + src/app/layout.tsx | 9 +++++++-- src/app/login/page.tsx | 4 +++- src/components/MobileHeader.tsx | 4 +++- src/components/Sidebar.tsx | 27 +++++++++++++++++---------- src/components/SiteProvider.tsx | 21 +++++++++++++++++++++ 7 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 src/components/SiteProvider.tsx diff --git a/Dockerfile b/Dockerfile index 4e6df33..097a644 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,9 @@ RUN find ./src -type f -name "route.ts" -print0 \ | xargs -0 sed -i "s/export const runtime = 'edge';/export const runtime = 'nodejs';/g" ENV DOCKER_ENV=true +# For Docker builds, force dynamic rendering to read runtime environment variables. +RUN sed -i "/const inter = Inter({ subsets: \['latin'] });/a export const dynamic = 'force-dynamic';" src/app/layout.tsx + # 生成生产构建 RUN pnpm run build @@ -42,18 +45,17 @@ ENV NODE_ENV=production ENV PORT=3000 ENV DOCKER_ENV=true -# 复制必要文件 -COPY --from=builder /app/public ./public -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/package.json ./package.json -COPY --from=builder /app/next.config.js ./next.config.js -COPY --from=builder /app/config.json ./config.json +# 从构建器中复制 standalone 输出 +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +# 从构建器中复制 public 和 .next/static 目录 +COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /app/config.json ./config.json # 切换到非特权用户 USER nextjs EXPOSE 3000 -# 使用 next binary 启动 -CMD ["node_modules/.bin/next", "start", "-H", "0.0.0.0", "-p", "3000"] \ No newline at end of file +# 使用 node 直接运行 server.js +CMD ["node", "server.js"] \ No newline at end of file diff --git a/next.config.js b/next.config.js index 0225bec..7925073 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,7 @@ /** @type {import('next').NextConfig} */ /* eslint-disable @typescript-eslint/no-var-requires */ const nextConfig = { + output: 'standalone', eslint: { dirs: ['src'], }, diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ef82afc..30549e4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,12 +4,13 @@ import { Inter } from 'next/font/google'; import './globals.css'; import AuthProvider from '../components/AuthProvider'; +import { SiteProvider } from '../components/SiteProvider'; import { ThemeProvider } from '../components/ThemeProvider'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: 'MoonTV', + title: process.env.SITE_NAME || 'MoonTV', description: '影视聚合', manifest: '/manifest.json', }; @@ -27,6 +28,8 @@ export default function RootLayout({ }: { children: React.ReactNode; }) { + const siteName = process.env.SITE_NAME || 'MoonTV'; + return ( - {children} + + {children} + diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 75f2fc9..9cb8ac7 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -3,6 +3,7 @@ import { useRouter, useSearchParams } from 'next/navigation'; import { Suspense, useState } from 'react'; +import { useSite } from '@/components/SiteProvider'; import { ThemeToggle } from '@/components/ThemeToggle'; function LoginPageClient() { @@ -11,6 +12,7 @@ function LoginPageClient() { const [password, setPassword] = useState(''); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); + const { siteName } = useSite(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -52,7 +54,7 @@ function LoginPageClient() {

- MoonTV + {siteName}

diff --git a/src/components/MobileHeader.tsx b/src/components/MobileHeader.tsx index c5a3f99..93e0024 100644 --- a/src/components/MobileHeader.tsx +++ b/src/components/MobileHeader.tsx @@ -2,9 +2,11 @@ import Link from 'next/link'; +import { useSite } from './SiteProvider'; import { ThemeToggle } from './ThemeToggle'; const MobileHeader = () => { + const { siteName } = useSite(); return (
@@ -12,7 +14,7 @@ const MobileHeader = () => { href='/' className='text-2xl font-bold text-green-600 tracking-tight hover:opacity-80 transition-opacity' > - MoonTV + {siteName}
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 1a81652..3679ea5 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Clover, Film, @@ -22,6 +24,8 @@ import { useState, } from 'react'; +import { useSite } from './SiteProvider'; + interface SidebarContextType { isCollapsed: boolean; } @@ -33,16 +37,19 @@ const SidebarContext = createContext({ export const useSidebar = () => useContext(SidebarContext); // 可替换为你自己的 logo 图片 -const Logo = () => ( - - - MoonTV - - -); +const Logo = () => { + const { siteName } = useSite(); + return ( + + + {siteName} + + + ); +}; interface SidebarProps { onToggle?: (collapsed: boolean) => void; diff --git a/src/components/SiteProvider.tsx b/src/components/SiteProvider.tsx new file mode 100644 index 0000000..283b962 --- /dev/null +++ b/src/components/SiteProvider.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { createContext, ReactNode, useContext } from 'react'; + +const SiteContext = createContext<{ siteName: string }>({ + siteName: 'MoonTV', // Default value +}); + +export const useSite = () => useContext(SiteContext); + +export function SiteProvider({ + children, + siteName, +}: { + children: ReactNode; + siteName: string; +}) { + return ( + {children} + ); +}