fix(nav,auth): trim nav to wireframe pages; auth gracefully degrades when DB unavailable in dev

This commit is contained in:
iye 2026-05-12 10:29:02 +08:00
parent 854a162109
commit 7949f9bcd1
3 changed files with 44 additions and 85 deletions

View File

@ -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 (
<footer className="border-t border-white/[0.06] bg-[rgba(8,5,26,0.6)] backdrop-blur-sm mt-20">
<div className="max-w-7xl mx-auto px-6 sm:px-8 py-10 grid gap-8 md:grid-cols-3 items-start">
{/* Brand */}
<div className="max-w-7xl mx-auto px-6 sm:px-8 py-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<Logo size="sm" href={null} />
<p className="mt-3 text-xs text-white/45 leading-relaxed">
Top12
<br />
Cyber Star · Virtual Idol Debut Project
<p className="mt-2 text-xs text-white/45 leading-relaxed">
Top12 · Cyber Star · Virtual Idol Debut Project
</p>
</div>
{/* Links */}
<nav className="md:col-span-1">
<p className="font-label text-[10px] tracking-[0.3em] uppercase text-purple-300 mb-3">
Information
</p>
<ul className="space-y-2 text-sm">
{FOOTER_LINKS.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-white/55 hover:text-purple-300 transition-colors"
>
{link.label}
</Link>
</li>
))}
</ul>
</nav>
{/* Caption */}
<div className="text-xs text-white/40 md:text-right">
<p className="font-label tracking-[0.25em] uppercase text-purple-300/70 mb-2">
<div className="text-xs text-white/40 sm:text-right">
<p className="font-label tracking-[0.25em] uppercase text-purple-300/70 mb-1">
© 2026 Cyber Star
</p>
<p>All rights reserved.</p>
<p className="mt-1 font-mono text-white/30">
airlabs.art · powered by Next.js
</p>
<p className="font-mono text-white/30">airlabs.art</p>
</div>
</div>
</footer>

View File

@ -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 {

View File

@ -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 }) {