跳到主要内容

· 阅读需 2 分钟
He Wei

参考资料

启用 Google Analytics API

进入 Google Cloud Console,在 API & ServicesLibrary 页面中,搜索 Google Analytics Reporting API

创建 Service Account

在 Google Cloud Console 的 IAM & AdminService Accounts 页面中,点击 Create Service Account 按钮,按照提示完成创建。只配置第一步的 Service account name 即可,其他的不用管。

创建完成后,记得复制 Service Account 的邮箱地址,后面会用到。

创建 API Key

点击创建好的 Service Account,在 Keys 页面中,点击 Add Key 按钮,选择 Create new keyJSON,创建 API Key,创建完成后,会自动下载一个 JSON 文件,这个文件就是 API Key。

配置指定网站的 GA API 权限

在指定网站的 Google Analytics 管理后台中,点击左下角的齿轮按钮,进入 PropertyProperty access management 页面,点击右上角的 + 号按钮,选择下拉菜单中的 Add Users 按钮,填入前面创建好的 Service Account 的邮箱,并设置其角色为 Viewer

记录网站的 GA Property ID

在指定网站的 Google Analytics 管理后台中,点击左下角的齿轮按钮,进入 PropertyProperty settings 页面,找到 Property ID,复制这个 ID,后面会用到。

测试 API 调用

可用的 JS 源码见:https://github.com/Dream4ever/Utils/blob/main/google-analytics/pageview.js。

· 阅读需 6 分钟
He Wei

安装 MOK 主题

将 MOK 主题压缩包上传到服务器的 ~ 目录下,因为如果尝试直接上传到 /var/www/powertidal.com 目录下,会因为权限不够导致上传失败。

scp -i C:\Users\${Username}\.ssh\key.pem .\mok.zip ecs-user@1.2.3.4:~/

然后再用 sudo mv mok.zip /var/www/powertidal.com/wp-content/themes 命令将 MOK 主题压缩包移动到网站目录下。

执行 cd /var/www/powertidal.com/wp-content/themes && sudo unzip mok.zip,将 MOK 主题文件解压。

如果点击上传按钮后,显示报错信息 Unable to create directory wp-content/uploads/2024/06. Is its parent directory writable by the server?,可以通过以下命令解决:

sudo mkdir -p /var/www/powertidal.com/wp-content/uploads
sudo chown -R www-data:www-data /var/www/powertidal.com/wp-content/uploads

启用 MOK 主题

访问 https://www.powertidal.com/wp-admin/themes.php,启用 MOK 主题即可。

对于英文版网站做同样操作。

访问 https://www.powertidal.com/wp-admin/themes.php?page=tbos_setting,在默认显示的第一个页面中,上传网站所需的 PC 端和移动端的 Logo 图片,图片尺寸按照网页上所说的建议尺寸调整即可。

中文版网站配置好 Logo 之后,英文版网站使用相同的 Logo URL 即可。

配置多语言

管理后台 - Appearance - MOK 主题设置 - 多语言

  • 前台语言:中文站选择中文,英文站选择英文
  • 语言切换
    • 打开 开启手机端显示语言切换自动跳转
    • 中文网址:https://www.powertidal.com
    • 英文网址:https://www.powertidal.com/en

多语言网站的文章切换

对于多语言网站,不同语言的文章在 Edit 页面中的 ID 应当是相同的,这样点击页面右上方的语言切换按钮的时候,才能正常切换到对应语言的文章。

多语言网站的文章分类

在管理后台 - Posts - Categories 页面中管理文章分类。

Name 为显示在页面中的分类名称,Slug 为分类的别名。

对于多语言网站,不同语言同一个分类的 ID 应当相同,这样切换语言后才能正常显示另一种语言下该分类的内容。

列表页小图与正文大图

在编辑文章时,Featured Image 是只在列表页显示的小图,如果要在正文中也显示,需要在正文中也插入图片。

设置顶部导航栏

管理后台 - Appearance - Menus

  1. Menu Settings - Display Location,选中 顶部导航
  2. 输入 Menu Name。
  3. 点击 Create Menu 创建菜单。

