使用Memos为博客搭建一个‘哔哔空间’

哔哔空间

有时候总想发点什么,因为是自己的一些胡思乱想,不想让被人看到,所以我需要一个私人的“空间”,之前用过木木大佬的木木ber,但是由于腾讯云取消了免费托管额度,导致我的哔哔空间停滞了。最近get到一个好东西,Memos是一种隐私优先的轻量级笔记服务,可以直接跑在我们自己服务器的Docker上,配合它自带的一些接口,终于又让我的哔哔空间复活了!

示例:哔哔空间(●’◡’●)

部署Memos

这里我们有个前提,就是你的服务器上已经安装并运行了Docker服务,如果没有Docker的话需要自行去了解怎样安装运行,这里主要是讲解如何部署Memos。

直接输入以下命令一键安装并运行Memos

1
docker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:stable

要注意~/.memos/目录是挂载目录,里面存放的是Memos的数据库数据,是我们迁移备份的重要文件,一定要定时做本地备份。

  • 下载运行成功后可以访问服务器ip:5230查看Memos的前端页面(记得要去你的服务器控制台安全组将5230端口开放),如下所示,下面可以调整系统语言。image-20240326212542099

  • 在以上页面创建一个管理员账户。进来后设置一下系统语言为中文。image-20240326213000752

  • 回到主页发一条memo,带上前缀#说说 ,并设置为公开。

    image-20240326213516953

为Memos配置域名

如果没有域名的同学可以直接跳过这一步,配置域名只是为了后面访问更加简洁美观,域名最好是已经备案过的域名,我们创建一个二级域名。

image-20240326214022048

域名创建好需要配置一下Nginx,因为很多同学只有一个服务器,所以需要使用Nginx代理不同的服务,配置文件可以参考如下

1
2
3
4
5
6
7
8
server {
listen 80;
server_name 你的域名;

location / {
proxy_pass http://127.0.0.1:5230;
}
}

配置好之后就可以直接用域名访问Memos的前端页面了,后面需要调用接口的地方也都可以使用域名调用,没有域名的同学直接使用IP:5230即可,效果是一样的。

给博客新增哔哔空间页面

这里前提是你的博客跟我的使用的是同样的主题,如果不是的话有些代码样式需要自行更改。

在博客主目录hexo new page "memos"新建一个页面,在/source/memos/目录下的index.md文件中粘贴以下代码,其中一些需要你做更改的地方我做出了标记(一处是封面地址,一处是你的Memos地址,一处是头像地址,最后一处是用户昵称)

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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

<div class="author-content author-content-item single"
style="background:url(https://bu.dusays.com/2023/12/10/6575b11d88a0a.png) left 28%/cover no-repeat">
<div class="card-content">
<div class="author-content-item-tips"></div><span class="author-content-item-title">哔哔空间</span>
</div>
</div>

<style>
.bber-reply {
cursor: pointer !important;
}
.bber-reply:hover {
color: var(--anzhiyu-main);
max-height: 35px;
}

.talk_item,
.tk-expand,
.tk-comments-container>.tk-comment,
.tk-submit:nth-child(1) {
background: rgba(255,255,255,.6);
border: 1px solid #e0e3ed;
box-shadow: 0 5px 10px rgb(189 189 189 / 10%);
transition: all .3s ease-in-out;
border-radius: 12px;
}

/* 瀑布溜布局卡片 */
.talk_item {
display: inline-block;
width: 32.7%;
margin-right: 1%;
padding: 15px 1rem 12px;
margin-bottom: 15px;
}

[data-theme=dark] .talk_item,
.tk-expand,
.tk-comments-container>.tk-comment,
.tk-submit:nth-child(1) {
background: none;
}

.talk_item:hover,
.tk-comments-container>.tk-comment:hover,
.tk-submit:nth-child(1):hover {
border-color: #49b1f5;
}

.tk-submit {
padding: 20px 10px 0;
}

.tk-comments-container>.tk-comment {
padding: 15px;
}

/* 页面初始化结束 */

#talk {
margin-top: 1rem;
}

#talk .loading {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}

#talk .loading img {
width: 200px;
}

.avatar {
margin: 0 !important;
width: 60px;
height: 60px;
border-radius: 10px !important;
}


.talk_bottom,
.talk_meta {
display: flex;
align-items: center;
width: 100%;
line-height: 1.5;
}

.talk_bottom {
justify-content: space-between;
}

.info {
display: flex;
flex-direction: column;
margin-left: 10px;
}

span.talk_nick {
color: #6dbdc3;
font-size: 1.2rem;
}

svg.is-badge.icon {
width: 15px;
margin-left: 5px;
padding-top: 3px;
}

span.talk_date {
font-size: 14px;
opacity: .6;
}

