mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -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 = {
|
||||
tenantId: 'bar',
|
||||
id: 'foo',
|
||||
name: 'hook_name',
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ export const createHookLibrary = (queries: Queries) => {
|
|||
} satisfies Omit<HookEventPayload, 'hookId'>;
|
||||
|
||||
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`);
|
||||
const json: HookEventPayload = { hookId: id, ...payload };
|
||||
const logEntry = new LogEntry(`TriggerHook.${hookEvent}`);
|
||||
|
@ -90,7 +90,7 @@ export const createHookLibrary = (queries: Queries) => {
|
|||
.post(url, {
|
||||
headers: { 'user-agent': 'Logto (https://logto.io)', ...headers },
|
||||
json,
|
||||
retry: { limit: retries },
|
||||
retry: { limit: 3 },
|
||||
timeout: { request: 10_000 },
|
||||
})
|
||||
.then(async (response) => {
|
||||
|
|
|
@ -13,7 +13,6 @@ const createPayload = (event: HookEvent, url = 'not_work_url'): Partial<Hook> =>
|
|||
config: {
|
||||
url,
|
||||
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 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 */
|
||||
// type: 'web';
|
||||
/** 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(),
|
||||
/** Additional headers that attach to the request */
|
||||
headers: z.record(z.string()).optional(),
|
||||
retries: z.number().gte(0).lte(3),
|
||||
});
|
||||
|
||||
export type HookConfig = z.infer<typeof hookConfigGuard>;
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
create table hooks (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
event varchar(128) /* @use HookEvent */ not null,
|
||||
config jsonb /* @use HookConfig */ not null,
|
||||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
);
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) 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,
|
||||
signing_key varchar(64) not null default '',
|
||||
enabled boolean not null default true,
|
||||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index hooks__id on hooks (tenant_id, id);
|
||||
|
||||
create index hooks__event on hooks (tenant_id, event);
|
||||
create index hooks__id on hooks (tenant_id, id);
|
||||
|
|
Loading…
Reference in a new issue