PVQ-UI
v1.4.3
Gitee

文档

PVQ-UI 的开发方案、项目规则、组件清单与调用方法。文档为代码唯一真源,本页直接渲染仓库中的真实 Markdown。

部署方案

PVQ-UI 部署方案(腾讯云 COS)

唯一真源:本文件。配置/脚本变更须先更新本文件再改代码。 关联:《PVQ-UI组件库开发方案.md》§3(产物 CDN 单一源)、《PVQ-UI组件库调用手册.md》。

1. 目标

组件库产物(CSS/JS)通过 coscli(腾讯云官方命令行)一键部署到腾讯云 COS;各插件经 pvq-ui-loader.php 以固定 URL 调用:

https://cdn-buke.pvq.ink/pvq-ui/v{ver}/css/pvq-ui.css

版本号隔离在路径中,旧版本保留不删;调用方按「全站一处定义版本」(推荐,见《调用手册》§2.3)或显式锁版本(见《调用手册》§2)。

2. 前置条件

  • 安装 coscli:https://cloud.tencent.com/document/product/436/63144
    • macOS:brew install coscli
    • 其他平台:下载官方二进制并加入 PATH
  • 腾讯云账号 + 已建存储桶(如 pvq-ui-125xxxxxxx)
  • 将自定义域名 cdn-buke.pvq.ink 绑定到该桶的 CDN 加速域名(未绑定时见 §6 临时方案)

3. 配置文件

3.1 cos.config.example.json(入库模板)

真实文件 cos.config.json 不入库(.gitignore 已忽略),本地复制后填入。

{
  "secretId":     "YOUR_SECRET_ID",
  "secretKey":    "YOUR_SECRET_KEY",
  "bucket":       "pvq-ui-125xxxxxxx",
  "region":       "ap-guangzhou",
  "customDomain": "https://cdn-buke.pvq.ink",
  "localDir":     "assets",
  "remotePrefix": "pvq-ui",
  "cacheControl": "max-age=31536000,immutable"
}
字段 说明
secretId / secretKey 腾讯云 API 密钥(仅本地,绝不入库)
bucket 存储桶名(含 APPID,如 pvq-ui-125xxxxxxx)
region 地域,如 ap-guangzhou
customDomain 绑定的自定义域名,用于自检与 loader 默认 URL
localDir 本地产物目录,默认 assets
remotePrefix COS 内前缀,默认 pvq-ui
cacheControl 上传时设置的 Cache-Control 响应头

3.2 coscli 认证(独立于仓库)

coscli 读 ~/.cos.yaml(由 coscli config 生成,含 secretId/secretKey/buckets/region)。deploy.sh 不传任何密钥,只调用 coscli,密钥安全由 coscli 本地配置承担,与仓库解耦。

4. 部署流程

cp cos.config.example.json cos.config.json   # 填入真实 bucket / region / 密钥
coscli config                                # 按提示生成 ~/.cos.yaml
./scripts/deploy.sh                          # 自动读取 assets/css/pvq-ui.css 的 @version 部署
./scripts/deploy.sh 1.1.3                    # 或显式指定版本号

脚本步骤:校验 coscli 与 cos.config.json → 读取版本号 → coscli sync assets/ cos://{bucket}/{prefix}/v{ver}/ -e {endpoint} --meta "Cache-Control: {cacheControl}" → curl 自检自定义域名 URL 返回 200 → php scripts/purge-cdn.php 刷新 CDN 边缘缓存(调用腾讯云 PurgeUrlsCache API,清除刚上传文件的边缘节点缓存,约 5 分钟生效;需 cos.config.json 内 secretId/secretKey,缺密钥则跳过并提示)。

依赖:本地需有 php CLI(用于解析 JSON 与读取 @version)。

4.1 为什么脚本显式传 -e(endpoint)

coscli 自行拼 endpoint 的前提是目标桶写在 ~/.cos.yaml 的 buckets 里。本机的该文件曾多次被重置,只剩 pvq-moe-1251529261、丢掉 CDN 实际使用的 buke-1251529261,于是报:

Head "https://buke-1251529261./pvq-ui%2Fv1.4.2%2Fcss%2Fpvq-ui.css": EOF