设置联系我们页面

  1. 在中英文后台新建相同 ID 的页面,页面模板选择 联系我们
  2. 为中英文网站分别填写对应语言的页面标题。
  3. 在中英文后台的 Appearance - Menus 页面中,将新建的联系我们页面添加到顶部导航栏中。
  4. 在中英文后台的 Appearance - MOK 主题设置 - 联系方式页面中,填写所需的联系方式即可。

开启在线留言功能

  1. 新建一个 Page,模板选 合作
  2. 管理后台 - Appearance - MOK 主题设置 - 页面中,开启 合作页面,并按照需求填写或开启所需功能。
  3. 管理后台 - Appearance - MOK 主题设置 - 联系方式中,创建一个 自定义 类型的联系方式,链接填第一步新建的 Page 的链接,账号/号码/地址点击留言

配置邮箱插件

参考文章 使用WP Mail SMTP插件解决WordPress注册和评论邮件无法发送的问题,先安装 WP Mail SMTP 插件。

然后设置 From Email,开启 Force From EmailReturn Path

Mailer 选择 Other,SMTP Hostsmtp.feishu.cnEncryptionSSLSMTP Port465,启用 Auto TLSAuthentication,下面的 SMTP UsernameSMTP Password 填飞书邮箱的账号和密码,最后点击 Save Settings 保存设置。

接着在 WP Mail SMTP 插件的 Tools 页面中,填写用于接收邮件的邮箱地址,点击 Send Email,如果收到邮件,则说明配置成功。

PS: 建议按照 Option 2: Generate a DMARC Record (Cloudflare Only) 链接中的建议,给发邮件的邮箱所在域名生成一个 DMARC 记录,以防止邮箱被标记为垃圾邮件。

其他配置

MOK 主题设置

  • 基本 - 其他:开启 关闭评论功能
  • 外观
    • 建站年份:填写当年年份
    • 关闭 主题由themebetter提供
  • 文章
    • 统计:关闭 点赞
    • 版权提示:关闭 产品详情页版权其他详情页版权
  • 分享
    • 网页分享:全部取消选中
  • 联系方式
    • 关闭 顶部-仅显示1个联系方式
    • 关闭:联系方式-微信公众号联系方式-客服微信联系方式-客服QQ联系方式-专属客服联系方式-24小时服务热线

· 阅读需 8 分钟
He Wei

配置 ECS

系统选择 Ubuntu 24.04,密码则在实例管理控制台页面,通过重置密码方式进行设置。

更新已有软件包

# 更新软件包列表
sudo apt update

# 升级所有软件包
sudo apt upgrade

安装 Nginx 并设置为开机启动

# 安装 Nginx
sudo apt install nginx

# 启动 Nginx
sudo systemctl start nginx

# 设置 Nginx 开机启动
sudo systemctl enable nginx

# 查看 Nginx 状态
sudo systemctl status nginx

配置 UFW 并设置为开机启动

# 查看 UFW 应用列表
sudo ufw app list

# 允许 Nginx HTTP 访问
sudo ufw allow 'Nginx HTTP'

# 允许 OpenSSH 访问
sudo ufw allow 'OpenSSH'

# 启用 UFW
sudo ufw enable

# 查看 UFW 状态
sudo ufw status

# 查看公网 IP
curl -4 icanhazip.com

安装 MySQL 并设置为开机启动

# 安装 MySQL
sudo apt install mysql-server -y

# 启动 MySQL
sudo systemctl start mysql

# 设置 MySQL 开机启动
sudo systemctl enable mysql

# 查看 MySQL 状态
sudo systemctl status mysql

加强 MySQL 安全设置

sudo mysql_secure_installation

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: y

MEDIUM Length >= 8, numeric, mixed case, and special characters

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y

安装 PHP 及相关依赖并设置为开机启动

# 安装 PHP 和 PHP-FPM
sudo apt install php php-fpm -y

# 安装 MySQL 及 WordPress 和 MOK 主题所需扩展
sudo apt install php-mysql php-cli php-xml -y

# 查看 PHP 版本
php -v

# 启动 PHP-FPM,PHP 版本基于前面查看的版本
sudo systemctl start php8.3-fpm

