diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 8eec698..3b50eb5 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,55 +1,21 @@ -import Link from "next/link"; import Logo from "./Logo"; -const FOOTER_LINKS = [ - { label: "活动规则", href: "/rules" }, - { label: "隐私协议", href: "/privacy" }, - { label: "用户协议", href: "/terms" }, - { label: "联系客服", href: "/support" }, -] as const; - export default function Footer() { return ( diff --git a/src/components/NavLinks.tsx b/src/components/NavLinks.tsx index 5c3e09e..dc00cb0 100644 --- a/src/components/NavLinks.tsx +++ b/src/components/NavLinks.tsx @@ -6,10 +6,8 @@ import { cn } from "@/lib/cn"; const NAV_ITEMS = [ { label: "HOME", href: "/" }, - { label: "VOTE", href: "/vote" }, { label: "RANKING", href: "/ranking" }, - { label: "NEWS", href: "/news" }, - { label: "ABOUT", href: "/about" }, + { label: "ME", href: "/me" }, ] as const; interface NavLinksProps { diff --git a/src/lib/auth.ts b/src/lib/auth.ts index b072786..3a72ae4 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,6 +1,5 @@ import NextAuth, { type NextAuthConfig } from "next-auth"; import Credentials from "next-auth/providers/credentials"; -import { PrismaAdapter } from "@auth/prisma-adapter"; import { prisma } from "./prisma"; import { z } from "zod"; @@ -9,11 +8,11 @@ import { z } from "zod"; * * 支持渠道(按优先级): * 1. 手机号 + 短信 OTP(国内主推) - * 2. 微信扫码(待团队配置 appId/secret 后开启) + * 2. 微信扫码(待团队配置 appId/secret 后开启 · 启用时需把 PrismaAdapter 加回来) * 3. 邮箱(境外用户备用) * - * 当前阶段:手机号 OTP 已接入,OTP 校验逻辑使用 Redis 缓存验证码 - * (Phase 11 完整版需团队配置短信服务,本文件已留好接入位)。 + * 当前阶段:使用 JWT session 策略,Credentials 不需要 Adapter。 + * 数据库不可用时自动降级为内存用户(开发态友好)。 */ const OtpCredentials = z.object({ @@ -24,7 +23,8 @@ const OtpCredentials = z.object({ }); export const authConfig: NextAuthConfig = { - adapter: PrismaAdapter(prisma), + // NOTE: Credentials + JWT 策略不需要 adapter。 + // 启用微信 OAuth 时再把 PrismaAdapter 加回来。 session: { strategy: "jwt" }, pages: { signIn: "/login", @@ -42,49 +42,44 @@ export const authConfig: NextAuthConfig = { if (!parsed.success) return null; const { phone, code } = parsed.data; - // TODO[团队]: 接入真实 OTP 校验 - // 1) 从 Redis 取 sms:otp:${phone} 比对 code - // 2) 校验失败 / 过期 → return null - // 3) 成功 → 删除验证码,继续创建/查询用户 const validOtp = await verifyOtp(phone, code); if (!validOtp) return null; - // 查询或创建用户 - const user = await prisma.user.upsert({ - where: { phone }, - create: { - phone, - nickname: `粉丝_${phone.slice(-4)}`, - loginType: "PHONE", - }, - update: { lastLoginAt: new Date() }, - }); - - return { - id: String(user.id), - name: user.nickname, - image: user.avatar ?? undefined, - }; + // 尝试持久化到数据库;失败则降级为内存身份(开发态) + try { + const user = await prisma.user.upsert({ + where: { phone }, + create: { + phone, + nickname: `粉丝_${phone.slice(-4)}`, + loginType: "PHONE", + }, + update: { lastLoginAt: new Date() }, + }); + return { + id: String(user.id), + name: user.nickname, + image: user.avatar ?? undefined, + }; + } catch (err) { + if (process.env.NODE_ENV === "production") { + console.error("[auth] DB upsert failed:", err); + return null; + } + // 开发态:用手机号末 9 位做确定性 ID,便于跨重启保持身份 + const fakeId = String(parseInt(phone.slice(-9), 10)); + console.warn( + `[auth] DB 不可用,开发态降级身份 id=${fakeId} phone=${phone}`, + ); + return { + id: fakeId, + name: `粉丝_${phone.slice(-4)}`, + }; + } }, }), - // TODO[团队]: 启用微信扫码登录 - // 需要 WECHAT_APP_ID / WECHAT_APP_SECRET 环境变量。 - // 实现可参考 https://authjs.dev/guides/configuring-oauth-providers - // - // { - // id: "wechat", - // name: "WeChat", - // type: "oauth", - // authorization: { url: "https://open.weixin.qq.com/connect/qrconnect", params: { scope: "snsapi_login" } }, - // token: "https://api.weixin.qq.com/sns/oauth2/access_token", - // userinfo: "https://api.weixin.qq.com/sns/userinfo", - // clientId: process.env.WECHAT_APP_ID, - // clientSecret: process.env.WECHAT_APP_SECRET, - // profile(p) { - // return { id: p.openid, name: p.nickname, image: p.headimgurl }; - // }, - // }, + // TODO[团队]: 启用微信扫码登录(需配置 WECHAT_APP_ID/SECRET 并加回 PrismaAdapter) ], callbacks: { async jwt({ token, user }) {