Hexo 作为静态博客,有着速度快、源代码在本地等优点,但比较折腾,评论等功能静态较难实现。虽然也没啥人评论,但本着完美主义折腾的原则,还是配置好评论系统。研究了一番,大家比较推崇 Waline
  既然博客内容都在本地自己掌握,那么评论也打算独立部署,不放到各种云上,起初想不用 docker 直接本地独立部署,遇到 node.js 各种版本和兼容性问题,就放弃采用 docker 部署。初步设想是宿主机安装 Ubuntu 24.04,本机安装 MySQL,docker 安装 Waline。配置 MySQL 时遇到“500: connect econnrefused 127.0.0.1:3306”、“500: getaddrinfo ENOTFOUND host.docker.internal”等问题,参考了相关文章,但并没有解决问题,后来仔细想了想逻辑,这种情况下配置 MySQL 的连接信息需要用 docker 虚拟局域网下的主机 IP 地址,如果 docker ip 是 172.17.0.2,那么一般主机的 IP 会是 172.17.0.1。安装好之后在docker内安装net-tools等工具,测试跟宿主的网络连接,通了的话就没问题了。docker-compose.yml 部分配置可参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
waline:
container_name: waline
image: lizheming/waline:latest
restart: always
ports:
- 8360:8360
volumes:
- ${PWD}/data:/app/data
environment:
MYSQL_HOST: '172.17.0.1'
MYSQL_PORT: '3306'
MYSQL_DB: 'waline'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'

  关于 nginx 反向代理,配置教程较多就不多说。
  关于现有评论迁移,参考这篇。迁移好之后,发现评论都出不来,研究了一下发现 Wordpress 里存的评论对应的 url 都是经过 UrlEncode 的,需要进行 UrlDecode,这样就能对上了,代码可以参考:

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# coding:utf-8

from dataclasses import dataclass
from datetime import datetime, timezone, timedelta
# 引入url处理库 unqote
from urllib.parse import unquote
import pymysql

@dataclass
class WPComment:
post_id: int
post_name: str
post_type: str
comment_ID: int
comment_author: str
comment_author_email: str
comment_author_url: str
comment_author_IP: str
comment_date: str
comment_content: str
comment_agent: str
comment_parent: int

@dataclass
class WPCounter:
post_id: int
post_name: str
post_type: str
post_views: int


#要替换的链接
replace_post_name = {
'the-chatty-jokers' : 'math-and-magic-the-chatty-jokers',
'dicing-with-destiny' : 'math-and-magic-dicing-with-destiny',
}


class WPDatabase:
def __init__(self):
self.db = pymysql.connect(
# 以下为数据库配置信息,按照实际修改
host='host',
user='user',
password='password',
database='database',
# MaxOS使用MAMP时需要加上unix_socket
# unix_socket='/Applications/MAMP/tmp/mysql/mysql.sock',
)

# 获取评论
def get_comments(self):
sql_comment = '''
SELECT
p.ID,
p.post_name,
p.post_type,
c.comment_ID,
c.comment_author,
c.comment_author_email,
c.comment_author_url,
c.comment_author_IP,
c.comment_date,
c.comment_content,
c.comment_agent,
c.comment_parent
FROM
`wp_comments` AS c,
`wp_posts` AS p
WHERE
c.comment_post_ID = p.ID AND
c.comment_approved != "spam"
ORDER BY
c.comment_ID ASC;
'''
cursor = self.db.cursor()
cursor.execute(sql_comment)
results = cursor.fetchall()

comments = []
for row in results:
wp_comment = WPComment(*row)
wp_comment.comment_content = wp_comment.comment_content.replace("'", "\\'")
wp_comment.comment_author = wp_comment.comment_author.replace("'", "\\'")
if wp_comment.post_name in replace_post_name:
wp_comment.post_name = replace_post_name[wp_comment.post_name]
comments.append(wp_comment)
return comments