判据:报错 URL 的域名畸形(https://<bucket>./,缺 endpoint 后缀)= 该桶没进 ~/.cos.yaml 的 buckets,而不是网络问题。 治本做法即脚本现在这样:由 cos.config.json 的 region 推出 endpoint 显式传入,不再依赖 ~/.cos.yaml 的桶清单:

ENDPOINT="cos.$REGION.myqcloud.com"          # ap-shanghai → cos.ap-shanghai.myqcloud.com
coscli sync assets/ cos://{bucket}/{prefix}/v{ver}/ -r -e "$ENDPOINT" --meta "Cache-Control:$CACHE"

⚠️ coscli 的 -e 只接受主机名(不带 https://);写成 https://cos.ap-shanghai.myqcloud.com 会报 invalid bucket format, please check your cos.BaseURL。 兜底修复(若仍需修配置):备份 ~/.cos.yaml 后,在 buckets 列表补一条 - name: buke-1251529261 / alias: 同名 / region: ap-shanghai(密钥字段沿用 base 段,勿打印)。

5. COS 存储结构 ↔ loader URL

cos://pvq-ui-125xxxxxxx/pvq-ui/v1.1.2/      # 版本目录(URL 的一部分,旧版本永久保留)
  ├─ css/
  │   ├─ pvq-ui.css
  │   ├─ pvq-ui-admin.css
  │   ├─ pvq-ui-editor.css
  │   └─ pvq-ui-frontend.css
  ├─ js/
  │   └─ pvq-ui.js
  └─ components.json        # 组件清单机器可读唯一真源(§3.5),与库同版本、位于版本目录根

层级约定:deploy.sh 直接 sync assets/(内含 css/、js/ 两个子目录),故 CSS 落在 v{ver}/css/、JS 落在 v{ver}/js/;components.json 由脚本单独 cp 到版本目录根。loader 拼 URL 时必须带上这两个子目录名(漏写会 404)。 components.json 与库 CSS/JS 同版本发布,官网画廊(site/inc/gallery.php)线上运行时读它,库一升级发布,画廊自动跟随多组件、变稳定性标签(详见《组件库开发方案》§3.5)。 对应 loader 默认 URL(CDN_BASE = https://cdn-buke.pvq.ink/pvq-ui,按 v{ver}/ + 子目录拼接): https://cdn-buke.pvq.ink/pvq-ui/v1.1.2/css/pvq-ui.css 等。

6. 调用方式

require_once 'pvq-ui-loader.php';
PVQ_UI_Loader::enqueue( array( 'admin' ), '1.3.1' );  // 版本号按当前锁定版本替换

自定义域名尚未绑定到 COS 时,站点可用常量覆盖为 COS 默认访问域名(注意 CSS 在 css/、JS 在 js/ 子目录,且常量名须与 loader 完全一致,前端层是 PVQ_UI_FRONT_URL):

define( 'PVQ_UI_URL',        'https://pvq-ui-125xxxxxxx.cos.ap-guangzhou.myqcloud.com/pvq-ui/v1.3.1/css/pvq-ui.css' );
define( 'PVQ_UI_ADMIN_URL',  'https://pvq-ui-125xxxxxxx.cos.ap-guangzhou.myqcloud.com/pvq-ui/v1.3.1/css/pvq-ui-admin.css' );
define( 'PVQ_UI_EDITOR_URL', 'https://pvq-ui-125xxxxxxx.cos.ap-guangzhou.myqcloud.com/pvq-ui/v1.3.1/css/pvq-ui-editor.css' );
define( 'PVQ_UI_FRONT_URL',  'https://pvq-ui-125xxxxxxx.cos.ap-guangzhou.myqcloud.com/pvq-ui/v1.3.1/css/pvq-ui-frontend.css' );
define( 'PVQ_UI_JS_URL',     'https://pvq-ui-125xxxxxxx.cos.ap-guangzhou.myqcloud.com/pvq-ui/v1.3.1/js/pvq-ui.js' );

7. 缓存策略

上传即设 Cache-Control: max-age=31536000, immutable。版本号已在路径中,更新即换路径,旧 URL 永久有效,长缓存零风险。

8. 安全

  • cos.config.json(真实密钥)与 ~/.cos.yaml 均 不入库;仅 cos.config.example.json 入库。
  • 严禁将 SecretId / SecretKey 提交到任何仓库或文档。
  • 本仓库 .gitignore 已忽略 cos.config.json、.cos.yaml、本地临时文件。

9. 多版本共存与回滚

每次 deploy 上传到独立 v{ver}/ 目录,旧版本保留。回滚只需调用方把 enqueue 的版本号改回旧版,COS 上无需删除旧目录。

10. 本方案不 bump 库产物版本

本次新增的是部署工具链(配置 + 脚本 + 文档),CSS/JS 产物内容未变,不提升库版本号;仅在 readme.md 增加「部署」段、CHANGELOG.md 补一条部署工具记录。

11. 官网部署(FTP → pvq-ui.pvq.ink)

官网是库的对外门面:展示全部组件、说明与调用方法,位于仓库 site/。采用 PHP 动态渲染 doc/ 下的 Markdown(文档为单一真源,改一处网站自动同步),组件资源直接引用 COS CDN(cdn-buke.pvq.ink),不另存一份。

目标主机为 PHP 虚拟主机,部署走 FTP:

cp ftp.config.example.json ftp.config.json   # 填入真实 FTP 信息(host/user/pass/remoteDir…)
php scripts/deploy-ftp.php                    # PHP 原生 ftp 扩展,镜像 site/ + doc/ 到 remoteDir

脚本行为:

  1. 读 ftp.config.json(真实文件,不入库)→ 连接(优先 FTPS ftp_ssl_connect,失败回退普通 FTP)→ 被动模式。
  2. 递归上传 site/ → remoteDir,并把 doc/ → remoteDir/doc(官网实时渲染用)。
  3. site/lib/Parsedown.php(Markdown 解析器)随 site/ 一并上传。
  4. 排除本地专用文件(v1.2.10 新增,重要):site/.local 是本地预览标记,config.php 检测到它就把 PVQ_CDN 指向 /assets/lib(本地软链副本、零缓存);线上必须走 CDN 模式,绝不能有 .local。早期脚本无排除逻辑,.local 被一并上传 → 线上进入本地模式 → 引用 /assets/lib/*,而该路径依赖 site/assets/lib -> ../../assets 的符号链接(FTP 无法在线上复现软链)→ 全部 404 → 官网组件无样式。故 deploy-ftp.php 内置排除名单(.local、.gitignore、.git、.DS_Store、node_modules、.codebuddy、index.html、404.html、.htaccess、.user.ini),只影响上传,不动仓库本地文件,本地预览照旧。
    • index.html / 404.html(v1.4.0 新增排除):二者是宝塔等 PHP 主机生成的「站点创建成功」占位页,.gitignore 已标注为非源码。不排除就会被上传成公网可访问的 /index.html,与官网首页并存(本主机 DirectoryIndex 以 index.php 优先、不会遮蔽首页,但占位页仍不该对公网可见)。若线上已存在旧副本,手动 FTP DELE /index.html、DELE /404.html 即可清除,无需重传全站。
    • .htaccess / .user.ini(v1.4.0 新增排除):同属主机本地环境文件(.gitignore 已标注)。site/.htaccess 只有 1 字节,若上传会覆盖主机自己的 rewrite 配置;site/.user.ini 含 open_basedir 等环境配置,线上为 root 所有,每次上传都以 553 Can't open that file 失败、只刷错误日志。二者一律不传。
    • 配套:site/assets/lib 软链仅供本地使用(单一真源),线上组件资源一律走 CDN(cdn-buke.pvq.ink/pvq-ui/v{ver}/),因此软链不可上传也不必上传。
    • 若线上已误传 .local:手动 FTP 删除(DELE /.local)即可切回 CDN 模式,无需重传全站。

安全与耦合:

  • 真实 ftp.config.json 含 FTP 密码,不入库(.gitignore 已忽略),仅 ftp.config.example.json 入库。
  • 网站版本与库版本耦合:site/inc/config.php 的 PVQ_UI_VERSION 必须与当前库版本一致;升库版本时同步修改它,否则网站引用的 CDN 资源路径会失效。
  • 域名 pvq-ui.pvq.ink 的 web 根指向 remoteDir,访问 https://pvq-ui.pvq.ink/ 即官网首页。
  • 组件清单 JSON 双轨(与《组件库开发方案》§3.5 一致):components.json 位于 site/inc/,随 site/ 由 deploy-ftp.php 上传到 web 根;同时 deploy.sh 把它同步到 COS pvq-ui/v{ver}/components.json。官网画廊 gallery.php 本地开发时读仓库内 site/inc/components.json(改 JSON 刷新即见),线上运行时读 COS 同版本 JSON——新增组件只改 components.json,画廊自动出现,无需改 gallery.php。
  • doc/ 必须随部署进入 web 根内的 doc/ 目录:线上 deploy-ftp.php 把 doc/ 上传到 remoteDir/doc,与铺在 web 根的 site/ 程序同级;docs.php 探测 is_dir(__DIR__.'/doc') 为真 → 走「同级 doc」分支,不越级访问。这是线上唯一正确形态,切勿让 docs.php 走到 ../doc 分支。

11.1 本地预览与 open_basedir 约束

docs.php 的文档根探测(site/docs.php:8):

$docBase = is_dir(__DIR__ . '/doc') ? __DIR__ . '/doc' : __DIR__ . '/../doc';
  • 线上(web 根 = remoteDir,site/ 内容铺在 web 根,doc/ 在 web根/doc):__DIR__.'/doc' 存在 → 走前半段,安全。
  • 本地直接把 site/ 当 web 根跑(宝塔建站 / 本地 nginx 根指向 site/):若主机对站点设 open_basedir 锁到 site/(+ /tmp),则仓库根 doc/(在 site/ 之外)不会被纳入 → docs.php 走到 ../doc 分支 → is_file() 被 open_basedir 拦截,报 Warning: is_file(): open_basedir restriction in effect … 并落到「文档未找到」。

方案 A(推荐,贴合线上架构):让 site/doc 存在即可,不动环境安全配置。

  • 本地执行:cp -r doc site/doc(真实复制,不可用软链——open_basedir 会解析软链真实路径,仍判越界)。
  • 把 site/doc/ 加入 .gitignore:仓库根的 doc/ 才是单一真源,site/doc 仅本地预览派生产物,不得入库。
  • 之后 docs.php 探测 is_dir(__DIR__.'/doc') 为真,warning 消失、文档正常渲染。
  • 改了仓库根 doc/ 后需重新 cp -r doc site/doc 才反映到本地预览(线上由 deploy-ftp.php 自动同步,无需手动)。
  • 备选方案 B(不推荐):放宽 open_basedir 把仓库根纳入。代价是削弱站点安全隔离,仅为迁就代码,非长久之计。