# 设置 PHP-FPM 开机启动,PHP 版本基于前面查看的版本
sudo systemctl enable php8.3-fpm

# 查看 PHP-FPM 状态,PHP 版本基于前面查看的版本
sudo systemctl status php8.3-fpm

配置 PHP-FPM

# 查看 PHP-FPM 进程
ss -pl | grep php

# 进入 PHP-FPM 配置目录
cd /etc/php/8.3/fpm/pool.d/

# 查看配置
cat www.conf
# 验证 PHP-FPM pool 名称为 www
[www]
# 验证 user 和 group 为 www-data
user = www-data
group = www-data

配置 Nginx

# 创建 Nginx 配置文件
# 这里只配置了 80 端口的 HTTP 请求
# 是因为用了 Cloudflare 的域名,Cloudflare 会自动配置好 HTTPS,所以服务端无需额外配置
sudo nano /etc/nginx/sites-available/your.domain
server {
listen 80;
server_name your.domain www.your.domain;
root /var/www/your.domain;

index index.html index.htm index.php;

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}

location ~ /\.ht {
deny all;
}

}
# 创建配置文件的软链接
sudo ln -s /etc/nginx/sites-available/your.domain /etc/nginx/sites-enabled/

# 删除默认配置文件的软链接
sudo unlink /etc/nginx/sites-enabled/default

# 测试配置文件
sudo nginx -t

# 重新加载 Nginx
sudo systemctl reload nginx

# 创建网站文件夹
sudo mkdir /var/www/your.domain

# 创建测试文件
sudo nano /var/www/your.domain/index.html
<html>
<head>
<title>your.domain website</title>
</head>
<body>
<h1>Hello World!</h1>

<p>This is the landing page of <strong>your.domain</strong>.</p>
</body>
</html>

访问 http://www.your.domain,如果看到页面,说明 Nginx 配置成功,删除刚才创建的测试文件。

测试 PHP 解析

sudo nano /var/www/your.domain/info.php
<?php
phpinfo();

访问 http://www.your.domain/info.php,如果看到页面,说明 PHP 解析成功。

# 删除测试文件
sudo rm /var/www/your.domain/info.php

测试 PHP 连接 MySQL

sudo mysql
CREATE DATABASE example_database;

CREATE USER 'example_user'@'%' IDENTIFIED WITH mysql_native_password BY 'ABcd1234!@#$';

GRANT ALL ON example_database.* TO 'example_user'@'%';

exit
mysql -u example_user -p
SHOW DATABASES;

CREATE TABLE example_database.todo_list (
item_id INT AUTO_INCREMENT,
content VARCHAR(255),
PRIMARY KEY(item_id)
);

INSERT INTO example_database.todo_list (content) VALUES ("My first important item");

SELECT * FROM example_database.todo_list;

exit
sudo nano /var/www/your.domain/todo_list.php
<?php
$user = "example_user";
$password = "ABcd1234!@#$";
$database = "example_database";
$table = "todo_list";

try {
$db = new PDO("mysql:host=localhost;dbname=$database", $user, $password);
echo "<h2>TODO</h2><ol>";
foreach($db->query("SELECT content FROM $table") as $row) {
echo "<li>" . $row['content'] . "</li>";
}
echo "</ol>";
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}

访问 http://www.your.domain/todo_list.php,如果看到页面,说明 PHP 连接 MySQL 成功。

然后删除测试数据库和用户:

DROP DATABASE example_database;

DROP USER 'example_user'@'%';

FLUSH PRIVILEGES;

exit
# 删除测试文件
sudo rm /var/www/your.domain/todo_list.php

安装配置 WordPress

先在 MySQL 中创建数据库和用户。

sudo mysql
# 多语言需要创建两个数据库
CREATE DATABASE wordpress;
CREATE DATABASE wordpress_en;

CREATE USER 'wordpress_admin'@'%' IDENTIFIED WITH mysql_native_password BY '****';

# 用同一个 DB ADMIN 账户管理两个数据库
GRANT ALL ON wordpress.* TO 'wordpress_admin'@'%';
GRANT ALL ON wordpress_en.* TO 'wordpress_admin'@'%';

exit
# 安装 unzip,用于解压 WordPress 和 MOK 主题的压缩包
sudo apt install unzip

