PassNAT LogoPassNAT

自建推送通知 Gotify:飞牛 NAS Docker 部署,家里的脚本有事就发一条,配内网穿透在外面也能收

作者

小编

发布时间

Curving abstract shapes with an orange and blue gradient

项目介绍

Gotify 是一个自己部署的消息推送服务器。说得再直白一点,它是一个收件箱 —— 家里的备份脚本跑完了、定时任务出错了、下载机把队列清空了,都可以往它这儿投一条消息,你打开网页就能看见。它自己不去看任何东西,也不去监控任何东西,消息全部是别的程序发进来的。

它最舒服的一点是不用刷新:网页开着放在那儿,新消息进来的时候页面自己就多出一条,因为它跟浏览器之间是一条 WebSocket 长连接。实测从外部发出一条到页面上出现,半秒左右。

东西放在哪儿是它跟各家在线推送服务最大的区别:消息、应用、token 全部落在你自己 NAS 的一个目录里,备份的时候把那个文件夹拷走就是一份完整备份。项目在 GitHub 上开源,MIT 协议,用 Go 写的,官方镜像给了 amd64、arm64、386、arm/v7 和 riscv64 五种架构,老一点的 NAS 和树莓派也跑得动。

它本身极轻:一个容器就够,数据库用内置的 SQLite,不需要再起一个数据库容器;实测跑起来之后容器占用不到二十兆内存,放在 NAS 上常年开着几乎感觉不到。

项目地址:gotify/server

部署安装

装在自己的 NAS 上之后,它默认只能在家里的网络里打开 —— 可人恰恰是出了门才最需要它:家里的机器有什么动静,在公司、在路上要能看见才有意义,只能在家看的通知等于没有通知。家里没有公网 IP 的话,可以用 帕斯内网穿透 把它开放到公网,飞牛的应用中心里就能直接装到插件,不用另外找工具。下面这套流程在飞牛 NAS 上从头到尾走一遍。

第一步:准备编排文件

下面这份编排可以直接用,只有一处需要按自己的情况改:冒号左边的宿主端口。

1services:
2 gotify:
3 image: gotify/server:latest
4 container_name: gotify
5 restart: unless-stopped
6 ports:
7 # 左边 3047 是 NAS 上对外的端口,浏览器敲它;右边 80 是容器里的
8 - "3047:80"
9 environment:
10 # 不填的话消息上的时间会差八小时
11 TZ: Asia/Shanghai
12 volumes:
13 # 消息和应用都存在 NAS 的这个目录里,备份直接把文件夹拷走
14 - ./data:/app/data
15

端口:冒号左边的 3047 是待会儿在浏览器里输的宿主端口,也是建隧道时要填的那个;右边的 80 是容器内部的端口,不要动。3047 如果被别的容器占了,换一个没用过的即可。

时区:TZ 填 Asia/Shanghai。这一行不填的话,消息上显示的时间会跟本地差八小时,一条刚发进来的消息可能显示成八小时前,看起来很奇怪。

数据目录:./data 里是数据库、应用图标和证书,也就是这个实例的全部家当。它是相对于编排文件所在目录的,所以整个项目文件夹拷走就是一份完整备份。

镜像标签跟着官方文档走用 latest 就行,不建议自己钉一个具体的版本号 —— 钉死之后上游更新了你这边还停在旧版。

第二步:在飞牛上建 Compose 项目

打开飞牛的 Docker 应用,切到 Compose,点右上角的新增项目。项目名称填 gotify。

路径这一栏点开,在「我的文件」下面挑一个空文件夹选中 —— 这个文件夹以后就是这个项目的家,编排文件和上面说的 data 目录都会放在里面。

来源选「创建 docker-compose.yml」,把上面那份编排整段贴进去。

下面「创建项目后立即启动」勾上,点创建。镜像只有二三十兆,拉完几乎立刻就跑起来了。

第三步:用内网穿透开放到公网(先别通电)

在飞牛的应用中心里搜 PassNAT,装上,账号登一次。打开插件点创建隧道:名字随便起;协议选 TCP;本地 IP 这一栏留空就好,默认就是 127.0.0.1,不用去查 NAS 的局域网地址;本地端口填上面那个 3047。

