From 7168e50a6e09e65debf3baf0f512c6713b874119 Mon Sep 17 00:00:00 2001 From: zyc <1439655764@qq.com> Date: Thu, 14 May 2026 17:31:00 +0800 Subject: [PATCH] fix: prod login + env-file driven config + scroll-snap bounce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - env: 解封 .env / .env.production 提交, 仅忽略 .env.local 系列; .env.production 承载 DATABASE_URL / AUTH_SECRET / AUTH_URL / SMS_* / NEXT_PUBLIC_TOS_DOMAIN, Dockerfile runner 阶段 COPY 进 运行时镜像, Next.js standalone 启动自动加载 - ci: 移除 kubectl 注入 secret 步骤(env 已烧入镜像), 保留占位避免 envFrom optional 引用告警, 修复 /api/auth/providers 500 (缺 AUTH_SECRET) - auth: signIn 失败透传 NextAuth 真实错误码, 不再被"验证码错误"一刀切掩盖 - home: 首页 scroll-snap-type 由 mandatory 改 proximity, 修复滚动到 底部被强制吸回候选区顶部的回弹 Co-Authored-By: Claude Opus 4.7 (1M context) --- .dockerignore | 4 ++-- .env.production | 23 +++++++++++++++++++++++ .gitea/workflows/deploy.yaml | 11 +++++------ .gitignore | 8 +++++--- Dockerfile | 4 ++++ src/app/login/LoginForm.tsx | 7 ++++++- src/app/page.tsx | 7 ++++--- src/components/auth/LoginModal.tsx | 9 ++++++++- 8 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 .env.production diff --git a/.dockerignore b/.dockerignore index b7be438..50d8697 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,8 +6,8 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* -.env -.env.* +.env.local +.env.*.local !.env.example .DS_Store *.pem diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..f1ac168 --- /dev/null +++ b/.env.production @@ -0,0 +1,23 @@ +# ============================================================= +# CYBER STAR · 生产环境变量 +# 仅在 NODE_ENV=production 时被 Next.js 加载 (部署容器里) +# 本机开发请用 .env.local 覆盖 +# ============================================================= + +# ── 数据库 · 火山 RDS VPC 内网 (从 K8s Pod 访问) ── +DATABASE_URL=mysql://zyc:Zyc188208@mysql8351f937d637.rds.ivolces.com:3306/cyberstar?charset=utf8mb4 + +# ── Auth.js JWT 签名密钥 ── +# 用 `openssl rand -base64 32` 重新生成 (不要复用本机 .env.local 那把) +AUTH_SECRET=eI6svHTg/Uj2EfyP5r0Dt0DbpJDhiX26lRqkC+EylUM= +AUTH_URL=https://cyberstar.airlabs.art +AUTH_TRUST_HOST=true + +# ── 火山 TOS 静态资源前缀 (build 时需要,因为 NEXT_PUBLIC_* 会被烧进 client bundle) ── +NEXT_PUBLIC_TOS_DOMAIN=https://cyber-star.tos-cn-shanghai.volces.com + +# ── 阿里云短信 ── +SMS_SIGN_NAME=广州气元科技 +SMS_TEMPLATE_CODE=SMS_506210397 +SMS_ACCESS_KEY=LTAI5t7jGzFH4ExkJ9TSmQyd +SMS_SECRET_KEY=u0d3OyTWe9BjnNjK81bvEElky4xcHk diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index e4ec111..5a12d3e 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -94,13 +94,12 @@ jobs: --docker-password="${{ env.CR_PASSWORD_ACTIVE }}" \ --dry-run=client -o yaml | kubectl apply -f - - # 2) 应用运行时 Secret(DB 连接串 + 阿里云短信凭据) + # 2) 运行时 env 已通过 .env.production 烧入镜像,不再需要 K8s Secret 注入 + # (Next.js standalone server 启动时从 cwd 自动加载 .env.production) + # 保留一个空的 cyberstar-env 占位,避免 web-deployment.yaml 的 + # envFrom: optional=true 在首次部署时找不到引用而告警 kubectl create secret generic cyberstar-env \ - --from-literal=DATABASE_URL='mysql://zyc:Zyc188208@mysql8351f937d637.rds.ivolces.com:3306/cyberstar?charset=utf8mb4' \ - --from-literal=SMS_SIGN_NAME='广州气元科技' \ - --from-literal=SMS_TEMPLATE_CODE='SMS_506210397' \ - --from-literal=SMS_ACCESS_KEY='LTAI5t7jGzFH4ExkJ9TSmQyd' \ - --from-literal=SMS_SECRET_KEY='u0d3OyTWe9BjnNjK81bvEElky4xcHk' \ + --from-literal=_PLACEHOLDER='env values live in .env.production' \ --dry-run=client -o yaml | kubectl apply -f - # 3) Apply manifests diff --git a/.gitignore b/.gitignore index f35b9c7..cda07be 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,11 @@ yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* -!.env.example +# env files +# 提交: .env (开发默认 / 占位)、.env.production (部署用真实值)、.env.example +# 不提交: .env.local 系列 —— 本机个人覆盖,避免顶掉提交值 +.env.local +.env.*.local # vercel .vercel diff --git a/Dockerfile b/Dockerfile index 2f595ba..6d6d634 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,10 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma +# 运行时 env: Next.js standalone server.js 启动时从 cwd 加载 .env.production +# (next build 已经把 NEXT_PUBLIC_* 烧进 bundle, 这里管的是服务端 env 如 DATABASE_URL / AUTH_SECRET) +COPY --from=builder --chown=nextjs:nodejs /app/.env.production ./.env.production + USER nextjs EXPOSE 3000 diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx index 0a060ed..20c6360 100644 --- a/src/app/login/LoginForm.tsx +++ b/src/app/login/LoginForm.tsx @@ -71,7 +71,12 @@ export default function LoginForm() { redirect: false, }); if (result?.error) { - setError("验证码错误或已失效"); + console.error("[login] signIn 返回错误:", result); + setError( + result.error === "CredentialsSignin" + ? "验证码错误或已失效" + : `登录失败:${result.error}`, + ); } else { router.push(callbackUrl); router.refresh(); diff --git a/src/app/page.tsx b/src/app/page.tsx index 7c2950b..2021406 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -39,12 +39,13 @@ export default function Home() { return sortArtists(list, sortKey); }, [artists, tagFilter, sortKey]); - // 仅在首页启用 scroll-snap mandatory:用户下滑就立即切换到下一个 snap 点 - // (Hero → Top12 → 候选区)。卸载时还原。 + // 仅在首页启用 scroll-snap:用户接近 Hero/Top12/候选区时自然吸附。 + // 用 proximity 而不是 mandatory —— mandatory 会把"滚到底部"强制吸回到最后一个 + // snap 点的 start(候选区顶部),表现为回弹;proximity 只在靠近时吸,远离不干预。 useEffect(() => { const root = document.documentElement; const prev = root.style.scrollSnapType; - root.style.scrollSnapType = "y mandatory"; + root.style.scrollSnapType = "y proximity"; return () => { root.style.scrollSnapType = prev; }; diff --git a/src/components/auth/LoginModal.tsx b/src/components/auth/LoginModal.tsx index 9883797..9cbc2e0 100644 --- a/src/components/auth/LoginModal.tsx +++ b/src/components/auth/LoginModal.tsx @@ -109,7 +109,14 @@ export default function LoginModal({ open, onClose, onSuccess }: LoginModalProps redirect: false, }); if (result?.error) { - setError("验证码错误或已失效"); + console.error("[login] signIn 返回错误:", result); + // 把 NextAuth 真实错误透出来,避免被"验证码错误或已失效"一刀切掩盖 + // (例如 server config 错误时,会显示 Configuration 而不是误导成验证码问题) + setError( + result.error === "CredentialsSignin" + ? "验证码错误或已失效" + : `登录失败:${result.error}`, + ); } else { onClose(); if (onSuccess) onSuccess();