将 WordPress 压缩包上传到服务器的 ~ 目录下,因为如果尝试直接上传到 /var/www/your.domain 目录下,会因为权限不够导致上传失败。

scp -i C:\Users\${Username}\.ssh\key.pem .\wordpress.zip ecs-user@1.2.3.4:~/

然后再用 sudo mv wordpress.zip /var/www/your.domain 命令将 WordPress 安装包移动到网站目录下。

执行 cd /var/www/your.domain && sudo unzip wordpress.zip,将 WordPress 文件解压。

注意,要确保解压后的文件和文件夹直接位于 /var/www/your.domain 目录下,而不是 /var/www/your.domain/wordpress 子目录下。

wp-config-sample.php 复制为 wp-config.php 以便修改。

执行 sudo vi /var/www/your.domain/wp-config.php,修改数据库连接信息。

访问 http://www.your.domain/wp-admin/install.php,按照提示完成安装。

配置 HTTPS

如果在输入密码的地方显示报错信息 password strength is unknown,有可能是在用 https 协议访问。可以在 wp-config.php 中添加一行:

$_SERVER['HTTPS'] = true;

搜索 Password strength unknown,在 https://wordpress.stackexchange.com/a/350453 中找到的这个解决方法。

配置管理后台访问权限

如果输入账号和密码登录后,显示报错信息 Sorry, you are not allowed to access this page., 则需要在 /var/www/your.domain/wp-config.php 中添加以下两行:

define( 'WP_HOME', 'https://www.your.domain' );
define( 'WP_SITEURL', 'https://www.your.domain' );

对于英文版网站,上面的两个 URL 需要增加 /en 子路径。

参考资料:https://developer.wordpress.org/advanced-administration/before-install/howto-install/

完成以上操作之后,将 /var/www/your.domain 目录下的所有文件复制到 /var/www/your.domain/en 目录下,并修改 /var/www/your.domain/en/wp-config.php 中的网站信息:

define( 'WP_HOME', 'https://www.your.domain/en' );
define( 'WP_SITEURL', 'https://www.your.domain/en' );

配置上传文件夹权限

sudo chown -R www-data:www-data /var/www/your.domain/wp-content/uploads
sudo chown -R www-data:www-data /var/www/your.domain/en/wp-content/uploads

导出/导入数据库

# 导出数据库
sudo mysqldump -u wordpress_admin -p --databases wordpress > ~/wordpress.sql

# 导入数据库
sudo mysql -u wordpress_admin -p < ~/wordpress.sql

相关资料

整体流程参考以下几个链接:

-How to Install Nginx, MySQL, PHP (LEMP Stack) on Ubuntu 24.04

· 阅读需 5 分钟
He Wei

SSH 登录后执行 zip 命令

公司阿里云 Windows 服务器上的日志需要每个月归档一下,以前都是手动操作,最近感觉太麻烦,于是研究了一下自动化的方案,因此有了这篇文章。

万事不决问 Kimi,这篇文章的主要思路和解决方案都是 Kimi 给出的,谨表谢意。

最开始给出的方案是 ssh -t ecs '...',经过研究,ssh -t 会为命令创建一个伪终端,这样命令就可以在远程服务器上以交互模式运行。

之前给另一个前端项目写的命令是调用 rm 来删除文件,这个命令在 Windows 服务器上能运行,说明调用的是服务器上的 git bash 带的命令。

但是 git bash 默认不带 zip 命令,于是搜索 git bash zip command pass variable 这组关键词之后,看到了 How to add man and zip to "git bash" installation on Windows 这篇文章,按照文章里的方法,给 git bash 安装了 zip 命令。

虽然 zip 命令安装成功了,但是执行 ssh -t ecs 'zip ...' 的时候,既不会报错,也不会执行命令。

经过研究,发现命令是没问题的,只不过 -t 参数加上之后会让命令无法执行。

于是干脆去掉这个参数,直接执行 ssh ecs '...',这样命令就能正常执行了。

将前一个月的年份和月份作为变量传递

由于需要归档的是服务器上前一个月各网站的 IIS 日志,所以需要将前一个月的年份和月份作为变量传递给远程服务器上的命令。

