搭建支持内网私有 npm 仓库的 unpkg CDN 站点

摘要

本文对 unpkg 的服务和实现原理作了简单介绍,并提供了私有化部署的 fork 修改方案参考。希望能对有此类需求的同学提供一定的帮助。

目录
[隐藏]

1 unpkg 简介

1.1 什么是 unpkg

unpkg 是一个实现对 npm 上的资源提供 CDN 的服务。官方的介绍是这样的:

The CDN for everything on npm.
unpkg is a fast, global content delivery network for everything on npm.

unpkg 官方地址为: https://unpkg.com

1.2 使用基于 unpkg 的 CDN 服务

unpkg 引用资源的基本格式是这样的:

unpkg.com/:package@:version/:file

以下为具体的例子:

# 直接访问 npm 包:会跳转至最新版本,可以以目录的形式查看该包的具体资源列表
https://unpkg.com/console-log-colors/

# 以目录的形式,查看某个版本的资源列表
https://unpkg.com/browse/console-log-colors@0.3.5/

# 查看一个文件的内容
https://unpkg.com/browse/console-log-colors@0.3.5/src/index.js

# 查看一个文件的内容(raw 原始源码,可作为 CDN 资源的引用)
https://unpkg.com/console-log-colors@0.3.5/src/index.js

以 CDN 方式引用 bootstrap 资源的示例:

<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.2.3/dist/css/bootstrap.min.css" crossorigin="anonymous">
<script src="https://unpkg.com/bootstrap@5.2.3/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>

1.3 unpkg 的实现原理

npm 包的更新与下载是通过 http://registry.npmjs.com 提供的标准接口实现的,可以通过该 registry 标准接口服务获取一个包的最新信息。获取到的包信息中包含了其所有版本的基本信息和 tgz 下载地址。示例:

unpkg 的原理即基于 http://registry.npmjs.com 提供的标准服务接口,在访问一个包时从 npmjs 获取包信息(并缓存一定的时间),从包信息中取得查询版本或最新版本的信息,拼接出 tgz 下载地址,然后请求下载并以流的方式解压,将解压后的资源进行处理后返回。如果是目录形式或文件预览的地址请求格式,还会进行服务器端渲染(React),返回渲染后的 html 页面。

由于 npm 包具体版本的资源内容是不会变更的,unpkg 对每一个确定性的版本目录或文件都设置了长达一年的 http 长缓存 header 信息,在标准浏览器下只要成功访问过一次,后续基本都会仅从浏览器缓存中读取,不再请求服务器资源。

2 unpkg 在国内

在国内类似的资源,可以搜索到且比较稳定的有如下两个:

简单分析了解一下可以得出如下初步的结论:

  • elemecdn 是完全基于 unpkg 官方源码进行的私有化部署,界面、功能表现、可访问资源均基本与 unpkg 官方表现一致。从异常资源报错信息来看缓存资源应该存放于阿里云 CDN 上。
  • zhimg 的访问形式基本与 unpkg 保持一致,但在目录访问的界面上有一定的差异,可以简单推测为参考 unpkg 做了个性化定制。而且添加了白名单机制,并非所有的 npm 包资源都可访问到。

此外,它们均没有提供官方的对外开放服务声明,也就是说个人学习研究默认可以白嫖,企业级应用是需要避免使用的,以免带来不确定的生产级问题。

3 部署支持私有 npm 仓库的 unpkg

3.1 为什么要私有化部署

  • unpkg 官方 CDN 服务器在境外,国内部分地区访问速度并不理想。
  • elemecdnzhimg 等国内可用资源不能作企业级服务。
  • 内网私有 npm 仓库上的包无法通过外部服务访问。
  • 提供一个快捷浏览内网 npm 私有包资源的渠道。
  • more…

就个人开发来说,unpkg 服务提供的目录访问方式,可以让我们无需下载即可方便快捷的了解一个内网私有 npm 包的资源。

3.2 私有化部署存在的问题

unpkg 代码仓库是开源的,因此我们可以基于其开源仓库代码进行私有化部署:

然而,直接使用官方仓库进行部署基本是行不通的,其也没有提供明确的私有化部署说明文档。根据个人分析总结,官方仓库存在的问题:

  • unpkg.com 的开发和部署为主,没有私有化部署的直接说明文档,私有化部署一般需作部分逻辑修改。
  • 默认使用了 CLOUDFLARE 上的服务做统计。私有化部署一般不会使用,可修改代码移除。
  • 近两年未更新,社区有少量 PR 但无人合并。
  • more…

3.3 基于官方仓库修改以适配私有化部署

前文已经做过分析说明,直接基于官方仓库代码进行私有化部署多多少少的会存在一些问题。我们可以 fork 官方仓库,修改源码以适配私有 npm 仓库的部署。

下面是本人 fork 官方仓库并做了一些修改适配的仓库,并添加了开发与私有部署的步骤指引:

该仓库的主要修改点有:

  • 支持并使用 pnpm 安装和管理依赖。
  • 将所有外部依赖包全部升级至当前最新版本并进行了兼容(2023-02)。
  • 升级并适配了 React 18。
  • 包下载地址由拼接改为从包信息中读取。私有化部署的下载地址可能与标准格式有差异。
  • 支持 .env[.local|.prod] 的方式配置环境变量,实现简单的私有化部署配置方式。
  • 支持 CLOUDFLARE 统计开关,默认关闭。
  • 支持 Google Analytics 配置为私有 ID,不配置默认为关闭。
  • 参考了部分官方仓库未合并的 PR 的修改。
  • More…

