No description
  • Python 66.9%
  • Vue 21.3%
  • CSS 6.4%
  • TypeScript 4.4%
  • Dockerfile 0.5%
  • Other 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-07 17:31:37 +08:00
docker feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
privflow feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
tests feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
web feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
.dockerignore feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
.env.example feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
.gitignore feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
compose.yaml feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
config.example.json feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
DEPLOYMENT.md feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
Dockerfile feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
LICENSE Initial commit 2026-08-07 17:31:19 +08:00
pyproject.toml feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00
README.md feat: add PrivFlow MVP with passkey auth 2026-08-07 17:31:37 +08:00

PrivFlow

PrivFlow 是一个面向个人使用的轻量自动化执行器,目标是覆盖个人最常用的 RSS、HTTP、文件和通知流程而不是复刻 n8n 的通用可视化编辑器。

部署请参阅 DEPLOYMENT.md

当前 MVP

  • manualintervalrss 三种触发器
  • RSS/Atom 轮询与去重
  • HTTP 请求与 JSON/文本解析
  • JSON 路径、正则提取
  • pick、模板、数组拼接、JSON 序列化等简单转换
  • 文件保存、文件下载
  • Console、Telegram、SMTP 邮件和 Webhook 输出
  • Telegram、SMTP 连接集中配置,多个 flow 可以复用
  • SQLite 运行记录、失败信息和调度检查点
  • 所有配置使用 JSON敏感值可用 ${ENV_VAR} 引用环境变量
  • 本地 HTTP API 与 TypeScript Web 配置界面
  • 单用户 Passkey 登录;首次注册后所有配置和运行 API 均受保护

快速开始

python -m privflow init privflow.json
python -m privflow validate -c privflow.json
python -m privflow list -c privflow.json
python -m privflow history --db .privflow/state.db

示例中的外部 flow 默认关闭;填入真实地址和凭据后,把对应 flow 的 enabled 改为 true,再执行:

python -m privflow run-once -c privflow.json --force

持续运行调度器:

python -m privflow daemon -c privflow.json --tick 15

Web 界面

前端使用 TypeScript + VitePython 继续负责执行器、SQLite 状态和配置 API。前端通过 /api/definitions 获取触发器和动作的字段定义,因此后续新增业务类型时,可以扩展后端定义和执行器,再由通用表单呈现配置。

首次构建并启动:

cd web
pnpm install
pnpm run build
cd ..
python -m privflow init privflow.json  # 首次运行时执行一次
python -m privflow serve -c privflow.json --port 8787

然后打开 http://localhost:8787,首次访问时注册第一把 Passkey。之后可在顶部“添加 Passkey”登记备用设备。默认 Passkey 配置仅匹配这个地址;如果以其他域名、端口或 HTTPS 访问,请按 DEPLOYMENT.md 设置 PRIVFLOW_RP_IDPRIVFLOW_RP_ORIGINPRIVFLOW_COOKIE_SECURE

Web 界面当前支持:

  • 查看 flow 和运行历史
  • 新建、编辑、删除 flow
  • 配置手动、定时、RSS 触发器
  • 配置 HTTP、通知、保存文件、下载、Webhook、数据转换动作
  • 对已保存的 SMTP 连接测试连通性和身份验证(不会发送测试邮件)
  • 从 Web 直接运行 flow
  • 从 Web 以调试模式运行 flow并在运行详情中逐步查看每个动作的输出
  • 显示后台运行中的任务状态,并自动刷新运行历史
  • 概览、flow 编辑和连接配置使用独立 URL支持浏览器前进后退例如 /flows/new/flows/example/edit/connections

开发前端时,可以让 Python API 运行在 8787 端口,再执行:

cd web
pnpm run dev

配置仍保存为本地 JSONWeb 编辑器只是它的管理入口;这样即使暂时不使用 Web也可以继续用 CLI 和配置文件。

命令行也可以对指定 flow 开启调试记录:

python -m privflow run-once -c privflow.json --flow my-flow --force --debug

普通运行仍保存原有的动作输出格式;只有调试运行会额外保存每一步的输出、状态和错误信息。

也可以安装成命令:

python -m pip install -e .
privflow list

Flow 示例

一个 RSS 到 Telegram 的 flow。Token 不放在 flow 里,而是引用预先配置的共享连接:

