分类
ghost

Ubuntu 配置 Nginx + Ghost

按照惯例,买好机器,登录进去。建议是境外服务器,可以省掉备案环节。但是不要干坏事哦。

1. 更新系统

apt-get update
apt-get upgrade

2. 创建 ssh-key

ssh-keygen -t rsa -b 4096 -C "my-email@meathill.com"

3. 添加 authorized_keys

使用 ssh key 登录可以大大提升服务器的安全性。

首先,将你的电脑上的公 key 添加到服务器 ~/.ssh/authorized_keys。接着编辑服务器上的 /etc/ssh/sshd_config,禁用密码登录。

ChallengeResponseAuthentication no
PasswordAuthentication no

最后重启 ssh 服务:service ssh restart

4. 安装 Node.js

按照 NodeSource Node.js Binary Distributions 的指引,安装对应版本的 Node.js—— Ghost 要求 LTS,所以目前只能用 v12。

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs

5. 安装 MySQL/MariaDB

Ghost 默认使用 SQLite 作为数据库,不过生产环境中 SQLite 性能不够,因此建议直接安装并使用成熟可靠的数据库软件。这里建议使用 MariaDB,开源免费。不同版本的 MariaDB 源可以在 MariaDB Repositories 找到,我一般使用清华的源:

sudo apt-get install software-properties-common
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://mirrors.ustc.edu.cn/mariadb/repo/10.5/ubuntu focal main'

# 添加完仓库和 key 之后,就可以安装了
sudo apt update
sudo apt install mariadb-server

MariaDB 的安装过程没有配置密码的环节,可以使用 重置 MariaDB root 密码 一文中介绍的方法先重置 root 密码,然后创建需要的用户和数据库。

create user 'ghost'@'localhost' identified by 'ghost';
create database `ghost`;
grant all on `ghost`.* to 'ghost'@'localhost';

6. 安装 ghost-cli

ghost-cli 是 ghost 提供的命令行管理工具,可以大大减少我们管理 Ghost 实例的时间。

# 安装
npm i ghost-cli -g

# 创建一个目录安装 ghost
cd /var/www
mkdir ghost
cd ghost

Ghost 要求我们不能用 root 用户维护实例,所以这时可能需要创建一个新用户,并赋予其 sudo 权限(方便操作目录权限之类的),我习惯命名为 ghost-admin:

adduser ghost-admin
usermod -aG sudo ghost-admin

接着,切换到 ghost-admin,安装 ghost

su ghost-admin
cd /var/www
# 改变权限
sudo chown -R ghost-admin:ghost-admin ghost
cd /var/www/ghost
ghost install

安装完成之后,执行 ghost ls,可以看到正在运行的 Ghost 实例,就是一切正常了:

+ sudo systemctl is-active ghost_fav-meathill-com
┌──────────────────┬────────────────┬─────────┬──────────────────────┬─────────────────────────┬──────┬─────────────────┐
│ Name             │ Location       │ Version │ Status               │ URL                     │ Port │ Process Manager │
├──────────────────┼────────────────┼─────────┼──────────────────────┼─────────────────────────┼──────┼─────────────────┤
│ my-ghost │ /var/www/ghost │ 3.31.5  │ running (production) │ http://my-ghost.com │ 2368 │ systemd         │
└──────────────────┴────────────────┴─────────┴──────────────────────┴─────────────────────────┴──────┴─────────────────┘

接下来,编辑 config.production.json,把数据库配置和域名配置都写进去,就基本可用了。

7. 安装并配置 Nginx

首先,配置源。目前 Ubuntu 20.04,代号 focal,命令如下:

deb https://nginx.org/packages/ubuntu/ focal nginx
deb-src https://nginx.org/packages/ubuntu/ focal nginx
sudo apt update
sudo apt install nginx

增加配置文件,并进行反向代理,即把外来的访问反向代理给 Ghost 服务:


