基于内容网格的内容管理系统

生活 Nov 3, 2020

文本网格(Content Mesh) 是 Gatsbyjs 联合创始人 Sam Bhagwat 提出的依靠多种微服务弹性构建内容项目的开发方式,它弥补了单一工具短板影响项目整体性能的短板。

基于内容网格理念,选择各类最佳工具,可以快速构建本地写作,快速发布,迅速上线项目,并使访问者获得极致体验。

随着Markdown Headless CMS、JAMstack、CI/CD 等技术的成熟,我们通过简单的集成各类工具,便可以搭建一套简单、高效、快速的内容管理系统。

系统构思

核心诉求

  1. 写作是根本目的,工具只起到辅助作用,要尽量采用有专业团队或开源社区长期维护的基础工具,并且部署方式也应尽可能简单,无需过多二次开发。
  2. 本地和网络要有合适的内容编辑器,方便内容生产。
  3. 网络访问体验要快速极致,并天然适应移动互联网。
  4. 内容生产、管理和分发彼此独立,一个环节微调,不必全局调整,内容便于整理、存储和迁移。
  5. 资源复用:对于文字、图片、视频等内容,可实现复用、节约空间。
  6. 各种微系统配置和文件备份、恢复方便,便于后期生产工具迁移。

整体框架

基于内容网格的内容管理系统

  1. 本地内容生产:PC 选择 Win 或 Mac 系统都可以。以自己习惯而定。编辑器唯一的要求是支持Markdown,便于格式统一和转换。
  2. 服务器后端:JAMstack 站点支持直接渲染 MD 文件,很多人也喜欢直接把 MD 文件发布到静态网站,供访客访问。但这存在一个问题,一旦更换主题或更换前端,需要重写 Yaml 文件设置标题、标签、日期等元数据来适应新主题。采用一个具有固定格式的 Headless CMS,统一管理文章,看似笨重,长远却是一劳永逸。另外,无头CMS 还有诸如自动社交分享、内容付费、RSS、订阅辅助等功能。 Ghost 自2013年发布以来,已经在 Github 上获得 35.2k Star,Ghost 后台界面简洁,支持富文本编辑和MD, 可以方便的插入图片、视频,并可作为内容源,发布内容到 Gatsby、Eleventy、Hexo 等静态网站生成器。
  3. 服务器前端:JAMstack 是前端趋势,访问快速安全、部署方便。Gatsby.js 是基于 React 的静态网站生成器,运行速度极快,是 JAMstack 优秀代表 。自2015年 Kyle Mathews 在 Github 上发布第一个版本以来,已经从简单的开源项目成长为商业产品,并拿到了几轮大额融资,这对核心团队稳定、产品优化升级具有重要意义,完全靠个人开发者或开源社区支持的纯开源软件走不长久。另外,Gatsby 丰富的插件库、影子系统、高人气开发社区,非常有利于二次开发。
  4. 云服务:云服务作为基础设施,大厂技术差异不大,我们主要用服务器作为内容网格数据存储和管理后台,日常并无访问压力,选择 Ghost 需要的最低配服务器即可。腾讯云被一毛不拔的 MJJ 亲切称为良心云,值得拥有。
  5. 代码托管:如果 Github 说自己是这个星球上最大的同性社交网站,恐怕没人反对,背靠微软,人多艳美,福利多多,个人开发者白嫖各种功能,Fork Repo、可以满足日常需要。
  6. 内容托管:静态网站托管(Website Hosting)是由云厂商提供的便捷、稳定、高拓展性的托管服务,无需自建服务器,即可一键部署网站应用,将静态网站发布到全网节点,实现稳定、高并发、快速访问等能力。腾讯云的对象存储和静态网站托管均可用于内容网格体系的内容托管
  7. 持续集成和自动部署: 使用内容网格发布内容时,由于内容管理、代码托管和内容发布分别在 Ghost、Github 和静态网站托管平台三个独立微服务上,需要借助三者的 API 功能,利用持续集成/持续部署(CI/CD) 实现内容的自动发布。 GitHub Actions 是 GitHub 的持续集成服务,云函数是腾讯云开发的 Serverless 执行环境,搭配使用两者,串联 Ghost、Github 和静态网站托管平台,可实现内容的自动发布。
  8. 如果喜欢国产的话, Coding 背靠腾讯,可无缝对接腾讯云,并且支持从 Github 导入代码,也有持续集成和自动部署 Gatsby 静态网站功能。
  9. 评论系统:知名 Blogger Pat Flynn 说“Without comments, a blog isn’t really a blog. To me, blogging is not just about publishing content, but also the two-way communication and community building aspects behind it.”(没有评论系统的博客不是真正的博客,对我而言,博客不仅是发布内容,还有背后的互动交流和社群建设)。内容网格需要的评论系统要适配静态托管,有评论数据存储、管理、导出等功能,还要有通知支持,及时通知评论者和博主评论情况,满足要求的有 ValineCommento等。