# 获取文章浏览量
def get_counters(self):
sql_counter = '''
SELECT
p.ID,
p.post_name,
p.post_type,
c.meta_value
FROM
`wp_postmeta` AS c,
`wp_posts` AS p
WHERE
c.meta_key = "views" AND
p.post_status = "publish" AND
c.post_id = p.ID
ORDER BY
c.post_id ASC;
'''
cursor = self.db.cursor()
cursor.execute(sql_counter)
results = cursor.fetchall()

counters = []
for row in results:
wp_counter = WPCounter(*row)
if wp_counter.post_name in replace_post_name:
wp_counter.post_name = replace_post_name[wp_counter.post_name]
wp_counter.post_views = int(wp_counter.post_views)
counters.append(wp_counter)
return counters


class WalineDatabase:
def __init__(self):
self.db = pymysql.connect(
# 以下为配置信息,根据实际修改
host='host',
user='user',
password='passowrd',
database='data',
# MaxOS使用MAMP时需要加上unix_socket
# unix_socket='/Applications/MAMP/tmp/mysql/mysql.sock',
)
self.wp_to_waline_id = {}
self.wp_reply_id = {}
self.wp_reply_nickname = {}

# 递归查找评论的根id
def find_root_id(self, child_id):
parent_id = self.wp_reply_id[child_id]
if parent_id == child_id:
return parent_id
else:
return self.find_root_id(parent_id)

# 添加评论
def add_comments(self, wp_comments):
cursor = self.db.cursor()
for wp_comment in wp_comments:
pid = 'NULL'
rid = 'NULL'
comment = ''
if wp_comment.comment_parent == 0:
# 新评论
comment = '<p>%s</p>\n'%(wp_comment.comment_content)
self.wp_reply_id[wp_comment.comment_ID] = wp_comment.comment_ID
else:
# 回复别人的评论
pid = self.wp_to_waline_id[wp_comment.comment_parent]
rid = self.wp_to_waline_id[self.find_root_id(wp_comment.comment_parent)]
reply_name = self.wp_reply_nickname[wp_comment.comment_parent]
comment = '<p><a class="at" href="#%s">@%s</a> , %s</p>\n'%(pid, reply_name, wp_comment.comment_content)
self.wp_reply_id[wp_comment.comment_ID] = wp_comment.comment_parent
# 利用unquote进行urlDecode
url = '/%s/'%(unquote(wp_comment.post_name))

sql_comment = f'''
INSERT INTO `wl_Comment` (`comment`, `insertedAt`, `ip`, `link`, `mail`, `nick`, `pid`, `rid`, `status`, `ua`, `url`)
VALUES ('{comment}', '{wp_comment.comment_date}', '{wp_comment.comment_author_IP}', '{wp_comment.comment_author_url}', '{wp_comment.comment_author_email}', '{wp_comment.comment_author}', {pid}, {rid}, 'approved', '{wp_comment.comment_agent}', '{url}');
'''
cursor.execute(sql_comment)
self.db.commit()

self.wp_to_waline_id[wp_comment.comment_ID] = cursor.lastrowid
self.wp_reply_nickname[wp_comment.comment_ID] = wp_comment.comment_author
print(wp_comment.comment_date, wp_comment.comment_author)

# 添加文章浏览量
def add_counters(self, wp_counters):
cursor = self.db.cursor()
for wp_counter in wp_counters:
if wp_counter.post_views == 0:
continue
# 利用unquote进行urlDecode
url = '/%s/'%(unquote(wp_counter.post_name))

sql_comment = f'''
INSERT INTO `wl_Counter` (`time`, `url`)
VALUES ({wp_counter.post_views}, '{url}');
'''
cursor.execute(sql_comment)
self.db.commit()
print(wp_counter.post_id, wp_counter.post_views)


if __name__ == '__main__':
wp_database = WPDatabase()
waline_database = WalineDatabase()

wp_comments = wp_database.get_comments()
waline_database.add_comments(wp_comments)

wp_counters = wp_database.get_counters()
waline_database.add_counters(wp_counters)