fix: prod login + env-file driven config + scroll-snap bounce
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6m54s

- 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) <noreply@anthropic.com>
This commit is contained in:
zyc 2026-05-14 17:31:00 +08:00
parent f6177fc542
commit 7168e50a6e
8 changed files with 57 additions and 16 deletions

View File

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

23
.env.production Normal file
View File

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

View File

@ -94,13 +94,12 @@ jobs:
--docker-password="${{ env.CR_PASSWORD_ACTIVE }}" \
--dry-run=client -o yaml | kubectl apply -f -
# 2) 应用运行时 SecretDB 连接串 + 阿里云短信凭据)
# 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

8
.gitignore vendored
View File

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

View File

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

View File

@ -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();

View File

@ -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;
};

View File

@ -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();