.talk_content {
line-height: 1.5;
margin-top: 10px;
}

.zone_imgbox {
display: flex;
flex-wrap: wrap;
--w: calc(25% - 8px);
gap: 10px;
margin-top: 5px;
}

.zone_imgbox a {
display: block;
border-radius: 12px;
max-height: 177px;
aspect-ratio: auto;
position: relative;
}

.zone_imgbox img {
width: auto;
height: 100%;
margin: 0 !important;
object-fit: contain;
}

/* 底部 */

.talk_bottom {
opacity: .9;
}

.talk_bottom a {
border-bottom: none !important;
}

span.talk_tag {
font-size: 14px;
}

.talk_content>a {
margin: 0 3px;
color: #ff7d73 !important;
}

.talk_content>a:hover {
text-decoration: none !important;
color: #ff5143 !important
}

/* 提醒 */

.limit {
transition: all .3s ease-in-out;
color: rgba(76, 73, 72, 0.6);
}

[data-theme=dark] .limit {
color: rgba(255, 255, 255, 0.5);
}

.limit {
display: none;
text-align: center;
margin-top: 20px;
color: #4c4948;
}

/* 哔哩哔哩视频适配 */
iframe {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}

.video {
position: relative;
padding: 30% 45%;
margin-top: 10px;
margin-bottom: 10px;
border-radius: 12px;
overflow: hidden;
}

/* 手机卡片自适应 */
@media screen and (max-width: 768px) {
.talk_item {
width: 49.3%;
margin-right: 1.4%;
}
.zone_imgbox a {
width: calc(50% - 3px);
max-height: calc(50% - 3px);
}
}

@media screen and (max-width: 1100px) {
.talk_item {
width: 100%;
margin-right: 0;
}
.zone_imgbox a {
width: calc(50% - 3px);
max-height: calc(50% - 3px);
}
}

@media screen and (max-width: 900px) {
.zone_imgbox {
--w: calc(33% - 5px);
}

#talk {
margin: 10px 3px 0
}

#post-comment {
margin: 0 3px
}
.zone_imgbox a {
width: calc(50% - 3px);
max-height: calc(50% - 3px);
}
}

@media screen and (max-width: 768px) {
.zone_imgbox {
gap: 6px;
}

.zone_imgbox {
--w: calc(50% - 3px);
}

span.talk_date {
font-size: 14px;
}
.zone_imgbox a {
width: calc(50% - 3px);
max-height: calc(50% - 3px);
}
}
</style>