内容规则

  1. 本地内容文件夹命名规则为:"YY-iNote-Posts"单位命名,如“2020-iNote-Posts”。

  2. 本地文章命名规则:“YYMMDD-标题”,如“20201005-基于内容网格的写作系统”。

  3. 本地内容 Yaml 元数据

    template: post
    title: 基于内容网格的内容管理系统
    slug: content-mesh
    date: 2020-10-26T05:58:20.522Z
    category: 生活
    tags:
      - 生活
    description: 利用 Ghost、Gihutb、云函数、Gatsby 服务,可以搭建基于内容网格(Content Mesh)的 JAMstack 内容管理系统。项目搭建简单迅速,并使访问者获得极致体验。
    draft: false
    featuredImage: https://static.inote.xyz/img/2020102601.png
    
  4. CMS数据标签与本地内容元数据对应规则:Post Title=title,URL=slug,tag=tags,Excerpt=Mata Data=description

  5. 图片/视频命名规则:“YYMMDD+当日编号”。如“2021103101”

  6. 域名分配

    主域名:https://inote.xyz
    CMS: https://cms.inote.xyz
    comment: https://cmt.inote.xyz
    静态资源:https://static.inotexyz
    

项目部署

本地内容生产

以 Mac 为例,内容写作用到的生产工具很简单,简单到只要会使用系统自带的文本编辑器就好。

  1. 配置:安装必要的brew、git、nodejs等应用。方便使用 GitHub 和 图床等远程服务。

    • 安装 brew
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
    • 安装 git 并配置
    # 利用终端,全局安装 git,没有安装时,mac 通过xcode自动安装git
    git
    # 配置git 账号
    git config --global user.name "x"
    git config --global user.email "i@gmail.com"
    # 本地生成公钥,私钥,在访达中使用 Option-Command-. 显示隐藏文件,在系统用户根目录 .ssh 文件夹中复制 id_rsa.pub 中的密钥配置到 GitHub 的 GitHub SSH key 中配置。 
    ssh-keygen -t rsa -C "i@gmail.com"
     # 在本地永久存储 GitHub 凭据,省的以后使用时,还需要频繁输入。
    git config --global credential.helper osxkeychain
    # 设置完成,可以在系统用户根目录的 .	gitconfig 文件的查看到如下内容,说明配置成功。
    [credential]
    	helper = osxkeychain
    
    
    • 配置 nodejs,直接从官网下载最新版安装包,安装即可。
  2. 输入法: 当前 Mac 上的主流输入法有四种,Mac 系统自带输入法,百度 Mac 输入法,搜狗 Mac 输入法 和鼠须管。

    功能 Mac 自带输入法 百度 Mac 输入法 搜狗 Mac 输入法 鼠须管
    上手难易 ‼️ ❗️ ❗️ ‼️
    云输入
    个人词库
    隐私保护
    多端同步 不支持 手动同步 自动同步 手动同步
    皮肤 通过“系统偏好设置-通用-强调色”自定义 支持第三方皮肤 支持第三方皮肤 支持第三方皮肤
    • Mac 自带输入法配置:本身没有多少可以配置的,为方便输入,可以把常用符号,短语,邮箱保存到 “系统偏好设置→键盘→文本”中。这样,录入输入码,即可出现短语。而且文本可以通过 iCloud 多端同步。如果要替换的内容多,可以把文本上拖拽到桌面,编辑后再拖回文本。下面是一些常用的字符和短语。

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <array>
      	<dict>
      		<key>phrase</key>
      		<string>α</string>
      		<key>shortcut</key>
      		<string>ai</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>β</string>
      		<key>shortcut</key>
      		<string>bei</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>✘</string>
      		<key>shortcut</key>
      		<string>cha</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>×</string>
      		<key>shortcut</key>
      		<string>cheng</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>=</string>
      		<key>shortcut</key>
      		<string>deng</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>°C</string>
      		<key>shortcut</key>
      		<string>du</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>°</string>
      		<key>shortcut</key>
      		<string>du</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>✓</string>
      		<key>shortcut</key>
      		<string>dui</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>☒</string>
      		<key>shortcut</key>
      		<string>fang</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>☑</string>
      		<key>shortcut</key>
      		<string>fang</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>γ</string>
      		<key>shortcut</key>
      		<string>ga</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>+</string>
      		<key>shortcut</key>
      		<string>jia</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>-</string>
      		<key>shortcut</key>
      		<string>jian</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>m³</string>
      		<key>shortcut</key>
      		<string>li</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>㎨</string>
      		<key>shortcut</key>
      		<string>mi</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>㎧</string>
      		<key>shortcut</key>
      		<string>mi</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>㎲</string>
      		<key>shortcut</key>
      		<string>mu</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>μ</string>
      		<key>shortcut</key>
      		<string>mu</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>Ω</string>
      		<key>shortcut</key>
      		<string>ou</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>㎢</string>
      		<key>shortcut</key>
      		<string>ping</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>m²</string>
      		<key>shortcut</key>
      		<string>ping</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>……</string>
      		<key>shortcut</key>
      		<string>sheng</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>.</string>
      		<key>shortcut</key>
      		<string>v</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>㎛</string>
      		<key>shortcut</key>
      		<string>wei</string>
      	</dict>
      	
      	<dict>
      		<key>phrase</key>
      		<string>→</string>
      		<key>shortcut</key>
      		<string>you</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>•</string>
      		<key>shortcut</key>
      		<string>yuan</string>
      	</dict>
      	<dict>
      		<key>phrase</key>
      		<string>←</string>
      		<key>shortcut</key>
      		<string>zuo</string>
      	</dict>
      </array>
      </plist>
      
    • 精美百度皮肤

    • 鼠须管设置

  3. 配置 obsidian

    obsidian 是当前流行的知识管理软件,支持 markdown 双链笔记和卡片日记应用,支持本地部署,保护数据隐私。它有丰富的插件功能,个人用户可免费使用,非常适合作为第二大脑,管理密集的网络化知识。

    要安装 obsidian ,直接从obsidian 官网下载即可。 安装后的配置可以在官方 help 中找到答案。如果需要 iPhone 、iPad 和 Mac 三端同步,且希望备份到版本管理工具 GitHub 的话,可以按如下操作。

    1. 通过 iOS 新建obsidian 库,并选择同步到 iCloud ,自动同步到 Mac 后,可以在 iCloud云盘中看到 Obsidian 文件夹。里面有刚在 iOS 端创建的文件夹
    2. 在 mac iCloud 库 obsidian 文件夹中删除在 iOS 端创建的文件夹,为建立 GitHub 本地库初始化做准备,GitHub 本地库只能在空文件夹中建立。
    3. 通过终端,进入 Mac 端iCloud 库 Obsidian 文件夹。可以使用下文推荐的超级右键,在 Obsidian 文件夹上右键新建终端窗口。
    4. 建立远程关联
    # 在github 网页端建立仓库,配置 .gitignore 忽略文件,防止上传缓存,删除的文件等。
       # to exclude Obsidian's settings (including plugin and hotkey configurations)
       .obsidian/
    
       # OR only to exclude workspace cache
       .obsidian/workspaces.json
    
       # Add below lines to exclude OS settings and caches
       .trash/
       .DS_Store
    # 初始化本地仓库
    git init
    # 在终端窗口 clone github 中的库,此处建议使用 SSH,因为网络原因,https 经常会出现 443 错误。
    git clone git@github.com:x/i.git
    # 书写内容,把本地文件 push 到 Github 仓库
    cd i(子目录)
    # 清理本地缓存文件,不然 .gitignore 不会起作用。
    git rm -r --cached .
    git add .
    git commit -m "update .gitignore"
    # 文件添加到仓库(.代表提交所有文件)
    git add .
    # 把文件提交到仓库
    git commit -m "增加obisidian"
    # 上传到github
    git push
    # 在网页查看是否成功
    
    1. 安装 Obsidian Git,并设置自动备份。

    除了官方核心插件外,也可以安装第三方插件。常用的第三方插件有

    • Advanced Tables:高级表格应用
    • Calendar:日历
    • Mindmap:思维导图
    • Outliner: 大纲增强
    • Ozan‘s Image in Editor Plugin:辅助实现可见即所得,功能还不太完美,Obsidian 官方也在开发可见即所得功能,可以期待下(20211031)。
    • Style Settings:用于自定义第三方主题
  4. Typroa 配置

    文字编辑不需要特殊配置,只需要在主题中选择自己喜欢的主题即可快速上手,对 PicGo 插件进行图像配置,可快速上传图片至图床,减少发布时的编辑量。

    在文件-图像-下载或更新中,安装picgo-core插件,然后在配置文件中进行相关配置:

    npm 安装 picgo -g

    sudo npm install picgo -g
    

    在typora 后台图像选择 custom command 命令输入

    /usr/local/bin/node /usr/local/bin/picgo upload
    

    点击 “验证图片上传选项”,在系统用户根目录下生成 .picgo/config.json,配置 config.json。

    {
      "picBed": {
        "uploader": "tcyun",
        "tcyun": {
          "secretId": "<SecretId>",
          "secretKey": "<SecretKey>",
          "bucket": "<存储桶名称>",
          "appId": "<APPID>",
          "area": "<区域>",
          "path": "<目录>",
          "customUrl": "<自定义域名>",
          "version": "v5"
        }
      },
      "picgoPlugins": {},
    }
    
  5. 其他Mac 应用推荐:

    • magent :分屏辅助
    • ishot:截图工具
    • 自动切换输入法 :固定输入法,免得切换
    • 超级右键:实现类似 win 鼠标右键功能