{
  "id": "rss-to-telegram",
  "trigger": {
    "type": "rss",
    "url": "https://example.com/feed.xml",
    "seconds": 1800,
    "process_history": false
  },
  "steps": [
    {
      "type": "notify",
      "channel": "telegram",
      "connection": "telegram-main",
      "message": "{{ item.title }}\n{{ item.link }}"
    }
  ]
}

RSS 触发器的 process_history 默认是 false。关闭时PrivFlow 会记录 flow 从停用变为启用的时间,只处理发布时间晚于该时间的条目;首次启用不会把订阅源已有的历史文章全部发送出来。将它设为 true 才会处理当前订阅源中的历史条目。无法解析发布时间的条目,在默认模式下会跳过;重新停用再启用会产生新的时间边界。

邮件通知使用同样的方式:

{
  "type": "notify",
  "channel": "email",
  "connection": "smtp-main",
  "to": "me@example.com",
  "subject": "PrivFlow 更新",
  "message": "{{ data }}"
}

如果 RSS 描述中包含 HTML可以使用 item.summary_html

{
  "type": "notify",
  "channel": "email",
  "connection": "smtp-main",
  "format": "html",
  "to": "me@example.com",
  "subject": "RSS 更新:{{ item.title }}",
  "message": "<html><body><h2>{{ item.title }}</h2>{{ item.summary_html }}</body></html>"
}

PrivFlow 会在 RSS 解析阶段和邮件发送阶段各做一次白名单过滤。目前允许 aimgbrpdivspanstrongem、列表、标题、引用和代码标签会移除脚本、iframe、表单、事件属性、样式属性以及非 HTTPS 图片地址。邮件同时包含纯文本降级版本。

模板变量与使用场景

所有动作配置字段都会在执行前递归渲染模板,包括 URL、请求体、文件路径、消息、邮件主题、邮件地址和 Webhook 请求体等。模板使用 {{ expression }},支持点号路径和数组下标,例如 {{ data.items.0.url }}

通用变量

每次真正执行一个 flow 时,都可以使用以下变量:

变量 含义
run_id SQLite 运行记录 ID
execution_id 本次执行的 UUID
flow 当前 flow 的完整配置,可访问 flow.idflow.triggerflow.steps
trigger 本次触发信息,可访问 trigger.typetrigger.fired_at
item 当前条目RSS 场景是 RSS 条目,其他普通场景默认为空对象
data 当前数据,会随着每个动作执行不断更新
response 最近一次 HTTP 请求的响应对象
steps.<step-id> 指定动作的输出;没有显式 id 时使用 steps.step_1steps.step_2

response 通常包含:response.statusresponse.headersresponse.textresponse.body。模板中一般优先使用 response.textJSON 请求解析后的结果会直接放入 data

变量不存在时会渲染为空字符串。完整表达式会保留原始类型:

{
  "body": "{{ data }}",
  "message": "当前数据:{{ data }}"
}

data 是对象或数组时,第一种写法会保留对象/数组类型,第二种写法会把它转换成 JSON 文本。

各触发场景

RSS 触发器每发现一个新条目执行一次动作,此时可用:

变量 含义
item.id 条目标识,用于去重
item.title 标题
item.link 原文链接
item.summary 去除 HTML 后的纯文本摘要
item.summary_html 经过白名单过滤的 HTML 摘要
item.published 发布时间;由 RSS/Atom 字段提供
item.source Feed 地址

例如 RSS → 邮件:

{
  "type": "notify",
  "channel": "email",
  "connection": "smtp-main",
  "format": "html",
  "to": "me@example.com",
  "subject": "RSS 更新:{{ item.title }}",
  "message": "<h2>{{ item.title }}</h2>{{ item.summary_html }}<p><a href=\"{{ item.link }}\">查看原文</a></p>"
}

手动触发和定时触发没有 RSS 条目,item 和初始 data 是空对象,但仍可使用 flowtriggerrun_idexecution_id

各动作场景

动作 执行后的 data 常用变量
HTTP 请求 根据 parse 得到 JSON 对象、文本或其他响应结果 response.statusresponse.textresponse.headersdata
提取数据 提取出的字段或正则结果 dataresponse.textsteps.<id>
数据转换 转换结果 dataitemsteps.<id>
保存文件 保存后的本地路径 data、之前动作的 steps.<id>
下载文件 下载后的本地路径 dataresponseitem
Webhook Webhook 响应对象 data.statusdata.textdata.headers
通知 通知结果对象 消息发送前可使用所有当前上下文变量
规则转换 生成后的规则文本;未变化时跳过后续动作 dataitemsteps.<id>

