@apiagex/database 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/dist/admin-permission-repository.d.ts +4 -4
- package/dist/admin-permission-repository.d.ts.map +1 -1
- package/dist/admin-permission-repository.js +19 -17
- package/dist/api-token-repository.d.ts +5 -5
- package/dist/api-token-repository.d.ts.map +1 -1
- package/dist/api-token-repository.js +19 -20
- package/dist/database-adapter.type.d.ts +18 -0
- package/dist/database-adapter.type.d.ts.map +1 -0
- package/dist/database-adapter.type.js +1 -0
- package/dist/entry-query.d.ts +2 -2
- package/dist/entry-query.d.ts.map +1 -1
- package/dist/entry-query.js +8 -7
- package/dist/entry-repository.d.ts +6 -6
- package/dist/entry-repository.d.ts.map +1 -1
- package/dist/entry-repository.js +46 -56
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/mysql-adapter.d.ts +17 -0
- package/dist/mysql-adapter.d.ts.map +1 -0
- package/dist/mysql-adapter.js +63 -0
- package/dist/permission-repository.d.ts +4 -4
- package/dist/permission-repository.d.ts.map +1 -1
- package/dist/permission-repository.js +24 -38
- package/dist/postgres-adapter.d.ts +17 -0
- package/dist/postgres-adapter.d.ts.map +1 -0
- package/dist/postgres-adapter.js +61 -0
- package/dist/provider-migrations.d.ts +14 -0
- package/dist/provider-migrations.d.ts.map +1 -0
- package/dist/provider-migrations.js +198 -0
- package/dist/realtime-repository.d.ts +11 -11
- package/dist/realtime-repository.d.ts.map +1 -1
- package/dist/realtime-repository.js +37 -27
- package/dist/realtime-session-repository.d.ts +3 -3
- package/dist/realtime-session-repository.d.ts.map +1 -1
- package/dist/realtime-session-repository.js +11 -11
- package/dist/relation-helpers.d.ts +3 -3
- package/dist/relation-helpers.d.ts.map +1 -1
- package/dist/relation-helpers.js +4 -6
- package/dist/role-repository.d.ts +6 -6
- package/dist/role-repository.d.ts.map +1 -1
- package/dist/role-repository.js +17 -24
- package/dist/schema-repository.d.ts +7 -7
- package/dist/schema-repository.d.ts.map +1 -1
- package/dist/schema-repository.js +63 -88
- package/dist/sqlite-adapter.d.ts +15 -0
- package/dist/sqlite-adapter.d.ts.map +1 -0
- package/dist/sqlite-adapter.js +48 -0
- package/dist/user-repository.d.ts +8 -6
- package/dist/user-repository.d.ts.map +1 -1
- package/dist/user-repository.js +17 -27
- package/dist/webhook-repository.d.ts +13 -13
- package/dist/webhook-repository.d.ts.map +1 -1
- package/dist/webhook-repository.js +47 -43
- package/package.json +6 -3
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApiagexDatabase } from "./database-adapter.type.js";
|
|
2
2
|
import type { EnqueueWebhookEventInput, RecordWebhookDeliveryInput, WebhookDeliveryRecord, WebhookDraft, WebhookEventRecord, WebhookEventStatus, WebhookRecord, WebhookSecretRecord } from "./webhook-repository.type.js";
|
|
3
|
-
export declare function createWebhook(db:
|
|
4
|
-
export declare function updateWebhook(db:
|
|
5
|
-
export declare function listWebhooks(db:
|
|
6
|
-
export declare function deleteWebhook(db:
|
|
7
|
-
export declare function enqueueWebhookEvent(db:
|
|
8
|
-
export declare function listPendingWebhookEvents(db:
|
|
9
|
-
export declare function listMatchingWebhooks(db:
|
|
10
|
-
export declare function listWebhookDeliveries(db:
|
|
11
|
-
export declare function countWebhookDeliveryAttempts(db:
|
|
12
|
-
export declare function hasSuccessfulWebhookDelivery(db:
|
|
13
|
-
export declare function recordWebhookDelivery(db:
|
|
14
|
-
export declare function updateWebhookEventStatus(db:
|
|
3
|
+
export declare function createWebhook(db: ApiagexDatabase, input: WebhookDraft): Promise<WebhookRecord>;
|
|
4
|
+
export declare function updateWebhook(db: ApiagexDatabase, webhookId: string, input: WebhookDraft): Promise<WebhookRecord>;
|
|
5
|
+
export declare function listWebhooks(db: ApiagexDatabase): Promise<WebhookRecord[]>;
|
|
6
|
+
export declare function deleteWebhook(db: ApiagexDatabase, webhookId: string): Promise<boolean>;
|
|
7
|
+
export declare function enqueueWebhookEvent(db: ApiagexDatabase, input: EnqueueWebhookEventInput): Promise<WebhookEventRecord>;
|
|
8
|
+
export declare function listPendingWebhookEvents(db: ApiagexDatabase, now?: string, limit?: number): Promise<WebhookEventRecord[]>;
|
|
9
|
+
export declare function listMatchingWebhooks(db: ApiagexDatabase, event: WebhookEventRecord): Promise<WebhookSecretRecord[]>;
|
|
10
|
+
export declare function listWebhookDeliveries(db: ApiagexDatabase, webhookId?: string): Promise<WebhookDeliveryRecord[]>;
|
|
11
|
+
export declare function countWebhookDeliveryAttempts(db: ApiagexDatabase, eventId: string, webhookId: string): Promise<number>;
|
|
12
|
+
export declare function hasSuccessfulWebhookDelivery(db: ApiagexDatabase, eventId: string, webhookId: string): Promise<boolean>;
|
|
13
|
+
export declare function recordWebhookDelivery(db: ApiagexDatabase, input: RecordWebhookDeliveryInput): Promise<WebhookDeliveryRecord>;
|
|
14
|
+
export declare function updateWebhookEventStatus(db: ApiagexDatabase, eventId: string, status: WebhookEventStatus, attempts: number, nextRetryAt: string | null): Promise<void>;
|
|
15
15
|
//# sourceMappingURL=webhook-repository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-repository.d.ts","sourceRoot":"","sources":["../src/webhook-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"webhook-repository.d.ts","sourceRoot":"","sources":["../src/webhook-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAGlB,aAAa,EACb,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AActC,wBAAsB,aAAa,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CASpG;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,eAAe,EACnB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,aAAa,CAAC,CAiBxB;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAGhF;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG5F;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAe7B;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,eAAe,EACnB,GAAG,SAA2B,EAC9B,KAAK,SAAK,GACT,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAK/B;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAKhC;AAED,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,eAAe,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAKlC;AAED,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,eAAe,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,eAAe,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,qBAAqB,CAAC,CAsBhC;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,eAAe,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { getSchemaById } from "./schema-repository.js";
|
|
3
3
|
const allowedEvents = new Set(["entry.created", "entry.updated", "entry.deleted"]);
|
|
4
|
-
export function createWebhook(db, input) {
|
|
5
|
-
const draft = normalizeWebhookDraft(db, input);
|
|
4
|
+
export async function createWebhook(db, input) {
|
|
5
|
+
const draft = await normalizeWebhookDraft(db, input);
|
|
6
6
|
const id = randomUUID();
|
|
7
7
|
const now = new Date().toISOString();
|
|
8
|
-
db.prepare(`INSERT INTO webhooks (id, name, url, secret, events_json, schema_id, active, created_at, updated_at)
|
|
8
|
+
await db.prepare(`INSERT INTO webhooks (id, name, url, secret, events_json, schema_id, active, created_at, updated_at)
|
|
9
9
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, draft.name, draft.url, draft.secret, JSON.stringify(draft.events), draft.schemaId, draft.active ? 1 : 0, now, now);
|
|
10
|
-
return publicWebhook(requireWebhook(db, id));
|
|
10
|
+
return publicWebhook(await requireWebhook(db, id));
|
|
11
11
|
}
|
|
12
|
-
export function updateWebhook(db, webhookId, input) {
|
|
13
|
-
const current = requireWebhook(db, webhookId);
|
|
14
|
-
const draft = normalizeWebhookDraft(db, { ...input, secret: input.secret ?? current.secret });
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return publicWebhook(requireWebhook(db, webhookId));
|
|
12
|
+
export async function updateWebhook(db, webhookId, input) {
|
|
13
|
+
const current = await requireWebhook(db, webhookId);
|
|
14
|
+
const draft = await normalizeWebhookDraft(db, { ...input, secret: input.secret ?? current.secret });
|
|
15
|
+
await db.prepare(`UPDATE webhooks SET name = ?, url = ?, secret = ?, events_json = ?, schema_id = ?, active = ?, updated_at = ?
|
|
16
|
+
WHERE id = ?`).run(draft.name, draft.url, draft.secret, JSON.stringify(draft.events), draft.schemaId, draft.active ? 1 : 0, new Date().toISOString(), webhookId);
|
|
17
|
+
return publicWebhook(await requireWebhook(db, webhookId));
|
|
19
18
|
}
|
|
20
|
-
export function listWebhooks(db) {
|
|
21
|
-
const rows = db.prepare(webhookSelectSql("ORDER BY created_at DESC")).all();
|
|
19
|
+
export async function listWebhooks(db) {
|
|
20
|
+
const rows = await db.prepare(webhookSelectSql("ORDER BY created_at DESC")).all();
|
|
22
21
|
return rows.map(rowToWebhook).map(publicWebhook);
|
|
23
22
|
}
|
|
24
|
-
export function deleteWebhook(db, webhookId) {
|
|
25
|
-
const result = db.prepare("DELETE FROM webhooks WHERE id = ?").run(webhookId);
|
|
23
|
+
export async function deleteWebhook(db, webhookId) {
|
|
24
|
+
const result = await db.prepare("DELETE FROM webhooks WHERE id = ?").run(webhookId);
|
|
26
25
|
return result.changes > 0;
|
|
27
26
|
}
|
|
28
|
-
export function enqueueWebhookEvent(db, input) {
|
|
27
|
+
export async function enqueueWebhookEvent(db, input) {
|
|
29
28
|
const id = randomUUID();
|
|
30
29
|
const now = new Date().toISOString();
|
|
31
30
|
const payload = {
|
|
@@ -34,51 +33,56 @@ export function enqueueWebhookEvent(db, input) {
|
|
|
34
33
|
entry: input.entry,
|
|
35
34
|
occurredAt: now,
|
|
36
35
|
};
|
|
37
|
-
db.prepare(`INSERT INTO webhook_events
|
|
36
|
+
await db.prepare(`INSERT INTO webhook_events
|
|
38
37
|
(id, event_type, schema_id, schema_slug, entry_id, payload_json, status, attempts, next_retry_at, created_at, updated_at)
|
|
39
38
|
VALUES (?, ?, ?, ?, ?, ?, 'pending', 0, NULL, ?, ?)`).run(id, input.eventType, input.schemaId, input.schemaSlug, input.entry.id, JSON.stringify(payload), now, now);
|
|
40
39
|
return requireWebhookEvent(db, id);
|
|
41
40
|
}
|
|
42
|
-
export function listPendingWebhookEvents(db, now = new Date().toISOString(), limit = 20) {
|
|
43
|
-
const rows = db
|
|
41
|
+
export async function listPendingWebhookEvents(db, now = new Date().toISOString(), limit = 20) {
|
|
42
|
+
const rows = await db
|
|
43
|
+
.prepare(eventSelectSql("WHERE status = 'pending' AND (next_retry_at IS NULL OR next_retry_at <= ?) ORDER BY created_at ASC LIMIT ?"))
|
|
44
44
|
.all(now, limit);
|
|
45
45
|
return rows.map(rowToEvent);
|
|
46
46
|
}
|
|
47
|
-
export function listMatchingWebhooks(db, event) {
|
|
48
|
-
const rows = db
|
|
47
|
+
export async function listMatchingWebhooks(db, event) {
|
|
48
|
+
const rows = await db
|
|
49
|
+
.prepare(webhookSelectSql("WHERE active = 1 AND (schema_id IS NULL OR schema_id = ?) ORDER BY created_at ASC"))
|
|
49
50
|
.all(event.schemaId);
|
|
50
51
|
return rows.map(rowToWebhook).filter((webhook) => webhook.events.includes(event.eventType));
|
|
51
52
|
}
|
|
52
|
-
export function listWebhookDeliveries(db, webhookId) {
|
|
53
|
+
export async function listWebhookDeliveries(db, webhookId) {
|
|
53
54
|
const suffix = webhookId ? "WHERE webhook_id = ? ORDER BY created_at DESC" : "ORDER BY created_at DESC";
|
|
54
|
-
|
|
55
|
+
return webhookId
|
|
55
56
|
? db.prepare(deliverySelectSql(suffix)).all(webhookId)
|
|
56
57
|
: db.prepare(deliverySelectSql(suffix)).all();
|
|
57
|
-
return rows;
|
|
58
58
|
}
|
|
59
|
-
export function countWebhookDeliveryAttempts(db, eventId, webhookId) {
|
|
60
|
-
const row = db
|
|
59
|
+
export async function countWebhookDeliveryAttempts(db, eventId, webhookId) {
|
|
60
|
+
const row = await db
|
|
61
|
+
.prepare("SELECT COUNT(*) as count FROM webhook_deliveries WHERE event_id = ? AND webhook_id = ?")
|
|
61
62
|
.get(eventId, webhookId);
|
|
62
|
-
return row
|
|
63
|
+
return row?.count ?? 0;
|
|
63
64
|
}
|
|
64
|
-
export function hasSuccessfulWebhookDelivery(db, eventId, webhookId) {
|
|
65
|
-
const row = db
|
|
65
|
+
export async function hasSuccessfulWebhookDelivery(db, eventId, webhookId) {
|
|
66
|
+
const row = await db
|
|
67
|
+
.prepare("SELECT id FROM webhook_deliveries WHERE event_id = ? AND webhook_id = ? AND status = 'success' LIMIT 1")
|
|
66
68
|
.get(eventId, webhookId);
|
|
67
69
|
return Boolean(row);
|
|
68
70
|
}
|
|
69
|
-
export function recordWebhookDelivery(db, input) {
|
|
71
|
+
export async function recordWebhookDelivery(db, input) {
|
|
70
72
|
const id = input.id ?? randomUUID();
|
|
71
|
-
|
|
72
|
-
db.prepare(`INSERT INTO webhook_deliveries
|
|
73
|
+
await db.prepare(`INSERT INTO webhook_deliveries
|
|
73
74
|
(id, event_id, webhook_id, url, status, status_code, response_body, error, attempt, created_at, next_retry_at)
|
|
74
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.eventId, input.webhookId, input.url, input.status, input.statusCode ?? null, trim(input.responseBody), trim(input.error), input.attempt,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.eventId, input.webhookId, input.url, input.status, input.statusCode ?? null, trim(input.responseBody), trim(input.error), input.attempt, new Date().toISOString(), input.nextRetryAt ?? null);
|
|
76
|
+
const delivery = await db.prepare(deliverySelectSql("WHERE id = ?")).get(id);
|
|
77
|
+
if (!delivery)
|
|
78
|
+
throw new Error("WEBHOOK_DELIVERY_NOT_FOUND");
|
|
79
|
+
return delivery;
|
|
80
|
+
}
|
|
81
|
+
export async function updateWebhookEventStatus(db, eventId, status, attempts, nextRetryAt) {
|
|
82
|
+
await db.prepare("UPDATE webhook_events SET status = ?, attempts = ?, next_retry_at = ?, updated_at = ? WHERE id = ?")
|
|
79
83
|
.run(status, attempts, nextRetryAt, new Date().toISOString(), eventId);
|
|
80
84
|
}
|
|
81
|
-
function normalizeWebhookDraft(db, input) {
|
|
85
|
+
async function normalizeWebhookDraft(db, input) {
|
|
82
86
|
const events = [...new Set(input.events)];
|
|
83
87
|
if (!input.name.trim())
|
|
84
88
|
throw new Error("WEBHOOK_NAME_REQUIRED");
|
|
@@ -86,19 +90,19 @@ function normalizeWebhookDraft(db, input) {
|
|
|
86
90
|
throw new Error("WEBHOOK_URL_INVALID");
|
|
87
91
|
if (events.length === 0 || events.some((event) => !allowedEvents.has(event)))
|
|
88
92
|
throw new Error("WEBHOOK_EVENTS_INVALID");
|
|
89
|
-
if (input.schemaId && !getSchemaById(db, input.schemaId))
|
|
93
|
+
if (input.schemaId && !(await getSchemaById(db, input.schemaId)))
|
|
90
94
|
throw new Error("SCHEMA_NOT_FOUND");
|
|
91
95
|
const secret = input.secret?.trim() || randomUUID();
|
|
92
96
|
return { name: input.name.trim(), url: input.url.trim(), secret, events, schemaId: input.schemaId ?? null, active: input.active ?? true };
|
|
93
97
|
}
|
|
94
|
-
function requireWebhook(db, webhookId) {
|
|
95
|
-
const row = db.prepare(webhookSelectSql("WHERE id = ?")).get(webhookId);
|
|
98
|
+
async function requireWebhook(db, webhookId) {
|
|
99
|
+
const row = await db.prepare(webhookSelectSql("WHERE id = ?")).get(webhookId);
|
|
96
100
|
if (!row)
|
|
97
101
|
throw new Error("WEBHOOK_NOT_FOUND");
|
|
98
102
|
return rowToWebhook(row);
|
|
99
103
|
}
|
|
100
|
-
function requireWebhookEvent(db, eventId) {
|
|
101
|
-
const row = db.prepare(eventSelectSql("WHERE id = ?")).get(eventId);
|
|
104
|
+
async function requireWebhookEvent(db, eventId) {
|
|
105
|
+
const row = await db.prepare(eventSelectSql("WHERE id = ?")).get(eventId);
|
|
102
106
|
if (!row)
|
|
103
107
|
throw new Error("WEBHOOK_EVENT_NOT_FOUND");
|
|
104
108
|
return rowToEvent(row);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apiagex/database",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Kysely database adapter package for Apiagex CMS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,9 +35,12 @@
|
|
|
35
35
|
"test": "vitest run"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"better-sqlite3": "^12.9.0"
|
|
38
|
+
"better-sqlite3": "^12.9.0",
|
|
39
|
+
"mysql2": "^3.22.3",
|
|
40
|
+
"pg": "^8.20.0"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
|
-
"@types/better-sqlite3": "^7.6.13"
|
|
43
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
44
|
+
"@types/pg": "^8.20.0"
|
|
42
45
|
}
|
|
43
46
|
}
|