# Hexo - 个人博客启动说明

# 简介

Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown (或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。即把用户的 markdown 文件,按照指定的主题解析成静态网页。

# 启动和部署

# 本地启动

  1. npm 安装 hexo 和对应主题所需要用到的依赖
  2. 进入博客所在目录 输入 hexo server 本地启动
  3. 重新生成静态文件资源 输入 hexo clean & hexo g
  4. 保存 aglina 搜索索引 输入 hexo aglina 中途可能需要配置 admink_key 的环境变量
  5. 将生成的资源文件推送到 gitee/github 上 输入 hexo d
  6. 更新 gitee pages

可能需要的问题:执行命名可能出现内存溢出的情况,可以用以下命令来执行

node --max-old-space-size=4096 node_modules/hexo/bin/hexo server

# 云服务器上部署

留底 后续可能弄一个自动化部署 只需要修改文章,其他步骤自动化 ( Qexo

所有项目通过 nginx 转发

开始:

  1. 购买服务器,可以用自己电脑或者买阿里的 99 计划优惠的服务器(99 一年)详细看官网

  2. 安装 docker

    --安装docker
    sudo yum update -y
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    sudo yum install -y docker-ce docker-ce-cli containerd.io
    sudo systemctl start docker
    sudo systemctl enable docker
    --校验
    docker -v
    --打印出了版本号代表成功
    Docker version 26.1.0, build 9714adc
  3. 安装 nginx(这里用的自己编译的方式)

    nginx 1.25.5 linux 安装 并安装到指定路径下
    要在Linux系统上安装Nginx到指定路径,你可以使用源代码进行编译安装。以下是基于Linux的命令行步骤:
    下载Nginx源代码:
    wget http://nginx.org/download/nginx-1.25.5.tar.gz
    解压源代码:
    tar -zxvf nginx-1.25.5.tar.gz
    进入解压后的目录:
    cd nginx-1.25.5
    配置安装路径(假设你想安装到 /home/software/nginx):
    ./configure --prefix=/home/software/nginx
    编译并安装:
    make
    sudo make install
    以上步骤会将Nginx安装到 /home/software/nginx 目录下。你可以通过修改 --prefix 参数值来安装到其他指定路径。如果你的系统中没有 make 命令,你需要安装 make 和 gcc。
    注意:如果你没有超级用户权限,你可能需要在 sudo make install 前面加上 sudo 来获取安装权限。 
    安装完成后的目录结构:
    drwx------  2 nobody root 4096 Apr 29 09:39 client_body_temp
    drwxr-xr-x  2 root   root 4096 Apr 30 00:36 conf
    drwx------  2 nobody root 4096 Apr 29 09:39 fastcgi_temp
    drwxr-xr-x  3 root   root 4096 Apr 30 10:57 html
    drwxr-xr-x  2 root   root 4096 Apr 29 09:39 logs
    drwx------ 12 nobody root 4096 Apr 30 09:52 proxy_temp
    drwxr-xr-x  2 root   root 4096 Apr 29 09:39 sbin
    drwx------  2 nobody root 4096 Apr 29 09:39 scgi_temp
    drwx------  2 nobody root 4096 Apr 29 09:39 uwsgi_temp
  4. 初始化 hexo 安装相关的依赖

    安装Node.js和npm:
    sudo yum update
    sudo yum -y nodejs npm
    使用npm安装Hexo:
    npm install -g hexo-cli
    创建一个新的文件夹作为Hexo的博客目录,并初始化:
    hexo init blog
    cd blog
    npm install
    或者仓库有源码 安装git拉取
    安装git:
    yum install git
    git clone https://xxx.xxx.xx
    初始化:
    npm install
    接着就是对应自己的博客主题安装对应主题需要的依赖
    启动Hexo服务器进行测试:
    hexo server
  5. 添加运维脚本,简化操作

    #!/bin/bash
    JAR_NAME="blog"
    PID_FILE="blog.pid"
    # JMX path
    APP_HOME="$(cd `dirname $0`; pwd)"
    # Check whether the pid path exists
    PID_PATH="$(cd "$(dirname "$0")";pwd)/run"
    if [ -d ${PID_PATH} ];then
        echo "${PID_PATH} is already exist." >> /dev/null
    else
        mkdir -p  ${PID_PATH}
    fi
    # Check whether the pid file exists
    if [ -f "${PID_PATH}/${PID_FILE}" ];then
        echo "${PID_PATH}/${PID_FILE} is already exist." >> /dev/null
    else
        touch ${PID_PATH}/${PID_FILE}
    fi
    tips() {
      echo ""
      echo "WARNING!!!......Tips, please use command: sh auto.sh [start|stop|restart|status|c|d|a|g].   For example: sh auto.sh start  "
      echo ""
      exit 1
    }
    start() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        nohup  nohup hexo server >/dev/null 2>&1 &
        echo $! >${PID_PATH}/${PID_FILE}
        echo "........................................Start ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    #生成静态文件
    g() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        hexo generate
        echo "........................................Generate static files ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    #清除生成的文件和缓存
    c() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        hexo clean
        echo "........................................Remove generated files and cache ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    #部署到服务器
    d() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        hexo deploy
        echo "........................................Deploy your website ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    #更新索引到 Algolia 服务器
    a() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        hexo algolia
        echo "........................................Index your posts on Algolia ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    stop() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        echo "Dinky pid is not exist in ${PID_PATH}/${PID_FILE}"
      else
        kill -9 $pid
        sleep 1
        echo "........................................Stop ${JAR_NAME} Successfully....................................."
        echo " " >${PID_PATH}/${PID_FILE}
      fi
    }
    status() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        echo ""
        echo "Service ${JAR_NAME} is not running!"
        echo ""
        exit 1
      else
        echo ""
        echo "Service ${JAR_NAME} is running. It's pid=${pid}"
        echo ""
      fi
    }
    restart() {
      echo ""
      stop
      start
      echo "........................................Restart Successfully........................................"
    }
    case "$1" in
    "start")
      start
      ;;
    "stop")
      stop
      ;;
    "status")
      status
      ;;
    "restart")
      restart
      ;;
    "c")
      c
      ;;
    "d")
      d
      ;;
    "a")
      a
      ;;
    "g")
      g
      ;;
    *)
      tips
      ;;
    esac
  6. 部署 hexo 的管理框架 qexo

    #去 github 下载压缩包,上传到服务器, 或者用 git clone 下来
    mkdir -p qexo
    cd qexo 
    tar -zxvf Qexo-3.2.1
    mv Qexo-3.2.1/* qexo/*
    #或者:
    git clone https://github.com/Qexo/Qexo.git /home/software/qexo && cd /home/software/qexo
    #创建连接数据库的配置文件
    vi configs.py
    #复制以下内容:
    import pymysql
    pymysql.install_as_MySQLdb()
    DOMAINS = ["127.0.0.1", "xxx.cn"]
    DATABASES = {
        'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'qexo',
                'USER': 'root',
                'PASSWORD': '123456',
                'HOST': '127.0.0.1',
                'PORT': '3306',
                'OPTIONS': {
                    "init_command": "SET sql_mode='STRICT_TRANS_TABLES'"
                }
        }
    }
    #因为用到 mysql 存储数据 需要安装 mysql, docker 安装:
    docker pull mysql
    docker run -p 3306:3306 --name mysql -v /home/software/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
    #查看日志
    docker logs -f mysql
    #安装成功后需要进入容器创建对应的库 上面配置文件的库为 qexo
    #进入容器:
    docker exec -it 镜像id bash
    mysql -u root -p
    create database qexo;
    #接着安装 qexo 对应的依赖
    pip3 install -r requirements_withoutmysql.txt
    #中途可能出现版本的问题, 需要修改文件中的版本后在重新执行
    #接下来运行以下命令:
    python3 manage.py makemigrations && python3 manage.py migrate
    #最后启动服务:
    python3 manage.py runserver 0.0.0.0:8000 --noreload
    #访问页面试试 如果出现 400 的错误需要添加配置,在 qexo/core/settings.py 中添加以下配置属性:
    ALLOWED_HOSTS = ['你实际访问的ip']
    #这里不是后台启动的 窗口关闭后服务就会关闭,这里写了个后台执行的脚本:
    #!/bin/bash
    JAR_NAME="qexo"
    PID_FILE="qexo.pid"
    # JMX path
    APP_HOME="$(cd `dirname $0`; pwd)"
    # Check whether the pid path exists
    PID_PATH="$(cd "$(dirname "$0")";pwd)/run"
    if [ -d ${PID_PATH} ];then
        echo "${PID_PATH} is already exist." >> /dev/null
    else
        mkdir -p  ${PID_PATH}
    fi
    # Check whether the pid file exists
    if [ -f "${PID_PATH}/${PID_FILE}" ];then
        echo "${PID_PATH}/${PID_FILE} is already exist." >> /dev/null
    else
        touch ${PID_PATH}/${PID_FILE}
    fi
    tips() {
      echo ""
      echo "WARNING!!!......Tips, please use command: sh auto.sh [start|stop|restart|status].   For example: sh auto.sh start  "
      echo ""
      exit 1
    }
    start() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        nohup nohup python3 manage.py runserver 0.0.0.0:8000 --noreload >/dev/null 2>&1 &
        echo $! >${PID_PATH}/${PID_FILE}
        echo "........................................Start ${JAR_NAME} Successfully........................................"
      else
        echo "${JAR_NAME} pid $pid is in ${PID_PATH}/${PID_FILE}, Please stop first !!!"
      fi
    }
    stop() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        echo "Dinky pid is not exist in ${PID_PATH}/${PID_FILE}"
      else
        kill -9 $pid
        sleep 1
        echo "........................................Stop ${JAR_NAME} Successfully....................................."
        echo " " >${PID_PATH}/${PID_FILE}
      fi
    }
    status() {
      pid=$(cat ${PID_PATH}/${PID_FILE})
      if [ -z $pid ]; then
        echo ""
        echo "Service ${JAR_NAME} is not running!"
        echo ""
        exit 1
      else
        echo ""
        echo "Service ${JAR_NAME} is running. It's pid=${pid}"
        echo ""
      fi
    }
    restart() {
      echo ""
      stop
      start
      echo "........................................Restart Successfully........................................"
    }
    case "$1" in
    "start")
      start
      ;;
    "stop")
      stop
      ;;
    "status")
      status
      ;;
    "restart")
      restart
      ;;
    *)
      tips
      ;;
    esac

# 云服务器相关配置

# 配置多个子域名指向同一台服务器的不同项目(需要结合 nginx)

  1. 购买域名

  2. 在阿里控制台中配置子域名 官网教程

  3. nginx 配置

    #比如我的主域名是 tzzfj.cn
    #配置了两个子域名:www.tzzfj.cn, scaffold.tzzfj.cn 
    #nginx 的配置如下
    #####################################
    ########### 博客首页域名访问 ##########
    #####################################
    server {
        #HTTPS 的默认访问端口 443。
        #如果未在此处配置 HTTPS 的默认访问端口,可能会造成 Nginx 无法启动。
        listen 443 ssl;
        #填写证书绑定的域名
        server_name www.tzzfj.cn;
        #填写证书文件绝对路径
        ssl_certificate cert/tzzfj.cn.pem;
        #填写证书私钥文件绝对路径
        ssl_certificate_key cert/tzzfj.cn.key;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout 5m;
        #自定义设置使用的 TLS 协议的类型以及加密套件(以下为配置示例,请您自行评估是否需要配置)
        #TLS 协议版本越高,HTTPS 通信的安全性越高,但是相较于低版本 TLS 协议,高版本 TLS 协议对浏览器的兼容性较差。
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        #表示优先使用服务端加密套件。默认开启
        ssl_prefer_server_ciphers on;
        ##### 博客首页 #####
        location / {
            proxy_pass   http://127.0.0.1;
            #重定向保留原有信息
            proxy_set_header    Host             $host:$server_port;
            proxy_set_header    X-Real-IP        $remote_addr;
            proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_set_header    HTTP_X_FORWARDED_FOR $remote_addr;
            proxy_connect_timeout    2000;
            proxy_read_timeout       2000;
            proxy_send_timeout       2000;
            #自用 标识原请求的协议、上下文路径、端口
            proxy_set_header Scheme $scheme;
            proxy_set_header Server-Port $server_port;
            #后端重定向协议修改(修改 location 的值)
            proxy_redirect http:// $scheme://;
        }
    }
    server {
        #HTTPS 的默认访问端口 443。
        #如果未在此处配置 HTTPS 的默认访问端口,可能会造成 Nginx 无法启动。
        listen 443 ssl;
        #填写证书绑定的域名
        server_name scaffold.tzzfj.cn;
        #填写证书文件绝对路径
        ssl_certificate cert/xxx.pem;
        #填写证书私钥文件绝对路径
        ssl_certificate_key cert/xxx.key;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout 5m;
        #自定义设置使用的 TLS 协议的类型以及加密套件(以下为配置示例,请您自行评估是否需要配置)
        #TLS 协议版本越高,HTTPS 通信的安全性越高,但是相较于低版本 TLS 协议,高版本 TLS 协议对浏览器的兼容性较差。
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        #表示优先使用服务端加密套件。默认开启
        ssl_prefer_server_ciphers on;
        location / {
            try_files $uri $uri/ /index.html;
        }
        
    }

# 网站备案

# 公安备案

# HEXO 主题个性化修改

  1. 修改可以自定义配置网站备案号和公安备案号

    配置:

    footer:
      # Specify the date when the site was setup. If not defined, current year will be used.
      since: 2023
      icon:
        name: sakura rotate
        # Change the color of icon, using Hex Code.
        color: "#ffc0cb"
      # Dependencies: https://github.com/theme-next/hexo-symbols-count-time
      count: true
      powered: false
      icp:
        enable: true
        value: "粤ICP备2024250517号-1"
      police:
        enable: true
        value: "粤公网安备44010602012754号"

    修改 footer.njk 的内容:

    <!--swig0-->
        <div class="powered-by">
            <img src="/images/icp_image.png" style="width:16px; display: inline-block; vertical-align: middle;">
            <a target="_blank" rel="noopener" href="https://beian.mps.gov.cn/#/query/webSearch?code=44010602012754" style="display: inline-block; vertical-align: middle; margin-left: 3px;"><!--swig1--></a>
            <!--swig2-->
                 <a href="http://beian.miit.gov.cn" style="display: inline-block; vertical-align: middle; margin-left: 5px;"><!--swig3--></a>
            <!--swig4-->
        </div>
      <!--swig5-->
  2. 修改默认使用 cdn 资源,有时候会访问很慢, 下载到本地直接改成访问本地 js 文件

    配置:

    vendors:
      enable: false

    修改 asset.js 的内容:

    hexo.extend.helper.register('_vendor_js', () => {
      const config = hexo.theme.config.vendors.js;
      // 修改处
      // 是否不启用 cdn
      const cdnEnable = hexo.theme.config.vendors.enable;
      if (!config) return '';
      //Get a font list from config
      let vendorJs = ['pace', 'pjax', 'fetch', 'anime', 'algolia', 'instantsearch', 'lazyload', 'quicklink'].map(item => {
        if (config[item]) {
          return config[item];
        }
        return '';
      });
      // 修改处
      if (cdnEnable !== true){
        return htmlTag('script', { src: `/assets/ext.js?v=${theme_env['version']}` }, '');
      }
      vendorJs = vendorJs.filter(item => item !== '');
      vendorJs = [...new Set(vendorJs)];
      vendorJs = vendorJs.join(',');
      let result = vendorJs ? `<script src="//cdn.jsdelivr.net/combine/${vendorJs}"></script>` : '';
      return vendorJs ? htmlTag('script', { src: `//cdn.jsdelivr.net/combine/${vendorJs}` }, '') : '';
    });
  3. 在本地编写的时候,插入图片是绝对路径,改成把图片放在特殊目录下,在 hexo 编译的时候替换成 url 访问

    在 scipts/filters 目录下添加一个钩子文件 在 hexo 编译的时候会执行到:

    // replace-image-paths.js
    //markdown 转 html 的前置处理器
    // 这里的处理器是将本地图片路径转网络图片路径
    // 文章中的图片路径是:E:/aaa/bbb/project_images/a.png
    // 可能到了其他电脑是:E:/sss/ddd/project_images/a.png
    // 这里所有的图片都统一放到 'project_images' 目录下,就有了约束,就好处理了
    'use strict';
    const path = require('path');
    hexo.extend.filter.register('before_post_render', function(data) {
      // 检查 data 是否包含内容
      const { config } = hexo;
      const theme = hexo.theme.config;
      if (data && data.content) {
        // 正则表达式匹配 Markdown 图片语法
        const imgRegex = /!\[.*?\]\((.*)\)/gm;
        // 替换匹配到的图片路径
        data.content = data.content.replace(imgRegex, (match, p1) => {
          // 检查路径是否包含 'project_images'
          if (p1.includes('project_images')) {
            // 替换路径
            const newPath = p1.replace(/.*project_images\//, config.url + '/project_images/');
            return match.replace(p1, newPath);
          }
          return match;
        });
      }
      console.log(config.url)
      return data;
    });

# 个人网盘

个人网盘使用 AList , 挂载的阿里云的共享文件,相关文档

# 部署流程

采用了手动部署的方式

  1. 下载 alist-linux-amd64.tar.gz 上传到服务器解压。

  2. 启动

    # 解压下载的文件,得到可执行文件:
    tar -zxvf alist-xxxx.tar.gz
    # 授予程序执行权限:
    chmod +x alist
    # 运行程序
    ./alist server
    # 获得管理员信息 以下两个不同版本,新版本也有随机生成和手动设置
    # 低于 v3.25.0 版本
    ./alist admin
    # 高于 v3.25.0 版本
    # 随机生成一个密码
    ./alist admin random
    # 手动设置一个密码 `NEW_PASSWORD` 是指你需要设置的密码
    ./alist admin set NEW_PASSWORD

# docker 部署

docker run -d --restart=unless-stopped -v /etc/alist:/opt/alist/data -p 5244:5244 -e PUID=0 -e PGID=0 -e UMASK=022 --name="alist" xhofe/alist:latest
#低版本 低于 v3.25.0 版本
docker exec -it alist ./alist admin
#高版本 高于 v3.25.0 版本
# 随机生成一个密码
docker exec -it alist ./alist admin random
# 手动设置一个密码,`NEW_PASSWORD` 是指你需要设置的密码
docker exec -it alist ./alist admin set NEW_PASSWORD

# 参考资料

  • Hexo 官方文档