5 Commits

Author SHA1 Message Date
湛兮 f8386d7b02 feat: 完善环境标识配置 2026-06-05 15:35:51 +08:00
湛兮 5fe2e2c75c chore: move Jenkins pipeline config out of repo 2026-06-05 15:08:11 +08:00
湛兮 3e1789e124 ci: harden production tag selection 2026-06-05 14:45:44 +08:00
湛兮 3efb2182b2 ci: add production tag pipeline 2026-06-05 14:38:25 +08:00
湛兮 6953f48344 chore: split dev and production deployment env 2026-06-05 12:32:16 +08:00
10 changed files with 113 additions and 6 deletions
+6
View File
@@ -0,0 +1,6 @@
ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3501/api
ROLE_USER_SESSION_COOKIE=role_user_session_develop
APP_ENV=develop
APP_ENV_LABEL=测试环境
PORT=3211
HOSTNAME=0.0.0.0
+5 -1
View File
@@ -1,2 +1,6 @@
ACCESS_MANAGE_API_BASE_URL=http://localhost:3500/api
ROLE_USER_SESSION_COOKIE=role_user_session
ROLE_USER_SESSION_COOKIE=role_user_session_local
APP_ENV=local
APP_ENV_LABEL=本地环境
PORT=3210
HOSTNAME=0.0.0.0
+6
View File
@@ -0,0 +1,6 @@
ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3500/api
ROLE_USER_SESSION_COOKIE=role_user_session
APP_ENV=production
APP_ENV_LABEL=生产环境
PORT=3210
HOSTNAME=0.0.0.0
+6
View File
@@ -0,0 +1,6 @@
ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3501/api
ROLE_USER_SESSION_COOKIE=role_user_session_develop
APP_ENV=develop
APP_ENV_LABEL=测试环境
PORT=3211
HOSTNAME=0.0.0.0
+17 -1
View File
@@ -26,11 +26,23 @@ pnpm dev
```bash
ACCESS_MANAGE_API_BASE_URL=http://localhost:3500/api
ROLE_USER_SESSION_COOKIE=role_user_session
ROLE_USER_SESSION_COOKIE=role_user_session_local
APP_ENV=local
APP_ENV_LABEL=本地环境
PORT=3210
HOSTNAME=0.0.0.0
```
`ACCESS_MANAGE_API_BASE_URL` 指向 `access-manage` 服务端 API 根路径。员工登录会调用后端 `POST /api/auth/employee/login`,登录成功后只把 JWT 写入 Next.js 服务端 HttpOnly Cookie,不写入 `localStorage`
环境标识按 `APP_ENV` 区分:
- `local`: 本地环境,默认使用 `.env.example` 复制出的 `.env.local`
- `develop`: 测试环境,参考 `.env.develop.example``.env.test.example` 保留为兼容入口。
- `production`: 生产环境,参考 `.env.production.example`
`APP_ENV_LABEL` 可覆盖页面右上角显示文案;未配置时会按 `APP_ENV` 自动显示“本地环境”“测试环境”或“生产环境”。Cookie 只在 `APP_ENV=production` 时设置 `Secure`,避免测试环境 HTTP 访问时登录后被浏览器丢弃 Cookie。
## 可用命令
```bash
@@ -38,6 +50,10 @@ pnpm dev
pnpm typecheck
pnpm lint
pnpm build
pnpm build:develop
pnpm build:prod
pnpm start:develop
pnpm start:prod
```
## 当前实现范围
+6 -1
View File
@@ -3,9 +3,14 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3210",
"dev": "APP_ENV=local APP_ENV_LABEL=本地环境 ACCESS_MANAGE_API_BASE_URL=http://localhost:3500/api ROLE_USER_SESSION_COOKIE=role_user_session_local next dev -p 3210",
"build": "next build",
"build:develop": "APP_ENV=develop APP_ENV_LABEL=测试环境 ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3501/api ROLE_USER_SESSION_COOKIE=role_user_session_develop next build",
"build:test": "pnpm build:develop",
"build:prod": "APP_ENV=production APP_ENV_LABEL=生产环境 ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3500/api ROLE_USER_SESSION_COOKIE=role_user_session next build",
"start": "next start",
"start:develop": "APP_ENV=develop APP_ENV_LABEL=测试环境 ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3501/api ROLE_USER_SESSION_COOKIE=role_user_session_develop next start -p 3211",
"start:prod": "APP_ENV=production APP_ENV_LABEL=生产环境 ACCESS_MANAGE_API_BASE_URL=http://127.0.0.1:3500/api ROLE_USER_SESSION_COOKIE=role_user_session next start -p 3210",
"lint": "eslint",
"typecheck": "tsc --noEmit --incremental false"
},
+16
View File
@@ -1029,6 +1029,22 @@ textarea:focus-visible {
color: var(--accent-ink);
}
.environment-badge {
background: rgba(20, 20, 24, 0.86);
border-radius: 999px;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
color: #ffffff;
font-size: 12px;
font-weight: 750;
letter-spacing: 0;
line-height: 1;
padding: 8px 10px;
position: fixed;
right: 12px;
top: 12px;
z-index: 60;
}
.skeleton {
animation: pulse 1.2s ease-in-out infinite;
background: linear-gradient(90deg, #ececef, #fafafa, #ececef);
+13 -2
View File
@@ -1,5 +1,7 @@
import type { Metadata, Viewport } from "next";
import { connection } from "next/server";
import { getAppEnvLabel } from "@/lib/environment";
import "./globals.css";
export const metadata: Metadata = {
@@ -24,10 +26,19 @@ export const viewport: Viewport = {
themeColor: "#ffffff"
};
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
await connection();
const environmentLabel = getAppEnvLabel();
return (
<html lang="zh-CN">
<body>{children}</body>
<body>
<div className="environment-badge" aria-label={`当前环境:${environmentLabel}`}>
{environmentLabel}
</div>
{children}
</body>
</html>
);
}
+35
View File
@@ -0,0 +1,35 @@
import "server-only";
export type AppEnv = "local" | "develop" | "production";
export function getAppEnv(): AppEnv {
const appEnv = process.env.APP_ENV;
if (appEnv === "production") {
return "production";
}
if (appEnv === "develop" || appEnv === "test") {
return "develop";
}
if (appEnv === "local") {
return "local";
}
return process.env.NODE_ENV === "production" ? "production" : "local";
}
export function getAppEnvLabel() {
if (process.env.APP_ENV_LABEL) {
return process.env.APP_ENV_LABEL;
}
const labels: Record<AppEnv, string> = {
local: "本地环境",
develop: "测试环境",
production: "生产环境"
};
return labels[getAppEnv()];
}
+3 -1
View File
@@ -2,6 +2,8 @@ import "server-only";
import { cookies } from "next/headers";
import { getAppEnv } from "@/lib/environment";
const DEFAULT_COOKIE_NAME = "role_user_session";
export function getSessionCookieName() {
@@ -17,7 +19,7 @@ export async function setSessionToken(token: string) {
cookieStore.set(getSessionCookieName(), token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
secure: getAppEnv() === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 8