Sentry集成

Sentry 是一个错误跟踪和性能监控服务。每当发生错误时,应用程序会向它发送一份报告。Sentry 会将相同的错误归并为一个 issue,显示堆栈跟踪和上下文,并在出现新 issue 时提醒团队。你可以使用 sentry.io 上的托管服务,也可以自行部署。借助 Sentry,你可以在一个地方找到所有站点的生产环境错误,而不必逐个搜索每个站点的错误日志。

概述

当你设置一个环境变量并且站点选择启用遥测后,Frappe 会将意外的服务器错误和 desk JavaScript 错误发送到 Sentry 项目。可选变量可以添加性能跟踪、性能分析和 SQL span。

  • 每个 bench 一个 DSN。 DSN 来自进程环境,而不是来自 site_config.json。由同一组 web 和 worker 进程服务的所有站点都会报告到同一个 Sentry 项目。
  • 两个开关。 DSN 为进程开启 Sentry SDK。系统设置中的复选框允许发送使用数据以改进应用程序(enable_telemetry)决定每个站点是否发送任何内容。
  • 错误跟随错误日志。 在服务器上,当 Frappe 写入一条错误日志时,事件会发送到 Sentry。如果你看到一条错误日志,Sentry 应该也有。
  • 站点通过名称区分。 每个事件都会将 Sentry 用户 ID 设置为站点名称,因此你可以在 Sentry 中按站点筛选。

设置

为 web 和 worker 进程设置 FRAPPE_SENTRY_DSN,重启它们,然后在每个应报告的站点上启用遥测。

变量 值 效果
FRAPPE_SENTRY_DSN 你项目的 DSN 必需。在 web 服务器和后台 worker 中启动 Sentry SDK,并在 desk 中加载浏览器 SDK。
SENTRY_TRACING_SAMPLE_RATE 0.0 到 1.0 之间的浮点数,例如 0.1 可选。为该比例的 web 请求和作业发送性能跟踪。
SENTRY_PROFILING_SAMPLE_RATE 0.0 到 1.0 之间的浮点数 可选。为该比例的被跟踪事务发送 CPU 性能分析。需要启用跟踪。
ENABLE_SENTRY_DB_MONITORING 任意非空值,例如 1 可选。将每个 SQL 查询记录为一个 span,并将每次数据库连接记录为一个面包屑。
  1. 在 Sentry 中创建一个项目(平台:Python)并复制其 DSN。
  2. 将这些变量添加到 Frappe web 服务器(gunicorn)以及每个 bench worker 进程的环境中。使用 supervisor 时,使用每个程序的 environment= 行。使用 Docker 时,在后端和 worker 容器上设置它们。
  3. 重启进程。Frappe 仅在进程启动时读取这些变量。
  4. 在每个站点上,打开系统设置并勾选允许发送使用数据以改进应用程序。从 CLI 执行:bench --site <site> execute frappe.db.set_single_value --args "['System Settings', 'enable_telemetry', 1]"</site>。
  5. 在浏览器中重新加载 desk,以便加载浏览器 SDK。

如果设置了 DSN 但站点的 enable_telemetry 处于关闭状态,该站点不会发送任何内容:没有服务器事件,没有浏览器事件,并且 desk 不会加载 SDK。

捕获的内容

Sentry 会获取开发者可以在代码中修复的错误。Frappe 有意向用户显示的错误则被排除在外。

来源 发送到 Sentry 不发送
Web 请求 以 HTTP 5xx 结束的未捕获异常。在开发者模式下,Frappe 记录的每个错误。 frappe.ValidationError、frappe.PermissionError 及其子类(例如 frappe.throw() 消息、必填字段错误)。正常的 4xx 响应。
后台作业 失败并写入错误日志的作业。Worker 在每个作业后刷新事件。 与 web 请求相同的异常过滤器。
frappe.log_error() 来自应用代码的每次调用,如果有活动异常则作为异常事件,否则作为带有标题和回溯的消息。 错误日志插入本身失败的调用。
Desk(浏览器) /desk 上的未捕获 JavaScript 错误和未处理的 promise 拒绝。 JS 中由 frappe.throw() 引发的错误。门户和网站页面:SDK 仅在 desk 中加载。会话跟踪已关闭。

服务器 SDK 运行时关闭了 Sentry 的默认集成。它不会挂钩 Python logging,因此 frappe.logger().error(...) 不会创建 Sentry 事件。来自 CSSUTILS 日志记录器的事件会被丢弃。

发布版本是 Frappe 版本(服务器上为 frappe.__version__,浏览器中为 frappe.boot.versions.frappe),因此你可以看到是哪个版本引入了错误。

性能监控

设置 SENTRY_TRACING_SAMPLE_RATE 以在 Sentry 中查看缓慢的端点和作业。在繁忙的站点上从低值开始(0.01 到 0.1),因为每个被跟踪的请求都会作为事务计费。

  • Web 请求。 Frappe 将 WSGI 应用包装在 Sentry 的中间件中。事务按端点命名:RPC 调用为 /api/method/<dotted.path></dotted.path>,否则为 URL 路径。
  • 后台任务。事务以任务的方法命名。计划任务则使用调度器任务类型。任务上下文包含任务 ID、其参数、是否为计划任务,以及它在队列中的等待时间(秒)。
  • SQL span。启用 ENABLE_SENTRY_DB_MONITORING 后,每次 frappe.db.sql() 调用都会变成一个带有查询文本的 span,并且每次数据库连接都会添加一条面包屑。这就是你查找 N+1 查询的方法。它也会给每次查询增加开销,所以在繁忙的生产环境中启用前请先测量。
  • 性能分析。SENTRY_PROFILING_SAMPLE_RATE 是被追踪事务的一部分,因此 0.5 在追踪率为 0.1 时会分析 5% 的请求。

当这三个变量中的任意一个被设置时,Frappe 还会在每个请求和任务开始时标记作用域(事务名称、站点、用户、追踪 ID)。这仅在启用了遥测的站点上发生。

数据与隐私

错误事件可能包含业务和个人数据。请像对待生产数据库一样对待你的 Sentry 项目,并限制可以读取它的人员。

Sentry 中的字段 内容
用户 ID 站点名称,例如 “erp.example.com”
标签 frappe_user 已登录用户,例如 “jane@example.com”,或 “Guest”
标签 frappe_trace_id X-Frappe-Request-ID 标头(如果请求带有的话)
请求 URL、方法、查询字符串和标头。SDK 会剥离 Cookie、Authorization 和其他敏感标头。不包含客户端 IP。
上下文 “JSON Body” / “Form Data” 完整的请求负载,未经过滤。这可能包含文档数据和个人数据。
额外 job 任务 ID、方法、所有任务参数、队列等待时间
堆栈跟踪 带有局部变量的代码帧(Sentry SDK 默认)
消息 对于 frappe.log_error() 调用:Error Log 标题和回溯
模块、argv 已安装的 Python 包和进程命令行
SQL span 仅查询文本。查询值不会被发送。

遥测复选框不涵盖性能数据。选择性加入检查针对错误事件和作用域标签运行。追踪、性能分析和 SQL span 会为进程中的每个请求和任务启动。如果一个 bench 托管了未选择加入的站点,请不要在其上设置这三个性能变量。

对于其他你绝不能存储的内容(例如包含国民身份证号或银行详细信息的字段),请使用 Sentry 的服务器端数据擦除功能。

故障排除

症状 检查项
完全没有事件 FRAPPE_SENTRY_DSN 是否在运行进程的环境中,而不仅仅是你的 shell 中?设置后你是否重启了 Web 和工作进程?站点的 enable_telemetry 是否开启?
存在 Error Log,但 Sentry 中没有任何内容 该异常是否是 ValidationError 或 PermissionError 的子类?这些会被有意跳过。
Sentry 中有 500 错误,但没有 Error Log 几秒后再检查。请求错误会以延迟插入的方式写入 Error Log。
缺少任务错误 检查工作进程是否具有 DSN。工作进程在启动时会启动自己的 SDK。
没有浏览器错误 打开 desk 并检查浏览器控制台中 frappe.boot.sentry_dsn 是否有值。如果为空,则站点的遥测已关闭。Content-Security-Policy 规则必须允许向你的 Sentry 主机发起请求。
事务过多或 Sentry 账单过高 降低 SENTRY_TRACING_SAMPLE_RATE 或将其移除,然后重启。
缺少来自脚本或 bench console 的错误 只有 Web 服务器和 bench worker 会启动 SDK。其他 CLI 命令不会向 Sentry 报告。

代码位于 frappe/utils/sentry.py、frappe/app.py(Web)、frappe/utils/background_jobs.py(工作进程)、frappe/utils/error.py(log_error 钩子)和 frappe/public/js/sentry.bundle.js(浏览器)中。

使用Frappe作为OAuth服务

在本指南中,我们将学习如何使用 Frappe Framework 通过 OAuth 对第三方应用进行身份验证。我们将学习如何设置 Grafana 服务器,以及如何连接到 Frappe Framework 来对 Grafana 进行身份验证。

我们希望本指南对初学者有所帮助,除了编写一些配置文件外,我们不会编写任何代码。

前提条件

  • 一台运行基于 Debian 的系统(Debian、Ubuntu、Pop OS 等)的服务器
  • 一个 Frappe Cloud 账户。您可以在此处注册 Frappe Cloud