你也可以基于仓库提交记录进行详细了解。

可以展望的更多改进:

  • 文件首次访问慢(因为需要下载 tgz 并解压),可考虑对 tgz 包进行服务端缓存,开发包缓存策略
  • 支持需要授权的私有包请求(官方已有未合并的相关 PR)
  • More…

3.4 unpkg 私有化部署流程参考

拉取代码并安装依赖:

git clone https://github.com/renxia/unpkg.git
cd unpkg
pnpm i

创建并编辑 .env.prod 内容,设置相关环境变量配置:

cp .env.sample .env.prod

项目构建并打包:

set NODE_ENV=production
pnpm build
pnpm pack

服务器部署:

tar zxvf unpkg-1.0.0.tgz
cd package
npm i --omit dev
# pnpm i -P
pm2 -n unpkg start.js

也可以基于以上命令编写 dockerfile,基于 docker 镜像进行部署。

4 开源项目结合免费 CDN 服务的另类玩法

除了 unpkg,著名的针对开源项目提供免费 CDN 服务的 jsdelivr 也支持 npm 资源的 CDN 访问。此外,它还支持 GitHub 仓库、WordPress 插件的资源 CDN 访问服务。由于 GitHub 在国内访问体验极差,基于 jsdelivr 服务实现代理请求的方案也衍生了许多有意思的开源实现。

unpkgjsdelivr 的基本目标是为开源社区项目提供一种免费 CDN 服务方案。但基于其基本服务能力和开源社区的开放能力,衍生了许多曲径通幽效果的应用方案。

4.1 服务请求代理加速

npmgithub 在国内访问的速度体验不太好,基于免费 CDN 服务做功能扩展,可实现对开源社区资源的加速访问。

如著名的 hunshcn/gh-proxy 项目,基于 CDN 服务实现对 github 资源代理请求。ghproxy 的基本能力:

  • 文件访问: https://mirror.ghproxy.com/github.com/lzwme/blog-examples/blob/main/README.md
  • 附件下载:wget https://mirror.ghproxy.com/https://github.com/stilleshan/ServerStatus/archive/master.zip
  • 仓库 clone:git clone https://mirror.ghproxy.com/github.com/lzwme/scoop-proxy-cn

基于 ghproxy 的基本能力,则又可衍生许多针对 github 服务加速的项目。如:

  • 资源类项目加速。如 lzwme/scoop-proxy-cn 即是本人尝试性创建的一个这样的仓库资源。它是一个 scoop 应用资源镜像库,基于 ghproxy 的能力进行下载资源链接转换,针对国内用户使用 scoop 安装从 github 下载的应用提供加速支持。
  • github 浏览加速。如 du33169/gh-proxy-buttons 是一个基于 gh-proxy 的油猴脚本,为 github 中的特定链接(releases、文件、项目地址)添加一个悬浮按钮,提供代理后的加速链接。此类魔改脚本非常多。
  • more…

github 全站代理访问。

  • https://github.com/netnr/workers
  • https://cors.zme.ink/https://github.com/lzwme/

至于 npm 资源,则有搭建私有 npm 的完整开源解决方案(如 cnpm 等),结合 unpkg 则可实现任意 npm 资源的极速访问。

其他类似 ghproxy 服务的项目:

  • (git-cloner/gitcache)[https://github.com/git-cloner/gitcache]

4.2 静态网页托管

提供静态托管服务的有许多,如 gh-pagesVercel 等,但都会因为合规问题,容易被防火长城拦截,因 DNS 污染而无法访问。

基本需求:简单的托管在线 demo 用于演示。只需要将静态网站项目发布为 npm 包,然后即可通过 unpkg 服务进行访问。示例:

  • https://unpkg.com/cocos2d/tests/public/index.html
  • http://10.49.121.43:8080/@gf/html-print@0.0.1/demo/comm/include.html

4.3 附件网盘、另类图床

网站 CDN 服务大多用于 cssjs 等资源的托管,但也可以上传附件、图片等。于是出现了利用 unpkgjsdelivr 服务托管图床的方案。

  • 对于 github:创建仓库并上传图片资源,然后利用 jsdelivr 服务引用。
  • 对于 npm:将 github 图床仓库发布为 npm 包,基于 unpkg 服务引用图片。
  • 基于 github actions 创建自动发布任务,新图片提交则发布新版本至 npm

示例:

  • http://unpkg.com/yongruizhang-blogimg/

需注意:

  • npm 作为图床可能会因包体积过大引发封号风险,一般单个包应不超过 1G。过大的资源包可以分多个包发布。
  • jsdelivr 对 github 仓库大小有限制,不超过 50M

这类方案如果流行起来,npm 及各种 npm 镜像都要被玩坏了。

5 总结与参考

本文对 unpkg 的服务和实现原理作了简单介绍,并提供了私有化部署的 fork 修改方案参考。希望能对有此类需求的同学提供一定的帮助。

除了 unpkg,著名的针对开源项目提供免费 CDN 服务的 jsdelivr 也支持 npm 资源的 CDN 访问。此外,它还支持 GitHub 仓库、WordPress 插件的资源 CDN 访问服务。由于 GitHub 在国内访问体验极差,基于 jsdelivr 服务实现代理请求的方案也衍生了许多有意思的开源实现,有兴趣可以进一步的进行相关探索。

点赞 (2)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code