cp /etc/nginx/site-available/default /etc/nginx/site-available/ghost.conf
ln -s /etc/nginx/site-available/ghost.conf /etc/nginx/site-enabled/ghost.conf

配置文件大体如下:

server {
    listen 80;
    listen [::]:80;

    server_name my-ghost.com;
    root /var/www/ghost/system/nginx-root; # Used for acme.sh SSL verification (https://acme.sh)

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;

    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

最后重启 nginx 服务或者重新加载配置即可:

# 一般建议先检查一下配置有没有问题
nginx -t
service nginx restart

总结

这次主要更新了使用 ghost-cli 安装和配置的内容,服务器也改用 MariaDB,新的技术使得安装配置都更简单了。

分类
html

使用 <input type=”file”> 上传 ZIP/RAR 文件

上传文件要用到 <input type="file">,这个元素有个 accept 属性,可以用来筛选文件类型,方便用户选择。按照 MDN 的说法,这个属性的值支持以下几种形式:

  1. 合法的文件扩展名,大小写不敏感,以 . 作为开头,比如 .jpg.pdf
  2. 合法的 MIME type,不需要扩展名
  3. 媒体文件,audio/* 适配任意声音文件,video/* 适配任意视频,image/* 适配任意图片

同时,属性值可以使用 , 连接,表示“或”的意思。注意,这里只能是单独的 ,,不能有空格,不然有空格的部分会失效。所以,比如,一个上传图片、以及 PDF 的元素就可以写成:

<input type="file" accept="image/*,.pdf">

使用扩展名比较方便,但是扩展名太多不方便管理,比如 .jpg.jpeg,容易漏掉,所以我更喜欢 MIME type。那么压缩文件的 MIME type 是什么呢?经过一番搜索和尝试,是:.zip,.rar,application/x-rar-compressed,application/zip,application/x-zip-compressed,application/octet-stream

分类
js 技术

基于 @vue/cli 的项目配置 browserslist

前些日子虽然写了 最近折腾 @babel/preset-env 的一些小心得,但其实没有正确的理解和配置 browserslist,所以今天问题又来了。

分类
js

Nuxt.js 支持 core-js 3

Vue CLI 升级到 v4 之后,将内部的 core-js 依赖升级到 v3,关于 core-js v3 和 core-js v2 之间的区别,我在 最近折腾 @babel/preset-env 的一些小心得 里简单介绍过。

升级完 Vue CLI 之后,在调用 nuxt generate 生成静态页,就会报错,因为 Nuxt.js 默认使用 core-js 2。这个时候,如果 Nuxt.js 版本在 2.6.0 之后,就只需要修改 nuxt.config.js 里的配置,指定 core-js 的版本:

module.exports = {
  build: {
    babel: {
      presets({ isServer }) {
        return [
          [
            require.resolve('@nuxt/babel-preset-app'),
            // require.resolve('@nuxt/babel-preset-app-edge'), // For nuxt-edge users
            {
              buildTarget: isServer ? 'server' : 'client',
              corejs: { version: 3 }
            }
          ]
        ]
      }
    }
  }
}

参考文档:https://nuxtjs.org/guide/release-notes#v2.6.0

说起来 Nuxt.js,用它发布静态页比想象中复杂,如果你想快速掌握这个技能,不妨看下我的这本小书:

分类
js

解决 Firefox 下的 race 问题

我厂有几个产品,需要从后端获取大量的信息,为了让用户能够近乎实时的看到这些信息,大部分数据都是通过 WebSocket 发给前端。这些产品在 Chrome 下表现正常,但是在 Firefox 下经常把数据格式搞乱,最终渲染失败。

因为 Firefox DevTools 没法解析 WebSocket 数据,而且市场占有率比较低,所以我一直没有解决这个问题。前几天终于把最小可复现实例搞出来,正准备研究,结果同事已经修好了。

预览版的 Firefox 终于可以在 DevTools 里查看 WebSocket 每一帧的数据,所以她尝试看了一下,发现从解析二进制数据的角度来看,Firefox 应该没问题。于是又回到代码,发现了一个可能产生 race 的点:

if (data instanceof Blob) {
  data = await new Response(data).arrayBuffer();
}

因为服务器返回的数据是二进制,所以我需要进行一次转换,把它变成 ArrayBuffer,然后再通过 TextDecoder 转换成文本,然后处理。Response.arrayBuffer 返回的是 Promise,所以我就很自然的用 await,并且在 Chrome 上运行良好。

但是在 Firefox 里,某些帧会后发先完成转换,a b c 变成 a c b,于是数据格式错乱,无法正常解析。我怀疑 Chrome 并没有真的把这一步保留到用户,而是同时存了两份数据,这样转换的时候直接给出数据就好,所以是微任务,不走 Event loop,不会产生 race。而 Firefox 则是实时转换,所以是宏任务,所以出问题。

我尝试去翻了一下源码,无奈平时没看过,所以没能找到证据。如果有哪位同学刚好知道,可以在评论里告诉我。

分类
招聘

代友招聘:每日优鲜 BI 部门招前端技术经理

有个关系很好的朋友近期加盟每日优鲜,负责 BI 系统,据他描述,组内有三名初级前端,需要一个高级前端做技术经理,负责带团队。

薪资范围:30K~40K,16个月

工作地点:北京望京

分类
招聘

面试题:如何理解加班

前阵子参加 SF 的征文活动 一起分享你的故事,我的文章 我的编程职业生涯 有幸得到大家的支持,最终得奖:汤青松老师的《PHP Web 安全开发实战》。书前两天寄到了,还没来得及细看,也没法写书评,所以想了想,再分享一段职场经验吧。

分类
perl

Perl 笔记

主要记录在 Windows 下使用 Perl 的经验。

WSL

  1. 内建 Perl 环境

安装

我安装的是 Strawberry 版,没啥好说的,下载安装即可。

分类
前端工具链

使用 Webpack 动态打包文件夹

各式各样功能强大的小语言是我厂机器编程的特色,为了让更多的同学提前感受到 DSL 的威力,我们开发了 demo 应用:https://demo.openresty.com.cn/

Demo 里面,需要添加一些范例代码,方便用户直接体验。这些代码,可以通过后端 API 获取,也可以直接编译到前端代码里。目前范例很少,直接打包到一起应该是最简单的做法。

但是如何打包是个问题。经过一些研究,我觉得这样做好:

  1. 负责小语言的同事直接把范例写在项目仓库里,Edgelang 就用 .edge 扩展名,Navlang 就用 .nav 扩展名
  2. 前端在 webpack 里实现一个 requireAll 的功能,把所有代码当成纯文本用 raw-loader 加载进来
  3. 这样添加了代码后,重新 build 一下就好。范例文件可以用 lazy-load 的方式加载

Webpack 提供了一个方法叫 require.context,可以深度遍历一个目录,返回一个 context module,用这个 API 我们就可以动态的加载这个目录下的文件——具体的讲解和参数说明大家看下文档吧,这个部分中文资料不太多,我不太确定译名。我们可以在范例代码的目录下放一个 index.js,负责加载所有代码范例:

function requireAll(r) {
  r.keys().forEach(r);
}

const Languages = [
  'edgelang',
  'navlang',
];

let context = require.context('./edgelang', true, /\.(edge|css)$/);
requireAll(context);
context = require.context('./navlang', true, /\.(nav|css)$/);
requireAll(context);

export default Languages;

然后我们改一下 webpack.config.js 就可以了。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(nav|edge|fan)$/,
        use: 'raw-loader',
      },
    ],
  },
};
分类
技术

我的编程职业生涯

经常爬论坛,时常看到年轻的同学对职业生涯有各种迷茫。赶上这次 SF 征文,索性聊一聊我个人的编程职业生涯,给大家一些参考吧。