<div id="talk" style="position: relative">
<div class='loading'><img src="/img/loading.gif" alt="加载中..."></div>
</div>
<div class="limit">- 只展示最近30条说说 -</div>
<script>
if (1) {
let url = 'http://memos.ayuisokay.com'
fetch(url + '/api/v1/memos?creatorId=1&tag=说说&limit=30').then(res => res.json()).then(data => { // 注意修改域名和用户id
let items = [],
html = '',
icon = '<svg viewBox="0 0 512 512"xmlns="http://www.w3.org/2000/svg"class="is-badge icon"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z"fill="#1da1f2"></path></svg>';
data.memos.forEach(item => { items.push(Format(item)) });
if (items.length == 30) document.querySelector('.limit').style.display = 'block';
items.forEach(item => {
html += `<div class="talk_item"><div class="talk_meta"><img class="no-lightbox nolazyload avatar" src="https://bu.dusays.com/2023/12/10/6575961420d93.jpg"><div class="info"><span class="talk_nick">aYu${icon}</span><span class="talk_date">${item.date}</span></div></div><div class="talk_content">${item.content}</div></div>` // 注意修改头像链接和名称
})
document.getElementById('talk').innerHTML = html
setTimeout(() => waterfall('#talk'), 50)
});
// 页面内容格式化
function Format(item) {
let date = getTime(new Date(item.createTime)),
content = item.content,
tag = item.content.match(/\{(.*?)\}/g),
imgs = [],
musics = content.match(/{\s*music\s*(.*)\s*}/g),
contentstr = content.substr(4),
text = '',
videos = content.match(/bilibili/i);
if (imgs) imgs = [];
if (item.resources && item.resources.length) {
item.resources.forEach(res => {
if (res.externalLink) {
imgs.push(res.externalLink);
} else {
// 新模型资源路径可能需要调整
imgs.push(`${url}/file/${res.name}/${res.filename}`);
}
});
}
// content = content.replace(/#(.*?)\s/g, '').replace(/{.*}/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '').replace(/```/g, '').replace(/\[(.*?)\]\((.*?)\)/g, `<a href="$2">@$1</a>`);
text = content.replace(/#(.*?)\s/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '').replace(/\{(.*?)\}/g, '')
content = text.replace(/\[(.*?)\]\((.*?)\)/g, `<a href="$2">@$1</a>`);
if (imgs) {
content += `<div class="zone_imgbox">`
imgs.forEach(e => content += `<a href="${e}" data-fancybox="gallery" class="fancybox" data-thumb="${e}"><img class="nolazyload talk-img" src="${e}"></a>`)
content += '</div>'
}
if (musics) musics.forEach(item => { content += `<meting-js auto="${item.replace(/{\s*music\s*(.*)\s*}/, '$1')}" theme="#4976f5" preload="metadata"></meting-js>` })
if (videos) videos.forEach(item => {
content = `<div style="position: relative; padding: 30% 45%;margin-top: 10px;margin-bottom: 10px"><iframe style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;" src="//player.bilibili.com/player.html?autoplay=0&bvid=${contentstr.replace(/{\s*bilibili\s*(.*)\s*}/, '$1').replace(/.*video\/([^\/]*).*/, '$1')}" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe></div>`
})
return {
content: content,
date: date,
tag: tag ? tag[0].replace(/\{(.*?)\}/, '$1') : '无标签',
text: text.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]' + `${imgs ? '[图片]' : ''}`)
}
}
// 页面时间格式化
function getTime(time) {
let d = new Date(time),
ls = [d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
for (let i = 0; i < ls.length; i++) {
ls[i] = ls[i] <= 9 ? '0' + ls[i] : ls[i] + ''
}
if (new Date().getFullYear() == ls[0]) return ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
else return ls[0] + '年' + ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
}
}
</script>

在配置文件中配置上新建的页面,这里参考我的配置文件,你要根据自己的喜好做更改

1
2
3
4
5
6
7
8
9
10
11
12
13
menu:
文章:
隧道: /archives/ || anzhiyu-icon-box-archive
分类: /categories/ || anzhiyu-icon-shapes
标签: /tags/ || anzhiyu-icon-tags

有趣:
小游戏: /cat/ || anzhiyu-icon-fish
小空调: /air-conditioner/ || anzhiyu-icon-fan

关于:
关于本人: /about/ || anzhiyu-icon-paper-plane
哔哔空间: /memos/ || anzhiyu-icon-lightbulb

hexo cl && hexo g && hexo s本地查看效果,没问题再hexo d,效果如下所示

image-20240326215953854

用iPhone发送一条memo

既然是哔哔空间,那就要做到随时随地想说啥说点啥,浏览器的话有大佬做了扩展插件可以用,但是我们日常使用手机的场景才是最多的,如何使用手机发送方便的发送memo才是大部分用户想要的。

由于博主是苹果用户所以只能用苹果手机举例子,苹果手机使用的是快捷指令,当然安卓手机应该也有类似的功能,不过具体的实现逻辑是差不多的。

  • 首先要去设置一个非常重要的东西,调用接口所使用的token

    image-20240326223425423

  • 复制出新创建的token,后面有用。

  • 来到我们的手机端,打开快捷指令,新建一个快捷指令,并重命名为你想要的名字。

    image-20240326224314398

  • 在下面的搜索框中搜索文本,键入以下命令

    1
    http://memos的域名或者IP:5230/api/v1/memo

    搜索变量,选择添加到变量(标图有误),拖到文本下面,将变量名更改为requesturl

    image-20240326225022950

  • 搜索请求输入,可以改个你喜欢的名字;再次搜索变量,选择添加到变量(标图有误),将变量名改为content

    image-20240326225744670

  • 搜索如果(相当于if),条件设为content变量有任何值,关掉否则项;搜索URL,选择URL项,将URL项拖到如果项下面,URL项中添加requesturl变量。

    image-20240326230600699

  • 搜索获取URL内容,拖到URL项下面;展开获取获取URL内容项,方法选择POST;添加新头部,键为Cookie,内容如下

    1
    memos.access-token=上面复制的token

    添加四个新字段

    1. 文本字段,键为content, 文本为 #说说 变量content,如下图所示。
    2. 文本字段,键为visibility, 文本为 PUBLIC,如下图所示。
    3. 数组字段,键为resourceIdList, 0项,如下图所示。
    4. 数组字段,键为relationList, 0项,如下图所示。

    image-20240326231511580

  • 搜索变量,选择设定变量,拖到获取URL内容项下面,将变量名改为result;搜索通知,选择显示通知,拖到变量项下面,显示通知项内容改为 发布成功:变量content 如下图所示。

    image-20240326232846319

  • 点击完成,退出编辑快捷指令;点击指令运行,输入文本后完成就可以发送一条memo啦。

    image-20240326233753799

  • 来到我们的博客查看一下效果。

    image-20240326234129847

这里的iPhone快捷指令可能比较乱,有什么问题可以评论给我留言。