推荐给需要被后续动作引用的动作设置显式 id

[
  {
    "id": "fetch-items",
    "type": "http",
    "url": "https://example.com/api/items",
    "parse": "json"
  },
  {
    "type": "extract",
    "from": "steps.fetch-items",
    "path": "items.0.url"
  },
  {
    "type": "notify",
    "channel": "telegram",
    "connection": "telegram-main",
    "message": "{{ data }}"
  }
]

transform.map_template 会为数组中的每个元素建立局部上下文。在该模板内部,itemdata 都指向当前元素:

{
  "type": "transform",
  "operation": "map_template",
  "template": "{{ item.url }}"
}

规则转换

rules 是一个内置的无代码动作,适合把按行排列的域名列表转换为规则文本,并记住上一次输入的首行。状态保存于 SQLiteflow_idstate_key 隔离。

{
  "id": "build-rules",
  "type": "rules",
  "from": "data",
  "format": "domain_suffix",
  "state_key": "updatedInfo",
  "skip_if_unchanged": true,
  "comment_prefix": "#",
  "entry_prefix": "DOMAIN-SUFFIX,",
  "ignore_blank": true,
  "strip": false
}

规则转换的行为如下:

  • 以输入文本第一行作为变更标识;
  • 首次运行或首行变化时生成规则并更新状态;
  • 首行未变化时将本次运行标记为 skipped,不执行后续动作;
  • comment_prefix 开头的行原样保留;
  • 普通非空行添加 entry_prefix
  • ignore_blank 控制是否忽略空行;
  • strip 控制是否去除每行首尾空白;
  • 状态在整个 workflow 成功后才提交,后续保存或发送失败时下一次仍会重试。

状态检查

state_guard 用于比较当前数据和持久化状态,适合“数据没有变化就不继续处理”的场景:

{
  "id": "check-version",
  "type": "state_guard",
  "from": "data.updated_at",
  "state_key": "last_updated_at",
  "on_unchanged": "skip"
}

动作会读取 from 指定的值,并按当前 flow_idstate_key 从 SQLite 查询旧值。首次运行或值发生变化时暂存新值并继续执行;值相同时,skip 会将本次运行标记为 skippedsuccess 会将本次运行标记为成功,但两者都会结束后续动作。新值只有在整个 flow 成功完成后才提交,后续动作失败时不会消耗这次变化。

连接与安全

privflow.jsonconnections 区域只保存连接元数据和环境变量名,例如:

{
  "id": "smtp-main",
  "type": "smtp",
  "host": "smtp.example.com",
  "port": 587,
  "username_env": "SMTP_USERNAME",
  "password_env": "SMTP_PASSWORD",
  "from": "you@example.com",
  "starttls": true
}

实际凭据由启动 PrivFlow 的环境提供:

export TELEGRAM_BOT_TOKEN='在密码管理器中读取的 token'
export SMTP_USERNAME='your-account'
export SMTP_PASSWORD='在密码管理器中读取的密码'

Web 界面不会读取或回显这些值它只显示环境变量是否已配置。API 也会拒绝保存 workflow 或 connection 中的明文 token、password 等字段。生产使用时建议通过系统服务、密码管理器或进程管理工具注入环境变量,不要把真实凭据写入 JSON、代码仓库或命令历史。

在“连接”页面保存 SMTP 配置后,可以点击“测试连接”。测试会建立 SMTP 会话、按配置执行 STARTTLS 并尝试登录,但不会发送邮件。若返回 535 Authentication failed,请重点检查用户名、密码环境变量是否指向正确值,以及服务商是否要求应用专用密码或授权码。

文件动作的相对路径默认位于 storage.base_dir 下。首次使用建议从 config.example.json 复制一份配置,并把真实配置加入 .gitignore,避免提交 Token 或 SMTP 密码。

设计边界

第一版刻意不包含可视化节点画布、插件市场、多用户权限、复杂表达式和分布式队列。先让表单配置和固定的个人流程稳定运行,再根据真实使用频率扩展能力。