或者

  • 一个 Frappe Framework 站点(如果与服务器在同一台机器上会更简单)
  • 基本的文本编辑知识

Frappe Framework

Frappe Framework 是一个低代码、基于 Python 和 JavaScript 的 Web 框架,用于构建令人愉悦的 Web 应用程序。Frappe Framework 最初是为构建企业级应用程序而开发的,但后来已发展成为一个可以轻松构建通用应用程序的框架。它是开源的,并采用 MIT 许可证。

Frappe Framework 的有趣之处在于,您无需编写代码即可让一切正常运行。它的设计初衷是让没有编程知识的人也能轻松使用。您可以生成数据库模式、REST API、Webhooks、报表等,而无需编写一行代码。您可以在此处查看所有功能。

Grafana

Grafana 是由 Grafana Labs 构建的一个出色的指标可视化平台。Grafana 可以根据数据生成交互式图表,并可用于告警。Grafana 具有仪表盘功能,可以从数据源拉取数据并生成最能反映数据价值的图表。Frappe 内部使用 Grafana 来可视化多个客户端的服务器统计数据,并为服务器提供大量洞察信息。

安装 Frappe Framework

安装 Frappe Framework 本身就是一门艺术。它需要许多组件的协同工作才能实现最佳运行效果。请不要因此感到畏惧;我们在 Framework 文档中提供了关于如何安装和设置 Frappe Framework 的详细指南。

如果您想采用更简单的方式,可以使用 Docker 安装和设置 Frappe Framework。您可以在此处了解更多信息。

最简单的方法是使用 Frappe Cloud 快速创建一个已安装 Frappe Framework 的新站点。

安装 Grafana

关于安装 Grafana,您可以参考 Grafana 官方安装指南。我们假设您正在基于 Debian 的系统上安装 Grafana。

安装完成后,您需要将 Grafana 添加到 systemd 中。您可以通过以下方式操作:

# add to systemd to start on bootup
systemctl enable grafana-server.service
# start the service
systemctl start grafana-server
# check status
systemctl status grafana-server

为 OAuth 配置 Frappe

登录您的站点并完成站点的入门流程后,我们将在 OAuth Client DocType 中创建一个新记录。

要访问 OAuth Client DocType,您可以使用万能搜索栏,或按 Ctrl + G 调出万能搜索栏并输入“OAuth Client”,自动补全功能将显示选项列表。

从下拉菜单中选择“新建 OAuth Client”,您将进入一个页面,可以在其中为要添加 OAuth 的应用程序配置 OAuth 范围和设置。

在这里,您需要配置:

  • 应用名称(此处为 Grafana)
  • Open ID 范围
  • 重定向 URI

根据 Grafana 文档,其重定向 URI 为 <grafana-url>/login/generic_oauth</grafana-url>。> 如果您所在的服务器没有 URL,可以为 <grafana-url></grafana-url> 部分提供服务器的 IP 地址。

在相应字段中添加详细信息后,您将收到一个 App Client ID 和 App Client Secret。这些信息将用于 Grafana 的 OAuth 配置。

为 OAuth 配置 Grafana

既然我们已经从 Frappe Framework 站点获得了客户端 ID 和客户端密钥,现在就可以连接到 Grafana 服务器进行身份验证了。

在 Ubuntu/Debian 系统上,Grafana 的配置文件位于 /etc/grafana 文件夹中。我们将编辑 grafana.ini 文件。

$ sudo vim grafana.ini

在配置文件中,要设置 OAuth,您需要编辑 [oauth.generic_oauth] 部分。在这里,您需要取消注释相关行并添加所需数据。

[auth.generic_oauth]
enabled = True
name = Frappe
client_id = ""
client_secret = ""
scopes = openid all
auth_url = 
<frappe_site_url>/api/method/frappe.integrations.oauth2.authorize
token_url = 
<frappe_site_url>/api/method/frappe.integrations.oauth2.get_token
api_url = 
<frappe_site_url>/api/method/frappe.integrations.oauth2.openid_profile
</frappe_site_url></frappe_site_url></frappe_site_url>

此外,如果您使用反向代理,还需要将 Grafana 服务器的域名添加到配置中。您可以在 grafana.ini 文件的 server 部分定义 domain 和 root_url。

完成此操作并保存文件后,您需要重启 Grafana 才能使其生效。您可以使用以下命令:

$ sudo systemctl restart grafana-server.service

在 Ubuntu/Debian 系统上重启 Grafana。

基本上就是这样。现在重新加载您的 Grafana 站点,您将在登录页面上看到一个“使用 Frappe 登录”按钮。

