一个轻量级的实时消息推送服务器,兼容 Gotify API,支持 WebSocket 实时通信、插件系统和多用户管理。
- 实时消息推送 - 基于 WebSocket 的实时消息传输
- Gotify API 兼容 - 完全兼容 Gotify REST API,支持青龙面板等第三方应用
- 多用户管理 - 支持多用户、多应用管理
- 插件系统 - 可扩展的插件架构,支持消息钩子
- 主题切换 - 支持亮色/暗色主题
- Docker 部署 - 开箱即用的 Docker 支持
- 轻量级架构 - 基于 SQLite,无需额外数据库依赖
- Node.js >= 18.0
- npm >= 9.0
- Docker (可选,用于容器化部署)
# 克隆项目
git clone https://github.com/mikus-loli/Miotify.git
cd miotify
# 安装依赖
npm install
# 构建前端
npm run build
# 启动服务
npm start服务将在 http://localhost:8080 启动。
# 使用 docker-compose
docker-compose up -d
# 或直接运行
docker run -d \
--name miotify \
-p 8080:8080 \
-v miotify-data:/app/data \
ghcr.io/mikus-loli/Miotify:latest首次启动时,JWT 密钥会自动生成并显示在日志中。查看日志:
docker logs miotify(首次启动时注意保存显示的 JWT Secret)
首次启动会自动创建管理员账号:
| 字段 | 值 |
|---|---|
| 用户名 | admin |
| 密码 | admin |
创建 .env 文件或设置环境变量:
| 变量名 | 说明 | 默认值 |
|---|---|---|
PORT |
服务端口 | 8080 |
JWT_SECRET |
JWT 密钥(可选,首次运行自动生成) | 自动生成 |
JWT_EXPIRES_IN |
Token 有效期 | 7d |
DB_PATH |
数据库路径 | ./data/miotify.db |
DEFAULT_ADMIN_USER |
默认管理员用户名 | admin |
DEFAULT_ADMIN_PASS |
默认管理员密码 | admin |
TZ |
时区 | Asia/Shanghai |
MAX_MESSAGE_LENGTH |
消息最大长度 | 5000 |
MAX_MESSAGES_PER_APP |
每应用最大消息数 | 200 |
TRUST_PROXY |
信任的反向代理层数(CDN/反代后保持 1;源站直连公网建议设 0,防止伪造 X-Forwarded-For 绕过限流) | 1 |
RATE_LIMIT_WINDOW_MS |
API 限流窗口(毫秒) | 900000(15 分钟) |
RATE_LIMIT_MAX |
每窗口最大请求数 | 100 |
LOG_RETENTION_COUNT |
日志保留条数上限(0 = 不限制) | 5000 |
LOG_RETENTION_DAYS |
日志保留天数上限(0 = 不限制) | 30 |
WS_MAX_CONNECTIONS_PER_USER |
单用户 WebSocket 最大连接数(0 = 不限制) | 5 |
CORS_ORIGIN |
跨域白名单(逗号分隔)。留空 = 同源部署,禁止跨域(推荐) | 空 |
首次启动说明:如果未设置
JWT_SECRET环境变量,系统会自动生成一个 128 字符的随机密钥并保存到数据库中,同时在控制台打印该密钥。请妥善保管此密钥,重启后会复用已保存的密钥。
# 同时启动前后端开发服务
npm run dev
# 仅启动后端(支持热重载)
npm run dev:server
# 仅启动前端
npm run dev:webMiotify 使用两种 Token:
- JWT Token - 用户登录后获取,用于管理 API
- App Token - 应用创建时生成,用于发送消息
POST /api/login
Content-Type: application/json
{
"name": "admin",
"pass": "admin"
}响应:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"id": 1,
"name": "admin",
"admin": true
}需要 JWT Token 认证(Authorization: Bearer <JWT_TOKEN>)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/application |
GET | 获取应用列表(token 掩码) |
/api/application |
POST | 创建应用 |
/api/application/:id |
GET | 获取应用详情 |
/api/application/:id |
PUT | 更新应用 |
/api/application/:id |
DELETE | 删除应用 |
/api/application/:id/token |
GET | 获取完整 token(仅属主/管理员,列表接口刻意掩码防泄露) |
/api/application/:id/image |
POST | 上传应用图标 |
/api/application/:id/image |
DELETE | 删除应用图标 |
POST /api/application
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
{
"name": "My App",
"description": "应用描述"
}响应:
{
"id": 1,
"token": "550e8400-e29b-41d4-a716-446655440000",
"name": "My App",
"description": "应用描述",
"image": null,
"user_id": 1,
"created_at": "2026-01-01 12:00:00"
}使用 App Token 认证:
POST /api/message
Authorization: Bearer <APP_TOKEN>
Content-Type: application/json
{
"title": "通知标题",
"message": "消息内容",
"priority": 5
}使用 JWT Token 认证:
GET /api/message?limit=50&appid=1
Authorization: Bearer <JWT_TOKEN>DELETE /api/message/:id
Authorization: Bearer <JWT_TOKEN>需要管理员权限
| 端点 | 方法 | 说明 |
|---|---|---|
/api/user |
GET | 获取用户列表 |
/api/user |
POST | 创建用户 |
/api/user/:id |
PUT | 更新用户信息 |
/api/user/:id/password |
PUT | 修改密码 |
/api/user/:id |
DELETE | 删除用户 |
连接地址:ws://host:port/ws
Token 通过 Sec-WebSocket-Protocol 子协议传递(第二个参数),不要放在 URL query 里——避免 token 泄露到访问日志/浏览器历史。
// 浏览器
const ws = new WebSocket('ws://localhost:8080/ws', ['miotify', 'YOUR_JWT_TOKEN']);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Message:', data);
};# Python (websockets 库)
import asyncio, websockets, json
async def listen(token):
async with websockets.connect('ws://localhost:8080/ws', subprotocols=['miotify', token]) as ws:
async for raw in ws:
data = json.loads(raw)
if data['type'] == 'message':
print('Message:', data['data'])
asyncio.run(listen('YOUR_JWT_TOKEN'))消息格式:
{
"type": "message",
"data": {
"id": 1,
"appid": 1,
"title": "通知标题",
"message": "消息内容",
"priority": 5,
"created_at": "2026-01-01 12:00:00"
}
}Miotify 兼容 Gotify REST API 的常用端点,可直接用于青龙面板等支持 Gotify 的应用。
| Gotify 端点 | 说明 |
|---|---|
POST /message |
发送消息(App Token) |
GET /message |
获取消息列表(Client Token) |
GET /message/:id |
获取单条消息 |
DELETE /message/:id |
删除单条消息 |
DELETE /message |
删除当前用户全部消息 |
GET /application |
获取应用列表(返回完整 token) |
GET /application/:id |
获取单个应用 |
POST /application |
创建应用 |
PUT /application/:id |
更新应用 |
DELETE /application/:id |
删除应用 |
POST /application/:id/image |
上传应用图标 |
GET /application/:id/message |
获取该应用的消息(官方单数路径) |
DELETE /application/:id/message |
删除该应用的全部消息 |
GET /current/user |
获取当前用户信息 |
POST /current/user/password |
修改当前用户密码(旧 token 立即失效) |
GET /health |
健康检查(含 health: "green") |
GET /version |
版本信息 |
兼容范围说明:Miotify 实现了 Gotify 的消息/应用/当前用户端点。Gotify 的
/user用户管理端点和/plugin插件端点未实现——对应功能请使用 Miotify 自身的/api/user与/api/plugins管理 API。
支持三种认证方式:
# 方式一:X-Gotify-Key 头
curl -H "X-Gotify-Key: <APP_TOKEN>" ...
# 方式二:token 查询参数
curl "http://host/message?token=<APP_TOKEN>" ...
# 方式三:Authorization Bearer
curl -H "Authorization: Bearer <APP_TOKEN>" ...在青龙面板的通知设置中:
| 配置项 | 值 |
|---|---|
GOTIFY_URL |
http://your-miotify-host:8080(不带 /message) |
GOTIFY_TOKEN |
Miotify 应用的 token |
GOTIFY_PRIORITY |
消息优先级(默认 0) |
plugins/
└── available/
├── email-forwarder.js # 邮件转发插件
├── napcat-forwarder.js # NapCat QQ转发插件
├── hermes-qq-notify.js # Hermes webhook 推送插件(QQ 等平台)
└── qq-direct-notify.js # QQ 官方 Bot API 直连推送插件
module.exports = {
meta: {
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
description: '插件描述',
author: 'Author',
license: 'MIT',
},
defaultConfig: {
enabled: true,
option1: 'default-value',
},
hooks: {
'message:beforeSend': async (ctx, message) => {
// 处理消息,返回 null 可阻止发送;
// 返回修改后的消息时需返回完整对象 { title, message, priority, appid }(缺失字段会丢失)
return message;
},
'message:afterSend': async (ctx, message) => {
// 消息发送后的处理
},
},
init: async (ctx) => {
// 插件初始化
const { config, log, db } = ctx;
log('info', 'Plugin initialized');
},
destroy: () => {
// 插件销毁
},
};| 钩子 | 参数 | 说明 |
|---|---|---|
message:beforeSend |
message | 消息发送前,返回 null 阻止发送 |
message:afterSend |
message | 消息发送后 |
message:onReceive |
message | 客户端拉取到消息时 |
user:onCreate |
user | 用户创建时 |
user:onDelete |
user | 用户删除时 |
app:onCreate |
app | 应用创建时 |
app:onDelete |
app | 应用删除时 |
plugin:onEnable |
plugin | 插件启用时 |
plugin:onDisable |
plugin | 插件停用时 |
将消息转发到指定邮箱。
配置项:
smtp.host- SMTP 服务器地址smtp.port- SMTP 端口smtp.secure- 是否使用 SSLsmtp.auth.user- SMTP 用户名smtp.auth.pass- SMTP 密码from- 发件人地址to- 收件人地址
将消息转发到 QQ(通过 NapCat)。
配置项:
httpUrl- NapCat HTTP 地址accessToken- Access TokentargetType- 目标类型(private/group)targetId- 目标 ID(QQ号/群号)forwardAllApps- 是否转发所有应用
收到消息后通过 Hermes Agent 的 webhook(--deliver-only 模式,零 LLM 成本)推送到
主人/机器人绑定的聊天平台(QQ、Telegram、微信等)。适合已有 Hermes Agent 网关的用户,
不需要额外部署 NapCat 等中转服务。
配置项:
webhookUrl- Hermes webhook 地址(hermes webhook subscribe创建后获得)webhookSecret- webhook 的 HMAC secret(hermes webhook list可查)minPriority- 最低优先级(默认 0 = 全部转发)forwardAllApps- 转发所有应用(默认 true;false 时只转发enabledApps列出的)enabledApps- 只转发指定应用 ID(forwardAllApps=false时生效,空数组 = 全部)maxContentLength- 消息内容截断长度(默认 500,防聊天平台超长)retries- 失败重试次数(指数退避,默认 2)
也可通过环境变量
HERMES_WEBHOOK_URL/HERMES_WEBHOOK_SECRET注入(优先于界面配置)。⚠️ webhookSecret是签名密钥,请妥善保管,不要提交到公开代码库。
收到消息后直接调用 QQ 开放平台机器人 API(C2C 消息)推送到主人 QQ。
不依赖任何中间服务(无需 NapCat、Hermes、webhook、反代):
Miotify → QQ 官方 Bot API → 主人 QQ。
配置项:
appId- QQ 开放平台机器人的 AppIDclientSecret- QQ 开放平台机器人的 ClientSecrettargetOpenId- 接收消息的用户 openid(C2C 单聊,可在机器人消息事件中获取)minPriority- 最低优先级(默认 0 = 全部转发)forwardAllApps- 转发所有应用(默认 true;false 时只转发enabledApps列出的)enabledApps- 只转发指定应用 ID(forwardAllApps=false时生效,空数组 = 全部)maxContentLength- 消息内容截断长度(默认 4000,QQ 文本消息上限)retries- 失败重试次数(指数退避,默认 2)
也可通过环境变量
QQ_APP_ID/QQ_CLIENT_SECRET/QQ_TARGET_OPENID注入(优先于界面配置)。⚠️ clientSecret是机器人密钥,请妥善保管,不要提交到公开代码库。 💡 access_token 自动缓存刷新(提前 60s),token 失效自动重试。
services:
miotify:
image: ghcr.io/mikus-loli/Miotify:latest
container_name: miotify
restart: unless-stopped
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- DEFAULT_ADMIN_USER=admin
- DEFAULT_ADMIN_PASS=admin
volumes:
- miotify-data:/app/data
volumes:
miotify-data:注意:首次启动时,如果未设置
JWT_SECRET环境变量,系统会自动生成一个随机密钥并保存到数据库中。后续重启将使用已保存的密钥。如需自定义密钥,可添加- JWT_SECRET=your-secret到环境变量。
# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f
# 重新构建
docker-compose up --build -d
# 停止服务
docker-compose down
# 停止并删除数据卷
docker-compose down -vA: 删除数据库文件重新启动,会重新创建默认管理员账号。
A: 设置环境变量 PORT=你的端口 或在 .env 文件中配置。
A: 检查是否使用了正确的 Token 类型:
- 发送消息需要使用 App Token
- 管理 API 需要使用 JWT Token
A: 确保:
GOTIFY_URL不带/message路径GOTIFY_TOKEN是应用的 token(UUID 格式)- Miotify 服务可被青龙面板访问
A: 备份 data/miotify.db 文件即可。Docker 部署时备份对应的数据卷。
# Docker 部署
docker-compose logs -f miotify
# 本地运行
# 日志直接输出到控制台Docker 容器权限问题,确保数据目录权限正确:
docker-compose down
docker-compose up --build -dJWT Token 过期,重新登录获取新 Token。
App Token 无效,检查是否使用了正确的应用 token。
curl http://localhost:8080/health正常响应:
{"status":"ok","websocket":0}miotify/
├── src/
│ ├── index.js # 入口文件
│ ├── config.js # 配置管理
│ ├── db/ # 数据库模块
│ ├── middleware/ # Express 中间件
│ ├── plugins/ # 插件管理器
│ ├── routes/ # API 路由
│ └── websocket/ # WebSocket 模块
├── web/ # 前端项目
│ └── src/
│ ├── api/ # API 客户端
│ ├── components/ # React 组件
│ ├── pages/ # 页面组件
│ ├── store/ # Zustand 状态管理
│ └── styles/ # 样式文件
├── plugins/ # 插件目录
│ └── available/ # 可用插件
├── examples/ # 示例代码
└── Dockerfile
后端:
- Node.js + Express
- SQLite (sql.js)
- WebSocket (ws)
- JWT 认证
前端:
- React + TypeScript
- Vite
- Zustand (状态管理)
- React Router
MIT License