服务器配置

  1. 服务器选择

    选择基本满足 Ghost 部署需求的服务器或轻量应用服务器即可, 需要用到的云函数、静态托管、COS等,最好与服务器位于同一个地区,以加快访问速度。

  2. 参考官方教程创建root用户

    // 为 root 用户创建密码 
    sudo passwd root
    // 放通 root 用户登录,设置 PermitRootLogin yes
    sudo vi /etc/ssh/sshd_config 
    // 重启 service ssh 服务
    sudo service ssh restart
    // 以 root 登录
    su root
    
  3. 端口:放通 TCP:22,3389,80,443,8086,20,21,465,587 等入站规端口。腾讯云默认限制云服务器 TCP 25 端口连接外部地址,在安全管控中解封 25 端口。

  4. 存储桶映射

    存储桶名称 映射功能
    ghost Ghost 图床
    static 静态资源
    coding Coding 静态网站

Ghost 部署

Ghost 官方教程非常详细,按流程操作即可安装成功。由于我们只把 Ghost 作为数据源,为保证主站 SEO,在设置中勾选 Make this site private,禁止搜索引擎爬取和收录。

设置腾讯云 COS 作为 Ghost 图床

使用腾讯云 COS 作为图床,有两种方法。

方法一:使用腾讯云 COSFS 将 COS 存储桶挂载到服务器 Ghost 安装目录