隧道建出来之后,先不要按电源 —— 下一步做完再开,理由见下。

第四步:先把默认口令改掉,再通电

Gotify 出厂自带一个管理员账号,用户名和密码都是 admin。它不像有些应用那样第一次打开让你现建管理员,账号在容器起来的那一刻就已经存在了。所以正确的顺序是:隧道先建好不通电,在家里的网络上用 NAS 地址加端口打开它,用 admin / admin 登进去,把密码改成你自己的,然后再回 PassNAT 插件里按电源。这样从它能被外面访问的第一秒起,用的就已经是新密码了。

改完按电源,状态转成运行中,面板上那个「IP:端口」就是在外面要用的公网地址,后面所有操作都用它。

怎么用

建一个应用,拿到 token

用公网地址打开、登录进去,第一眼是空的 —— 前面说了,它是收件箱,得先有人往里投。而往里投消息要先有一个「应用」:应用就是发件人的身份,家里每一类东西给它开一个,消息进来的时候你一眼就知道是谁发的。

点顶上的 APPS,再点右上角的 CREATE APPLICATION,填一个名字(比如「家里的备份」),描述可以不填,默认优先级留 0,点 CREATE。

点完 CREATE 会弹一个框,标题是 Token Confirmation,上面写着 Your token will only be shown once。框里那一串就是发消息用的凭据,等下要填进命令里 —— 注意它字面意思就是「只显示这一次」,点下面的 COPY TO CLIPBOARD 复制走,或者先记到别处,再点 FINISH 关掉。

当时没存也不用重建应用:回到应用列表,那一行最右边有三个图标,最左边的钥匙是「重新生成 token」,点它会给你一串新的。不过旧的那串会当场失效,已经在用旧 token 的脚本记得跟着改。

发一条消息进来

拿到 token 就可以发了。任何能发 HTTP 请求的东西都行,最省事的是 curl:

1curl -X POST "http://你的公网地址/message?token=<换成你自己的token>" \
2 -H "Content-Type: application/json; charset=utf-8" \
3 -d '{"title":"备份完成","message":"昨晚的照片备份跑完了,用时 4 分 12 秒。","priority":5}'

把「你的公网地址」换成 PassNAT 给你的那个「IP:端口」,把尖括号那一段换成刚才复制的 token。title 是消息标题,message 是正文,priority 是优先级 —— 数字越大,消息左边那条色带越显眼。回车之后,如果你的网页正开着,不用刷新,消息自己就排进去了。

有一点要提醒:这条命令里带了中文,请在 UTF-8 的终端里跑。NAS 上的 shell 默认就是 UTF-8,直接粘进去没问题;在 Windows 的命令行里直接粘,中文可能会按别的编码发出去,存下来就是一串乱码。真遇到了,把那段 JSON 存成一个 UTF-8 的文件,用 --data-binary @文件名 代替 -d 就好了。

想在写脚本之前先试一条,也可以不用命令行:在左边点开某个应用,上面会多出一个 PUSH MESSAGE 按钮,填个标题和正文点 SEND,效果跟命令发进来的一模一样 —— 用来确认隧道和 token 都通了很方便。

接下来把这条命令放进你本来就有的地方:备份脚本的最后一行、定时任务的收尾命令、或者任何一个跑完想告诉你一声的程序里。它们各自用各自的 token,消息就会按应用分开归到左边的列表里。

一些使用上的细节

界面上的按钮是英文的(USERS、APPS、CLIENTS 这些),不过要点的地方就那么几个,而且画面上占地方最大的那块 —— 消息标题和正文 —— 是你自己发进去的,中文显示完全正常,标点和书名号都不会出问题。

左边那一栏会把消息按应用分开。想只看某一类,点对应的应用名就行;右上角的 DELETE ALL 是清空当前这一栏,不是清空整个库。

数据都在项目文件夹的 data 目录里。想搬到另一台机器,把整个文件夹拷过去、用同样的编排起一个容器就能接着用,应用和 token 都还在,家里的脚本一行都不用改。

家里人各要一个收件箱的话,USERS 里可以再建用户,各人的应用和消息互不干扰。

如果暂时不想让外面访问,去 PassNAT 插件里把那条隧道的电源关掉就行,内网照常用,不用动容器。