diff --git a/.env.example b/.env.example index 9fbd458..fb81105 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,17 @@ # ============================================================================= -# 管理端本地配置示例:复制为 .env.local 后按需修改 +# 管理端配置:复制为 .env 后按需修改(本地/线上同一套) # ============================================================================= # 三端联调:Laravel 8000、管理端 3801、玩家端 3800 -# 生产部署时 Laravel CORS 需含 https://lotteryadmin.tanumo.com,https://lotteryfront.tanumo.com # ----------------------------------------------------------------------------- -# Laravel API -# - 浏览器始终请求同源 /api/v1 -# - 宝塔线上负责把 /api/* 转发到 Laravel -# - 本地 npm run dev 时,Next 临时把 /api/* 代理到 LOTTERY_API_UPSTREAM +# Laravel API — 唯一必配项 +# - 浏览器请求同源 /api/v1 +# - Next 自动转发到 LOTTERY_API_UPSTREAM(无需宝塔单独配 /api 转发) # ----------------------------------------------------------------------------- +# 本地 LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 +# 线上(Laravel 在本机时用内网地址更快) +# LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 # LOTTERY_API_UPSTREAM=https://lotterylaravel.tanumo.com # Next 开发:局域网 IP 访问(逗号分隔 host,无协议) diff --git a/.gitignore b/.gitignore index 7b8da95..fc6edee 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ .DS_Store *.pem +# pm2 +/logs + # debug npm-debug.log* yarn-debug.log* diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs new file mode 100644 index 0000000..f0d566a --- /dev/null +++ b/ecosystem.config.cjs @@ -0,0 +1,49 @@ +/** + * 管理端(lotteryadmin)宝塔 PM2 配置 + * + * 部署步骤(在服务器项目目录执行): + * 1. 复制 .env.example 为 .env,按线上域名填写 + * 2. npm ci && npm run build + * 3. 修改下方 cwd 为服务器上的实际路径(默认即本文件所在目录) + * 4. pm2 start ecosystem.config.cjs + * 5. pm2 save && pm2 startup + * + * 宝塔:PM2 管理器 → 添加项目 → 选择本文件。 + * Nginx:反代到 127.0.0.1:3801 即可;/api 由 Next 转发到 LOTTERY_API_UPSTREAM,无需单独配。 + */ + +const path = require("path"); + +/** 改成服务器上的绝对路径,例如 /www/wwwroot/lotteryadmin.tanumo.com */ +const APP_CWD = path.resolve(__dirname); + +module.exports = { + apps: [ + { + name: "lotteryadmin", + cwd: APP_CWD, + script: "node_modules/next/dist/bin/next", + args: "start -p 3801 -H 127.0.0.1", + interpreter: "node", + exec_mode: "fork", + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: "1G", + time: true, + merge_logs: true, + out_file: path.join(APP_CWD, "logs/pm2-out.log"), + error_file: path.join(APP_CWD, "logs/pm2-error.log"), + + env: { + NODE_ENV: "production", + PORT: "3801", + + // Laravel 根地址(无尾部 /);同机部署填 http://127.0.0.1:8000 + LOTTERY_API_UPSTREAM: "http://127.0.0.1:8000", + }, + + // env_file: path.join(APP_CWD, ".env"), + }, + ], +}; diff --git a/package.json b/package.json index da8d391..0a583ed 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "next dev --port 3801", "build": "next build", - "start": "next start", + "start": "next start --port 3801", "lint": "eslint" }, "dependencies": { diff --git a/src/app/api/[...path]/route.ts b/src/app/api/[...path]/route.ts index 0c9f3f1..0bf9962 100644 --- a/src/app/api/[...path]/route.ts +++ b/src/app/api/[...path]/route.ts @@ -1,12 +1,12 @@ import { type NextRequest } from "next/server"; -import { proxyLotteryApiInDev } from "@/lib/lottery-api-dev-proxy"; +import { proxyLotteryApi } from "@/lib/lottery-api-proxy"; type RouteContext = { params: Promise<{ path: string[] }> }; async function handle(request: NextRequest, context: RouteContext) { const { path } = await context.params; - return proxyLotteryApiInDev(request, path); + return proxyLotteryApi(request, path); } export const GET = handle; diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index c0b473c..5f2073e 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -16,7 +16,7 @@ "submit": "Log in", "submitting": "Signing in…", "captchaLoadFailed": "Failed to load captcha. Check the API or network.", - "apiBaseMissingToast": "API proxy is not enabled: make sure /api is forwarded to Laravel", + "apiBaseMissingToast": "API proxy is not enabled: check LOTTERY_API_UPSTREAM points to Laravel", "captchaRequired": "Refresh the captcha first", "welcome": "Welcome, {{name}}", "networkFailed": "Network request failed", diff --git a/src/i18n/locales/ne/auth.json b/src/i18n/locales/ne/auth.json index a5f97fa..2cfc110 100644 --- a/src/i18n/locales/ne/auth.json +++ b/src/i18n/locales/ne/auth.json @@ -16,7 +16,7 @@ "submit": "लगइन", "submitting": "लगइन हुँदैछ…", "captchaLoadFailed": "क्याप्चा लोड गर्न सकिएन। API वा नेटवर्क जाँच गर्नुहोस्।", - "apiBaseMissingToast": "API proxy सक्षम छैन: /api Laravel मा forward भएको छ कि छैन जाँच गर्नुहोस्", + "apiBaseMissingToast": "API proxy सक्षम छैन: LOTTERY_API_UPSTREAM Laravel तर्फ छ कि छैन जाँच गर्नुहोस्", "captchaRequired": "पहिले क्याप्चा रिफ्रेस गर्नुहोस्", "welcome": "स्वागत छ, {{name}}", "networkFailed": "नेटवर्क अनुरोध असफल भयो", diff --git a/src/i18n/locales/zh/auth.json b/src/i18n/locales/zh/auth.json index a8bf5b7..a8a18e7 100644 --- a/src/i18n/locales/zh/auth.json +++ b/src/i18n/locales/zh/auth.json @@ -16,7 +16,7 @@ "submit": "登录", "submitting": "登录中…", "captchaLoadFailed": "无法获取验证码,请检查接口或网络", - "apiBaseMissingToast": "API 代理未启用:请确认宝塔或本地 Next 已转发 /api 到 Laravel", + "apiBaseMissingToast": "API 代理未启用:请检查 LOTTERY_API_UPSTREAM 是否指向 Laravel", "captchaRequired": "请先刷新验证码", "welcome": "欢迎,{{name}}", "networkFailed": "网络请求失败", diff --git a/src/lib/lottery-api-base.ts b/src/lib/lottery-api-base.ts index a57dc6c..894f799 100644 --- a/src/lib/lottery-api-base.ts +++ b/src/lib/lottery-api-base.ts @@ -1,18 +1,15 @@ const DEFAULT_LOTTERY_API_ORIGIN = "http://127.0.0.1:8000"; -/** Laravel 根地址(无尾部 `/`),仅供 Next 本地开发代理使用。 */ +/** Laravel 根地址(无尾部 `/`),供 Next 代理与 middleware 服务端请求。 */ export function lotteryApiOrigin(): string { - const configured = - process.env.LOTTERY_API_UPSTREAM?.trim() || - process.env.NEXT_PUBLIC_LOTTERY_API_BASE_URL?.trim(); + const configured = process.env.LOTTERY_API_UPSTREAM?.trim(); return (configured || DEFAULT_LOTTERY_API_ORIGIN).replace(/\/$/, ""); } /** * axios `baseURL`: * - 浏览器始终请求同源 `/api/v1` - * - 线上由宝塔转发 `/api/*` 到 Laravel - * - 本地开发由 `app/api/[...path]/route.ts` 临时代理到 `LOTTERY_API_UPSTREAM` + * - Next `app/api/[...path]/route.ts` 转发到 `LOTTERY_API_UPSTREAM`(本地/线上同一套) */ export function resolveLotteryApiV1Base(): string { return "/api/v1"; diff --git a/src/lib/lottery-api-dev-proxy.ts b/src/lib/lottery-api-proxy.ts similarity index 86% rename from src/lib/lottery-api-dev-proxy.ts rename to src/lib/lottery-api-proxy.ts index 3292dd1..8b8e8a3 100644 --- a/src/lib/lottery-api-dev-proxy.ts +++ b/src/lib/lottery-api-proxy.ts @@ -20,15 +20,11 @@ const STRIP_RESPONSE_HEADERS = [ "content-length", ]; -/** 本地开发:/api/* → Laravel(Turbopack 下 next.config rewrites 常不生效)。 */ -export async function proxyLotteryApiInDev( +/** 浏览器请求同源 `/api/*`,Next 转发到 `LOTTERY_API_UPSTREAM`(本地/线上同一套)。 */ +export async function proxyLotteryApi( request: NextRequest, pathSegments: string[], ): Promise { - if (process.env.NODE_ENV !== "development") { - return NextResponse.json({ msg: "Not Found" }, { status: 404 }); - } - const path = pathSegments.join("/"); const target = `${lotteryApiOrigin()}/api/${path}${request.nextUrl.search}`;