结合 Kimi 和 Cursor 给出的方案,最终用下面的代码实现了需求:

# 获取上一个月的年份和月份
# 即使本月是 1 月,上一个月也会自动计算为前一年的 12 月
YYYY=$(date -d "$(date) -1 month" "+%Y")
YY=$(date -d "$(date) -1 month" "+%y")
MM=$(date -d "$(date) -1 month" "+%m")

拿到了年份和月份,在 bash 中就可以用 ${YYYY}${YY}${MM} 的方式来使用了。

将指定目录下匹配规则的文件进行压缩

这个需求很简单,指定目录下的目标文件名符合 u_exYYMMDD.log 的格式,这样的文件可以用 u_ex${YY}${MM}* 的方式来匹配。

因为需要压缩后删除源文件,所以用 zip -m 参数来压缩。

另外压缩时不需要带上文件的目录结构,所以用 -j 参数。

这样完整的 zip 命令就是 zip -mj ${ZIP_FILE} ${LOG_DIR}/u_ex${YY}${MM}*

将多个目录下的文件压缩到多个对应的目录中

IIS 为每个网站创建的日志目录格式是 W3SVC1W3SVC2 这种,而自己用来存放压缩后的每个月日志的目录格式是 W3SVC1_aaaaW3SVC2_bbbb 这种,所以需要将每个网站的日志压缩到对应的目录中。

问了一下 kimi,给出了下面的方案,很好用。

declare -A websites
websites["W3SVC2"]="aaaa"
websites["W3SVC3"]="bbbb"
......

然后就可以用下面的语句来遍历这个数组,执行压缩命令了。

for index in "${!websites[@]}"; do
ZIPFILE_DIR="/path/of/archive/${index}_${websites[$index]}"
LOG_DIR="/path/of/logs/${index}"
ZIP_FILE="${ZIPFILE_DIR}/${YYYY}-${MM}.zip"

# 构建压缩命令
# -m 表示压缩后删除源文件
# -j 表示压缩时不带目录结构
CMD="ssh ecs1 \"zip -mj ${ZIP_FILE} ${LOG_DIR}/u_ex${YY}${MM}*\""

# 执行压缩命令
echo ""
echo "正在归档网站 ${websites[$index]} $YYYY年$MM月的日志..."
eval $CMD
done

总结

  • ssh ecs '...' 的方式执行命令,命令中需要用到变量时,需要用 ${...} 的方式来获取。
  • declare -A 的方式来定义数组,用 websites["W3SVC1"]="aaaa" 的方式来给数组赋值,用 websites[$index] 的方式来获取数组的值。
  • for index in "${!websites[@]}"; do ... done 的方式来遍历数组。
  • eval 的方式来执行命令。
  • zip -mj ${ZIP_FILE} ${LOG_DIR}/u_ex${YY}${MM}* 的方式来压缩并删除源文件。

· 阅读需 1 分钟
He Wei

在 iOS 系统中,如果打开文件下载链接,会像下图这样,不显示文件名称,只显示一个 null。

image

解决办法就是用下面这段代码实现下载功能:

function download (url, fileName) {
const x = new XMLHttpRequest()
x.responseType = 'blob'
x.open('GET', url, true)
x.send()
x.onload = () => {
const downloadElement = document.createElement('a')
const href = window.URL.createObjectURL(x.response) // create download url
downloadElement.href = href
downloadElement.download = fileName // set filename (include suffix)
document.body.appendChild(downloadElement) // append <a>
downloadElement.click() // click download
document.body.removeChild(downloadElement) // remove <a>
window.URL.revokeObjectURL(href) // revoke blob
}
}

参考链接:微信内置浏览器下载pdf的时候标题为null?

· 阅读需 1 分钟
He Wei

· 阅读需 1 分钟
He Wei

无效命令

  1. rm + Windows 格式的路径 ssh -t ecs1 "'rm -r e:\upcweb\uppbook\yd\_nuxt\*'"

有效命令

  1. rm + Linux 格式的路径 ssh -t ecs1 "'rm -r /e/upcweb/uppbook/yd/_nuxt/*'"