COSFS 工具支持将 COS 存储桶挂载到本地,像使用本地文件系统一样直接操作腾讯云对象存储中的对象,COSFS 提供的主要功能包括:文件读写、目录操作、链接操作、权限管理、uid/gid 管理等功能。使用该方式配置图床,图片实际存储在 COS 中,但图片访问路径像把图片存储在本地一样。根据官方教程进行如下操作:

此方法并不适合用作 ghost 图床,在 ghost 升级、数据读取等存在不稳定性,建议采用第二种方法,用 ghost 图床插件进行设置。

  1. 获取源码

    git clone https://github.com/tencentyun/cosfs /usr/cosfs
    
  2. 安装依赖

    sudo apt-get install automake autotools-dev g++ git libcurl4-gnutls-dev libfuse-dev libssl-dev libxml2-dev make pkg-config fuse
    
  3. 编译和安装 COSFS

    cd /usr/cosfs
    ./autogen.sh
    ./configure
    make
    sudo make install
    cosfs --version  
    
  4. 配置

    a. 登录腾讯云 - 控制台 - 对象存储 - 存储桶获取相关信息

    • BucketName-APPID : test-1250000000 为存储桶名称
    • 区域地址 : https://cos.ap-shanghai.myqcloud.com 存储桶所在的区域域名.

    b. 登录腾讯云 - 控制台 - 访问管理,创建子账号并赋予 COS 相关权限,生成SecretIdSecretKey.

    • SecretId : AKI2mW1G4Owjuq5zOCWEgt68ua0EIo5nBq6
    • SecretKey : AdqxqFOJ3ZLqA6mCLuc86J2h5C9O1MQf

    c. 配置密钥文件,在文件/etc/passwd-cosfs中,写入您的存储桶名称(格式为 ),以及该 存储桶对应的 ,三项之间使用半角冒号隔开。为了防止密钥泄露,COSFS 要求将密钥文件的权限设置成640,配置/etc/passwd-cosfs密钥文件的命令格式如下:

    // 官方样本
    // sudo su  # 切换到 root 身份,以修改 /etc/passwd-cosfs 文件;如果已经为 root 用户,无需执行该条命令。
    echo <BucketName-APPID>:<SecretId>:<SecretKey> > /etc/passwd-cosfs
    chmod 640 /etc/passwd-cosfs
    // 设置权限
    chmod 640 /etc/passwd-cosfs
    
  5. 挂载 COS 到 Ghost 图片目录

    将已经在密钥文件中配置好信息的存储桶挂载到指定目录

    // 官方样本
    cosfs <BucketName-APPID> <MountPoint> -ourl=<CosDomainName> -odbglevel=info -oallow_other
    -o nonempty // 挂载到本地的目录不为空时.
    -oallow_other //允许其他用户访问,允许Web直连下载.
    

    其中:

    • 为本地挂载目录(例如/mnt)。
    • 为存储桶对应的访问域名,形式为http://cos.<Region>.myqcloud.com (适用于 XML API,请勿在该参数中携带存储桶名称),其中 为地域简称, 例如 ap-guangzhou 、 eu-frankfurt 等。更多地域信息,请参见 可用地域
    • -odbglevel 指定日志级别。
    • -oallow_other 允许非挂载用户访问挂载文件夹。
  • -ononempty 文件夹有内容

具体到 Ghost ,配置如下

cosfs XXX-12500000 /var/www/ghost/content/images -ourl=https://cos.ap-shanghai.myqcloud.com -odbglevel=info -onoxattr -oallow_other
  1. 卸载

    // 官方
    umount -l /mnt/cosfs
    // 本地
    umount -l /var/www/ghost/content/images
    
  2. 设定 COSFS 开机自动挂载,在 /etc/fstab 文件中添加如下的内容,其中,_netdev 选项使得网络准备好后再执行当前命令

    sudo vim /etc/fstab
    // 官方
    cosfs#examplebucket-1250000000 /mnt/cosfs fuse _netdev,allow_other,url=http://cos.ap-guangzhou.myqcloud.com,dbglevel=info
    // 本地
    cosfs#文件夹有内容  /var/www/ghost/content/images fuse _netdev,allow_other,url=http://cos.ap-shanghai.myqcloud.com,dbglevel=info
    
  3. 挂载成功后,可以看到 COS 中对应目录中的图片成功加载到 /var/www/ghost/content/images 中。

方法二:使用 Ghost-qcloud-cos 插件把 COS 设置为图床

  1. 图床使用 ZhelinCheng 开发的 ghost-qcloud-cos 插件。

  2. 安装过程与 ZhelinCheng 给出的方法略有出入。

    a. cd 到 Ghost 安装目录

    cd /var/www/ghost
    

    b. 安装cos模块

    sudo npm install ghost-qcloud-cos
    

    c. 创建存储文件夹(如果不存在)

    sudo mkdir -p content/adapters/storage
    //设置文件夹权限
    sudo chown <user>:<user> content/adapters/storage
    sudo chown ubuntu:ubuntu content/adapters/storage
    sudo chmod 775 content/adapters/storage
    
  3. 将模块文件复制到该文件夹

     sudo cp -vR node_modules/ghost-qcloud-cos content/adapters/storage/ghost-qcloud-cos
    
  4. 创建名为“ cos-store.js”的脚本文件,并录入以下内容

       sudo touch  content/adapters/storage/cos-store.js
       sudo vim  content/adapters/storage/cos-store.js
       // 填写以下内容
       module.exports = require('ghost-qcloud-cos');
    
  5. 配置 config.[env].json

    cd /var/www/ghost/
    sudo vim config.production.json
    // 用你的COS信息替换,记得 COS 访问权限在后台设置为“公有读私有写”
         "storage": {
         "active": "cos-store",
         "cos-store": {
           "rename": "true",
           "secretId": "<SecretId>",
           "secretKey": "<SecretKey>",
           "bucket": "<存储桶名称>",
           "Region": "<区域>"
           }
        },
    
  6. 重启生效。

       ghost restart
    
  7. 运行 ghost restart 常见错误及解决方法

    // Message: The content folder is not owned by the current user.Ensure the content folder has correct permissions and try again.运行:
    sudo chown -R <user>:<user> ./content
    //  Message: Systemd process manager has not been set up or is corrupted.
    // 如果再次错误,Run ghost setup linux-user systemd and try again.运行:
    ghost setup linux-user systemd 
    ghost restart
    

设置 Gmail 邮箱为后台 SMTP 服务器

  1. 启用 Gmail IMAP

    在 Gmail 邮箱 → 设置 → 转发和 POP/IMAP → IMAP访问中启用 IMAP

  2. 启用 Gmail 安全性较低的应用的访问权限

    打开链接Less secure,将“不够安全的应用访问权限"设置为"启用"。

    如果 Google 账户已启用两步验证,则需要使用应用专用密码才能访问不够安全的应用。

    转到 Google 帐号,在左侧导航面板中,选择安全性,再选择其他应用,生成应用专用密码。

  3. 配置 Ghost 服务器

    编辑根目录下 config.production.json,修改mail{}部分,如果没有启用两步验证,pass 赋值 Gmail 密码,如果启用了两步验证,pass 赋值 第2步生成的应用专属密码,重启生效。

     "mail": {
        "transport": "Direct"
      },
      # 修改为: 
      "mail": {
        "transport": "SMTP",
        "options": {
            "service": "Gmail",
            "auth": {
                "user": "<Gmail 邮箱地址>",
                "pass": "<Gmail 邮箱密码或应用专属密码>"
    			}
    		}
    	},
    