如果您一切操作正确,您将能够使用 Frappe 站点的凭据登录 Grafana。

基于令牌的身份验证

基于令牌的身份验证是一种安全技术,用于验证尝试访问服务器的用户身份。该功能自 v11.0.3 版本起可用。服务会验证安全令牌并处理用户请求。

它是如何工作的?

身份验证是应用程序确认用户身份的过程。传统上,应用程序通过会话 Cookie 来维持身份,依赖于存储在服务器端的会话 ID。这迫使开发者创建会话存储,要么每台服务器单独存储,要么实现一个完全独立的会话存储层。

令牌身份验证是一种更现代的方法,旨在解决服务器端会话 ID 存储机制无法解决的问题。使用令牌代替会话 ID 可以降低服务器负载。

令牌的增删改查

作为开发者,您可以使用令牌执行完整的增删改查操作。
一旦生成了令牌,该令牌就可以用于授权您的用户发出的各个请求,这些请求会传递到您的应用程序中。您的应用程序将验证随每个请求一起发送的令牌。

API 密钥和 API 秘钥的组合构成一个令牌,该令牌用于在您的应用程序中进行身份验证,并且可以用于验证 RPC 和 REST API 的请求。

生成令牌

您可以为每个用户生成一个 api-key 和一个 api-secret,它们共同构成一个令牌。

  • api-key:用于标识用户身份。
  • api-secret:用于验证请求。

可以通过 Web 界面、命令行或远程过程调用(RPC)来生成 api-key 和 api-secret:

  • RPC 方式:
    /api/method/frappe.core.doctype.user.user.generate_keys?user="user_name"
  • 命令行方式:
    bench execute frappe.core.doctype.user.user.generate_keys --args ['user_name']
  • Web 方式:
    用户 -> API 访问 -> 生成密钥

请查看 Guides / Integrations / Rest Api / Token Based Authentication 了解如何在 API 请求中使用该令牌。

注意:

  1. API 密钥无法重新生成。
  2. 只有具有系统管理员角色的用户才能生成密钥。

如何设置OAuth 2?

Frappe 内置了基于 oauthlib 的 OAuth 2.0 提供程序。第三方应用现在可以基于 Frappe 的角色和用户权限系统来访问用户资源。要设置一个应用进行访问,请遵循以下步骤。

OAuth 2 定义了四种角色

资源所有者

一个能够授予对受保护资源访问权限的实体。当资源所有者是个人时,通常称为最终用户。

资源服务器

托管受保护资源的服务器,能够使用访问令牌接受和响应受保护资源的请求。

客户端

代表资源所有者并经过其授权来发起受保护资源请求的应用程序。“客户端”一词并不暗示任何特定的实现特征(例如,
应用程序是在服务器、桌面还是其他设备上执行)。

授权服务器

在成功验证资源所有者并获得授权后,向客户端颁发访问令牌的服务器。

设置 OAuth 2 提供程序

系统管理员可以在 OAuth Provider Settings 中设置确认消息的行为为 Force 或 Auto。
如果选择“强制”,系统将始终要求用户确认。如果选择“自动”,则仅当用户没有活动令牌时,系统才会要求确认。

前往

设置 > 集成 > OAuth Provider Settings

添加主服务器

这是托管所有用户的主服务器。例如:https://frappe.io。要将其设置为主服务器,请前往 设置 > 集成 > 社交登录密钥 并添加新的 Frappe Social Login Key。在 Base URL 字段中输入 https://frappe.io。此 URL 会在所有连接到该服务器进行身份验证的其他 Frappe 服务器中重复使用。实际上,这就是主要的身份提供程序 (IDP)。

在此服务器下,根据需要添加任意数量的 OAuth Client。

添加客户端应用

作为系统管理员,请前往

设置 > 集成 > OAuth Client

要添加客户端,请填写以下详细信息

  1. 应用名称:输入应用名称,例如 CAVS
  2. 跳过授权:如果选中此项,则在身份验证期间不会出现任何确认消息。跳过授权意味着该客户端被视为受信任的客户端。
  3. 范围:向用户显示的范围列表以及确认消息。范围之间用空格分隔。
  4. 重定向 URI:重定向 URI 列表,用空格分隔。
  5. 默认重定向 URI:从重定向 URI 列表中选择的默认重定向 URI。
  6. 授权类型:选择 Authorization Code 或 Implicit。
  7. 响应类型:如果授权类型是 Authorization Code,则选择 Code;或者选择 Token。

使用 OAuth 2

请参阅 API 文档以了解如何在您的应用程序中使用 OAuth 2。

谷歌日历集成

Frappe 提供了与 Google 日历的集成功能,以便所有用户同步其事件。

设置