注意:按照 这里 的说明,需要执行的命令先用双引号包裹,然后再用单引号包裹,这样才能成功执行。

· 阅读需 4 分钟
He Wei

前情提要

有一个业务,在 PC 端以网站的形式呈现,在手机端以小程序的形式提供服务。

为了统一使用一套用户身份,需要在 PC 端网站上实现微信扫码登录功能,将用户在微信小程序中的 openId 传给网页端。

最开始调研的方案,是在后端生成小程序码并发送给前端。后来在 V2EX 咨询了一下,发现这种方案有每分钟的调用频率显示,并且对服务端消耗比较大,对方建议采用普通二维码跳转小程序的方式。

官方文档:扫普通链接二维码打开小程序

参考文章:微信小程序踩坑系列之扫普通链接二维码打开小程序扫描普通二维码进入小程序

官方文档说得还是不够清楚,又看了几篇个人分享的心得,结合自己的实际操作,总结了一下。

整体流程

以下仅列出关键步骤。

  1. 在服务端随机生成一个 UUID,返回给 PC Web 端。
  2. PC Web 端生成一个普通二维码,内容为 https://www.abc.com/biz/?q=${uuid}
  3. 手机微信扫描二维码,跳转到所配置的小程序页面,并传入上面二维码对应的 URL,其中包含 UUID。小程序将用户 openId 和 UUID 传给服务端。
  4. 服务端将 UUID 和 openId 关联为 key-value,以便后续使用。
  5. PC Web 端轮询服务端,获取 UUID 关联的 openId,如果获取成功,则登录。

配置二维码跳转小程序

在小程序管理后台的 开发设置 页面,可配置 扫普通链接二维码打开小程序 的功能。

先配置 二维码规则,填写 https://www.abc.com/biz/

然后配置 前缀占用规则,如果选择是,则对于前一步配置的二维码 URL,当前规则将独占所有匹配的子规则,即 https://www.abc.com/biz/*,其他跳转规则将不能再使用满足条件的子规则。

再配置要跳转到的 小程序功能页面,扫码后会跳转到这个页面。

接着配置 测试范围,这个选项在规则发布之后也可以修改。

最后配置 测试链接。注意,在跳转规则未发布的情况下,测试链接也是有效的。这样方便进行功能测试,以免发布无效的规则,浪费每月额度。

· 阅读需 4 分钟
He Wei

整体流程

公司的业务需求是需要一个邮箱验证服务,用户注册后需要验证邮箱,才能继续使用服务。这个服务的基本流程如下:

  1. 用户注册时,填写邮箱地址作为用户名。
  2. 后端收到注册请求时,生成一个随机的验证码,发送到用户的邮箱。
  3. 用户收到邮件后,在注册页面输入邮件中的验证码,并继续注册。
  4. 后端接口验证验证码是否正确,以及是否与邮箱匹配。
  5. 验证成功后,注册该用户。
  6. 验证失败后,提示用户重新验证。
  7. 验证码的有效期为 24 小时。
  8. 验证码只能使用一次。
  9. 验证码错误次数超过 3 次,验证码失效。
  10. 验证码失效后,用户需要重新注册。

后端服务

后端服务使用 Strapi + @strapi/provider-email-nodemailer 这个插件。

配置插件

安装好上面的插件之后,在 Strapi 项目的 config/plugins.js 中配置邮箱服务的信息。

module.exports = ({ env }) => ({
email: {
config: {
provider: 'nodemailer',
providerOptions: {
host: env('SMTP_HOST', 'smtp.qiye.aliyun.com'),
port: env('SMTP_PORT', 465),
secure: true,
auth: {
user: env('**@**.com'),
pass: env('****'),
},
},
settings: {
defaultFrom: '**@**.com',
defaultReplyTo: 'hello@example.com',
},
},
},
})

以上配置在本地开发环境也是可以正常使用的,自己在 Chrome 中切换到了 Charles 代理模式,访问邮箱所属域名下的 URL,通过 Charles 的 Rewrite 功能把请求重写到了本地,这样就能在本地方便地测试邮件发送功能了。

