diff --git a/package.json b/package.json index 1ea1c26..5fb8a86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/noco", - "version": "0.0.4", + "version": "0.0.5", "description": "", "main": "index.js", "scripts": { @@ -21,7 +21,7 @@ ], "author": "abearxiong (https://www.xiongxiao.me)", "license": "MIT", - "packageManager": "pnpm@10.23.0", + "packageManager": "pnpm@10.24.0", "type": "module", "devDependencies": { "@kevisual/dts": "^0.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1962eb4..f5c388d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,6 @@ settings: importers: .: - dependencies: - form-data: - specifier: ^4.0.5 - version: 4.0.5 devDependencies: '@kevisual/dts': specifier: ^0.0.3 diff --git a/src/api.ts b/src/api.ts index 644cad5..f46d183 100644 --- a/src/api.ts +++ b/src/api.ts @@ -53,6 +53,7 @@ export class Query { fetchOptions.body = JSON.stringify(options.data); } return fetch(url.href, fetchOptions).then(async (response) => { + console.log('Response Status:', response.status, response); if (!response.ok) { return { code: response.status, message: response.statusText }; } diff --git a/src/meta/index.ts b/src/meta/index.ts index 7cc825f..6cce742 100644 --- a/src/meta/index.ts +++ b/src/meta/index.ts @@ -1,6 +1,7 @@ import { Query } from '../api.ts'; import { MetaBases, BaseOptions } from './base.ts'; import { MetaTables } from './tables.ts'; +import { Webhook } from './webhook.ts'; export type MetaOptions = { query: Query; }; @@ -13,12 +14,16 @@ export class Meta { query: Query; bases: MetaBases; tables: MetaTables; + webhooks: Webhook; constructor(options?: MetaOptions) { - this.query = options?.query; + this.query = options?.query; this.bases = new MetaBases({ query: this.query }); this.tables = new MetaTables({ query: this.query }); + this.webhooks = new Webhook({ query: this.query }); } } // 重新导出所有 meta 相关的类型和类 -export * from './base.ts'; \ No newline at end of file +export * from './base.ts'; + +export * from './webhook.ts'; \ No newline at end of file diff --git a/src/meta/webhook.ts b/src/meta/webhook.ts new file mode 100644 index 0000000..095f316 --- /dev/null +++ b/src/meta/webhook.ts @@ -0,0 +1,139 @@ +import { Query, ResponseList } from '../api.ts'; + +export type WebhookOptions = { + query: Query; +}; + +export class Webhook { + private query?: Query; + constructor(options?: WebhookOptions) { + this.query = options?.query; + } + listTableWebhooks(tableId: string): Promise> { + return this.query.makeRequest( + `/api/v2/meta/tables/${tableId}/hooks`, + { + method: 'GET', + } + ); + } + createTableWebhook( + tableId: string, + data: WebhookItemCore + ): Promise { + return this.query.makeRequest( + `/api/v2/meta/tables/${tableId}/hooks`, + { + method: 'POST', + data: { + version: 'v3', + event: 'manual', + ...data, + }, + } + ); + } + updateTableWebhook( + hookId: string, + data: WebhookItemCore + ): Promise { + return this.query.makeRequest( + `/api/v2/meta/hooks/${hookId}`, + { + method: 'PUT', + data, + } + ); + } + deleteTableWebhook(hookId: string): Promise { + return this.query.makeRequest( + `/api/v2/meta/hooks/${hookId}`, + { + method: 'DELETE', + } + ); + } +} +const operations = ['insert', 'update', 'delete', 'bulk_insert', 'bulk_update', 'bulk_delete']; +type WebhookOperation = typeof operations[number]; + +/** + * Webhook 项目类型定义 + */ +export type WebhookItem = { + id: string; + source_id: string; + base_id: string; + fk_model_id: string; + title: string; + description: string; + env: string; // 'all' + type: 'manual' | null; + operation: WebhookOperation[]; + async: boolean; + payload: boolean; + url: string; + headers: Record; + condition: boolean; + notification: string; + retries: number; + retry_interval: number; + timeout: number; + active: boolean; + created_at: string; + updated_at: string; + version: string; // 'v3' + trigger_field: boolean; + trigger_fields: any[]; +}; + +export type WebhookItemCore = { + id?: string; + title: string; + description?: string | null; + operation: WebhookOperation | WebhookOperation[]; + /** + * Webhook 触发的 json的 + * 不能为空对象 + * @example '{"type":"URL","include_user":false,"payload":{"method":"POST","body":"{{ json event }}","headers":[{"enabled":false,"name":"","value":""}],"parameters":[{"enabled":false,"name":"tableId","value":"mecdgojq151iwk9"}],"path":"https://kevision.xiongxiao.me/api/router","auth":""},"trigger_form":false}' + */ + notification: string | Record; + /** + * 内置必填 + */ + event: string; // after | 'manual', + /** + * 内置必填 + */ + version?: string; // 'v3' + + /** + * 是否启用 + */ + active?: boolean; +} +export type WebhookNotification = { + type: 'URL' | 'Email' | 'DingTalk' | 'WeCom' | 'FeiShu'; + include_user: boolean; + payload: { + method: 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH'; + /** + * 请求体 + * {{ json event }} + */ + body: string; + headers: Array<{ + enabled: boolean; + name: string; + value: string; + }>; + parameters: Array<{ + enabled: boolean; + name: string; + value: string; + }>; + path: string; + auth: string; + }; + trigger_form: boolean; +} \ No newline at end of file diff --git a/test/common.ts b/test/common.ts index adca2c8..2c824e2 100644 --- a/test/common.ts +++ b/test/common.ts @@ -5,7 +5,7 @@ import util from 'node:util'; // # 签到表 // const table = 'mcby44q8zrayvn9' // 本地 -const table = 'mi89er1m951pb3g' +const table = 'mecdgojq151iwk9' export const nocoApi = new NocoApi({ baseURL: config.NOCODB_URL || 'http://localhost:8080', token: config.NOCODB_API_KEY || '', diff --git a/test/list-hook.ts b/test/list-hook.ts new file mode 100644 index 0000000..430f271 --- /dev/null +++ b/test/list-hook.ts @@ -0,0 +1,47 @@ +import { nocoApi } from "./common"; +import bun from 'bun' +import util from 'node:util' +const tableId = 'mecdgojq151iwk9'; // starred_repos + + +const res = await nocoApi.meta.webhooks.listTableWebhooks(tableId); + +console.log('表格 Webhook 列表:'); +console.log(util.inspect(res, { depth: null })); +// { +// id: 'hkc9lp4n3w7co4l6', +// source_id: 'bdva8kahx60lu45', +// base_id: 'pdzc3q50vwnng5r', +// fk_model_id: 'mecdgojq151iwk9', +// title: '更新列表', +// description: null, +// env: 'all', +// type: null, +// event: 'after', +// operation: [ 'insert', 'update', 'delete' ], +// async: false, +// payload: true, +// url: null, +// headers: null, +// condition: false, +// notification: '{"type":"URL","include_user":false,"payload":{"method":"POST","body":"{{ json event }}","headers":[{"enabled":false,"name":"","value":""}],"parameters":[{"enabled":false,"name":"tableId","value":"mecdgojq151iwk9"}],"path":"https://kevision.xiongxiao.me/api/router","auth":""},"trigger_form":false}', +// retries: 0, +// retry_interval: 60000, +// timeout: 60000, +// active: true, +// created_at: '2025-11-29 01:57:05+00:00', +// updated_at: '2025-11-29 02:05:31+00:00', +// version: 'v3', +// trigger_field: false, +// trigger_fields: [] +// } +const hookNotify = { "type": "URL", "include_user": false, "payload": { "method": "POST", "body": "{{ json event }}", "headers": [{ "enabled": false, "name": "", "value": "" }], "parameters": [{ "enabled": false, "name": "tableId", "value": "mecdgojq151iwk9" }], "path": "https://kevision.xiongxiao.me/api/router", "auth": "" }, "trigger_form": false } +const createHook = await nocoApi.meta.webhooks.createTableWebhook(tableId, { + title: '测试通过 API 创建的 Webhook2', + // operation: ['insert', 'update', 'delete'], + operation: ['insert', 'update', 'delete'], + notification: hookNotify, + event: 'manual', +}); +console.log('创建 Webhook 结果:'); +console.log(util.inspect(createHook, { depth: null })); \ No newline at end of file