部署 Commento 评论

  1. 用 root 用户, 安装 postgresql 和 postgresql-contrib

    sudo apt-get update
    sudo apt-get upgrade
    sudo apt install postgresql postgresql-contrib
    
  2. 用 root 用户,创建系统用户 commento,数据库用户 commento 和数据库 commento。Ubuntu系统利用 apt 安装 PostgreSQL 时,默认已安装 postgres 用户,利用其创建新用户 commento , 并为其配置数据库 commento,完成配置后,打开 PostgreSQL 的后台.

    // 用root用户,创建系统用户commento,并赋予sudo权限
    adduser commento
    usermod -aG sudo commento
    // 用 commento 用户, 利用 postgres ,创建新用户 commento,并为其配置数据库 commento。
    sudo -u postgres createuser commento
    sudo -u postgres createdb -O commento commento
    // 用 commento 用户,为数据库用户 commento 创建数据库密码
    psql
    commento=# ALTER ROLE commento with PASSWORD '<commento-passwd>';
    commento=# \q
    // 打开PostgreSQL的后台
    sudo systemctl enable postgresql
    
  3. 利用 root 用户,在官网下载 commento 最新二进制文件,解压到 /usr/local/bin/commento文件夹中

    sudo su
    wget https://dl.commento.io/release/commento-v1.8.0-linux-glibc-amd64.tar.gz
    mkdir /usr/local/bin/commento
    tar xvf commento-v1.8.0-linux-glibc-amd64.tar.gz -C /usr/local/bin/commento
    
  4. 利用 ghost 用户和 acme.sh 为 commento 域名签发 ssl 证书,并配置反向代端口

    sudo -i -u ubuntu
    cd /var/www/ghost
    ghost config url https://cmt.inote.xyz
    ghost setup nginx ssl
    ghost config url https://cms.inote.xyz
    // 把cms.inote.xyz 的端口改回2368
    cd /var/www/ghost
    vim config.production.json
    "port": 2368,
    // 找到location中proxy_pass一项,把端口号改成喜欢的端口号<commento_port>比如:8086
    sudo vim /var/www/ghost/system/files/cmt.inote.xyz.conf
    sudo vim /var/www/ghost/system/files/cmt.inote.xyz-ssl.conf
    // commento目前不兼容nosniff, 搜索nosniff,切换到 root 用户,删除或注释掉它
    sudo vim /etc/nginx/snippets/ssl-params.conf
    //add_header X-Content-Type-Options nosniff;
    
  5. 用 root 用户,创建 commento.service 服务

    sudo vim /etc/systemd/system/commento.service
    [Unit]
    Description=Commento daemon service
    After=network.target postgresql.service
    
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/commento/commento
    Environment=COMMENTO_ORIGIN=<域名>
    Environment=COMMENTO_PORT=8086
    Environment=COMMENTO_POSTGRES=postgres://commento:commento@127.0.0.1:5432/commento?sslmode=disable
    
    
    // 用户注册完成后,再取消注释
    // Environment=COMMENTO_SMTP_HOST=smtp.gmail.com
    // Environment=COMMENTO_SMTP_PORT=587
    // Environment=COMMENTO_SMTP_USERNAME=<gmail 邮箱>
    // Environment=COMMENTO_SMTP_PASSWORD=Gmail 邮箱密码或应用专属密码
    // Environment=COMMENTO_SMTP_FROM_ADDRESS=<gmail 邮箱>
    
    // 用户注册完成后,再取消注释
    // Environment=COMMENTO_FORBID_NEW_OWNERS=true
    
    // 反垃圾邮件设置
    // Environment=COMMENTO_AKISMET_KEY=<AKISMET_KEY>
    
    [Install]
    WantedBy=multi-user.target
    
  6. 启动服务

    // 用 commento 用户,运行如下命令
    sudo chmod u+x /etc/systemd/system/commento.service
    sudo systemctl start commento
    sudo systemctl enable commento
    // 用 commento 用户,重启 NGINX 路由
    sudo nginx -s reload
    // 检查 commento 是否在运行中
    sudo lsof -i -n|grep LISTEN|grep commento
    
  7. 通过浏览器访问 https://cmt.inote.xyz/signup 完成相关配置

  8. 登录 commento 用户重新配置 commento.service,包括 SMTP,取消注释新用户注册、AKISMET反垃圾评论设置等,配置完后,重启服务。

    su - commento
    sudo systemctl enable --now commento
    

制作镜像或快照,以备不时之需

完成以上步骤后,一个运行 Ghost 博客和 Commento 评论系统的服务器已经搭建完成。为了减少重装麻烦,可以利用腾讯云快照和镜像功能,定期备份。

使用 Gatsby 作为博客前端

在本地部署,完成二次开发

  1. 安装 Nodejs、Github Desktop、Gatsby CLI,配置本地开发环境。

  2. npm,yarn 设置淘宝源,加速国内访问速度,为保证依赖安装成功率,建议使用 yarn 命令。

    // 移除 npm 代理
    npm config rm proxy
    npm config rm https-proxy
    // 查看当前配置
    npm config get registry
    // npm 设置淘宝源
    npm config set registry https://registry.npm.taobao.org
    // yarn 设置淘宝源
    yarn config get registry
    yarn config set registry https://registry.npm.taobao.org -g
    // 安装 cnpm
    npm install -g cnpm
    // 清理缓存
    npm cache clean --force
    yarn cache clean
    // 切换回 npm 源
      npm config set registry  https://registry.npmjs.org
    
  3. 选择 Gatsby-Starter-Try-Ghost ,部署在本地。

    //  clone repo 到本地
    git clone https://github.com/<user-name>/<repo-name>.git <local-dir>
    // 安装依赖
    cd <loacl-dir> 
    yarn
    //  运行开发模式
    yarn develop
    // 运行生产模式
    yarn build
    
  4. 在本地通过 http://localhost:8000 浏览站点,修改调试。

