diff --git a/README.md b/README.md index 46bb711..e211892 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,6 @@ Pull Bot 会反复触发无效的 PR 和垃圾邮件,严重干扰项目维护 | NEXT_PUBLIC_ENABLE_BLOCKAD | 开启智能去广告功能(实验性) | true / false | false | | NEXT_PUBLIC_SEARCH_MAX_PAGE | 搜索接口可拉取的最大页数 | 1-50 | 5 | | NEXT_PUBLIC_AGGREGATE_SEARCH_RESULT | 搜索结果默认是否按标题和年份聚合 | true / false | true | -| NEXT_PUBLIC_SITE_NAME | 站点名称 | 任意 string | MoonTV | ## 配置说明 @@ -205,10 +204,6 @@ MoonTV 支持标准的苹果 CMS V10 API 格式。 - 如因公开分享导致的任何法律问题,用户需自行承担责任 - 项目开发者不对用户的使用行为承担任何法律责任 -## Star History - -[![Star History Chart](https://api.star-history.com/svg?repos=senshinya/MoonTV&type=Date)](https://www.star-history.com/#senshinya/MoonTV&Date) - ## License [MIT](LICENSE) © 2025 MoonTV & Contributors diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 3b5d174..3974847 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -24,6 +24,41 @@ export async function POST(req: NextRequest) { ); } + // 登录成功:写入 HttpOnly Cookie + const res = NextResponse.json({ ok: true }); + res.cookies.set({ + name: 'password', + value: password, + httpOnly: true, + sameSite: 'lax', + secure: process.env.NODE_ENV === 'production', + maxAge: 60 * 60 * 24 * 30, // 30 天 + path: '/', + }); + + return res; + } catch (error) { + return NextResponse.json({ error: '服务器错误' }, { status: 500 }); + } +} + +// 使用 Cookie 校验登录状态 +export async function GET(req: NextRequest) { + try { + const result = process.env.PASSWORD; + + // 未设置 PASSWORD 则直接放行 + if (!result) { + return NextResponse.json({ ok: true }); + } + + const cookiePassword = req.cookies.get('password')?.value; + const matched = cookiePassword === result; + + if (!matched) { + return NextResponse.json({ ok: false }, { status: 401 }); + } + return NextResponse.json({ ok: true }); } catch (error) { return NextResponse.json({ error: '服务器错误' }, { status: 500 }); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index abf3d25..04f7d3b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,22 +3,15 @@ import { Inter } from 'next/font/google'; import './globals.css'; -import AuthProvider from '../components/AuthProvider'; -import { SiteNameProvider } from '../components/SiteNameContext'; import { ThemeProvider } from '../components/ThemeProvider'; const inter = Inter({ subsets: ['latin'] }); -export const dynamic = 'force-dynamic'; -export const runtime = 'edge'; - -export function generateMetadata(): Metadata { - return { - title: process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTV', - description: '影视聚合', - manifest: '/manifest.json', - }; -} +export const metadata: Metadata = { + title: 'MoonTV', + description: '影视聚合', + manifest: '/manifest.json', +}; export const viewport: Viewport = { width: 'device-width', @@ -33,24 +26,19 @@ export default function RootLayout({ }: { children: React.ReactNode; }) { - const siteName = process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTV'; - return ( - - - {children} - - + + {children} + ); diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index d7a0b27..5221c57 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -3,12 +3,9 @@ import { useRouter, useSearchParams } from 'next/navigation'; import { Suspense, useState } from 'react'; -import { useSiteName } from '@/components/SiteNameContext'; import { ThemeToggle } from '@/components/ThemeToggle'; -export const dynamic = 'force-dynamic'; - -function LoginPageClient({ siteName }: { siteName: string }) { +function LoginPageClient() { const router = useRouter(); const searchParams = useSearchParams(); const [password, setPassword] = useState(''); @@ -30,11 +27,6 @@ function LoginPageClient({ siteName }: { siteName: string }) { }); if (res.ok) { - // 保存密码以供后续请求使用 - if (typeof window !== 'undefined') { - localStorage.setItem('password', password); - } - const redirect = searchParams.get('redirect') || '/'; router.replace(redirect); } else if (res.status === 401) { @@ -55,7 +47,7 @@ function LoginPageClient({ siteName }: { siteName: string }) {

- {siteName} + MoonTV

@@ -91,10 +83,9 @@ function LoginPageClient({ siteName }: { siteName: string }) { } export default function LoginPage() { - const siteName = useSiteName(); return ( - + ); } diff --git a/src/components/AuthProvider.tsx b/src/components/AuthProvider.tsx deleted file mode 100644 index 590ac9b..0000000 --- a/src/components/AuthProvider.tsx +++ /dev/null @@ -1,46 +0,0 @@ -'use client'; - -import { usePathname, useRouter } from 'next/navigation'; -import { useEffect } from 'react'; - -interface Props { - children: React.ReactNode; -} - -export default function AuthProvider({ children }: Props) { - const router = useRouter(); - const pathname = usePathname(); - - useEffect(() => { - // 登录页或 API 路径不做校验,避免死循环 - if (pathname.startsWith('/login')) return; - - const password = localStorage.getItem('password'); - const fullPath = - typeof window !== 'undefined' - ? window.location.pathname + window.location.search - : pathname; - - // 有密码时验证 - (async () => { - try { - const res = await fetch('/api/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ password }), - }); - - if (!res.ok) { - // 校验未通过,清理并跳转登录 - localStorage.removeItem('password'); - router.replace(`/login?redirect=${encodeURIComponent(fullPath)}`); - } - } catch (error) { - // 网络错误等也认为未登录 - router.replace(`/login?redirect=${encodeURIComponent(fullPath)}`); - } - })(); - }, [pathname, router]); - - return <>{children}; -} diff --git a/src/components/MobileHeader.tsx b/src/components/MobileHeader.tsx index 84309d8..c5a3f99 100644 --- a/src/components/MobileHeader.tsx +++ b/src/components/MobileHeader.tsx @@ -2,13 +2,9 @@ import Link from 'next/link'; -import { useSiteName } from './SiteNameContext'; import { ThemeToggle } from './ThemeToggle'; -export const dynamic = 'force-dynamic'; - const MobileHeader = () => { - const siteName = useSiteName(); return (
@@ -16,7 +12,7 @@ const MobileHeader = () => { href='/' className='text-2xl font-bold text-green-600 tracking-tight hover:opacity-80 transition-opacity' > - {siteName} + MoonTV
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 436498a..1a81652 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Clover, Film, @@ -24,8 +22,6 @@ import { useState, } from 'react'; -import { useSiteName } from './SiteNameContext'; - interface SidebarContextType { isCollapsed: boolean; } @@ -37,17 +33,13 @@ const SidebarContext = createContext({ export const useSidebar = () => useContext(SidebarContext); // 可替换为你自己的 logo 图片 -interface LogoProps { - siteName: string; -} - -const Logo = ({ siteName }: LogoProps) => ( +const Logo = () => ( - {siteName} + MoonTV ); @@ -64,8 +56,6 @@ declare global { } } -export const dynamic = 'force-dynamic'; - const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { const router = useRouter(); const pathname = usePathname(); @@ -168,8 +158,6 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { { icon: VenetianMask, label: '日漫', href: '/douban?type=tv&tag=日本动画' }, ]; - const siteName = useSiteName(); - return ( {/* 在移动端隐藏侧边栏 */} @@ -193,7 +181,7 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { }`} >
- {!isCollapsed && } + {!isCollapsed && }