点击展开更新日志

2024

12-06

  1. 新增了缺失的更新日志(正是在下)
  2. 新增图床配置
  3. 新增 Nginx 配置

2025

04-14

新增Waline数据库备份内容。

nexttime

会有些什么呢(❁´◡`❁)

概述

记录一下 Hexo 从毛坯房到现在的主要建设过程。

评论

Gitalk

Waline

⚓ 先吐个槽,这个 B 玩意搞了一晚上,我真服了哦

有问题可以去官网:Waline-评论系统

简介

Waline 部署有服务端、数据库,客户端已经在 Butterfly 主题集成了所以不用再管。服务端官方是用的免费的 LeanCloud,建议用国际版不需要备案域名,也有其它 vercel 之类的可以免费部署,所以我选择独立部署(?)。数据库也是单独部署,现成的可以用官方上面提到的 MongoDB 等。

数据库部署

使用 RPM 的方式部署 MySQL 8.4.1

  1. 下载 RPM 包仓库源

    1
    2
    3
    # https://dev.mysql.com/downloads/repo/yum/
    wget https://dev.mysql.com/get/mysql84-community-release-el7-1.noarch.rpm
    yum localinstall mysql84-community-release-el7-1.noarch.rpm

    如果安装失败: Could not resolve host: mirrorlist.centos.org ,替换为阿里云源即可:

    1
    2
    3
    4
    5
    6
    7
    cd /etc/yum.repo.d
    # 备份
    mkdir backup
    mv -f *.repo backup

    # 下载镜像
    wget xxx
  2. 安装 MySQL

    1
    2
    3
    4
    5
    6
    7
    8
    9
     yum install mysql-community-server
    #
    yum install mysql-community-{server,client,client-plugins,icu-data-files,common,libs}

    # 查看版本
    mysql --version

    # 启动 MySQL
    systemctl enable --now mysqld
  3. 登录 MySQL

    1
    2
    3
    4
    5
    6
    # 获取临时密码
    grep "temporary password" /var/log/mysqld.log
    mysql -uroot -p

    # 修改默认密码
    ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass4!';
  4. 创建表结构
    waline.sql 的内容复制/上传到服务器某个位置,比如 /data/waline/waline.sql

    登录服务器:

    1
    2
    3
    create database waline;
    use waline;
    source /data/waline/waline.sql

    如果没有报错那就创建好了,继续后面的步骤。如果有报错先解决错误!

  5. 修改配置文件 my.cnf

    1
    2
    3
    4
    5
    vim /etc/my.cnf
    # 在 `[mysqld]` 块下添加 mysql_native_password=ON

    # 重启服务
    systemctl restart mysqld

    pk7bEkR.png

    如果是 MySQL 5.7 就不需要做这步,直接跳过!
    Waline 密码认证用的还是 MySQL Native Password 插件,但是 MySQL 8.4 已默认不启用了,所以要手动开一下,记得重启服务。——参考链接

  6. 创建用户

    1
    2
    3
    4
    5
    # MySQL 8.4
    create user 'waline'@'%' identified with mysql_native_password by 'MyPassword!';

    # MySQL 5.7
    create user 'waline'@'%' identified by 'MyPassword!';

    这里对于来源 IP,如果是实机部署 服务端,可以指定具体 IP 或 loclahost,但我是用的容器部署 waline,所以不设置不限制。

  7. 赋权

    1
    grant all privileges on waline.* to waline;
  8. 备份
    既然好不容易才将数据库建好,使用一段时间后自然会有很多数据(是谁的数据库里只有自己的测试数据啊😶。。),避免数据意外丢失的方法就是多备份,不要相信什么我只要不乱搞就不会丢的想法。。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #!/bin/bash
    # Author: Evergarden
    # Created: 2025/04/14
    # Updated: 2025/04/14
    # Version: V0.1
    # Description: waline 数据库备份脚本

    # 备份目录,不需要带 /
    BACKUP_DIR="/data/backup/sftp/database"
    # 备份错误日志及DUMP日志目录
    DUMP_LOG_PATH="/data/backup/sftp/database/logs"
    DB_HOST="127.0.0.1"
    DB_USER="root"
    DB_PASSWORD="114514"
    # 文件名日期格式 yyyymmdd
    DATE_SUFFIX=$(date +%Y%m%d)
    # 数据库文件及日志保存天数
    KEEP_DAYS=7

    # 执行备份
    mysqldump --host="${DB_HOST}" -u${DB_USER} -p${DB_PASSWORD} --add-drop-table --add-locks --comments --all-databases --log-error=${DUMP_LOG_PATH}/dump_error.log > ${BACKUP_DIR}/walineDB_${DATE_SUFFIX}.sql 2>> ${DUMP_LOG_PATH}/dump.log

    # 清理历史备份
    find ${BACKUP_DIR} -type f -mtime +${KEEP_DAYS} -print

    肯定有人要问了,你这备份到本地服务器崩了也一样啊,所以你猜为什么它的父级目录为什么叫 sftp 😎。
    不过这里就需要参考另外一篇文章了:Linux搭建SFTP服务器

服务端部署

  1. 创建 docker-compose.yml
    找一个喜欢的位置创建 docker-compose.yml 文件,输入以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    version: '3'

    services:
    waline:
    container_name: waline
    image: lizheming/waline:latest
    restart: on-failure
    ports:
    - 8360:8360
    volumes:
    - ${PWD}/data:/app/data
    environment:
    TZ: 'Asia/Shanghai'
    MYSQL_HOST: 172.17.0.1
    MYSQL_PORT: 3306
    MYSQL_DB: waline
    MYSQL_USER: waline
    MYSQL_PASSWORD: Waline!0722
    MYSQL_PREFIX: 'wl_'
    MYSQL_CHARSET: utf8mb4
    MYSQL_SSL: true
    JWT_TOKEN: 'violet'
    SITE_NAME: 'Evergarden'
    SITE_URL: 'https://evergarden.top'
    SECURE_DOMAINS: 'evergarden.com'
    AUTHOR_EMAIL: '[email protected]'
    security_opt:
    - seccomp=unconfined

这里有官方的文件有一点出入,做一点说明

  • 端口不需要改,至少不要自多聪明(指某位正在写这四个字的人)改成 3306,这个是服务端的端口,和数据库没有半毛钱关系。
  • 环境变量:参考所用数据库修改即可。MYSQL_HOST 实机就写实机的地址,但如果你恰好和我一样服务端在容器,数据库在主机,这个需要写主机的 docker0 网络地址,默认是 172.17.0.1,记得确认一下。
  • security_opt:为了解决 Assertion failed: (0) == (uv_thread_create(t.get(), start_thread, this)) 的异常,是因为 docker 容器受到资源的限制,无法创建线程。添加此配置允许容器执行全部的系统调用。
    docker 这么写 docker run --security-opt seccomp=unconfined yourhub/yourimage:version参考链接
  • MYSQL_SSL 可以尝试开启,我最开始开了但是设置密码控制台报错,用的 http 请求后端不接受,所以嫌麻烦就关了。
  1. 启动容器

    1
    2
    3
    4
    5
    6
    7
    docker-compose up -d

    # 查看容器状态
    docker ps

    # 查看启动日志
    docker logs waline

    如果正常启动并没有报错,那就真是太好了。我这里光配置文件、数据库连接有问题就搞了好久。。。

  2. 开放端口
    需要到阿里云安全组开放服务端的端口 8360 ,如果没有修改过的话。

  3. 反代
    在我的前端服务器上加上一个 Nginx 的配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    vim waline.server.conf

    # 输入以下内容
    # MySQL
    server
    {
    listen 80;
    listen 443 ssl;
    http2 on;
    server_name waline.domain.top;
    # root /www/wwwroot/your.domain.server.name;
    # if ($server_port !~ 443){
    # rewrite ^(/.*)$ https://$host$1 permanent;
    # }

    # SSL setting
    ssl_certificate fullchain.pem;
    ssl_certificate_key privkey.pem;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    # ssl_prefer_server_ciphers on;
    # ssl_session_cache shared:SSL:10m;
    # ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";

    # proxy to 8360
    location / {
    proxy_pass http://walineServer;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header REMOTE-HOST $remote_addr;
    add_header X-Cache $upstream_cache_status;
    proxy_ssl_session_reuse off;
    proxy_ssl_server_name on;
    # proxy_ssl_name $proxy_host;

    # cache
    add_header Cache-Control no-cache;
    expires 12h;
    }
    }

    :label:Tips:

    注释掉 SSL 的部分是因为走的 CF 不需要,但是注释掉之后会报错,啥错误忘了。。

    尤其是要删/注释掉重写那三行,根据自己实际情况配置,我不需要所以注释了。

    配置 upstream

    1
    2
    3
    4
    vim upstream/waline.upstream.conf
    upstream walineServer {
    server 47.107.48.245:8360;
    }
  4. 验证

    此时就可以通过上面配置的域名访问了:

    • 评论系统:<domain>
    • 评论管理:<domain>/ui
    • 注册:<domain>/ui/register ,首个注册的人会被设定成管理员。
  5. CF 配置
    CF 上的 SSL 设置为 Full 模式。注意要在 Nginx 的配置中加上 SSL 了。

主题配置

修改 butterfly 主题配置:

1
2
3
4
5
waline:
serverURL: https://waline.evergardenviolet.top/ # Waline server address url
bg: # waline background
pageview: false
option:
  • serverURL 需要使用 https ,最后需要加上 /

故障处理

仅记录我遇到的故障和异常,处理我忘了,搞到凌晨。。。

  1. net::ERR_CONNECTION_CLOSED

  2. SSL_do_handshake() failed
    (SSL: error:1408F10B:SSL routines:ssl3_get_record:wrong version number)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    3. 403,522 等

    # Post 美化

    实现文章页一页到底的效果。如果是浅色背景就暂时不建议这么弄,所以我滚回去了。

    请参考[矩阵佬](https://android99.com/2021/08/10/butterfly-top-image-modify/)的博客~,多谢大佬的教程!

    ## 新建 Styl 文件

    在 `ROOT\themes\butterfly\source\css\_page` 目录新建 `topimg.styl` 添加以下内容:

    ```bash
    // 顶部图
    #page-header, #page-header:before
    background-color: transparent !important
    background-image: unset !important

    .top-img
    height: 12.5rem
    display: block
    margin: -50px -40px 50px -40px
    border-top-left-radius: inherit
    border-top-right-radius: inherit
    background-position: center center
    -webkit-background-size: cover
    -moz-background-size: cover
    background-size: cover
    background-repeat: no-repeat
    transition: all 0.3s

    .read-mode
    display: none

    @media screen and (max-width: 768px)
    margin: -1.8rem -0.7rem 1.8rem -0.7rem

    [data-theme='dark'] &
    filter: brightness(0.8)

修改 post.pug

修改 ROOT\themes\butterfly\layout\post.pug 文件:

1
2
3
4
5
block content
#post
if top_img === false
include includes/header/post-info.pug
+ .top-img.gist(style=`background-image: url(${url_for(top_img)})`)

注意事项

cover 图片请勿加入(),因为括号会破坏 css 结构。导致无法加载 cover。

如/img/1(1).webp。会加载不出来的。建议用-1(杠 1)等方式替代括号。

修改原理

基本原理就是插入一个新的标签作为头图,再用 css 隐藏旧头图和定义新头图的样式

参考及致谢

[Charon-小 N 同学](https://www.imcharon.com/60/)

RSS

插件安装

在 hexo 根目录下:

1
npm install hexo-generator-feed --save

配置站点配置文件

_config.yml 中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# feed
feed:
enable: true #是否启用插件
type: atom #有atom和rss2两个选项,使用默认atom就好了
path: atom.xml #也用默认配置atom.xml就行
limit: 20 #展示文章的数量,使用 0 或 false 代表展示全部
hub: #这个我没用上,根据官网,空着就行
content: #默认是false,true的话会在rss文件中包含整个文章内容
content_limit: 140 #摘要长度
content_limit_delim: " " #没看明白官方的意思,就跟着默认不填了
order_by: -date #采用日期进行排序
icon: icon.png #给rss链接配置icon
autodiscovery: true #自动发现提要
template: #给rss文章配置模板

配置主题配置文件

_config.butterfly.yml 中添加:

1
rss: /atom.xml

修改 social settings

1
fa-solid fa-rss: /atom.xml || RSS || '#FFCC66'

重新生成

留言页

修改为信笺样式的样式。

配置

  1. BLOG_ROOT 执行指令:
    1
    npm install hexo-butterfly-envelope --save
  2. 在站点配置文件或主题配置文件添加配置项(其一即可)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # envelope_comment
    # see https://akilar.top/posts/e2d3c450/
    envelope_comment:
    enable: true #控制开关
    custom_pic:
    cover: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg #信笺头部图片
    line: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png #信笺底部图片
    beforeimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png # 信封前半部分
    afterimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png # 信封后半部分
    message: #信笺正文,多行文本,写法如下
    - 有什么想问的?
    - 有什么想说的?
    - 有什么想吐槽的?
    - 哪怕是有什么想吃的,都可以告诉我哦~
    bottom: 自动书记人偶竭诚为您服务! #仅支持单行文本
    height: #1050px,信封划出的高度
    path: #【可选】comments 的路径名称。默认为 comments,生成的页面为 comments/index.html
    front_matter: #【可选】comments页面的 front_matter 配置
    title: 留言板
    comments: true
  1. 新建 BOLG_ROOT\themes\butterfly\layout\includes\page\envelop.pug

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #article-container
    if top_img === false
    h1.page-title= page.title
    #maincontent
    #form-wrap
    img#beforeimg(src='https://cdn.jsdelivr.net/gh/Akilarlxh/[email protected]/source/img/before.png')
    #envelope
    form
    .formmain
    img.headerimg(src=url_for(theme.envelope_comment.cover))
    .comments-main
    h3.title3=`来自` + config.author + `的留言:`
    .comments
    each i in theme.envelope_comment.message
    div=`${i}`
    .bottomcontent
    img.bottomimg(src='https://ae01.alicdn.com/kf/U0968ee80fd5c4f05a02bdda9709b041eE.png')
    p.bottomhr=`${theme.envelope_comment.bottom}`
    img#afterimg(src='https://cdn.jsdelivr.net/gh/Akilarlxh/[email protected]/source/img/after.png')
    != page.content
  2. 新建 [BLOG_ROOT]\themes\butterfly\source\css\_layout\commentsbar.styl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    if hexo-config('envelope_comment.enable')
    $hoverHeight = hexo-config('envelope_comment.height') ? convert(hexo-config('envelope_comment.height')) : 1050px
    @media screen and (max-width: 600px)
    #beforeimg,
    #afterimg
    display none !important

    @media screen and (min-width: 600px)
    #article-container
    img
    margin 0 auto 0rem
    #form-wrap
    overflow hidden
    height 447px
    position relative
    top 0px
    transition all 1s ease-in-out .3s
    z-index 0
    &:hover
    height $hoverHeight
    top -200px
    #maincontent
    width 530px
    margin 20px auto 0
    #beforeimg
    position absolute
    bottom 126px
    left 0px
    background-repeat no-repeat
    width 530px
    height 317px
    z-index -100
    pointer-events none
    #afterimg
    position absolute
    bottom -2px
    left 0
    background-repeat no-repeat
    width 530px
    height 259px
    z-index 100
    pointer-events none
    #envelope
    position relative
    overflow visible
    width 500px
    margin 0px auto
    transition all 1s ease-in-out .3s
    padding-top 200px

    .headerimg
    width 100%
    overflow hidden
    pointer-events none

    .formmain
    background white
    width 95%
    max-width 800px
    margin auto auto
    border-radius 5px
    border 1px solid
    overflow hidden
    -webkit-box-shadow 0px 0px 20px 0px rgba(0, 0, 0, 0.12)
    box-shadow 0px 0px 20px 0px rgba(0, 0, 0, 0.18)
    .comments-main
    padding 5px 20px
    .title3
    text-decoration none
    color $theme-color
    text-align center
    .comments
    text-align center
    border-bottom #ddd 1px solid
    border-left #ddd 1px solid
    padding-bottom 20px
    background-color #eee
    margin 15px 0px
    padding-left 20px
    padding-right 20px
    border-top #ddd 1px solid
    border-right #ddd 1px solid
    padding-top 20px

    .bottomcontent
    text-align center
    margin-top 40px

    .bottomimg
    width 100%
    margin 5px auto 5px auto
    display block
    pointer-events none

    .bottomhr
    font-size 12px
    text-align center
    color #999

    [data-theme='dark']
    .formmain
    background rgb(50, 50, 50)
    .comments
    background rgba(90, 90, 90, 0.8)
  3. 修改 [BLOG_ROOT]\themes\butterfly\layout\page.pug

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    block content
    #page
    case page.type
    when 'tags'
    include includes/page/tags.pug
    when 'link'
    include includes/page/flink.pug
    when 'categories'
    include includes/page/categories.pug
    when 'artitalk'
    include includes/page/artitalk.pug
    + when 'envelope'
    + include includes/page/envelope.pug
    default
    include includes/page/default-page.pug
  4. 修改主题配置文件 [BLOG_ROOT]\_config.butterfly.yml,新增配置项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    envelope_comment:
    enable: true #开关
    cover: https://ae01.alicdn.com/kf/U5bb04af32be544c4b41206d9a42fcacfd.jpg #信笺封面图
    message: #信笺内容,支持多行
    - 有什么想问的?
    - 有什么想说的?
    - 有什么想吐槽的?
    - 哪怕是有什么想吃的,都可以告诉我哦~
    bottom: 自动书记人偶竭诚为您服务! #信笺结束语,只能单行
    height: #调整信笺划出高度,默认1050px
  5. 新建留言界面(如果没有的话)

    1
    hexo new page comments

参考及致谢

图床

免费图床 or 自建

无须时间、金钱成本和任何投入,上传即可使用。随便 bing 一下就能找到很多各种各样的免费图床,这里不做推荐。

同理,毕竟是免费的,问题自然也是存在的,一方面网络及稳定性不可保证(国外图床那就更。。。比如码字的某位的站点偶尔看不到图),其次是安全和隐私性就不要想了,再者就是容量限制。如果也能接收,那其实用着也还不错。

其实一开始是没有自建图床的打算的,后面出现了两次问题,算是契机吧:

  • 直接删了图:可能是上传的动漫图片触及版权规则了,认了,没啥狡辩的
  • 加载缓慢:打开站点首页加载时间很慢很慢,一看 F12,就奶奶的图半天加载不出来

所以还是决定自己建一个,正好阿里云的 40G 平时也用不完,CPU 也闲得没事,不拿来压榨压榨心里过不去。

自建选择——EasyImages

以“开源图床”为关键字 bing,可以找到很多开源图床,比如 EasyImages2.0,原因无他,开源+还在更新。

安装

参考资料:

Docker 安装方式

官方提供的安装方式很简单,然后本地添加 php 处理后存在问题,所以还是采用了 Docker 的方式。

  1. 准备数据存放目录

    1
    mkdir -p /data/easyimages/{config,i}
  2. 编写 docker-compose.yml

    1
    2
    # 创建一个 docker-compose.yml 文件
    vim /data/easyimages/docker-compose.yml

    内容,根据实际情况调整挂载卷位置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    version: "3.3"
    services:
    easyimage:
    image: ddsderek/easyimage:latest
    container_name: easyimage
    ports:
    - "8080:80"
    environment:
    - TZ=Asia/Shanghai
    - PUID=1000
    - PGID=1000
    - DEBUG=false
    volumes:
    - "/data/easyimages/config:/app/web/config"
    - "/data/easyimages/i:/app/web/i"
    restart: unless-stopped
  3. 启动容器

    1
    docker-compose up -d
  4. 访问页面
    记得先去后台放行 8080 端口,然后就可以使用 http://<IP>:8080 访问了。

🙄: 这种方式我只能用 IP+端口访问,配置了反代(lucky 也试过了)但是请求不到 js 和 css,不知道哪里有问题,所以还是放弃了。至于宝塔,不想用罢了。

直接安装

参考资料:

其实一开始是没看懂咋配,后面反应过来了,直接打开就行了。。。

  1. 修改 Nginx 配置,以支持 PHP
    因为我最开始的配置没有配置 PHP,且没有安装 PHP,所以重新生成了一套配置。

    主站点域名保持不变,img 二级域名勾选【启用 PHP】,PHP 服务选择【TCP 127.0.0.1:9000】

    更新配置:

    1
    2
    3
    4
    # 将配置打包下载并上传到服务器(可以直接 wget)
    cd /usr/local/openresty/nginx/conf
    tar -czvf nginx_$(date +'%F_%H-%M-%S').tar.gz nginx.conf sites-available/ sites-enabled/ nginxconfig.io/
    tar -xzvf nginxconfig.io-evergarden.top,img.evergarden.top.tar.gz | xargs chmod 0644

    检查一下图床配置是否具有以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    # img.domain.com.conf
    server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name img.domain.com;
    set $base /data/easyimages/EasyImages2.0;
    root $base;

    # SSL
    ssl_certificate /data/nginx/ssl/fullchain.pem;
    ssl_certificate_key /data/nginx/ssl/key.pem;

    # security
    include nginxconfig.io/security.conf;

    # logging
    access_log /usr/local/openresty/nginx/logs/access_img.log combined buffer=512k flush=1m;
    error_log /usr/local/openresty/nginx/logs/error_img.log info;

    # index.php
    index index.php;

    # index.html fallback
    location / {
    try_files $uri $uri/ /index.html;
    }

    # index.php fallback
    location ~ ^/api/ {
    try_files $uri $uri/ /index.php?$query_string;
    }

    # additional config
    include nginxconfig.io/general.conf;

    # handle .php
    location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    include nginxconfig.io/php_fastcgi.conf;
    }
    }

    核心是 handle .php 那部分,要配置处理的后端地址。

  2. 编译安装 PHP8.4
    先解释一下,可以使用 yum install php ,这个版本是 7.4,我测试存在问题,所以安装了 8 版本。

    1. 下载解压 PHP 包

      1
      2
      3
      # 下载地址:https://www.php.net/downloads.php,如果下不动可以下到本地上传
      # 解压:
      tar xvf php-8.4.1.tar.gz
    2. 编译配置

      1
      2
      cd php-8.4.1
      ./configure --prefix=/usr/local/install/php8 --with-config-file-path=/usr/local/install/php8/etc --with-curl --with-freetype --enable-gd --with-jpeg --with-gettext --with-kerberos --with-libdir=lib64 --with-libxml --with-mysqli --with-openssl --with-pdo-mysql --with-pdo-sqlite --with-pear --enable-sockets --with-mhash --with-ldap-sasl --with-xsl --with-zlib --with-zip -with-bz2 --with-iconv --enable-fpm --enable-pdo --enable-bcmath --enable-mbregex --enable-mbstring --enable-opcache --enable-pcntl --enable-shmop --enable-soap --enable-sockets --enable-sysvsem --enable-xml --enable-sysvsem --enable-cli --enable-opcache --enable-intl --enable-calendar --enable-static --enable-mysqlnd

      如果在 CentOS8 上编译 PHP8 报错了,/usr/bin/ld: dynamic STT_GNU_IFUNC symbol mb_utf16be_to_wchar’ with pointer equality in`

      在 configure 命令中加入 --enable-mbstring=shared 重新编译。

      具体可以看 issue:Github-issue。感谢大佬!

      如果中途报错缺少依赖可以到上面参考资料里查询原因和解决方法。

    3. 编译安装

      1
      2
      3
      4
      make
      make install
      # 检查安装情况
      /usr/local/install/php8/bin/php --version
    4. 配置管理

      # 生成 php.ini
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      cp php.ini-production /usr/local/install/php8/etc/php.ini

      # www.conf
      cd /usr/local/install/php8/etc/php-fpm.d/
      cp www.conf.default www.conf

      # php-fpm 配置
      cd /usr/local/install/php8/etc/
      cp php-fpm.conf.default php-fpm.conf

      # php-fpm 可执行文件
      # ① 创建存放配置文件的目录
      mkdir /usr/local/install/php8/fpm
      # ②从源码中复制一份fpm可执行脚本。如果报错那就换成你源码的位置,我这里是 /root 下
      cp /root/php-8.4.1/sapi/fpm/init.d.php-fpm /usr/local/install/php8/fpm/php-fpm
      # ③ 将php-fpm修改为可执行文件
      chmod 740 /usr/local/install/php8/fpm/php-fpm
    5. 管理 PHP
      使用 php-fpm 进行管理 PHP。

      1
      /usr/local/install/php8/fpm/php-fpm start

      检查进程状态:

      1
      ps aux | grep php | grep -v grep

      查看 php-fpm 监听的端口:

      1
      ss -tulnp | grep php
    6. 添加环境变量

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # ① 修改环境变量
      vim /etc/profile

      # ② 添加环境变量
      PATH=$PATH:/usr/local/install/php8/bin
      export PATH

      # ③ 刷新配置
      source /etc/profile
    7. 检查

      1
      php --version
  3. 安装 EasyImages2.0

    1. 找一个喜欢的位置,然后下载下来
    1
    git clone https://github.com/icret/EasyImages2.0.git
    1. 修改 nginx 配置
      比如我下载位置在 /data/easyimages ,所以需要在图床配置中设置 php root 目录在 /data/easyimages/EasyImagesindex.php 的父目录,比如步骤 1 所示。
    2. 赋予图床目录权限

    参考资料:php 引入文件权限问题

    默认图片保存位置在安装目录 i 目录下,需要给 PHP 访问权限。

    修改 Nginx conf/nginxconfig.io/php_fastcgi.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 404
    try_files $fastcgi_script_name =404;

    # default fastcgi_params
    include fastcgi_params;

    # fastcgi settings
    fastcgi_index index.php index.html;
    fastcgi_buffers 8 16k;
    fastcgi_buffer_size 32k;

    # fastcgi params
    fastcgi_param DOCUMENT_ROOT $realpath_root;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    fastcgi_param PHP_ADMIN_VALUE "open_basedir=$base/:/usr/lib/php/:/tmp/:/data/easyimages/EasyImages2.0/:/i/"; # 重点是这里添加 `:/i/`

    该问题报错为 is not within the allowed path php8

    1. 重载 Nginx 就可以进入图床了
    2. 第一次进入会进行环境检查和安装,如果哪一项不符合根据说明处理就行了,没有难度。
      安装的时候,注意勾选【删除安装目录】,否则之后每次输入域名都会进入安装界面;如果忘记了,也可以手动删除安装目录下 install 目录。
  4. 修改权限
    如果此时登录进去修改配置保存,刷新还是原来的配置,查看 Nginx 的配置发现:

    1
    1752 FastCGI sent in stderr: "PHP message: PHP Warning:  fopen(/data/easyimages/EasyImages2.0/config/config.php): Failed to open stream: Permission denied

    修改程序目录权限:chmod 755 -R /data/easyimages/EasyImages

    1
    2
    # 修改 PHP 用户和用户组
    vim /usr/local/install/php8/etc/php-fpm.d/www.conf

    修改以下内容:

    1
    2
    3
    4
    - user = nobody
    - group = nobody
    + user = nginx
    + group = nginx

    重启服务:

    1
    /usr/local/install/php8/fpm/php-fpm restart

报错

❌️ failed to create network easyimages_default: Error response from daemon: Failed to Setup IP tables

如果遇到了以上报错,重启一下 docker 服务,大概就能解决: systemctl restart docker

反向代理

😰 绷不住了,网站上线几个月了,居然都没有补充 Nginx 方面的内容(不过也无伤大雅),那为什么现在想起来了呢?因为没有证书泛域名证书,想要搭图床遇到了:net::ERR_CERT_COMMON_NAME_INVALID

防来防去终究还是防住了自己,用的是单域名证书,没有泛域名的。。。然后翻了一下博客,居然没有教我怎么申请的内容,然后,然后就没有然后了。

Nginx 部署

可以下载 Nginx 官方包编译安装,但不建议,对于像我这样又懒又小白的,建议就用 OpenResty,为什么捏 👀,因为 OpenResty 会将一些常用的模块(https、压缩等)直接封装进安装包,就不必像 Nginx 一样需要逐个添加了。

  1. 确认系统版本:

    1
    2
    # cat /proc/version
    Linux version 4.18.0-348.7.1.el8_5.x86_64 ([email protected]) (gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)) #1 SMP Wed Dec 22 13:25:12 UTC 2021

    我这里的版本 el8 表示是 CentOS8。

  2. 进入二进制包下载页面选择对应版本安装即可
    以 CentOS8 为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # add the yum repo:
    wget https://openresty.org/package/centos/openresty.repo
    sudo mv openresty.repo /etc/yum.repos.d/openresty.repo

    # update the yum index:
    sudo yum check-update

    # 安装
    yum install -y openresty

    工具包没必要安装,如果需要就自己装吧。

  3. 至此,就可以启动测试了
    安装目录: /usr/local/openresty ,然后下一层 nginx 就和编译安装 Nginx 的目录一样了

Nginx 基础配置

如果是大佬,自己写配置当然没问题,倒不如说最推荐自己根据需要写配置。

但如果恰好(我是说恰好哦)你和我一样,不太了解 Nginx 配置,只想做一些基本配置跑起来就可以,那么这里有一个很方便的可视化配置的地方:Nginx 配置-DigitalOcean

由 DigitalOcean 提供的社区工具,可视化配置 Nginx。根据需要选择配置即可,不清楚就保持默认。

配置完成后,在下方会实施生成对应的 Nginx 配置,而且使用方法也写的很清楚了。

☘: 还是要看看实际目录层级啊,不要两眼一闭就是 CV。

SSL 证书

如果是付费域名,可以到各自的域名提供商处申请一个,不过捏,现在一次只能申请三个月的(我买的阿里云是这样的,其它家的不清楚),到期得手动续,既然这样,那也就没有使用必要了,所以也不推荐了。

所以我们选择世界上使用率最高的证书品牌——Let’s Encrypt!

官方建议使用 Certbot 申请,不过我习惯使用 acme。

  1. 先来到 acme.sh-Github 开源地址看看

  2. 安装 acme.sh

    1
    2
    3
    # 修改成自己的邮箱
    # 任何用户都可以,安装到 ~/.acme 目录下
    curl https://get.acme.sh | sh -s [email protected]
  3. 申请证书
    申请证书需要进行验证,支持 HTTP 验证和 DNS 验证,建议用 DNS。配合 DNS API 实现自动续签。

    1. 以 CF 为例:获取 API Key,如何获取看上面官方介绍(懒,不想补图)。
      打开 API Tokens section

      • Permission:【Zone】-【DNS】-【Edit】
      • Zone Resources:【Include】-【Specific zone】-【你的名字域名】
      • 提交:【Continue to summary】

      之后会得到一个 API Key,复制一下

    2. 申请证书

      1
      2
      3
      4
      export CF_Key="API Key"
      export CF_Email="[email protected]"

      ./acme.sh --issue --dns dns_cf -d '*.example.com'

      如果你幸运的也遇到了 invalid domain ,检查一下 API Token 的域名和申请的是同一个不(某人就不一致,但傻兮兮的搜原因,我不说是谁),然后更新 acme 再试试。

    3. 【可选-非 2 即 3】另外补充一下手动使用 DNS 验证的方式(某位正在码字的家伙还在用):

      1
      2
      3
      4
      5
      # 1.
      acme.sh --issue -d *.example.com --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please
      # 2. 手动到 DNS 服务商添加生成的 TXT 解析记录,等几分钟生效后
      # 3. 申请
      acme.sh --renew -d *.example.com --yes-I-know-dns-manual-mode-enough-go-ahead-please [--ecc] [--force]

      后面两个选项一般用不到,如果报错了就加上。

  4. 复制证书
    将生成的证书复制到指定目录给 Nginx 使用。

    1
    2
    3
    4
    acme.sh --install-cert -d *.example.com \
    --key-file /data/ssl/key.pem \
    --fullchain-file /data/ssl/cert.pem \
    --reloadcmd "/usr/local/openresty/nginx/sbin/nginx -s reload"

    提醒一点(官方也说了),证书用证书链这个(包含中间证书),如果用单证书那个,可能会报错(我遇到过)。

    再提醒一点,生成在 .acme 目录下的证书为 .cer 后缀,不需要关注,和 pem 是一样的,只是后缀不一样。

    再再提醒一点,reload 的命令一定要写对,不然是不可以自动续签的哦。(ps: 如果用了 systemctl 管理那就用对应的命令,这里我就直接全路径了)

错误&警告

punycode deprecated

DeprecationWarning: The punycode module is deprecated. Please use a userland alternative instead.(Use node --trace-deprecation ... to show where the warning was created)

  • 场景描述:使用 hexo g 生成时报 punycode deprecated 警告
  • 原因:即使通过 npm 更新到最新版,用户空间模块也不会隐藏核心模块,原来的导入方式仍是废弃的模块,需要修改导入模块为 const punycode = require('punycode/');
  • 处理:
    1. 更新包至最新版
    1
    npm install punycode --save
    1. 进入 $HEXO_HOME/node_modules ,打开 Git Bash ,查找包含引入 pungcode 模块的 index.js 文件:
    1
    grep -r "require('punycode')" ./*
    1. 我的是在 ./markdown-it/lib/index.js 这里,将 require('pungcode') 修改为 require('punycode/'),引入新的模块
    2. 重新生成就不会有警告了。

Permission denied (publickey,gssapi-keyex,gssapi-with-mic)

  • 场景描述:为远程服务器(阿里云)博客用户(git)配置了免密登录,本地秘钥文件名为 id_rsa_git~/.ssh/config 文件中配置如下:

    1
    2
    3
    4
    5
    6
    Host hexo
    HostName 127.0.0.1
    User git
    Port 22
    IdentityFile ~/.ssh/id_rsa_git

    可以使用 ssh hexo 免密登录。配置文件仓库地址为 [email protected]:/home/git/hexo,执行 hexo d 时报错:Permission Denied

  • 原因:配置了 ~/.ssh/config 之后需要使用 ssh <HostName> 的方式,如果用 git@IP 的方式就会报错,包括在命令行也是一样。

  • 处理:修改配置文件仓库地址为:git@<HostName>:/home/git/hexo 的形式,将 IP 换成 config 中的别名。

补充一句:配置文件中仓库地址的 IP 就算用域名也不可以哦,如果你把 config 中的 HostName 设置成域名那就 emmm

post 标题异常

  • 场景描述:使用 hexo n 生成的文章标题异常,类似于 [Object Object]: null
  • 原因:模板中占位符格式错误,两个大括号之间多了空格
  • 处理:删除模板中两个大括号之间的空格,即 {{ title }},错误的为 { { title } }。之所以这样,是因为使用 Prettier 插件会自动格式化加上。。。