这里有几点要注意:

  1. host 那里填写的是你的邮箱服务商的 SMTP 服务器地址,由于用的是阿里云的企业邮箱,所以参考这篇文档 阿里邮箱IMAP、POP、SMTP地址和端口信息 里的地址。
  2. port 那里要填写对应的端口号,阿里邮箱的 SMTP 端口是 465,同时 secure 要设置为 true
  3. 在邮箱所属域名的 DNS 解析设置里,同样要把 SMTP 服务器地址填写进去,这样才能正常发送邮件。知道这一点,是因为在用阿里云企业邮箱给自己的 QQ 邮箱发邮件失败,QQ 邮箱给的错误信息里有相关文档:什么是SPF?如何设置SPF来防止我的邮件被拒收呢?,阿里云也搜到了相关文档:退信提示“spf check failed”,按照阿里云的设置搞定了。注意 DNS 解析修改后,要等待一段时间(10 分钟)才能生效。
  4. 再次发送测试邮件时,QQ 邮箱又返回了 550 Mail content denied 这个错误信息,原来是邮件内容涉嫌大量群发。看了一下,是代码自动生成的内容,修改了之后就可以了。

· 阅读需 6 分钟
He Wei

配置 ECS

系统选择 Ubuntu 22.04,密码则在实例管理控制台页面,通过重置密码方式进行设置。

但是设置密码之后,依然无法远程 SSH 登录,猜测可能是系统防火墙的问题,先不管了,先把整体环境配置好。

安装 Nginx

sudo apt update

sudo apt install nginx

配置防火墙

sudo ufw app list

sudo ufw allow 'Nginx HTTP'

sudo ufw allow 'OpenSSH'

sudo ufw enable

sudo ufw status

curl -4 icanhazip.com

安装配置 MySQL

sudo apt install mysql-server

sudo mysql_secure_installation

VALIDATE PASSWORD COMPONENT can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: y

MEDIUM Length >= 8, numeric, mixed case, and special characters

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y

安装 PHP

sudo apt install php8.1-fpm php-mysql php-simplexml

配置 Nginx

sudo nano /etc/nginx/sites-available/your_domain

server {
listen 80;
server_name your_domain www.your_domain;
root /var/www/your_domain;

index index.html index.htm index.php;

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}

location ~ /\.ht {
deny all;
}

}

sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/

sudo unlink /etc/nginx/sites-enabled/default

sudo nginx -t

sudo systemctl reload nginx

sudo mkdir /var/www/your_domain

sudo nano /var/www/your_domain/index.html

<html>
<head>
<title>your_domain website</title>
</head>
<body>
<h1>Hello World!</h1>

<p>This is the landing page of <strong>your_domain</strong>.</p>
</body>
</html>

http://server_domain_or_IP

测试 PHP 解析

sudo nano /var/www/your_domain/info.php

<?php
phpinfo();

access http://server_domain_or_IP/info.php

sudo rm /var/www/your_domain/info.php

测试 PHP 连接 MySQL

sudo mysql

CREATE DATABASE example_database;

CREATE USER 'example_user'@'%' IDENTIFIED WITH mysql_native_password BY 'xxxx';

GRANT ALL ON example_database.* TO 'example_user'@'%';

exit

mysql -u example_user -p

SHOW DATABASES;

CREATE TABLE example_database.todo_list (
item_id INT AUTO_INCREMENT,
content VARCHAR(255),
PRIMARY KEY(item_id)
);

INSERT INTO example_database.todo_list (content) VALUES ("My first important item");

SELECT * FROM example_database.todo_list;

exit

sudo nano /var/www/your_domain/todo_list.php

<?php
$user = "example_user";
$password = "xxxx";
$database = "example_database";
$table = "todo_list";

try {
$db = new PDO("mysql:host=localhost;dbname=$database", $user, $password);
echo "<h2>TODO</h2><ol>";
foreach($db->query("SELECT content FROM $table") as $row) {
echo "<li>" . $row['content'] . "</li>";
}
echo "</ol>";
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}

http://server_domain_or_IP/todo_list.php

sudo rm /var/www/your_domain/todo_list.php

安装配置 WordPress

先在 MySQL 中创建数据库和用户。

# 如果需要多语言,则创建两个数据库
CREATE DATABASE wordpress;
CREATE DATABASE wordpress_en;

