From b274ccdfeca37d308abcc3b8091daa1f5d941944 Mon Sep 17 00:00:00 2001 From: toom1996 <23cm.cn@gmail.com> Date: Sun, 23 Aug 2026 00:58:33 +0800 Subject: [PATCH] update --- .env.example | 11 +- .gitignore | 5 + DEPLOY.md | 4 +- Dockerfile | 14 +- astro.config.mjs | 18 +- deploy.sh | 2 +- docker-compose.yml | 5 +- src/components/Index.astro | 4 +- .../{RunwayLooksDetail.astro => Item.astro} | 2 +- src/components/pages/article.astro | 2 +- src/components/pages/brands.astro | 6 +- src/layouts/Layout.astro | 2 +- src/lib/api.ts | 376 +++++++++++++++--- src/lib/auth.ts | 87 ---- src/lib/data.ts | 106 ++--- src/lib/http.ts | 208 ++++++++++ src/lib/ssg.ts | 96 ----- src/lib/ssr.ts | 34 -- src/pages/cn/article.astro | 2 +- src/pages/en/article.astro | 2 +- src/pages/en/items/[id].astro | 6 +- 21 files changed, 612 insertions(+), 380 deletions(-) rename src/components/{RunwayLooksDetail.astro => Item.astro} (99%) delete mode 100644 src/lib/auth.ts create mode 100644 src/lib/http.ts delete mode 100644 src/lib/ssg.ts delete mode 100644 src/lib/ssr.ts diff --git a/.env.example b/.env.example index f77dba8..b55ec6a 100644 --- a/.env.example +++ b/.env.example @@ -16,9 +16,16 @@ HASHID_SECRET=change-me-in-prod-hashid # ---- SSG 内部接口令牌(可选)---- # SSG_TOKEN=some-long-random-token +# ---- 前端开发/构建变量(本地 astro dev / 本地 build 用;Docker 部署见 Dockerfile/docker-compose)---- +# 公开/运行期接口基址(浏览器运行期 + 按需渲染 SSR)。空 = 同源 /api。 +# 本地联调后端时填 http://localhost:8090;生产可置空走同源。 +#BASE_API=http://localhost:8090 +# 构建期 SSG 内部端口基址,指向后端独立的 SSG 内部端口(只绑宿主回环,nginx 不反代)。 +#BASE_API_SSG=http://localhost:8091 + # 说明: -# - 前端运行期走同源 /api(nginx 反代到 backend:8090),PUBLIC_API_BASE 在 Dockerfile 中置空 -# - 前端构建期(SSG)通过 compose 的 build.args.SSG_API=http://localhost:8091 拉数据, +# - 前端运行期走同源 /api(nginx 反代到 backend:8090),BASE_API 在 Dockerfile 中置空 +# - 前端构建期(SSG)通过 compose 的 build.args.BASE_API_SSG=http://localhost:8091 拉数据, # 指向后端独立的 SSG 内部端口(只绑宿主回环,nginx 不反代) # - 若后端 SSG_TOKEN 非空,前端构建需传相同 SSG_TOKEN(compose 已对齐) # - backend 监听 8090 对外 + 8091 对内;生产建议去掉 8090 的 ports 暴露 diff --git a/.gitignore b/.gitignore index 016b59e..c99e093 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,11 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* +# API 调试日志(前端 http.ts 记录请求/响应) +logs/ +api-debug.log +*.log + # environment variables .env .env.production diff --git a/DEPLOY.md b/DEPLOY.md index 08c15c3..28c9eba 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -16,7 +16,7 @@ - **前端**:Astro 构建为静态 `dist/`,用 nginx 托管;`/api` 由 nginx 反代到 `backend:8090`(同源,无跨域)。 - **后端**:Go 二进制,监听 `8090`,连 `db` 服务。 -- **构建期 SSR**:`brands.astro` 在构建时拉数据,地址用 `API_SSR`(容器内部 `http://localhost:8090`,配合 `build.network: host` 访问已启动的 backend 容器);运行期浏览器走同源 `/api`。 +- **构建期取数**:品牌页等通过 SSG 内部接口(`/api/ssg/*`,基址 `BASE_API_SSG`,默认 `http://localhost:8091`)在构建时拉数据;运行期浏览器走同源 `/api`(基址 `BASE_API` 置空)。 ## 二、服务器目录约定 @@ -75,4 +75,4 @@ cd /opt/fashion/test - 后端环境变量名(`DB_HOST`/`JWT_SECRET` 等)需与实际 `config` 读取一致;如后端用别的变量名,改 `docker-compose.yml` 的 `environment` 即可。 - `JWT_SECRET` 生产务必改成随机长串;默认值是占位的。 - 生产可去掉 `backend.ports` 的 `8090:8090` 暴露,仅由前端 nginx 在同一 compose 网络内反代访问,更安全。 -- 前端 `PUBLIC_API_BASE` 在 Dockerfile 中置空(同源 `/api`);如需独立域名,改为绝对地址并在后端已开启 CORS(`*`)。 +- 前端 `BASE_API` 在 Dockerfile 中置空(同源 `/api`);如需独立域名,改为绝对地址并在后端已开启 CORS(`*`)。 diff --git a/Dockerfile b/Dockerfile index 9eac3e1..b6454b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,16 +3,12 @@ FROM node:22-alpine AS build WORKDIR /app # 构建期 SSG 拉数据用的「后端 SSG 内部地址」。 -# 在 docker-compose 里通过 build.args.SSG_API 传入(= http://localhost:8091, +# 在 docker-compose 里通过 build.args.BASE_API_SSG 传入(= http://localhost:8091, # 配合 build.network: host,让前端构建容器能访问已启动的 backend 容器的 SSG 内部端口)。 # 该端口只绑宿主回环(127.0.0.1:8091),nginx 绝不反代,因此 SSG 全量数据不会外泄。 -# SSG_API 与运行期 PUBLIC_API_BASE(8090)完全分离,二者指向不同端口。 -ARG SSG_API=http://localhost:8091 -ENV SSG_API=$SSG_API - -# 兼容旧命名:API_SSR 仍可作为 SSG 基址(lib/ssg.ts 优先读 SSG_API)。 -ARG API_SSR=http://localhost:8091 -ENV API_SSR=$API_SSR +# BASE_API_SSG 与运行期 BASE_API(8090)完全分离,二者指向不同端口。 +ARG BASE_API_SSG=http://localhost:8091 +ENV BASE_API_SSG=$BASE_API_SSG # SSG 内部端口的可选访问令牌(防御纵深)。仅构建期 node fetch 使用,且不带 PUBLIC_ 前缀, # 故绝不会进入前端 bundle。置空时后端不校验(依赖回环绑定即可)。 @@ -20,7 +16,7 @@ ARG SSG_TOKEN="" ENV SSG_TOKEN=$SSG_TOKEN # 运行期浏览器调用地址:置空 = 同源相对路径 /api(由下方 nginx 反代到 backend:8090) -ENV PUBLIC_API_BASE="" +ENV BASE_API="" COPY package*.json ./ RUN npm ci diff --git a/astro.config.mjs b/astro.config.mjs index 55d87d7..532540e 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,5 +1,5 @@ // @ts-check -import { defineConfig } from 'astro/config'; +import { defineConfig, envField } from 'astro/config'; import { fileURLToPath } from 'node:url'; import tailwindcss from '@tailwindcss/vite'; @@ -39,5 +39,19 @@ export default defineConfig({ adapter: node({ mode: 'standalone' - }) + }), + + // 环境变量声明:统一命名规范 + // BASE_API —— 公开/运行期 API 基址(浏览器 + 按需渲染共用,空 = 同源 /api) + // BASE_API_SSG —— 构建期 SSG 内部端口基址(仅服务端,绝不进前端 bundle) + // SSG_TOKEN —— SSG 内部接口可选访问令牌(仅服务端) + // 注意:Astro 仅自动把 PUBLIC_ 前缀变量暴露给客户端,BASE_API 没有该前缀, + // 故必须在 schema 中声明为 access:'public',否则浏览器端 import.meta.env.BASE_API 为 undefined。 + env: { + schema: { + BASE_API: envField.string({ context: 'client', access: 'public', optional: true }), + BASE_API_SSG: envField.string({ context: 'server', access: 'secret', optional: true }), + SSG_TOKEN: envField.string({ context: 'server', access: 'secret', optional: true }), + }, + }, }); \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index cda8617..beced48 100644 --- a/deploy.sh +++ b/deploy.sh @@ -28,7 +28,7 @@ for i in $(seq 1 60); do sleep 2 done -echo "==> [4/5] 构建前端镜像(API_SSR=http://localhost:8090,构建期网络=host)" +echo "==> [4/5] 构建前端镜像(构建期网络=host,经 BASE_API_SSG 拉取 SSG 数据)" docker compose build frontend echo "==> [5/5] 启动前端" diff --git a/docker-compose.yml b/docker-compose.yml index 7361943..39127f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,9 +51,8 @@ services: build: context: . args: - SSG_API: http://localhost:8091 # 构建期 SSG 拉数据:指向 backend 的 SSG 内部端口(配合 network: host 访问回环绑定的 8091) - API_SSR: http://localhost:8091 # 兼容旧命名(效果同 SSG_API) - SSG_TOKEN: ${SSG_TOKEN:-} # 与 backend 的 SSG_TOKEN 保持一致;为空则构建期无需携带 token + BASE_API_SSG: http://localhost:8091 # 构建期 SSG 拉数据:指向 backend 的 SSG 内部端口(配合 network: host 访问回环绑定的 8091) + SSG_TOKEN: ${SSG_TOKEN:-} # 与 backend 的 SSG_TOKEN 保持一致;为空则构建期无需携带 token network: host restart: unless-stopped ports: diff --git a/src/components/Index.astro b/src/components/Index.astro index 2d49522..95203d0 100644 --- a/src/components/Index.astro +++ b/src/components/Index.astro @@ -4,14 +4,14 @@ import ComingSoon from "@/components/ComingSoon.astro" import { toAbs } from "@/lib/api" import { getI18n } from "@/i18n/utils" import { href, PATH } from '@/lib/routes' -import { getSsgPopularBrands } from "@/lib/ssg" +import { getSsgPopularBrands } from "@/lib/api" // 国际化:一行取当前语言 + 翻译函数 const { locale, t } = getI18n(Astro) // 首页「热门品牌」区块:构建期(SSG 内部接口,server-only)拉取 20 个热门品牌, // 封面/标题取自每个品牌代表走秀,直接烧入静态 HTML,运行期不再请求;后端不可达则静默留空(区块隐藏)。 -// 具体 base 地址 + token 拼接已在 @/lib/ssg 内封装。 +// 具体 base 地址 + token 拼接已在 @/lib/api 的 SSG 函数内封装。 const popular = await getSsgPopularBrands(20) type GalleryItem = { diff --git a/src/components/RunwayLooksDetail.astro b/src/components/Item.astro similarity index 99% rename from src/components/RunwayLooksDetail.astro rename to src/components/Item.astro index e68d145..c7704db 100644 --- a/src/components/RunwayLooksDetail.astro +++ b/src/components/Item.astro @@ -3,7 +3,7 @@ import type { RawArticle } from "@/lib/data" import Breadcrumb from "@/components/Breadcrumb.astro" import { getI18n } from "@/i18n/utils" import { href, PATH } from '@/lib/routes' -import { ssrBase } from "@/lib/ssr" +import { ssrBase } from "@/lib/api" interface Props { article: RawArticle | null diff --git a/src/components/pages/article.astro b/src/components/pages/article.astro index 254f65b..bbbe4b1 100644 --- a/src/components/pages/article.astro +++ b/src/components/pages/article.astro @@ -1,7 +1,7 @@ --- import type { RawArticle } from "@/lib/data" import Breadcrumb from "@/components/Breadcrumb.astro" -import { ssrBase } from "@/lib/ssr" +import { ssrBase } from "@/lib/api" interface Props { article: RawArticle | null diff --git a/src/components/pages/brands.astro b/src/components/pages/brands.astro index 398fa8e..ab84655 100644 --- a/src/components/pages/brands.astro +++ b/src/components/pages/brands.astro @@ -1,5 +1,7 @@ --- -import { getSsgBrands, type SsgBrand } from "@/lib/ssg" +import { getSsgBrands, type SsgBrand } from "@/lib/api" +import { getI18n } from "@/i18n/utils" +import { href, PATH } from "@/lib/routes" type Brand = SsgBrand @@ -11,7 +13,7 @@ type Brand = SsgBrand // · 每个字母一行:左侧字母标签 + 右侧该字母下品牌名横向铺成多列 // · 无副标题、无数字 badge、无大字标题、无悬停黑底、无搜索框 // 构建期 SSR 拉数据走 SSG 内部接口 /api/ssg/brands(不对外暴露,位于独立内部端口 8091); -// base 地址 + token 拼接已在 @/lib/ssg 内封装。后端不可达则回落空数组(页面显示空索引)。 +// base 地址 + token 拼接已在 @/lib/api 的 SSG 函数内封装。后端不可达则回落空数组(页面显示空索引)。 const { locale } = getI18n(Astro) const brands = await getSsgBrands({ size: 300, only_with_articles: 1, featured: 1 }) const loadError = brands.length === 0 diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index c58b4b9..c36c5f9 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -112,7 +112,7 @@ const queryString = Astro.url.search;