为了实现与 Google 日历的同步,您需要在 Google Cloud Platform 中连接到您的应用程序,然后为每位用户创建一个账户:

  1. 在 Google Cloud Platform 上创建一个新项目,并生成新的 OAuth 2.0 凭据
  2. 将 https://{yoursite} 添加到“已授权的 JavaScript 来源”中
  3. 将 https://{yoursite}?cmd=frappe.integrations.doctype.gcalendar_settings.gcalendar_settings.google_callback 添加为“已授权的重定向 URI”
  4. 在 Gcalendar 应用程序中添加您的客户端 ID 和客户端密钥:在“模块 > 集成 > Google 日历 > GCalendar 设置”中

此步骤成功完成后,每位用户都可以在“Google 日历 > GCalendar 账户”中创建自己的账户。
系统将请求用户授权您的 Google 应用程序访问其日历信息,然后用户将被重定向到一个成功页面。

功能

  1. 在 Google 日历中创建新日历
  • 每位用户都可以为其 Google 日历选择一个专属名称。
  1. 将事件从 Frappe 同步到 GCalendar
  • 在 Frappe 中创建的所有事件都会在 Google 日历中创建。
  • 重复事件也会作为重复事件创建。
  • 您可以选择性地为事件添加 Google Meet 会议链接。
  • 所有参与者(用户邮箱)都会被添加到事件中。
  • 在 Frappe 中修改的事件会在 Google 日历中更新。
  • 在 Frappe 中删除的事件会在 Google 日历中删除。
  1. 将事件从 GCalendar 同步到 Frappe
  • 在 Google 日历中创建的事件会在 Frappe 中创建。
  • 在 Google 日历中更新的事件会在 Frappe 中更新。

同步模块遵循 Frappe 的授权规则:只有公开的事件,或者用户是事件所有者时,事件才会被同步。

限制

目前,如果在 Google 日历中取消了某个重复事件的单个实例,此更改不会反映到 Frappe 中。

社交登录密钥

添加社交登录提供商,如 Facebook、Frappe、Github、Google、Microsoft 等,并启用社交登录功能。

设置社交登录

要添加社交登录密钥,请前往

集成 > 认证 > 社交登录密钥

社交登录密钥

  1. 选择社交登录提供商,或选择“自定义”
  2. 如果提供商需要,请输入“基础 URL”
  3. 如需启用,请勾选“启用社交登录”,以便在登录界面显示图标
  4. 同时,请根据提供商要求填写客户端 ID 和客户端密钥。

例如:社交登录密钥

  • 社交登录提供商:Frappe
  • 客户端 ID:ABCDEFG
  • 客户端密钥:123456
  • 启用社交登录:Check
  • 基础 URL:https://erpnext.org(部分提供商需要)

为提供商生成客户端 ID 和客户端密钥

  • 创建 Google API 控制台项目和客户端 ID
  • 手动为 Facebook 构建登录流程
  • 为 GitHub 创建 OAuth 应用
  • 使用 OpenID Connect 和 Azure Active Directory 授权 Web 应用访问
  • 在 Salesforce 上创建已连接应用

Webhooks

Webhook 是“用户自定义的 HTTP 回调”。对于选定的文档类型,您可以根据需要创建一个 Webhook,使其在特定条件下触发特定文档事件。

当 doc_event 发生时,源站点会向 Webhook 配置的 URI 发起 HTTP 请求。用户可以配置这些 Webhook,利用一个站点上的事件来触发另一个站点上的行为。

配置 Webhook

要添加 Webhook,请前往:

集成 > Webhook > Webhook

  1. 选择需要触发 Webhook 的文档类型(例如:Quotation)。
  2. 选择将触发 Webhook 的文档事件(例如:on_update)。
  3. (可选)您可以设置额外的文档条件,以便在特定场景下触发 Webhook。
  4. 输入一个有效的请求 URL,该 URL 将接收 Webhook 数据。
  5. 选择请求方法。默认方法为 POST。
  6. (可选)您可以向请求中添加 HTTP 头。(例如:如果需要发送 API 密钥,这会很有用)。

您也可以通过取消勾选 Enabled 复选框来禁用 Webhook。只有启用的 Webhook 才会被触发。

数据结构

  • 如果您的请求结构基于表单,您可以在表格中选择文档中的字段,该表格使用字段名作为 key。
  • 如果您的请求结构基于 JSON,您可以使用 Jinja 模板从文档中插入字段(请确保将字段用双引号括起来)。

Webhook 安全

为了可选地为您的 Webhook 请求增加安全性,并确保 Webhook 确实来自 Frappe,您可以随请求设置一个“Webhook 密钥”。请勿公开分享该密钥。