CREATE USER 'wordpress_admin'@'%' IDENTIFIED WITH mysql_native_password BY 'xxxx';

# 用同一个 DB ADMIN 账户管理两个数据库
GRANT ALL ON wordpress.* TO 'wordpress_admin'@'%';
GRANT ALL ON wordpress_en.* TO 'wordpress_admin'@'%';

sudo apt install unzip

将 WordPress 文件解压到 /var/www/your_domain,再复制一份到 your_domain/en。

将两处的 wp-config-sample.php 复制为 wp-config.php。

vi /var/www/your_domain/wp-config.php,修改数据库连接信息,en 子目录下的相同文件也做同样操作。

访问 http://server_domain_or_IP/wp-admin/install.php,按照提示完成安装,en 子目录下的相同。

如果在输入密码的地方报错 password strength is unknown,有可能是在用 https 协议访问。可以在 wp-config.php 中添加一行:

$_SERVER['HTTPS'] = true; 

搜索 Password strength unknown,在 https://wordpress.stackexchange.com/a/350453 中找到的这个解决方法。

用户名:abc,密码:****

https://developer.wordpress.org/advanced-administration/before-install/howto-install/

使用 WP 主题

将主题的 zip 文件上传到服务器。

如果 Nginx 报错 413 Request Entity Too Large,可以修改 /etc/nginx/nginx.conf,添加或修改 client_max_body_size 为 100M。

http {
client_max_body_size 100M;
}

点击上传按钮后报错 Unable to create directory wp-content/uploads/2024/06. Is its parent directory writable by the server?,可以通过以下命令解决:

sudo chown -R www-data:www-data /var/www/your_domain/wp-content/uploads

但是这样的话,WordPress 还要求配置 FTP 用户名和密码,这不麻烦了吗,不折腾。

家里电脑没法通过 SSH 连接服务器,但是办公室可以,于是就用办公室电脑的 Termius 的 SFTP 功能上传了主题文件。

由于 ecs-user 账户权限有限,所以先上传到 /home/ecs-user,然后再用 sudo mv 命令移动到 /var/www/your_domain/wp-content/themes。

接着用 sudo unzip 解压到压缩包所在目录下。

导出/导入数据库

# 导出数据库
sudo mysqldump -u wordpress_admin -p --databases wordpress > ~/wordpress.sql

# 导入数据库
sudo mysql -u wordpress_admin -p < ~/wordpress.sql

问题记录

在 WordPress 后台启用主题之后,刷新页面报错:

There has been a critical error on this website. Please check your site admin email inbox for instructions.

Learn more about troubleshooting WordPress.

解决方法:将主题文件夹重命名即可。但是这样的话就没法用这个主题了,这样并不能从根本上解决问题。

参考文章:https://kinsta.com/knowledgebase/there-has-been-a-critical-error-on-your-website/。

问了一下客服,让按照 https://themebetter.com/wp-debug.html 的方法,开启 DEBUG 模式。

开启之后,报错信息如下:

Fata error: Uncaugnt Error: call to undefined function simplexml_load_string() in
/var/www/your_domain/wp-content/themes/mok/inc/update.php:72 Stack trace: #0
/var/www/your_domain/wp-content/themes/mok/inc/update.php(4): get_latest_theme_version() #1
/var/www/your_domain/wp-includes/class-wp-hook.php(324): update_notifier_menu() #2
/var/www/your_domain/wp-includes/class-wp-hook.php(348): WP Hook->apply_filters() #3
/var/www/your_domain/wp-includes/plugin.php(517): WP Hook->do_action() #4
/var/www/your_domain/p-admin/includes/menu.php(161): do_action() #5
/var/www/your_domain/wpadmin/menu,php(422): require_once('...') #6
/var/www/lxklsh,com/wp-admin/admin.php(158):reguire('...') #7
/var/www/your_domain/wp-admin/index.php(10): require_once('...') #8
{main} thrown in /var/www/your_domain/wp-content/themes/mok/inc/update.php on line 72

客服说是没有装 PHP 的 simplexml 扩展,装了之后就没问题了。

相关资料

整体流程参考 https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu。