Skip to content

Repository files navigation

Miotify

一个轻量级的实时消息推送服务器,兼容 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 部署

# 使用 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:web

API 文档

认证方式

Miotify 使用两种 Token:

  1. JWT Token - 用户登录后获取,用于管理 API
  2. App Token - 应用创建时生成,用于发送消息

认证 API

登录

POST /api/login
Content-Type: application/json

{
  "name": "admin",
  "pass": "admin"
}

响应:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "id": 1,
  "name": "admin",
  "admin": true
}

应用管理 API

需要 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"
}

消息 API

发送消息

使用 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

需要管理员权限

端点 方法 说明
/api/user GET 获取用户列表
/api/user POST 创建用户
/api/user/:id PUT 更新用户信息
/api/user/:id/password PUT 修改密码
/api/user/:id DELETE 删除用户

WebSocket API

连接地址: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"
  }
}

Gotify 兼容

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 插件停用时

内置插件

邮件转发插件 (email-forwarder)

将消息转发到指定邮箱。

配置项:

  • smtp.host - SMTP 服务器地址
  • smtp.port - SMTP 端口
  • smtp.secure - 是否使用 SSL
  • smtp.auth.user - SMTP 用户名
  • smtp.auth.pass - SMTP 密码
  • from - 发件人地址
  • to - 收件人地址

NapCat QQ转发插件 (napcat-forwarder)

将消息转发到 QQ(通过 NapCat)。

配置项:

  • httpUrl - NapCat HTTP 地址
  • accessToken - Access Token
  • targetType - 目标类型(private/group)
  • targetId - 目标 ID(QQ号/群号)
  • forwardAllApps - 是否转发所有应用

Hermes webhook 推送插件 (hermes-qq-notify)

收到消息后通过 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 官方 Bot API 直连插件 (qq-direct-notify)

收到消息后直接调用 QQ 开放平台机器人 API(C2C 消息)推送到主人 QQ。 不依赖任何中间服务(无需 NapCat、Hermes、webhook、反代): Miotify → QQ 官方 Bot API → 主人 QQ。

配置项:

  • appId - QQ 开放平台机器人的 AppID
  • clientSecret - QQ 开放平台机器人的 ClientSecret
  • targetOpenId - 接收消息的用户 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 失效自动重试。

Docker 部署

docker-compose.yml

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 -v

常见问题

Q: 忘记密码怎么办?

A: 删除数据库文件重新启动,会重新创建默认管理员账号。

Q: 如何修改默认端口?

A: 设置环境变量 PORT=你的端口 或在 .env 文件中配置。

Q: 消息发送失败返回 401?

A: 检查是否使用了正确的 Token 类型:

  • 发送消息需要使用 App Token
  • 管理 API 需要使用 JWT Token

Q: 青龙面板通知失败?

A: 确保:

  1. GOTIFY_URL 不带 /message 路径
  2. GOTIFY_TOKEN 是应用的 token(UUID 格式)
  3. Miotify 服务可被青龙面板访问

Q: 如何备份数据?

A: 备份 data/miotify.db 文件即可。Docker 部署时备份对应的数据卷。

故障排除

查看日志

# Docker 部署
docker-compose logs -f miotify

# 本地运行
# 日志直接输出到控制台

常见错误

EACCES: permission denied

Docker 容器权限问题,确保数据目录权限正确:

docker-compose down
docker-compose up --build -d

Token invalid or expired

JWT Token 过期,重新登录获取新 Token。

Application not found

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

License

MIT License

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages