diff --git a/.env.example b/.env.example index f7045b0..e98635c 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,16 @@ # ============================================================================= -# 玩家端本地配置示例:复制为 .env.local 后按需修改 +# 玩家端配置:复制为 .env 后按需修改(本地/线上同一套) # ============================================================================= # ----------------------------------------------------------------------------- -# Laravel API -# - 浏览器始终请求同源 /api/v1 -# - 宝塔线上负责把 /api/* 转发到 Laravel -# - 本地 npm run dev 时,Next 临时把 /api/* 代理到 LOTTERY_API_UPSTREAM +# Laravel API — 唯一必配项 +# - 浏览器请求同源 /api/v1 +# - Next 自动转发到 LOTTERY_API_UPSTREAM(无需宝塔单独配 /api 转发) # ----------------------------------------------------------------------------- -# 本地 Laravel +# 本地 LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 -# 本地前端连线上 API(开发代理,无需改线上 CORS) +# 线上 +# LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 # LOTTERY_API_UPSTREAM=https://lotterylaravel.tanumo.com # Next 开发:局域网 IP 访问(逗号分隔 host,无协议) @@ -19,7 +19,7 @@ LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 # 可选:大厅 play/effective 的 ?currency= # NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY=NPR -# 可选:入口授权失败时返回主站 +# 可选:入口授权失败时返回主站(build 前设置,会打进 JS) # NEXT_PUBLIC_MAIN_SITE_URL=http://localhost:5173 # Reverb:本地全栈联调时取消注释,并 php artisan reverb:start;不配则走轮询 diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs new file mode 100644 index 0000000..62df4a0 --- /dev/null +++ b/ecosystem.config.cjs @@ -0,0 +1,53 @@ +/** + * 玩家端(lotteryfront)宝塔 PM2 配置 + * + * 部署步骤(在服务器项目目录执行): + * 1. 复制 .env.example 为 .env,按线上域名填写(见下方 env 说明) + * 2. npm ci && npm run build + * 3. 修改下方 cwd 为服务器上的实际路径 + * 4. pm2 start ecosystem.config.cjs + * 5. pm2 save && pm2 startup # 开机自启(按 PM2 提示执行) + * + * 宝塔:网站 → Node 项目 → PM2 管理器 → 添加项目 → 选择本文件,或 SSH 执行 pm2 start。 + * Nginx:反代到 127.0.0.1:3800 即可;/api 由 Next 转发到 LOTTERY_API_UPSTREAM,无需单独配。 + */ + +const path = require("path"); + +/** 改成服务器上的绝对路径,例如 /www/wwwroot/lottery.yourdomain.com */ +const APP_CWD = path.resolve(__dirname); + +module.exports = { + apps: [ + { + name: "lotteryfront", + cwd: APP_CWD, + script: "node_modules/next/dist/bin/next", + args: "start -p 3800 -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: "3800", + + // Laravel 根地址(无尾部 /);同机部署填 http://127.0.0.1:8000 + LOTTERY_API_UPSTREAM: "http://127.0.0.1:8000", + + // 构建时需存在;运行时可留空若已在 build 时写入 + // NEXT_PUBLIC_MAIN_SITE_URL: "https://main.yourdomain.com", + }, + + // PM2 5.2+:可把变量放在 .env,由 PM2 注入(需 npm run build 前也有一份供 Next 编译) + // env_file: path.join(APP_CWD, ".env"), + }, + ], +}; 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/lib/csp-config.ts b/src/lib/csp-config.ts index 6b9f473..785f827 100644 --- a/src/lib/csp-config.ts +++ b/src/lib/csp-config.ts @@ -65,7 +65,7 @@ export function generateCSP(extraParentOrigins: string[] = []): string { // 字体允许同源 "font-src": ["'self'"], - // 连接允许同源;线上 API 由宝塔挂在同源 /api 下 + // 连接允许同源;API 由 Next 代理到 LOTTERY_API_UPSTREAM "connect-src": [ "'self'", // WebSocket 连接 diff --git a/src/lib/lottery-api-base.ts b/src/lib/lottery-api-base.ts index 951a83e..894f799 100644 --- a/src/lib/lottery-api-base.ts +++ b/src/lib/lottery-api-base.ts @@ -1,6 +1,6 @@ const DEFAULT_LOTTERY_API_ORIGIN = "http://127.0.0.1:8000"; -/** Laravel 根地址(无尾部 `/`),仅供 Next 本地开发代理与 middleware 服务端请求。 */ +/** Laravel 根地址(无尾部 `/`),供 Next 代理与 middleware 服务端请求。 */ export function lotteryApiOrigin(): string { const configured = process.env.LOTTERY_API_UPSTREAM?.trim(); return (configured || DEFAULT_LOTTERY_API_ORIGIN).replace(/\/$/, ""); @@ -9,8 +9,7 @@ export function lotteryApiOrigin(): string { /** * 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}`;