利用组件影子(Component Shadowing)功能,更换字体

利用 Gatsby 主题的组件影子功能,对字体进行替换。比如,替换主题 Gatsby-starter-try-ghost 的字体,只需要在src目录下新建gatsby-theme-try-ghost文件夹, 从Gatsby-starter-try-ghost使用的主题gatsby-theme-try-ghost 复制对应目录和文件,进行字体栈替换,然后运行 Gatsby bulid,字体替换成功。

常用的中文字体栈如下:

// 使用中文字体栈,在 CSS 中用以下配置替换 font-family
font-family: "lucida grande", "lucida sans unicode", lucida, helvetica, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;

配置 Commento 插件

yarn add gatsby-theme-ghost-commento
// In your gatsby-config.js
plugins: [
    {
        resolve: `gatsby-theme-ghost-commento`,
        options: {
            url: `<commento-url>`,
        },
    },
]

配置 Google 统计

yarn add gatsby-plugin-google-analytics
// In your gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: "YOUR_GOOGLE_ANALYTICS_TRACKING_ID",
        head: false,
        exclude: ["/preview/**", "/do-not-track/me/too/"],
        pageTransitionDelay: 0,
        // Defers execution of google analytics script after page load
        defer: false,
      },
    },
  ],
}

配置 Google adsense,在静态文件夹stacit中添加 ads.txt,然后安装依赖,并完成设置

yarn add @isamrish/gatsby-plugin-google-adsense
// In your gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `@isamrish/gatsby-plugin-google-adsense`,
      options: {
        googleAdClientId: "YOUR_GOOGLE_ADSENSE_TRACKING_ID",
        head: true
      }
    }
  ]
};

把 Gatsby 数据源替换为自己的数据,在根目录建立.ghost.json

touch .ghost.json
// In your .ghost.json
{
  "development": {
    "apiUrl": "<Your_Ghost-URL>",
    "contentApiKey": "<Your_Ghost_Content——API>"
  },
  "production": {
    "apiUrl": "<Your_Ghost-URL>",
    "contentApiKey": "<Your_Ghost_Content——API>"
  }
}

利用 Github Desktop 把本地开发完成的 Gatsby 目录文件 push 回 repo。

Gatsby 部署常见错误

  1. Windows Powersell 无法将“apt-get”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。用管理员模式,运行以下命令,并选A
Set-ExecutionPolicy Unrestricted
  1. 解决 raw.githubusercontent.com 被污染 ,利用站长工具找对应IP地址,然后再修改本地 host

  2. node_modules缺少个别安装包,可利用 cnpm 单独安装

    cnpm i -g mozjpeg@7.0.0
    yarn add gatsby-2.24.67.tgz
    

自动部署和持续集成

方法一:利用 Github 实现自动部署和持续集成

