mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
refactor(schemas): update hook schema (#3788)
This commit is contained in:
parent
1066e5c707
commit
8fc5b78def
6 changed files with 133 additions and 29 deletions
|
@ -21,8 +21,12 @@ const url = 'https://logto.gg';
|
||||||
const hook: Hook = {
|
const hook: Hook = {
|
||||||
tenantId: 'bar',
|
tenantId: 'bar',
|
||||||
id: 'foo',
|
id: 'foo',
|
||||||
|
name: 'hook_name',
|
||||||
event: HookEvent.PostSignIn,
|
event: HookEvent.PostSignIn,
|
||||||
config: { headers: { bar: 'baz' }, url, retries: 3 },
|
events: [HookEvent.PostSignIn],
|
||||||
|
signingKey: 'signing_key',
|
||||||
|
enabled: true,
|
||||||
|
config: { headers: { bar: 'baz' }, url },
|
||||||
createdAt: Date.now() / 1000,
|
createdAt: Date.now() / 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ export const createHookLibrary = (queries: Queries) => {
|
||||||
} satisfies Omit<HookEventPayload, 'hookId'>;
|
} satisfies Omit<HookEventPayload, 'hookId'>;
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
rows.map(async ({ config: { url, headers, retries }, id }) => {
|
rows.map(async ({ config: { url, headers }, id }) => {
|
||||||
consoleLog.info(`\tTriggering hook ${id} due to ${hookEvent} event`);
|
consoleLog.info(`\tTriggering hook ${id} due to ${hookEvent} event`);
|
||||||
const json: HookEventPayload = { hookId: id, ...payload };
|
const json: HookEventPayload = { hookId: id, ...payload };
|
||||||
const logEntry = new LogEntry(`TriggerHook.${hookEvent}`);
|
const logEntry = new LogEntry(`TriggerHook.${hookEvent}`);
|
||||||
|
@ -90,7 +90,7 @@ export const createHookLibrary = (queries: Queries) => {
|
||||||
.post(url, {
|
.post(url, {
|
||||||
headers: { 'user-agent': 'Logto (https://logto.io)', ...headers },
|
headers: { 'user-agent': 'Logto (https://logto.io)', ...headers },
|
||||||
json,
|
json,
|
||||||
retry: { limit: retries },
|
retry: { limit: 3 },
|
||||||
timeout: { request: 10_000 },
|
timeout: { request: 10_000 },
|
||||||
})
|
})
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
|
|
|
@ -13,7 +13,6 @@ const createPayload = (event: HookEvent, url = 'not_work_url'): Partial<Hook> =>
|
||||||
config: {
|
config: {
|
||||||
url,
|
url,
|
||||||
headers: { foo: 'bar' },
|
headers: { foo: 'bar' },
|
||||||
retries: 3,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
105
packages/schemas/alterations/next-1683292832-update-hooks.ts
Normal file
105
packages/schemas/alterations/next-1683292832-update-hooks.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import { generateStandardId } from '@logto/shared';
|
||||||
|
import { sql } from 'slonik';
|
||||||
|
|
||||||
|
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||||
|
|
||||||
|
enum HookEvent {
|
||||||
|
PostRegister = 'PostRegister',
|
||||||
|
PostSignIn = 'PostSignIn',
|
||||||
|
PostResetPassword = 'PostResetPassword',
|
||||||
|
}
|
||||||
|
|
||||||
|
type HookConfig = {
|
||||||
|
url: string;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
retries?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Hook = {
|
||||||
|
tenantId: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
event: HookEvent | null;
|
||||||
|
events: HookEvent[];
|
||||||
|
config: HookConfig;
|
||||||
|
signingKey: string;
|
||||||
|
enabled: boolean;
|
||||||
|
createdAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const alteration: AlterationScript = {
|
||||||
|
up: async (pool) => {
|
||||||
|
await pool.query(sql`
|
||||||
|
alter table hooks
|
||||||
|
add column name varchar(256) not null default '',
|
||||||
|
add column events jsonb not null default '[]'::jsonb,
|
||||||
|
add column signing_key varchar(64) not null default '',
|
||||||
|
add column enabled boolean not null default true,
|
||||||
|
alter column event drop not null;
|
||||||
|
drop index hooks__event;
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
down: async (pool) => {
|
||||||
|
await pool.query(sql`
|
||||||
|
delete from hooks where enabled = false;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const { rows: hooks } = await pool.query<Hook>(sql`
|
||||||
|
select * from hooks;
|
||||||
|
`);
|
||||||
|
|
||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
for (const { id, tenantId, events, config } of hooks) {
|
||||||
|
const { retries, ...rest } = config;
|
||||||
|
|
||||||
|
const updatedConfig = {
|
||||||
|
...rest,
|
||||||
|
retries: retries ?? 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (events.length === 0) {
|
||||||
|
await pool.query(sql`
|
||||||
|
update hooks
|
||||||
|
set config = ${JSON.stringify(updatedConfig)}
|
||||||
|
where id = ${id} and tenant_id = ${tenantId};
|
||||||
|
`);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [index, event] of events.entries()) {
|
||||||
|
if (index === 0) {
|
||||||
|
await pool.query(sql`
|
||||||
|
update hooks
|
||||||
|
set event = ${event},
|
||||||
|
config = ${JSON.stringify(updatedConfig)}
|
||||||
|
where id = ${id} and tenant_id = ${tenantId};
|
||||||
|
`);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new hook when there are multiple events
|
||||||
|
const hookId = generateStandardId();
|
||||||
|
|
||||||
|
await pool.query(sql`
|
||||||
|
insert into hooks (id, tenant_id, event, config)
|
||||||
|
values (${hookId}, ${tenantId}, ${event}, ${JSON.stringify(updatedConfig)});
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* eslint-enable no-await-in-loop */
|
||||||
|
|
||||||
|
await pool.query(sql`
|
||||||
|
alter table hooks
|
||||||
|
alter column event set not null,
|
||||||
|
drop column name,
|
||||||
|
drop column events,
|
||||||
|
drop column signing_key,
|
||||||
|
drop column enabled;
|
||||||
|
create index hooks__event on hooks (tenant_id, event);
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default alteration;
|
|
@ -200,23 +200,17 @@ export enum HookEvent {
|
||||||
|
|
||||||
export const hookEventGuard: z.ZodType<HookEvent> = z.nativeEnum(HookEvent);
|
export const hookEventGuard: z.ZodType<HookEvent> = z.nativeEnum(HookEvent);
|
||||||
|
|
||||||
export type HookConfig = {
|
export const hookEventsGuard = hookEventGuard.array();
|
||||||
|
|
||||||
|
export type HookEvents = z.infer<typeof hookEventsGuard>;
|
||||||
|
|
||||||
|
export const hookConfigGuard = z.object({
|
||||||
/** We don't need `type` since v1 only has web hook */
|
/** We don't need `type` since v1 only has web hook */
|
||||||
// type: 'web';
|
// type: 'web';
|
||||||
/** Method fixed to `POST` */
|
/** Method fixed to `POST` */
|
||||||
url: string;
|
|
||||||
/** Additional headers that attach to the request */
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
/**
|
|
||||||
* Retry times when hook response status >= 500.
|
|
||||||
*
|
|
||||||
* Must be less than or equal to `3`. Use `0` to disable retry.
|
|
||||||
**/
|
|
||||||
retries: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hookConfigGuard: z.ZodType<HookConfig> = z.object({
|
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
|
/** Additional headers that attach to the request */
|
||||||
headers: z.record(z.string()).optional(),
|
headers: z.record(z.string()).optional(),
|
||||||
retries: z.number().gte(0).lte(3),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type HookConfig = z.infer<typeof hookConfigGuard>;
|
||||||
|
|
|
@ -2,12 +2,14 @@ create table hooks (
|
||||||
tenant_id varchar(21) not null
|
tenant_id varchar(21) not null
|
||||||
references tenants (id) on update cascade on delete cascade,
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(21) not null,
|
id varchar(21) not null,
|
||||||
event varchar(128) /* @use HookEvent */ not null,
|
name varchar(256) not null default '',
|
||||||
|
event varchar(128) /* @use HookEvent */,
|
||||||
|
events jsonb /* @use HookEvents */ not null default '[]'::jsonb,
|
||||||
config jsonb /* @use HookConfig */ not null,
|
config jsonb /* @use HookConfig */ not null,
|
||||||
|
signing_key varchar(64) not null default '',
|
||||||
|
enabled boolean not null default true,
|
||||||
created_at timestamptz not null default(now()),
|
created_at timestamptz not null default(now()),
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create index hooks__id on hooks (tenant_id, id);
|
create index hooks__id on hooks (tenant_id, id);
|
||||||
|
|
||||||
create index hooks__event on hooks (tenant_id, event);
|
|
||||||
|
|
Loading…
Reference in a new issue