如果启用,在请求发送前会添加一个额外的请求头(X-Frappe-Webhook-Signature),其值由密钥根据请求负载生成,格式为 base64 编码的 HMAC-SHA256 哈希值。

Webhook 请求示例

  • 文档类型:Quotation
  • 文档事件:on_update
  • 请求 URL:https://httpbin.org/post
  • 请求结构:Form URL-Encoded
  • Webhook 请求头:
  1. 键:Content-Type:,值:application/x-www-form-urlencoded
  • Webhook 数据:
  1. 字段名:name,键:id
  2. 字段名:items,键:lineItems

上述配置将生成以下 JSON 请求(由 Frappe 服务器在 Quotation – on_update 时发送至 https://httpbin.org/post):

{
 "args": {},
 "data": "{"lineItems": [{"stock_qty": 1.0, "base_price_list_rate": 1.0, "image": "", "creation": "2017-09-14 13:41:58.373023", "base_amount": 1.0, "qty": 1.0, "margin_rate_or_amount": 0.0, "rate": 1.0, "owner": "Administrator", "stock_uom": "Unit", "base_net_amount": 1.0, "page_break": 0, "modified_by": "Administrator", "base_net_rate": 1.0, "discount_percentage": 0.0, "item_name": "I1", "amount": 1.0, "actual_qty": 0.0, "net_rate": 1.0, "conversion_factor": 1.0, "warehouse": "Finished Goods - R", "docstatus": 0, "prevdoc_docname": null, "uom": "Unit", "description": "I1", "parent": "QTN-00001", "brand": null, "gst_hsn_code": null, "base_rate": 1.0, "item_code": "I1", "projected_qty": 0.0, "margin_type": "", "doctype": "Quotation Item", "rate_with_margin": 0.0, "pricing_rule": null, "price_list_rate": 1.0, "name": "QUOD/00001", "idx": 1, "item_tax_rate": "{}", "item_group": "Products", "modified": "2017-09-14 17:09:51.239271", "parenttype": "Quotation", "customer_item_code": null, "net_amount": 1.0, "prevdoc_doctype": null, "parentfield": "items"}], "id": "QTN-00001"}",
 "files": {},
 "form": {},
 "headers": {
 "Accept": "*/*",
 "Accept-Encoding": "gzip, deflate",
 "Connection": "close",
 "Content-Length": "1075",
 "Host": "httpbin.org",
 "User-Agent": "python-requests/2.18.1"
 },
 "json": {
 "id": "QTN-00001",
 "lineItems": [
 {
 "actual_qty": 0.0,
 "amount": 1.0,
 "base_amount": 1.0,
 "base_net_amount": 1.0,
 "base_net_rate": 1.0,
 "base_price_list_rate": 1.0,
 "base_rate": 1.0,
 "brand": null,
 "conversion_factor": 1.0,
 "creation": "2017-09-14 13:41:58.373023",
 "customer_item_code": null,
 "description": "I1",
 "discount_percentage": 0.0,
 "docstatus": 0,
 "doctype": "Quotation Item",
 "gst_hsn_code": null,
 "idx": 1,
 "image": "",
 "item_code": "I1",
 "item_group": "Products",
 "item_name": "I1",
 "item_tax_rate": "{}",
 "margin_rate_or_amount": 0.0,
 "margin_type": "",
 "modified": "2017-09-14 17:09:51.239271",
 "modified_by": "Administrator",
 "name": "QUOD/00001",
 "net_amount": 1.0,
 "net_rate": 1.0,
 "owner": "Administrator",
 "page_break": 0,
 "parent": "QTN-00001",
 "parentfield": "items",
 "parenttype": "Quotation",
 "prevdoc_docname": null,
 "prevdoc_doctype": null,
 "price_list_rate": 1.0,
 "pricing_rule": null,
 "projected_qty": 0.0,
 "qty": 1.0,
 "rate": 1.0,
 "rate_with_margin": 0.0,
 "stock_qty": 1.0,
 "stock_uom": "Unit",
 "uom": "Unit",
 "warehouse": "Finished Goods - R"
 }
 ]
 },
 "url": "https://httpbin.org/post"
}

示例 JSON 请求体

对于销售交易上的 Webhook,我们可以按如下方式构造 JSON 数据:

{
 "id": "{{ doc.name }}",
 "total": "{{ doc.total }} EUR",
 "total_net_weight": "{{ doc.total_net_weight }} kg",
 "customer": "{{ doc.customer }}",
 "created": "{{ doc.creation }}",
 "items": {{ doc["items"] | tojson }}
}

Webhook 请求日志

每次成功请求都会创建一条 Webhook 请求日志,其中包含所有相关详细信息。

OpenID Connect与Frappe社交登录

OpenID Connect

Frappe 也使用 OpenID Connect 这一重要标准来认证用户。要使用 id_token 获取 access_token,请在授权请求期间将 openid 作为 scope 参数的值传递。

如果 scope 是 openid,则包含 access_token 的 JSON 响应还将包含一个使用 HS256 和 Client Secret 签名的 JSON Web Token(id_token)。解码后的 id_token 包含 at_hash。

带有 scope openid 的 Bearer Token 示例

{
 "token_type": "Bearer",
 "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJpc3MiOiJodHRwczovL21udGVjaG5pcXVlLmNvbSIsImF0X2hhc2giOiJOQlFXbExJUy1lQ1BXd1d4Y0EwaVpnIiwiYXVkIjoiYjg3NzJhZWQ1YyIsImV4cCI6MTQ3Nzk1NTYzMywic3ViIjoiNWFjNDE2NThkZjFiZTE1MjI4M2QxYTk0YjhmYzcwNDIifQ.1GRvhk5wNoR4GWoeQfleEDgtLS5nvj9nsO4xd8QE-Uk",
 "access_token": "ZJD04ldyyvjuAngjgBrgHwxcOig4vW",
 "scope": "openid",
 "expires_in": 3600,
 "refresh_token": "2pBTDTGhjzs2EWRkcNV1N67yw0nizS"
}

Frappe 社交登录设置

在此示例中,有 2 台服务器,

主服务器

这是托管所有用户的主服务器。例如:https://frappe.io。要将此服务器设置为主服务器,请前往 设置 > 集成 > 社交登录密钥,并在 Frappe Server URL 字段中输入 https://frappe.io。此 URL 会在所有连接到该服务器进行认证的其他 Frappe 服务器中重复使用。实际上,这就是主要的身份提供者(IDP)。

在此服务器下,根据需要添加任意数量的 OAuth Client。因为我们正在设置一台应用服务器,所以只需添加一个 OAuth Client

Frappe 应用服务器

这是连接到 IDP 的客户端。请前往此服务器上的 设置 > 集成 > 社交登录密钥,并为 Frappe Client ID 和 Frappe Client Secret 添加适当的值(请参考在主服务器中添加的客户端)。如前所述,将 Frappe Server URL 保持为 https://frappe.io

现在,您将在登录页面上看到 Frappe 图标。点击此图标即可使用在主服务器(IDP)中创建的账户登录 https://frappe.io

注意:如果在注册客户端时勾选了 Skip Authorization,则不会显示允许或拒绝授予资源访问权限的页面。如果应用程序属于同一组织内部,并且需要无缝的用户体验,则可以使用此选项。

步骤

第 1 部分:在 Frappe 身份提供者(IDP)上

登录 IDP

在 IDP 上添加 OAuth 客户端

在 IDP 上设置服务器 URL

第 2 部分:在 Frappe 应用服务器上

在应用服务器上设置 Frappe Client ID 和 Frappe Client Secret(请参考在 IDP 上设置的客户端)

注意:Frappe 服务器 URL 是存储您组织身份信息的主服务器。

应用服务器上的登录界面(使用 frappe 登录)

第 3 部分:重定向到 IDP

在 IDP 上使用用户登录

在 IDP 上确认访问权限

第 4 部分:返回应用服务器

使用来自 IDP 的 ID 登录应用服务器

基于令牌的身份验证

基于令牌的身份验证是一种安全技术,用于验证尝试访问服务器的用户身份。该功能自 v11.0.3 版本起可用。服务会验证安全令牌并处理用户请求。

它是如何工作的?

身份验证是应用程序确认用户身份的过程。传统上,应用程序通过会话 Cookie 来维持身份,依赖存储在服务器端的会话 ID。这迫使开发者创建会话存储,要么每台服务器单独存储,要么实现一个完全独立的会话存储层。

令牌身份验证是一种更现代的方法,旨在解决服务器端会话 ID 存储机制无法解决的问题。使用令牌代替会话 ID 可以降低服务器负载。

令牌的增删改查

作为开发者,您可以使用令牌执行完整的增删改查操作。
一旦生成了令牌,该令牌即可用于授权用户向您的应用发出的各个请求。您的应用会验证随每个请求一起发送的令牌。

API 密钥和 API 秘钥组合在一起形成一个令牌,该令牌用于验证您与应用的连接,并且可用于验证 RPC 和 REST API 的请求。

生成令牌

您可以为每个用户生成一个 API 密钥和一个 API 秘钥,两者组合在一起即形成一个令牌。

  • API 密钥:用于标识用户。
  • API 秘钥:用于验证请求。

API 密钥和 API 秘钥可以通过 Web 界面、命令行或远程过程调用(RPC)生成:

  • RPC:
    /api/method/frappe.core.doctype.user.user?user="user_name"
  • 命令行:
    bench execute frappe.core.doctype.user.user --args ['user_name']
  • Web:
    用户 -> API 访问 -> 生成密钥

请查看 Guides / Integrations / Rest Api / Token Based Authentication 了解如何在 API 请求中使用令牌。

注意:

  1. API 密钥无法重新生成。
  2. 只有具有系统管理员角色的用户才能生成密钥。

谷歌企业应用套件

Frappe 允许您使用 Google 的 Gsuite 文档作为模板,并从中生成一个新的 Gsuite 文档,该文档将放置在您选择的文件夹中。变量可以使用标准的 Jinja2 格式填充到 Gsuite 文档的正文和名称中。生成后,Gsuite 文档将作为附件与 DocType 保持关联。

Gsuite 文档是通过调用任何 DocType 的“附加文件”功能生成的。

此功能的一个常见用例是根据客户/员工/供应商数据填充合同。

1. 启用与 Google Gsuite 的集成

1.1 发布 Google Apps 脚本

如果您将使用默认脚本,可以直接跳到 1.2 节

  1. 前往 https://script.google.com
  2. 创建一个新项目。点击 文件 > 新建 > 项目
  3. 将 工作台 > 探索 > 集成 > GSuite 设置 > Google Apps 脚本 中的代码复制到剪贴板,然后粘贴到 script.google.com 中空的 Code.gs 文件中
  4. 保存项目。点击 文件 > 保存 > 输入新项目名称
  5. 部署应用。点击 发布 > 部署为网络应用
  6. 将“当前网络应用 URL”复制到 工作台 > 探索 > 集成 > GSuite 设置 > 脚本 URL
  7. 点击确定,但不要关闭脚本

1.2

1.2 获取 Google 访问权限

  1. 前往您的 Google 项目控制台并选择您的项目或创建一个新项目。https://console.developers.google.com
  2. 在库中点击 Google Drive API 并 启用
  3. 点击 凭据 > 创建凭据 > OAuth 客户端 ID
  4. 填写表单:
  • 网络应用
  • 授权重定向 URI 为 http://{{ yoursite }}/?cmd=frappe.integrations.doctype.gsuite_settings.gsuite_settings.gsuite_callback
  1. 将客户端 ID 和客户端密钥复制到 工作台 > 探索 > 集成 > GSuite 设置 > 客户端 ID 和客户端密钥
  2. 保存 GSuite 设置

1.3 测试脚本

  1. 点击 允许 GSuite 访问,您将被重定向以选择用户并授予访问权限。如果出现任何错误,请验证您使用的授权重定向 URI 是否正确。
    您可以在错误页面 URL 的最后部分找到 Gsuite 重定向的完整 URI。检查协议 http:// 或 https:// 是否与您使用的一致。
  2. 点击 运行脚本测试。系统应提示您授予权限。

2. GSuite 模板

2.1 将 Google 文档用作模板

  1. 创建一个新文档或使用您已有的文档。根据需要设置变量。变量使用 {{VARIABLE}} 定义,其中 变量 是您 DocType 的字段

例如,
如果此文档将用于员工,且 DocType 具有 name 字段,则您可以在 Google 文档中使用它,如 {{name}}

  1. 从文档的 URL 中获取该文档的 ID。
    例如:在此文档 URL https://docs.google.com/document/d/1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso/edit 中,文档 ID 为 1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso

  2. 获取您希望放置生成文档的文件夹的 ID。(如果您希望将生成的文档放置在 Google Drive 根目录中,可以跳过此步骤。)

例如:在此文件夹 URL https://drive.google.com/drive/u/0/folders/0BxmFzZZUHbgyQzVJNzY5eG5jbmc 中,文件夹 ID 为 0BxmFzZZUHbgyQzVJNzY5eG5jbmc

2.2 将模板关联到 DocType

  1. 前往 工作台 > 探索 > 集成 > GSuite 模板 > 新建
  2. 填写表单:
  • 模板名称(例如:Employee Contract)
  • 关联 DocType(例如:Employee)
  • 模板 ID 是您从 Google 文档中获取的文档 ID(例如:1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso)
  • 文档名称是新文件的名称。您可以使用 DocType 中的字段(例如:Employee Contract of {name})
  • 目标 ID 是您从此模板创建的文件的文件夹 ID。(例如:0BxmFzZZUHbgyQzVJNzY5eG5jbmc)

3. 创建文档

  1. 前往一个您已有模板的文档(例如:员工 > 张三)
  2. 点击 附加文件
  3. 在 GSuite 文档 部分选择模板,然后点击 附加
  4. 您应该会看到生成的文档已创建并附加
  5. 点击附加的文档将在 Gsuite 中打开它