当 Ghost 中发生内容变化(内容发布、更新、删除)时,利用 Ghost API 向 Gatsby 的 Github Repo 发送 Github Action 触发通知,通过 Github Action 更新 Gatsby,然后再把 Gatsby Build的新 public同步到静态站点。 由于 Ghost API 只是一个简单的 Webhook , 而触发 Github Action repository_dispatch 需要包含 header、repo owner 、Gihhub 个人令牌等信息的 cURL 作为外部事件。因此,我们利用云函数作为中间件,把 Ghost 的 Webhook 通知转换为可以触发 repository_dispatch的通知。

  1. 创建腾讯云函数,运行环境选择 python,编辑函数代码内容,创建 API 网关触发器,复制访问路径,添加到 Ghost Integrations 的 wenbhook中 , Event 选择 Site changed。

    // -*- coding: utf8 -*-
    // 替换 <owner>、<repo>、<access-token>
    import requests
    def main_handler(event, context):
        r = requests.post("https://api.github.com/repos/<owner>/<repo>/dispatches",
        json = {"event_type": "update from cms"},
        headers = {"User-Agent":'<owner>}',
                  'Content-Type': 'application/json',
                  'Accept': 'application/vnd.github.everest-preview+json',
                  'Authorization': 'token <access-token>})
        if r.status_code == 204:
            return "This's OK!" 
        else:
            return r.status_code
    
  2. 在 Gatsby Repo 创建 Github Action,变量在各服务的获取后,配置在 repo的 Secrets 中。在静态托管环节,有两种方案:a. 把 Gatsby 部署到腾讯云静态网站托管,当Ghost 删除文章后,本方法目前不能同步执行删除静态文件。b. 把 Gatsby 部署到 COS,本方法可以利用cosmd 命令自动删除ghost cms 删除对应静态文件。并完成相关配置。

    // push 代码、repository_dispatch 时触发 workflow
    on: [push, repository_dispatch]
    
    jobs:
      deploy:
      
        // 指定运行所需要的虚拟机环境
        runs-on: ubuntu-latest
        name: Ghost-SCF-Github-Gatsby-Static
        steps:
        
    	 // 获取源码
          - name: Checkout
            uses: actions/checkout@v2
            
    	//  设置 Nodejs
          - name: Set Node.js
            uses: actions/setup-node@master
            with:
              node-version: 10.x
              
        // 安装依赖
          - name: Install dependencies
            run: yarn install --prod --pure-lockfile
            
        //  运行生产模式
          - name: Build
            run: yarn run build
            env:
              GHOST_API_URL: "https://gatsby.ghost.org"
              GHOST_CONTENT_API_KEY: ${{ secrets.ghost_content_api_key }}
              GATSBY_ENV: "prod"
              
         // a. 利用云开发 Github Action 扩展,把静态文件目录 public 部署到云端。     
          - name: Deploy static to Tencent CloudBase
            id: deployStatic
            uses: TencentCloudBase/cloudbase-action@v1.1.1
            with:
              secretId: ${{ secrets.SECRET_ID }}
              secretKey: ${{ secrets.SECRET_KEY }}
              envId: ${{ secrets.ENV_ID }}
              staticSrcPath: public
              
        // b. 利用 coscmd  扩展,把静态文件目录 public 部署到 COS。 
          - name: Install coscmd
            run: sudo pip install coscmd
            
      
          - name: Configure coscmd
            env:
              SECRET_ID: ${{ secrets.SecretId }}
              SECRET_KEY: ${{ secrets.SecretKey }}
              BUCKET: front-gatsby-1250000000
              REGION: ap-nanjing
            run: coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
             
          - name: Upload to Cos
            run: coscmd upload -rs --delete -f ./public/ /    
    

方法二:利用 Coding 实现自动集成和自动部署

  1. 将 Github Repo 设置为公开,通过 https://<team>.coding.net/import导入代码。
  2. 在项目设置开发者选项关联 Github Repo。这时候,可以再将 Github Repo 设置为隐私模式。
  3. 按照 定时同步私有代码库到 CODING方法,设置代码更新自动同步(代码源更新-代码更新时自动执行)。
  4. 在持续部署静态网站中选择选择部署 Gatsby 静态网站,网站即可自动部署到腾讯云 COS,设置自定义域名,一个 Gatsby 网站就部署完成了。
  5. 为了在 Ghost 更新时,Coding 静态网站也同步更新,我们还需要通过 Ghost webhook 、腾讯云函数(SCF)、Coding API 触发进行持续集成设置。点击部署列表-立即触发-前往参数默认值设置,进入静态网站持续部署设置页面,在触发规制,API 触发中选择生成 curl 命令触发示例 设置项目令牌过期时间,并复制代码。
  6. 在腾讯云函数中新建云函数,运行环境选择 CustomRuntime,模板选择 shell函数示例,把从 Coidng API 触发复制来的代码,贴入 index.sh, 删除config.jsonserverless.yaml保存函数。在触发管理中,创建 API 触发管理器,复制访问路径。
  7. 把从 SCF 复制的访问路径,粘贴到 Ghost Webhook中,设置完成触发规则,当 Ghost 更新时,Coding 静态网站即可完成自动部署,并同步到 腾讯云对应 COS 中。
  8. 在 COS 的函数计算中设置好 CDN缓存刷新函数 确保 CDN 边缘及时更新最新内容。
  9. 为节约调用 SCF 费用,把触发 Coding API 对应函数,运行内存设置为64MB,执行超时设置为1秒。CDN 刷新函数的时间类型选择全部创建全部删除,触发条件前缀设置为index.html(只有当index.html发生变化时,才触发 CDN 刷新函数执行刷新任务。)运行内存设置为256MB,执行超时时间设置为1秒。

利用 Gatsby Cloud 监控静态网站运行情况

Gatsby Cloud 中添加 Github Repo,进行CMS预览、Lighthouse 评分,部署异常信息,运行状态监测等。

资源费用

名称 配置 费用
域名 .xyz 69/年
Ghost 开源 免费
云服务器 1核2G3M, 轻量服务器 588元/3年
Commento 开源 免费
Github 个人版:无限个私人Repo 免费
Github Action 私人版:2核CPU, 7G RAM,14G SSD硬盘虚拟机, 每月 2000分钟构建时间 免费
Gatsby 开源 免费
Gatsby Cloud 个人版:每月100次RTEs 免费
SCF 每月免费额度:资源使用量40万GBs,调用次数100万次。
COS 每月免费额度:标准存储容量50GB,外网下行流量10GB,CDN 回源流量10GB,读请求数100 万次,写请求数:100 万次。 免费

Tags

席一舟

四野八荒,苦茶清禅。渔樵江渚,浊酒西风。 螽斯蓝寿,稀禾芡菱。嬉笑怒骂,行藏自由。 策马悬崖,雕弓满月。惊涛拍岸,毕力遐方。 银鞍白马,一剑寒霜。此生谁料,咸是妙明。

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.