扩展入口点
钩子的入口点是扩展包的 src/ 文件夹中的 index 文件。 它导出一个注册函数来注册一个或多个事件监听器。
入口点示例:
export default ({ filter, action }) => {
filter('items.create', () => {
console.log('Creating Item!')
})
action('items.create', () => {
console.log('Item created!')
})
}
Events
您的挂钩可以触发各种不同的事件。 事件由其类型和名称定义。 有五种事件类型可供选择:
如果您希望钩子在事件发生前触发,请使用过滤器钩子。 当您希望挂钩在事件发生后触发时,请使用动作挂钩。
Filter
过滤器挂钩在事件被触发之前作用于事件的有效负载。 它们允许您检查、修改或取消事件。
下面是通过抛出标准 Directus 异常来取消“创建”事件的示例。
export default ({ filter }, { exceptions }) => {
const { InvalidPayloadException } = exceptions
filter('items.create', async (input) => {
if (LOGIC_TO_CANCEL_EVENT)
throw new InvalidPayloadException(WHAT_IS_WRONG)
return input
})
}
过滤器寄存器函数接收两个参数:
- 事件名称
- 每当事件触发时执行的回调函数。
回调函数本身接收三个参数:
- 可修改的有效载荷
- 特定于事件的元对象
- 上下文对象
上下文对象具有以下属性:
database
— 当前数据库事务schema
— 当前使用的 API 模式accountability
— 当前用户的信息
表现
过滤器在不小心实施时会影响性能,因为它们以阻塞方式执行。 这尤其适用于触发“读取”事件的过滤器,其中单个请求可能导致大量数据库读取。
Action
动作挂钩在定义的事件之后执行并接收与事件相关的数据。 当您需要自动响应项目或服务器操作的 CRUD 事件时,使用操作挂钩。
动作寄存器函数接收两个参数:
- 事件名称
- 每当事件触发时执行的回调函数。
回调函数本身接收两个参数:
- 特定于事件的元对象
- 上下文对象
上下文对象具有以下属性:
database
— 当前数据库事务schema
— 当前使用的 API 模式accountability
— 关于当前用户的信息
Init
Init 挂钩在 Directus 生命周期内的定义点执行。 使用 init 挂钩对象将逻辑注入内部服务。
初始化寄存器函数接收两个参数:
- 事件名称
- 每当事件触发时执行的回调函数。
回调函数本身接收一个参数:
- 特定于事件的元对象
Schedule
计划挂钩在特定时间点执行,而不是在 Directus 执行特定操作时执行。 这是通过 node-cron
支持的。
要设置计划事件,请提供一个 cron 语句作为“schedule()”函数的第一个参数。 例如 schedule('15 14 1 * *', <...>)
(在第 1 天的 14:15)或 schedule('5 4 * * sun', <...> )
(周日 04:05)。
下面是一个注册调度挂钩的例子。
import axios from 'axios'
export default ({ schedule }) => {
schedule('*/15 * * * *', async () => {
await axios.post('http://example.com/webhook', { message: 'Another 15 minutes passed...' })
})
}
Embed
将自定义 JavaScript 或 CSS 注入数据洞察中的“”和“”标签。
嵌入寄存器函数接收两个参数:
- 嵌入的位置,
head
或body
。 - 要嵌入的值,可以是字符串或返回字符串的函数。
下面是注册嵌入挂钩的示例。
export default ({ embed }, { env }) => {
// Google Tag Manager Example
embed(
'head',
() => `<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${env.GTM_ID}');</script>
<!-- End Google Tag Manager -->`
)
// Sentry Example
embed(
'head',
'<script src="https://browser.sentry-cdn.com/7.21.1/bundle.min.js" integrity="sha384-xOL2QebDu7YNMtC6jW2i5RpQ5RcWOyQMTwrWBiEDezpjjXM7mXhYGz3vze77V91Q" crossorigin="anonymous"></script>'
)
embed(
'body',
() => `<script>
Sentry.init({
dsn: "${env.SENTRY_DSN}" // "https://examplePublicKey@o0.ingest.sentry.io/0",
release: "my-project-name@${env.npm_package_version}",
integrations: [new Sentry.BrowserTracing()],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
</script>`
)
}
Available Events
Filter Events
Name | Payload | Meta |
---|---|---|
request.not_found | false | request , response |
request.error | 请求错误 | -- |
database.error | 数据库错误 | client |
auth.login | 登录负载 | status , user , provider |
auth.jwt | 授权令牌 | status , user , provider , type |
authenticate | 空的责任对象 | req |
(<collection>.)items.query | 项目查询 | collection |
(<collection>.)items.read | 阅读的项目 | query , collection |
(<collection>.)items.create | 新项目 | collection |
(<collection>.)items.update | 更新的项目 | keys , collection |
(<collection>.)items.delete | 物品的钥匙 | collection |
<system-collection>.create | 新项目 | collection |
<system-collection>.update | 更新的项目 | keys , collection |
<system-collection>.delete | 物品的钥匙 | collection |
System Collections<system-collection>
应替换为系统集合名称之一 activity
, collections
, fields
, files
(except create/update), folders
, permissions
, presets
, relations
, revisions
, roles
, settings
, users
or webhooks
.
Action Events
Name | Meta |
---|---|
server.start | server |
server.stop | server |
response | request , response , ip , duration , finished |
auth.login | payload , status , user , provider |
files.upload | payload , key , collection |
(<collection>.)items.read | payload , query , collection |
(<collection>.)items.create | payload , key , collection |
(<collection>.)items.update | payload , keys , collection |
(<collection>.)items.delete | keys , collection |
(<collection>.)items.sort | collection , item , to |
<system-collection>.create | payload , key , collection |
<system-collection>.update | payload , keys , collection |
<system-collection>.delete | keys , collection |
System Collections<system-collection>
应替换为系统集合名称之一 activity
, collections
, fields
, files
(except create/update), folders
, permissions
, presets
, relations
, revisions
, roles
, settings
, users
or webhooks
.
Init Events
Name | Meta |
---|---|
cli.before | program |
cli.after | program |
app.before | app |
app.after | app |
routes.before | app |
routes.after | app |
routes.custom.before | app |
routes.custom.after | app |
middlewares.before | app |
middlewares.after | app |
Register Function
register 函数接收一个包含特定类型的 register 函数的对象作为第一个参数:
filter
— 监听过滤器事件action
— 监听动作事件init
— 监听初始化事件schedule
— 在特定时间点执行函数
第二个参数是具有以下属性的上下文对象:
services
— 所有 API 内部服务exceptions
— 可用于抛出“适当”错误的 API 异常对象database
— 连接到当前数据库的 Knex 实例getSchema
— 读取服务中使用的完整可用模式的异步函数env
— 解析的环境变量logger
— Pino 实例。emitter
— 事件发射器 实例,可用于触发其他扩展的自定义事件。
事件循环
使用发射器实现自定义事件时,请确保您永远不会直接或间接发射您的钩子当前正在处理的相同事件,因为这会导致无限循环!
Example: Sync with External
import axios from 'axios'
export default ({ filter }, { services, exceptions }) => {
const { MailService } = services
const { ServiceUnavailableException, ForbiddenException } = exceptions
// Sync with external recipes service, cancel creation on failure
filter('items.create', async (input, { collection }, { schema, database }) => {
if (collection !== 'recipes')
return input
const mailService = new MailService({ schema, knex: database })
try {
await axios.post('https://example.com/recipes', input)
await mailService.send({
to: 'person@example.com',
template: {
name: 'item-created',
data: {
collection,
},
},
})
}
catch (error) {
throw new ServiceUnavailableException(error)
}
input.syncedWithExample = true
return input
})
}